Newer
Older
// 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 "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),
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),
configEnabled(false),
px4AirframeConfig(NULL),
#ifdef QUPGRADE_SUPPORT
firmwareDialog(NULL),
#endif
planeBack(":/files/images/px4/rc/cessna_back.png"),
planeSide(":/files/images/px4/rc/cessna_side.png"),
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);
Lorenz Meier
committed
ui->advancedMenuButton->setEnabled(false);
ui->airframeMenuButton->setEnabled(false);
ui->sensorMenuButton->setEnabled(false);
ui->rcMenuButton->setEnabled(false);
Lorenz Meier
committed
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()));
Lorenz Meier
committed
connect(ui->rcCopyTrimButton, SIGNAL(clicked()),
this, SLOT(copyAttitudeTrim()));
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);
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);
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()));
for (unsigned int i = 0; i < chanMax; i++) {
rcValueReversed[i] = UINT16_MAX;
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;
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;
rcMappedNormalizedValue[i] = 0.0f;
firmwareMenuButtonClicked();
updateTimer.setInterval(150);
connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateView()));
updateTimer.start();
ui->rcLabel->setText(tr("NO RADIO CONTROL INPUT DETECTED. PLEASE ENSURE THE TRANSMITTER IS ON."));
QGCPX4VehicleConfig::~QGCPX4VehicleConfig()
{
delete ui;
}
void QGCPX4VehicleConfig::rcMenuButtonClicked()
{
//TODO eg ui->stackedWidget->findChild("rcConfig");
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_RC);
}
void QGCPX4VehicleConfig::sensorMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_SENSOR_CAL);
}
void QGCPX4VehicleConfig::generalMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_GENERAL_CONFIG);
ui->tabTitleLabel->setText(tr("General Configuration Options"));
}
void QGCPX4VehicleConfig::advancedMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_ADV_CONFIG);
ui->tabTitleLabel->setText(tr("Advanced Configuration Options"));
void QGCPX4VehicleConfig::airframeMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_AIRFRAME_CONFIG);
ui->tabTitleLabel->setText(tr("Airframe Configuration"));
}
void QGCPX4VehicleConfig::firmwareMenuButtonClicked()
{
ui->stackedWidget->setCurrentIndex(WIDGET_INDEX_FIRMWARE);
void QGCPX4VehicleConfig::identifyChannelMapping(int aert_index)
{
if (chanCount == 0 || aert_index < 0)
return;
int oldmapping = rcMapping[aert_index];
channelWanted = aert_index;
for (unsigned i = 0; i < chanMax; i++) {
if (i >= chanCount) {
channelWantedList[i] = 0;
channelWantedList[i] = rcValue[i];
}
}
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)
{
Don Gagne
committed
Q_UNUSED(enabled);
if (!ui->dsm2RadioButton->isChecked() && !ui->dsmxRadioButton && !ui->dsmx8RadioButton) {
// 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);
}
}
Lorenz Meier
committed
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
void QGCPX4VehicleConfig::copyAttitudeTrim() {
if (configEnabled) {
QMessageBox warnMsgBox;
warnMsgBox.setText(tr("Attitude trim denied during RC calibration"));
warnMsgBox.setInformativeText(tr("Please end the RC calibration before doing attitude trim."));
warnMsgBox.setStandardButtons(QMessageBox::Ok);
warnMsgBox.setDefaultButton(QMessageBox::Ok);
(void)warnMsgBox.exec();
}
// Not aborted, but warn user
msgBox.setText(tr("Confirm Attitude Trim"));
msgBox.setInformativeText(tr("On clicking OK, the current Roll / Pitch / Yaw stick positions will be set as trim values in auto flight. Do NOT reset your trim values after this step."));
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);//allow user to cancel upload after reviewing values
int msgBoxResult = msgBox.exec();
if (QMessageBox::Cancel == msgBoxResult) {
return;
// do not execute
}
mav->startRadioControlCalibration(2);
QGC::SLEEP::msleep(100);
mav->endRadioControlCalibration();
}
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;
}
Lorenz Meier
committed
bool throttleDone = false;
while (!throttleDone) {
// Set trim to min if stick is close to min
if (abs(rcValue[throttleMap] - rcMin[throttleMap]) < 100) {
rcTrim[throttleMap] = rcMin[throttleMap]; // throttle
throttleDone = true;
}
// Set trim to max if stick is close to max
else if (abs(rcValue[throttleMap] - rcMax[throttleMap]) < 100) {
rcTrim[throttleMap] = rcMax[throttleMap]; // throttle
throttleDone = true;
}
else
{
// Reject
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 zero throttle position and then click OK."));
warnMsgBox.setStandardButtons(QMessageBox::Ok);
warnMsgBox.setDefaultButton(QMessageBox::Ok);
(void)warnMsgBox.exec();
// wait long enough to get some data
QGC::SLEEP::msleep(500);
}
}
// 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";
tstellanova
committed
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()
{
Lorenz Meier
committed
if (chanCount < 5 && !mav) {
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
committed
// XXX magic number: Set to 1 for radio input disable
mav->startRadioControlCalibration(1);
Lorenz Meier
committed
// 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;
}
configEnabled = true;
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++) {
identifyChannelMapping(i);
}
//QMessageBox::information(0,"Information","Additional channels have not been mapped, but can be mapped in the channel table below.");
configEnabled = false;
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
committed
msgBox.setText(tr("Information"));
msgBox.setInformativeText(tr("Please move the sticks to their extreme positions, including all switches. Then click on the OK button once finished"));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.show();
msgBox.move((frameGeometry().width() - msgBox.width()) / 4.0f,(frameGeometry().height() - msgBox.height()) / 1.5f);
int msgBoxResult = msgBox.exec();
if (QMessageBox::Ok == msgBoxResult) {
stopCalibrationRC();
}
}
void QGCPX4VehicleConfig::stopCalibrationRC()
{
// Try to identify inverted channels, but only for R/P/Y/T
for (int i = 0; i < 4; i++) {
detectChannelInversion(i);
}
Lorenz Meier
committed
QMessageBox::information(0,"Trims","Ensure THROTTLE is in the LOWEST position and roll / pitch / yaw are CENTERED. Click OK to continue");
configEnabled = false;
ui->rcCalibrationButton->setText(tr("Start RC Calibration"));
Lorenz Meier
committed
ui->rcCalibrationButton->blockSignals(true);
ui->rcCalibrationButton->setChecked(false);
ui->rcCalibrationButton->blockSignals(false);
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) {
qDebug() << "SETTING TRIM";
setTrimPositions();
QString statusstr = tr("The calibration has been finished. Please click OK to upload it to the autopilot.");
// 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();
Lorenz Meier
committed
// Done, exit calibration mode now
mav->endRadioControlCalibration();
if (QMessageBox::Cancel == msgBoxResult) {
Lorenz Meier
committed
return; //don't commit these values
} else {
QMessageBox::information(0,"Uploading the RC Calibration","The configuration will now be uploaded and permanently stored.");
writeCalibrationRC();
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
}
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;
John Tapsell
committed
tool = new QGCToolWidget("", "", parent);
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")) {
John Tapsell
committed
tool = new QGCToolWidget("", "", tab);
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);
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
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")) {
John Tapsell
committed
tool = new QGCToolWidget("", "", tab);
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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;
}