Skip to content
Snippets Groups Projects
QGCPX4VehicleConfig.cc 78.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • // On Windows (for VS2010) stdint.h contains the limits normally contained in limits.h
    // It also needs the __STDC_LIMIT_MACROS macro defined in order to include them (done
    // in qgroundcontrol.pri).
    #ifdef WIN32
    #include <stdint.h>
    #else
    #include <limits.h>
    #endif
    
    #include <QTimer>
    #include <QDir>
    #include <QXmlStreamReader>
    #include <QMessageBox>
    
    
    #include "QGCPX4VehicleConfig.h"
    
    #include "QGC.h"
    #include "QGCToolWidget.h"
    
    #include "UASManager.h"
    
    #include "LinkManager.h"
    
    #include "UASParameterCommsMgr.h"
    
    #include "ui_QGCPX4VehicleConfig.h"
    
    #include "px4_configuration/QGCPX4AirframeConfig.h"
    
    #include "px4_configuration/QGCPX4SensorCalibration.h"
    
    #include <dialog_bare.h>
    
    #define WIDGET_INDEX_FIRMWARE 0
    #define WIDGET_INDEX_RC 1
    #define WIDGET_INDEX_SENSOR_CAL 2
    #define WIDGET_INDEX_AIRFRAME_CONFIG 3
    #define WIDGET_INDEX_GENERAL_CONFIG 4
    #define WIDGET_INDEX_ADV_CONFIG 5
    
    
    #define MIN_PWM_VAL 800
    #define MAX_PWM_VAL 2200
    
    
    QGCPX4VehicleConfig::QGCPX4VehicleConfig(QWidget *parent) :
        QWidget(parent),
        mav(NULL),
        chanCount(0),
    
        channelWanted(-1),
    
        channelReverseStateWanted(-1),
    
        rcRoll(0.0f),
        rcPitch(0.0f),
        rcYaw(0.0f),
        rcThrottle(0.0f),
        rcMode(0.0f),
    
        rcAssist(0.0f),
        rcMission(0.0f),
        rcReturn(0.0f),
        rcFlaps(0.0f),
    
        rcAux1(0.0f),
        rcAux2(0.0f),
    
        dataModelChanged(true),
    
        calibrationEnabled(false),
    
        #ifdef QUPGRADE_SUPPORT
        firmwareDialog(NULL),
        #endif
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        planeBack(":/files/images/px4/rc/cessna_back.png"),
        planeSide(":/files/images/px4/rc/cessna_side.png"),
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        px4SensorCalibration(NULL),
    
        ui(new Ui::QGCPX4VehicleConfig)
    {
        doneLoadingConfig = false;
    
    
        channelNames << "Roll / Aileron";
        channelNames << "Pitch / Elevator";
        channelNames << "Yaw / Rudder";
        channelNames << "Throttle";
    
        channelNames << "Main Mode Switch";
        channelNames << "Assist Switch";
        channelNames << "Mission Switch";
        channelNames << "Return Switch";
        channelNames << "Flaps";
        channelNames << "Aux1";
    
        channelNames << "Aux2";
        channelNames << "Aux3";
        channelNames << "Aux4";
        channelNames << "Aux5";
        channelNames << "Aux6";
    
        channelNames << "Aux7";
        channelNames << "Aux8";
    
        setObjectName("QGC_VEHICLECONFIG");
        ui->setupUi(this);
    
    
        ui->advancedMenuButton->setEnabled(false);
        ui->airframeMenuButton->setEnabled(false);
        ui->sensorMenuButton->setEnabled(false);
        ui->rcMenuButton->setEnabled(false);
    
        ui->generalMenuButton->hide();
    
        px4AirframeConfig = new QGCPX4AirframeConfig(this);
        ui->airframeLayout->addWidget(px4AirframeConfig);
    
    
        px4SensorCalibration = new QGCPX4SensorCalibration(this);
        ui->sensorLayout->addWidget(px4SensorCalibration);
    
    
    #ifdef QUPGRADE_SUPPORT
        firmwareDialog = new DialogBare(this);
        ui->firmwareLayout->addWidget(firmwareDialog);
    
    
        connect(firmwareDialog, SIGNAL(connectLinks()), LinkManager::instance(), SLOT(connectAll()));
        connect(firmwareDialog, SIGNAL(disconnectLinks()), LinkManager::instance(), SLOT(disconnectAll()));
    
    #else
    #error Please check out QUpgrade from http://github.com/LorenzMeier/qupgrade/ into the QGroundControl folder.
    
        QLabel* label = new QLabel(this);
        label->setText("THIS VERSION OF QGROUNDCONTROL WAS BUILT WITHOUT QUPGRADE. To enable firmware upload support, checkout QUpgrade WITHIN the QGroundControl folder");
        ui->firmwareLayout->addWidget(label);
    #endif
    
    
        ui->rollWidget->setOrientation(Qt::Horizontal);
        ui->rollWidget->setName("Roll");
        ui->yawWidget->setOrientation(Qt::Horizontal);
        ui->yawWidget->setName("Yaw");
        ui->pitchWidget->setName("Pitch");
        ui->throttleWidget->setName("Throttle");
        ui->radio5Widget->setOrientation(Qt::Horizontal);
        ui->radio5Widget->setName("Radio 5");
        ui->radio6Widget->setOrientation(Qt::Horizontal);
        ui->radio6Widget->setName("Radio 6");
        ui->radio7Widget->setOrientation(Qt::Horizontal);
        ui->radio7Widget->setName("Radio 7");
        ui->radio8Widget->setOrientation(Qt::Horizontal);
        ui->radio8Widget->setName("Radio 8");
    
        ui->radio9Widget->setOrientation(Qt::Horizontal);
        ui->radio9Widget->setName("Radio 9");
        ui->radio10Widget->setOrientation(Qt::Horizontal);
        ui->radio10Widget->setName("Radio 10");
        ui->radio11Widget->setOrientation(Qt::Horizontal);
        ui->radio11Widget->setName("Radio 11");
        ui->radio12Widget->setOrientation(Qt::Horizontal);
        ui->radio12Widget->setName("Radio 12");
        ui->radio13Widget->setOrientation(Qt::Horizontal);
        ui->radio13Widget->setName("Radio 13");
        ui->radio14Widget->setOrientation(Qt::Horizontal);
        ui->radio14Widget->setName("Radio 14");
        ui->radio15Widget->setOrientation(Qt::Horizontal);
        ui->radio15Widget->setName("Radio 15");
        ui->radio16Widget->setOrientation(Qt::Horizontal);
        ui->radio16Widget->setName("Radio 16");
        ui->radio17Widget->setOrientation(Qt::Horizontal);
        ui->radio17Widget->setName("Radio 17");
        ui->radio18Widget->setOrientation(Qt::Horizontal);
        ui->radio18Widget->setName("Radio 18");
    
        connect(ui->rcMenuButton,SIGNAL(clicked()),
                this,SLOT(rcMenuButtonClicked()));
        connect(ui->sensorMenuButton,SIGNAL(clicked()),
                this,SLOT(sensorMenuButtonClicked()));
        connect(ui->generalMenuButton,SIGNAL(clicked()),
                this,SLOT(generalMenuButtonClicked()));
        connect(ui->advancedMenuButton,SIGNAL(clicked()),
                this,SLOT(advancedMenuButtonClicked()));
        connect(ui->airframeMenuButton, SIGNAL(clicked()),
                this, SLOT(airframeMenuButtonClicked()));
        connect(ui->firmwareMenuButton, SIGNAL(clicked()),
                this, SLOT(firmwareMenuButtonClicked()));
    
        connect(ui->advancedCheckBox, SIGNAL(clicked(bool)), ui->advancedGroupBox, SLOT(setVisible(bool)));
        ui->advancedGroupBox->setVisible(false);
    
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        connect(ui->advancedCheckBox, SIGNAL(clicked(bool)), ui->graphicsView, SLOT(setHidden(bool)));
        ui->graphicsView->setScene(&scene);
    
        scene.addPixmap(planeBack);
        scene.addPixmap(planeSide);
    
        // XXX hide while WIP
        ui->graphicsView->hide();
    
    
        ui->rcCalibrationButton->setCheckable(true);
    
        ui->rcCalibrationButton->setEnabled(false);
    
        connect(ui->rcCalibrationButton, SIGNAL(clicked(bool)), this, SLOT(toggleCalibrationRC(bool)));
    
        ui->spektrumPairButton->setCheckable(false);
    
        ui->spektrumPairButton->setEnabled(false);
        connect(ui->spektrumPairButton, SIGNAL(clicked(bool)), this, SLOT(toggleSpektrumPairing(bool)));
    
        //TODO connect buttons here to save/clear actions?
    
        UASInterface* tmpMav = UASManager::instance()->getActiveUAS();
        if (tmpMav) {
            ui->pendingCommitsWidget->initWithUAS(tmpMav);
    
    tstellanova's avatar
    tstellanova committed
            ui->pendingCommitsWidget->update();
    
            setActiveUAS(tmpMav);
    
    tstellanova's avatar
    tstellanova committed
        }
    
        connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)),
                this, SLOT(setActiveUAS(UASInterface*)));
    
    
        // Connect RC mapping assignments
        connect(ui->rollSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setRollChan(int)));
        connect(ui->pitchSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPitchChan(int)));
        connect(ui->yawSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setYawChan(int)));
        connect(ui->throttleSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setThrottleChan(int)));
        connect(ui->modeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setModeChan(int)));
    
        connect(ui->assistSwSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAssistChan(int)));
        connect(ui->missionSwSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setMissionChan(int)));
        connect(ui->returnSwSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setReturnChan(int)));
        connect(ui->flapsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFlapsChan(int)));
    
        connect(ui->aux1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAux1Chan(int)));
        connect(ui->aux2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAux2Chan(int)));
    
        // Connect RC reverse assignments
        connect(ui->invertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setRollInverted(bool)));
        connect(ui->invertCheckBox_2, SIGNAL(clicked(bool)), this, SLOT(setPitchInverted(bool)));
        connect(ui->invertCheckBox_3, SIGNAL(clicked(bool)), this, SLOT(setYawInverted(bool)));
        connect(ui->invertCheckBox_4, SIGNAL(clicked(bool)), this, SLOT(setThrottleInverted(bool)));
        connect(ui->invertCheckBox_5, SIGNAL(clicked(bool)), this, SLOT(setModeInverted(bool)));
    
        connect(ui->assistSwInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAssistInverted(bool)));
        connect(ui->missionSwInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setMissionInverted(bool)));
        connect(ui->returnSwInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setReturnInverted(bool)));
        connect(ui->flapsInvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setFlapsInverted(bool)));
        connect(ui->aux1InvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAux1Inverted(bool)));
        connect(ui->aux2InvertCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAux2Inverted(bool)));
    
    
        connect(ui->rollButton, SIGNAL(clicked()), this, SLOT(identifyRollChannel()));
        connect(ui->pitchButton, SIGNAL(clicked()), this, SLOT(identifyPitchChannel()));
        connect(ui->yawButton, SIGNAL(clicked()), this, SLOT(identifyYawChannel()));
        connect(ui->throttleButton, SIGNAL(clicked()), this, SLOT(identifyThrottleChannel()));
        connect(ui->modeButton, SIGNAL(clicked()), this, SLOT(identifyModeChannel()));
    
        connect(ui->assistSwButton, SIGNAL(clicked()), this, SLOT(identifyAssistChannel()));
        connect(ui->missionSwButton, SIGNAL(clicked()), this, SLOT(identifyMissionChannel()));
        connect(ui->returnSwButton, SIGNAL(clicked()), this, SLOT(identifyReturnChannel()));
        connect(ui->flapsButton, SIGNAL(clicked()), this, SLOT(identifyFlapsChannel()));
    
        connect(ui->aux1Button, SIGNAL(clicked()), this, SLOT(identifyAux1Channel()));
        connect(ui->aux2Button, SIGNAL(clicked()), this, SLOT(identifyAux2Channel()));
    
        connect(ui->persistRcValuesButt,SIGNAL(clicked()), this, SLOT(writeCalibrationRC()));
    
        //set rc values to defaults
    
        for (unsigned int i = 0; i < chanMax; i++) {
    
            rcValue[i] = UINT16_MAX;
    
            rcValueReversed[i] = UINT16_MAX;
    
            rcMapping[i] = i;
    
            rcToFunctionMapping[i] = i;
    
            channelWantedList[i] = (float)UINT16_MAX;//TODO need to clean these up!
            rcMin[i] = 1000.0f;
            rcMax[i] = 2000.0f;
    
    
            // Mapping not established here, so can't pick values via mapping yet!
            rcMappedMin[i] = 1000;
            rcMappedMax[i] = 2000;
            rcMappedValue[i] = UINT16_MAX;
    
    Lorenz Meier's avatar
    Lorenz Meier committed
            rcMappedValueRev[i] = UINT16_MAX;
    
            rcMappedNormalizedValue[i] = 0.0f;
        }
    
        for (unsigned int i = chanMax -1; i < chanMappedMax; i++) {
            rcMapping[i] = -1;
            rcMappedMin[i] = 1000;
            rcMappedMax[i] = 2000;
            rcMappedValue[i] = UINT16_MAX;
    
    Lorenz Meier's avatar
    Lorenz Meier committed
            rcMappedValueRev[i] = UINT16_MAX;
    
            rcMappedNormalizedValue[i] = 0.0f;
    
        firmwareMenuButtonClicked();
    
    
        updateTimer.setInterval(150);
        connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateView()));
        updateTimer.start();
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->rcLabel->setText(tr("NO RADIO CONTROL INPUT DETECTED. PLEASE ENSURE THE TRANSMITTER IS ON."));
    
    
    QGCPX4VehicleConfig::~QGCPX4VehicleConfig()
    {
        delete ui;
    }
    
    
    void QGCPX4VehicleConfig::rcMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        //TODO eg ui->stackedWidget->findChild("rcConfig");
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_RC);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->tabTitleLabel->setText(tr("Radio Calibration"));
    
    }
    
    void QGCPX4VehicleConfig::sensorMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_SENSOR_CAL);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->tabTitleLabel->setText(tr("Sensor Calibration"));
    
    }
    
    void QGCPX4VehicleConfig::generalMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_GENERAL_CONFIG);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->tabTitleLabel->setText(tr("General Configuration Options"));
    
    }
    
    void QGCPX4VehicleConfig::advancedMenuButtonClicked()
    {
    
    tstellanova's avatar
    tstellanova committed
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_ADV_CONFIG);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->tabTitleLabel->setText(tr("Advanced Configuration Options"));
    
    void QGCPX4VehicleConfig::airframeMenuButtonClicked()
    {
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_AIRFRAME_CONFIG);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->tabTitleLabel->setText(tr("Airframe Configuration"));
    
    void QGCPX4VehicleConfig::firmwareMenuButtonClicked()
    {
        ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_FIRMWARE);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        ui->tabTitleLabel->setText(tr("Firmware Upgrade"));
    
    void QGCPX4VehicleConfig::identifyChannelMapping(int aert_index)
    {
    
        if (chanCount == 0 || aert_index < 0)
    
    
        int oldmapping = rcMapping[aert_index];
    
        for (unsigned i = 0; i < chanMax; i++) {
    
            if (i >= chanCount) {
                channelWantedList[i] = 0;
    
        msgBox.setText(tr("Detecting %1 ...\t\t").arg(channelNames[channelWanted]));
        msgBox.setInformativeText(tr("Please move stick, switch or potentiometer for this channel all the way up/down or left/right."));
        msgBox.setStandardButtons(QMessageBox::NoButton);
    
        skipActionButton = msgBox.addButton(tr("Skip"),QMessageBox::RejectRole);
        msgBox.exec();
        skipActionButton->hide();
        msgBox.removeButton(skipActionButton);
        if (msgBox.clickedButton() == skipActionButton ){
    
            channelWanted = -1;
            rcMapping[aert_index] = oldmapping;
        }
    
        skipActionButton = NULL;
    
    void QGCPX4VehicleConfig::toggleCalibrationRC(bool enabled)
    {
        if (enabled)
        {
            startCalibrationRC();
        }
        else
        {
            stopCalibrationRC();
        }
    }
    
    
    void QGCPX4VehicleConfig::toggleSpektrumPairing(bool enabled)
    {
    
        if (!ui->dsm2RadioButton->isChecked() && !ui->dsmxRadioButton && !ui->dsmx8RadioButton) {
    
    Lorenz Meier's avatar
    Lorenz Meier committed
            // Reject
            QMessageBox warnMsgBox;
            warnMsgBox.setText(tr("Please select a Spektrum Protocol Version"));
            warnMsgBox.setInformativeText(tr("Please select either DSM2 or DSM-X\ndirectly below the pair button,\nbased on the receiver type."));
            warnMsgBox.setStandardButtons(QMessageBox::Ok);
            warnMsgBox.setDefaultButton(QMessageBox::Ok);
            (void)warnMsgBox.exec();
    
        UASInterface* mav = UASManager::instance()->getActiveUAS();
    
        if (mav) {
            int rxSubType;
            if (ui->dsm2RadioButton->isChecked())
                rxSubType = 0;
            else if (ui->dsmxRadioButton->isChecked())
                rxSubType = 1;
            else // if (ui->dsmx8RadioButton->isChecked())
                rxSubType = 2;
            mav->pairRX(0, rxSubType);
        }
    
    void QGCPX4VehicleConfig::setTrimPositions()
    {
    
        int rollMap = rcMapping[0];
        int pitchMap = rcMapping[1];
        int yawMap = rcMapping[2];
        int throttleMap = rcMapping[3];
    
    
        // Reset all trims, as some might not be touched
        for (unsigned i = 0; i < chanCount; i++) {
            rcTrim[i] = 1500;
        }
    
        // Set trim to min if stick is close to min
    
        if (abs(rcValue[throttleMap] - rcMin[throttleMap]) < 100) {
            rcTrim[throttleMap] = rcMin[throttleMap];   // throttle
    
        }
        // Set trim to max if stick is close to max
    
        else if (abs(rcValue[throttleMap] - rcMax[throttleMap]) < 100) {
            rcTrim[throttleMap] = rcMax[throttleMap];   // throttle
    
            QMessageBox warnMsgBox;
            warnMsgBox.setText(tr("Throttle Stick Trim Position Invalid"));
            warnMsgBox.setInformativeText(tr("The throttle stick is not in the min position. Please set it to the minimum value"));
            warnMsgBox.setStandardButtons(QMessageBox::Ok);
            warnMsgBox.setDefaultButton(QMessageBox::Ok);
            (void)warnMsgBox.exec();
    
        }
    
        // Set trim for roll, pitch, yaw, throttle
    
        rcTrim[rollMap] = rcValue[rollMap]; // roll
        rcTrim[pitchMap] = rcValue[pitchMap]; // pitch
        rcTrim[yawMap] = rcValue[yawMap]; // yaw
    
    
        // Mode switch and optional modes, might not be mapped (== -1)
        for (unsigned i = 4; i < chanMappedMax; i++) {
            if (rcMapping[i] >= 0 && rcMapping[i] < (int)chanCount) {
                rcTrim[rcMapping[i]] = ((rcMax[rcMapping[i]] - rcMin[rcMapping[i]]) / 2.0f) + rcMin[rcMapping[i]];
            } else if (rcMapping[i] != -1){
                qDebug() << "RC MAPPING FAILED #" << i << "VAL:" << rcMapping[i];
            }
        }
    
    void QGCPX4VehicleConfig::detectChannelInversion(int aert_index)
    
        if (chanCount == 0 || aert_index < 0 || aert_index >= (int)chanMappedMax)
            return;
    
        bool oldstatus = rcRev[rcMapping[aert_index]];
        channelReverseStateWanted = aert_index;
    
        // Reset search list
        for (unsigned i = 0; i < chanMax; i++) {
            if (i >= chanCount) {
                channelReverseStateWantedList[i] = 0;
            }
            else {
                channelReverseStateWantedList[i] = rcValue[i];
            }
        }
    
        QStringList instructions;
        instructions << "ROLL: Move stick left";
    
        instructions << "PITCH: Move stick down";//matches the other sticks: should cause DECREASE in raw rc channel value when not reversed
    
        instructions << "YAW: Move stick left";
        instructions << "THROTTLE: Move stick down";
        instructions << "MODE SWITCH: Push down / towards you";
        instructions << "ASSISTED SWITCH: Push down / towards you";
        instructions << "MISSION SWITCH: Push down / towards you";
        instructions << "RETURN SWITCH: Push down / towards you";
        instructions << "FLAPS: Push down / towards you or turn dial to the leftmost position";
        instructions << "AUX1: Push down / towards you or turn dial to the leftmost position";
        instructions << "AUX2: Push down / towards you or turn dial to the leftmost position";
    
    
        msgBox.setText(tr("%1 Direction").arg(channelNames[channelReverseStateWanted]));
    
        msgBox.setInformativeText(tr("%2").arg((aert_index < instructions.length()) ? instructions[aert_index] : ""));
    
        msgBox.setStandardButtons(QMessageBox::NoButton);
    
        skipActionButton = msgBox.addButton(tr("Skip"),QMessageBox::RejectRole);
        msgBox.exec();
        skipActionButton->hide();
        msgBox.removeButton(skipActionButton);
        if (msgBox.clickedButton() == skipActionButton ){
            channelReverseStateWanted = -1;
            rcRev[rcMapping[aert_index]] = oldstatus;
        }
        skipActionButton = NULL;
    
    }
    
    void QGCPX4VehicleConfig::startCalibrationRC()
    {
    
        if (chanCount < 5) {
    
            QMessageBox::warning(0,
                                 tr("RC not Connected"),
                                 tr("Is the RC receiver connected and transmitter turned on? Detected %1 radio channels. To operate PX4, you need at least 5 channels. ").arg(chanCount));
    
    Lorenz Meier's avatar
    Lorenz Meier committed
            ui->rcCalibrationButton->setChecked(false);
    
        // reset all channel mappings above Ch 5 to invalid/unused value before starting calibration
        for (unsigned int j= 5; j < chanMappedMax; j++) {
            rcMapping[j] = -1;
        }
    
    
    
        QMessageBox::warning(0,tr("Safety Warning"),
    
                             tr("Starting RC calibration.\n\nEnsure that motor power is disconnected, all props are removed, RC transmitter and receiver are powered and connected.\n\nReset transmitter trims to center, then click OK to continue"));
    
        //go ahead and try to map first 8 channels, now that user can skip channels
        for (int i = 0; i < 8; i++) {
    
        //QMessageBox::information(0,"Information","Additional channels have not been mapped, but can be mapped in the channel table below.");
        configEnabled = false;
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        QMessageBox::information(0, tr("Information"),tr("Click OK, then move all sticks to their extreme positions, watching the min/max values to ensure you get the most range from your controller. This includes all switches"));
        ui->rcCalibrationButton->setText(tr("Finish RC Calibration"));
    
        resetCalibrationRC();
        calibrationEnabled = true;
        ui->rollWidget->showMinMax();
        ui->pitchWidget->showMinMax();
        ui->yawWidget->showMinMax();
        ui->throttleWidget->showMinMax();
        ui->radio5Widget->showMinMax();
        ui->radio6Widget->showMinMax();
        ui->radio7Widget->showMinMax();
        ui->radio8Widget->showMinMax();
    
        ui->radio9Widget->showMinMax();
        ui->radio10Widget->showMinMax();
        ui->radio11Widget->showMinMax();
        ui->radio12Widget->showMinMax();
        ui->radio13Widget->showMinMax();
        ui->radio14Widget->showMinMax();
        ui->radio15Widget->showMinMax();
        ui->radio16Widget->showMinMax();
        ui->radio17Widget->showMinMax();
        ui->radio18Widget->showMinMax();
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    
        QMessageBox::information(0, tr("Information"), tr("Please click on the <Finish RC Calibration> button once finished"));
    
    }
    
    void QGCPX4VehicleConfig::stopCalibrationRC()
    {
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        if (!calibrationEnabled)
            return;
    
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        // Try to identify inverted channels, but only for R/P/Y/T
        for (int i = 0; i < 4; i++) {
            detectChannelInversion(i);
        }
    
    
        QMessageBox::information(0,"Trims","Ensure all controls are centered and throttle is in the lowest position. Click OK to continue");
    
        calibrationEnabled = false;
    
        ui->rcCalibrationButton->setText(tr("Start RC Calibration"));
    
        ui->rollWidget->hideMinMax();
        ui->pitchWidget->hideMinMax();
        ui->yawWidget->hideMinMax();
        ui->throttleWidget->hideMinMax();
        ui->radio5Widget->hideMinMax();
        ui->radio6Widget->hideMinMax();
        ui->radio7Widget->hideMinMax();
        ui->radio8Widget->hideMinMax();
    
        ui->radio9Widget->hideMinMax();
        ui->radio10Widget->hideMinMax();
        ui->radio11Widget->hideMinMax();
        ui->radio12Widget->hideMinMax();
        ui->radio13Widget->hideMinMax();
        ui->radio14Widget->hideMinMax();
        ui->radio15Widget->hideMinMax();
        ui->radio16Widget->hideMinMax();
        ui->radio17Widget->hideMinMax();
        ui->radio18Widget->hideMinMax();
    
        for (unsigned int i = 0; i < chanCount; i++) {
            if (rcMin[i] > 1350) {
    
            if (rcMax[i] < 1650) {
    
        qDebug() << "SETTING TRIM";
        setTrimPositions();
    
    
        QString statusstr;
    
        statusstr = tr("This is the RC calibration information that will be sent to the autopilot if you click OK. To prevent transmission, click Cancel.");
    
        statusstr += tr("  Normal values range from 1000 to 2000, with disconnected channels reading 1000, 1500, 2000\n\n");
    
        statusstr += tr("Channel\tMin\tCenter\tMax\n");
        statusstr += "-------\t---\t------\t---\n";
    
        for (unsigned int i=0; i < chanCount; i++) {
    
            statusstr += QString::number(i) +"\t"+ QString::number(rcMin[i]) +"\t"+ QString::number(rcValue[i]) +"\t"+ QString::number(rcMax[i]) +"\n";
    
    
    
        msgBox.setText(tr("Confirm Calibration"));
        msgBox.setInformativeText(statusstr);
        msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);//allow user to cancel upload after reviewing values
        int msgBoxResult = msgBox.exec();
        if (QMessageBox::Cancel == msgBoxResult) {
            return;//don't commit these values
        }
    
        QMessageBox::information(0,"Uploading the RC Calibration","The configuration will now be uploaded and permanently stored.");
        writeCalibrationRC();
    
    }
    
    void QGCPX4VehicleConfig::loadQgcConfig(bool primary)
    {
        Q_UNUSED(primary);
        QDir autopilotdir(qApp->applicationDirPath() + "/files/" + mav->getAutopilotTypeName().toLower());
        QDir generaldir = QDir(autopilotdir.absolutePath() + "/general/widgets");
        QDir vehicledir = QDir(autopilotdir.absolutePath() + "/" + mav->getSystemTypeName().toLower() + "/widgets");
        if (!autopilotdir.exists("general"))
        {
         //TODO: Throw some kind of error here. There is no general configuration directory
            qWarning() << "Invalid general dir. no general configuration will be loaded.";
        }
        if (!autopilotdir.exists(mav->getAutopilotTypeName().toLower()))
        {
            //TODO: Throw an error here too, no autopilot specific configuration
            qWarning() << "Invalid vehicle dir, no vehicle specific configuration will be loaded.";
        }
    
        // Generate widgets for the General tab.
        QGCToolWidget *tool;
        bool left = true;
        foreach (QString file,generaldir.entryList(QDir::Files | QDir::NoDotAndDotDot))
        {
            if (file.toLower().endsWith(".qgw")) {
                QWidget* parent = left?ui->generalLeftContents:ui->generalRightContents;
    
                if (tool->loadSettings(generaldir.absoluteFilePath(file), false))
                {
                    toolWidgets.append(tool);
                    QGroupBox *box = new QGroupBox(parent);
                    box->setTitle(tool->objectName());
                    box->setLayout(new QVBoxLayout(box));
                    box->layout()->addWidget(tool);
                    if (left)
                    {
                        left = false;
                        ui->generalLeftLayout->addWidget(box);
                    }
                    else
                    {
                        left = true;
                        ui->generalRightLayout->addWidget(box);
                    }
                } else {
                    delete tool;
                }
            }
        }
    
    
         //TODO fix and reintegrate the Advanced parameter editor
    //    // Generate widgets for the Advanced tab.
    //    foreach (QString file,vehicledir.entryList(QDir::Files | QDir::NoDotAndDotDot))
    //    {
    //        if (file.toLower().endsWith(".qgw")) {
    //            QWidget* parent = ui->advanceColumnContents;
    //            tool = new QGCToolWidget("", parent);
    //            if (tool->loadSettings(vehicledir.absoluteFilePath(file), false))
    //            {
    //                toolWidgets.append(tool);
    //                QGroupBox *box = new QGroupBox(parent);
    //                box->setTitle(tool->objectName());
    //                box->setLayout(new QVBoxLayout(box));
    //                box->layout()->addWidget(tool);
    //                ui->advancedColumnLayout->addWidget(box);
    
    //            } else {
    //                delete tool;
    //            }
    //        }
    //    }
    
    
    
        // Load tabs for general configuration
        foreach (QString dir,generaldir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
        {
    
            QPushButton *button = new QPushButton(this);
    
            connect(button,SIGNAL(clicked()),this,SLOT(menuButtonClicked()));
            ui->navBarLayout->insertWidget(2,button);
            button->setMinimumHeight(75);
            button->setMinimumWidth(100);
            button->show();
            button->setText(dir);
            QWidget *tab = new QWidget(ui->stackedWidget);
            ui->stackedWidget->insertWidget(2,tab);
            buttonToWidgetMap[button] = tab;
            tab->setLayout(new QVBoxLayout());
            tab->show();
            QScrollArea *area = new QScrollArea(tab);
            tab->layout()->addWidget(area);
            QWidget *scrollArea = new QWidget(tab);
            scrollArea->setLayout(new QVBoxLayout(tab));
            area->setWidget(scrollArea);
            area->setWidgetResizable(true);
            area->show();
            scrollArea->show();
            QDir newdir = QDir(generaldir.absoluteFilePath(dir));
            foreach (QString file,newdir.entryList(QDir::Files| QDir::NoDotAndDotDot))
            {
                if (file.toLower().endsWith(".qgw")) {
    
                    if (tool->loadSettings(newdir.absoluteFilePath(file), false))
                    {
                        toolWidgets.append(tool);
                        //ui->sensorLayout->addWidget(tool);
                        QGroupBox *box = new QGroupBox(tab);
                        box->setTitle(tool->objectName());
                        box->setLayout(new QVBoxLayout(tab));
                        box->layout()->addWidget(tool);
                        scrollArea->layout()->addWidget(box);
                    } else {
                        delete tool;
                    }
                }
            }
        }
    
        // Load additional tabs for vehicle specific configuration
        foreach (QString dir,vehicledir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
        {
    
            QPushButton *button = new QPushButton(this);
    
            connect(button,SIGNAL(clicked()),this,SLOT(menuButtonClicked()));
            ui->navBarLayout->insertWidget(2,button);
    
            QWidget *tab = new QWidget(ui->stackedWidget);
            ui->stackedWidget->insertWidget(2,tab);
            buttonToWidgetMap[button] = tab;
    
            button->setMinimumHeight(75);
            button->setMinimumWidth(100);
            button->show();
            button->setText(dir);
            tab->setLayout(new QVBoxLayout());
            tab->show();
            QScrollArea *area = new QScrollArea(tab);
            tab->layout()->addWidget(area);
            QWidget *scrollArea = new QWidget(tab);
            scrollArea->setLayout(new QVBoxLayout(tab));
            area->setWidget(scrollArea);
            area->setWidgetResizable(true);
            area->show();
            scrollArea->show();
    
            QDir newdir = QDir(vehicledir.absoluteFilePath(dir));
            foreach (QString file,newdir.entryList(QDir::Files| QDir::NoDotAndDotDot))
            {
                if (file.toLower().endsWith(".qgw")) {
    
                    tool->addUAS(mav);
                    if (tool->loadSettings(newdir.absoluteFilePath(file), false))
                    {
                        toolWidgets.append(tool);
                        //ui->sensorLayout->addWidget(tool);
                        QGroupBox *box = new QGroupBox(tab);
                        box->setTitle(tool->objectName());
                        box->setLayout(new QVBoxLayout(box));
                        box->layout()->addWidget(tool);
                        scrollArea->layout()->addWidget(box);
                        box->show();
                        //gbox->layout()->addWidget(box);
                    } else {
                        delete tool;
                    }
                }
            }
        }
    }
    void QGCPX4VehicleConfig::menuButtonClicked()
    {
        QPushButton *button = qobject_cast<QPushButton*>(sender());
        if (!button)
        {
            return;
        }
        if (buttonToWidgetMap.contains(button))
        {
            ui->stackedWidget->setCurrentWidget(buttonToWidgetMap[button]);
        }
    
    }
    
    void QGCPX4VehicleConfig::loadConfig()
    {
        QGCToolWidget* tool;
    
        QDir autopilotdir(qApp->applicationDirPath() + "/files/" + mav->getAutopilotTypeName().toLower());
        QDir generaldir = QDir(autopilotdir.absolutePath() + "/general/widgets");
        QDir vehicledir = QDir(autopilotdir.absolutePath() + "/" + mav->getSystemTypeName().toLower() + "/widgets");
        if (!autopilotdir.exists("general"))
        {
         //TODO: Throw some kind of error here. There is no general configuration directory
            qWarning() << "Invalid general dir. no general configuration will be loaded.";
        }
        if (!autopilotdir.exists(mav->getAutopilotTypeName().toLower()))
        {
            //TODO: Throw an error here too, no autopilot specific configuration
            qWarning() << "Invalid vehicle dir, no vehicle specific configuration will be loaded.";
        }
        qDebug() << autopilotdir.absolutePath();
        qDebug() << generaldir.absolutePath();
        qDebug() << vehicledir.absolutePath();
        QFile xmlfile(autopilotdir.absolutePath() + "/arduplane.pdef.xml");
        if (xmlfile.exists() && !xmlfile.open(QIODevice::ReadOnly))
        {
            loadQgcConfig(false);
            doneLoadingConfig = true;
            return;
        }
        loadQgcConfig(true);
    
        QXmlStreamReader xml(xmlfile.readAll());
        xmlfile.close();
    
        //TODO: Testing to ensure that incorrectly formatted XML won't break this.
        while (!xml.atEnd())
        {
            if (xml.isStartElement() && xml.name() == "paramfile")
            {
                xml.readNext();
                while ((xml.name() != "paramfile") && !xml.atEnd())
                {
                    QString valuetype = "";
                    if (xml.isStartElement() && (xml.name() == "vehicles" || xml.name() == "libraries")) //Enter into the vehicles loop
                    {
                        valuetype = xml.name().toString();
                        xml.readNext();
                        while ((xml.name() != valuetype) && !xml.atEnd())
                        {
                            if (xml.isStartElement() && xml.name() == "parameters") //This is a parameter block
                            {
                                QString parametersname = "";
                                if (xml.attributes().hasAttribute("name"))
                                {
                                        parametersname = xml.attributes().value("name").toString();
                                }
                                QVariantMap genset;
                                QVariantMap advset;
    
                                QString setname = parametersname;
                                xml.readNext();
                                int genarraycount = 0;
                                int advarraycount = 0;
                                while ((xml.name() != "parameters") && !xml.atEnd())
                                {
                                    if (xml.isStartElement() && xml.name() == "param")
                                    {
                                        QString humanname = xml.attributes().value("humanName").toString();
                                        QString name = xml.attributes().value("name").toString();
                                        QString tab= xml.attributes().value("user").toString();
                                        if (tab == "Advanced")
                                        {
                                            advset["title"] = parametersname;
                                        }
                                        else
                                        {
                                            genset["title"] = parametersname;
                                        }
                                        if (name.contains(":"))
                                        {
                                            name = name.split(":")[1];
                                        }
                                        QString docs = xml.attributes().value("documentation").toString();
                                        paramTooltips[name] = name + " - " + docs;
    
                                        int type = -1; //Type of item
                                        QMap<QString,QString> fieldmap;
                                        xml.readNext();
                                        while ((xml.name() != "param") && !xml.atEnd())
                                        {
                                            if (xml.isStartElement() && xml.name() == "values")
                                            {
                                                type = 1; //1 is a combobox
                                                if (tab == "Advanced")
                                                {
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "TYPE"] = "COMBO";
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_DESCRIPTION"] = humanname;
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_PARAMID"] = name;
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COMPONENTID"] = 1;
                                                }
                                                else
                                                {
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "TYPE"] = "COMBO";
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_DESCRIPTION"] = humanname;
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_PARAMID"] = name;
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COMPONENTID"] = 1;
                                                }
                                                int paramcount = 0;
                                                xml.readNext();
                                                while ((xml.name() != "values") && !xml.atEnd())
                                                {
                                                    if (xml.isStartElement() && xml.name() == "value")
                                                    {
    
                                                        QString code = xml.attributes().value("code").toString();
                                                        QString arg = xml.readElementText();
                                                        if (tab == "Advanced")
                                                        {
                                                            advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_TEXT"] = arg;
                                                            advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_VAL"] = code.toInt();
                                                        }
                                                        else
                                                        {
                                                            genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_TEXT"] = arg;
                                                            genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_ITEM_" + QString::number(paramcount) + "_VAL"] = code.toInt();
                                                        }
                                                        paramcount++;
                                                    }
                                                    xml.readNext();
                                                }
                                                if (tab == "Advanced")
                                                {
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COUNT"] = paramcount;
                                                }
                                                else
                                                {
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_COMBOBOX_COUNT"] = paramcount;
                                                }
                                            }
                                            if (xml.isStartElement() && xml.name() == "field")
                                            {
                                                type = 2; //2 is a slider
                                                QString fieldtype = xml.attributes().value("name").toString();
                                                QString text = xml.readElementText();
                                                fieldmap[fieldtype] = text;
                                            }
                                            xml.readNext();
                                        }
                                        if (type == -1)
                                        {
                                            //Nothing inside! Assume it's a value, give it a default range.
                                            type = 2;
                                            QString fieldtype = "Range";
                                            QString text = "0 100"; //TODO: Determine a better way of figuring out default ranges.
                                            fieldmap[fieldtype] = text;
                                        }
                                        if (type == 2)
                                        {
                                            if (tab == "Advanced")
                                            {
                                                advset[setname + "\\" + QString::number(advarraycount) + "\\" + "TYPE"] = "SLIDER";
                                                advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_DESCRIPTION"] = humanname;
                                                advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_PARAMID"] = name;
                                                advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_COMPONENTID"] = 1;
                                            }
                                            else
                                            {
                                                genset[setname + "\\" + QString::number(genarraycount) + "\\" + "TYPE"] = "SLIDER";
                                                genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_DESCRIPTION"] = humanname;
                                                genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_PARAMID"] = name;
                                                genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_COMPONENTID"] = 1;
                                            }
                                            if (fieldmap.contains("Range"))
                                            {
                                                float min = 0;
                                                float max = 0;
                                                //Some range fields list "0-10" and some list "0 10". Handle both.
                                                if (fieldmap["Range"].split(" ").size() > 1)
                                                {
                                                    min = fieldmap["Range"].split(" ")[0].trimmed().toFloat();
                                                    max = fieldmap["Range"].split(" ")[1].trimmed().toFloat();
                                                }
                                                else if (fieldmap["Range"].split("-").size() > 1)
                                                {
                                                    min = fieldmap["Range"].split("-")[0].trimmed().toFloat();
                                                    max = fieldmap["Range"].split("-")[1].trimmed().toFloat();
                                                }
                                                if (tab == "Advanced")
                                                {
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_MIN"] = min;
                                                    advset[setname + "\\" + QString::number(advarraycount) + "\\" + "QGC_PARAM_SLIDER_MAX"] = max;
                                                }
                                                else
                                                {
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_MIN"] = min;
                                                    genset[setname + "\\" + QString::number(genarraycount) + "\\" + "QGC_PARAM_SLIDER_MAX"] = max;
                                                }
                                            }
                                        }
                                        if (tab == "Advanced")
                                        {
                                            advarraycount++;
                                            advset["count"] = advarraycount;
                                        }
                                        else
                                        {
                                            genarraycount++;
                                            genset["count"] = genarraycount;
                                        }
                                    }
                                    xml.readNext();
                                }
                                if (genarraycount > 0)
                                {
                                    QWidget* parent = this;
                                    if (valuetype == "vehicles")
                                    {
                                        parent = ui->generalLeftContents;
                                    }
                                    else if (valuetype == "libraries")
                                    {
                                        parent = ui->generalRightContents;
                                    }
    
                                    tool = new QGCToolWidget(parametersname, parametersname, parent);
    
                                    tool->addUAS(mav);
                                    tool->setSettings(genset);
                                    QList<QString> paramlist = tool->getParamList();
                                    for (int i=0;i<paramlist.size();i++)