/** ****************************************************************************** * * @file outputcalibrationpage.cpp * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. * @addtogroup * @{ * @addtogroup OutputCalibrationPage * @{ * @brief *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * 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. * * This program is distributed in the hope that it will be useful, 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 this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "outputcalibrationpage.h" #include "ui_outputcalibrationpage.h" #include "systemalarms.h" #include "uavobjectmanager.h" const QString OutputCalibrationPage::MULTI_SVG_FILE = QString(":/setupwizard/resources/multirotor-shapes.svg"); const QString OutputCalibrationPage::FIXEDWING_SVG_FILE = QString(":/setupwizard/resources/fixedwing-shapes-wizard.svg"); const QString OutputCalibrationPage::GROUND_SVG_FILE = QString(":/setupwizard/resources/ground-shapes-wizard.svg"); OutputCalibrationPage::OutputCalibrationPage(SetupWizard *wizard, QWidget *parent) : AbstractWizardPage(wizard, parent), ui(new Ui::OutputCalibrationPage), m_vehicleBoundsItem(0), m_currentWizardIndex(-1), m_calibrationUtil(0) { ui->setupUi(this); qDebug() << "calling output calibration page"; m_vehicleRenderer = new QSvgRenderer(); // move the code that was here to setupVehicle() so we can determine which image to use. m_vehicleScene = new QGraphicsScene(this); ui->vehicleView->setScene(m_vehicleScene); } OutputCalibrationPage::~OutputCalibrationPage() { if (m_calibrationUtil) { delete m_calibrationUtil; m_calibrationUtil = 0; } OutputCalibrationUtil::stopOutputCalibration(); delete ui; } void OutputCalibrationPage::loadSVGFile(QString file) { if (QFile::exists(file) && m_vehicleRenderer->load(file) && m_vehicleRenderer->isValid()) { ui->vehicleView->setScene(m_vehicleScene); } } void OutputCalibrationPage::setupActuatorMinMaxAndNeutral(int motorChannelStart, int motorChannelEnd, int totalUsedChannels) { // Default values for the actuator settings, extra important for // servos since a value out of range can actually destroy the // vehicle if unlucky. // Motors are not that important. REMOVE propellers always!! OutputCalibrationUtil::startOutputCalibration(); for (int servoid = 0; servoid < 12; servoid++) { if (servoid >= motorChannelStart && servoid <= motorChannelEnd) { // Set to motor safe values m_actuatorSettings[servoid].channelMin = LOW_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].channelNeutral = LOW_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].channelMax = getHighOutputRate(); m_actuatorSettings[servoid].isReversableMotor = false; // Car and Tank should use reversable Esc/motors if ((getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_CAR) || (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL)) { m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].isReversableMotor = true; // Set initial output value m_calibrationUtil->startChannelOutput(servoid, NEUTRAL_OUTPUT_RATE_MILLISECONDS); m_calibrationUtil->stopChannelOutput(); } } else if (servoid < totalUsedChannels) { // Set to servo safe values m_actuatorSettings[servoid].channelMin = NEUTRAL_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].channelMax = NEUTRAL_OUTPUT_RATE_MILLISECONDS; // Set initial servo output value m_calibrationUtil->startChannelOutput(servoid, NEUTRAL_OUTPUT_RATE_MILLISECONDS); m_calibrationUtil->stopChannelOutput(); } else { // "Disable" these channels m_actuatorSettings[servoid].channelMin = LOW_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].channelNeutral = LOW_OUTPUT_RATE_MILLISECONDS; m_actuatorSettings[servoid].channelMax = LOW_OUTPUT_RATE_MILLISECONDS; } } } void OutputCalibrationPage::setupVehicle() { m_actuatorSettings = getWizard()->getActuatorSettings(); m_wizardIndexes.clear(); m_vehicleElementIds.clear(); m_vehicleHighlightElementIndexes.clear(); m_channelIndex.clear(); m_currentWizardIndex = 0; m_vehicleScene->clear(); resetOutputCalibrationUtil(); switch (getWizard()->getVehicleSubType()) { case SetupWizard::MULTI_ROTOR_TRI_Y: // Loads the SVG file resourse and sets the scene loadSVGFile(MULTI_SVG_FILE); // The m_wizardIndexes array contains the index of the QStackedWidget // in the page to use for each step. m_wizardIndexes << 0 << 1 << 1 << 1 << 2; // All element ids to load from the svg file and manage. m_vehicleElementIds << "tri" << "tri-frame" << "tri-m1" << "tri-m2" << "tri-m3" << "tri-s1"; // The index of the elementId to highlight ( not dim ) for each step // this is the index in the m_vehicleElementIds - 1. m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4; // The channel number to configure for each step. m_channelIndex << 0 << 0 << 1 << 2 << 3; setupActuatorMinMaxAndNeutral(0, 2, 4); getWizard()->setActuatorSettings(m_actuatorSettings); break; case SetupWizard::MULTI_ROTOR_QUAD_X: loadSVGFile(MULTI_SVG_FILE); m_wizardIndexes << 0 << 1 << 1 << 1 << 1; m_vehicleElementIds << "quad-x" << "quad-x-frame" << "quad-x-m1" << "quad-x-m2" << "quad-x-m3" << "quad-x-m4"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4; m_channelIndex << 0 << 0 << 1 << 2 << 3; setupActuatorMinMaxAndNeutral(0, 3, 4); break; case SetupWizard::MULTI_ROTOR_QUAD_PLUS: loadSVGFile(MULTI_SVG_FILE); m_wizardIndexes << 0 << 1 << 1 << 1 << 1; m_vehicleElementIds << "quad-p" << "quad-p-frame" << "quad-p-m1" << "quad-p-m2" << "quad-p-m3" << "quad-p-m4"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4; m_channelIndex << 0 << 0 << 1 << 2 << 3; setupActuatorMinMaxAndNeutral(0, 3, 4); break; case SetupWizard::MULTI_ROTOR_HEXA: loadSVGFile(MULTI_SVG_FILE); m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1; m_vehicleElementIds << "hexa" << "hexa-frame" << "hexa-m1" << "hexa-m2" << "hexa-m3" << "hexa-m4" << "hexa-m5" << "hexa-m6"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6; m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5; setupActuatorMinMaxAndNeutral(0, 5, 6); break; case SetupWizard::MULTI_ROTOR_HEXA_COAX_Y: loadSVGFile(MULTI_SVG_FILE); m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1; m_vehicleElementIds << "hexa-y6" << "hexa-y6-frame" << "hexa-y6-m2" << "hexa-y6-m1" << "hexa-y6-m4" << "hexa-y6-m3" << "hexa-y6-m6" << "hexa-y6-m5"; m_vehicleHighlightElementIndexes << 0 << 2 << 1 << 4 << 3 << 6 << 5; m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5; setupActuatorMinMaxAndNeutral(0, 5, 6); break; case SetupWizard::MULTI_ROTOR_HEXA_H: loadSVGFile(MULTI_SVG_FILE); m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1; m_vehicleElementIds << "hexa-h" << "hexa-h-frame" << "hexa-h-m1" << "hexa-h-m2" << "hexa-h-m3" << "hexa-h-m4" << "hexa-h-m5" << "hexa-h-m6"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6; m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5; setupActuatorMinMaxAndNeutral(0, 5, 6); break; case SetupWizard::MULTI_ROTOR_HEXA_X: loadSVGFile(MULTI_SVG_FILE); m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1; m_vehicleElementIds << "hexa-x" << "hexa-x-frame" << "hexa-x-m1" << "hexa-x-m2" << "hexa-x-m3" << "hexa-x-m4" << "hexa-x-m5" << "hexa-x-m6"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6; m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5; setupActuatorMinMaxAndNeutral(0, 5, 6); break; // Fixed Wing case SetupWizard::FIXED_WING_DUAL_AILERON: loadSVGFile(FIXEDWING_SVG_FILE); m_wizardIndexes << 0 << 1 << 2 << 2 << 2 << 2; m_vehicleElementIds << "aileron" << "aileron-frame" << "aileron-motor" << "aileron-ail-left" << "aileron-ail-right" << "aileron-elevator" << "aileron-rudder"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5; m_channelIndex << 0 << 2 << 0 << 5 << 1 << 3; setupActuatorMinMaxAndNeutral(2, 2, 6); // should be 5 instead 6 but output 5 is not used getWizard()->setActuatorSettings(m_actuatorSettings); break; case SetupWizard::FIXED_WING_AILERON: loadSVGFile(FIXEDWING_SVG_FILE); m_wizardIndexes << 0 << 1 << 2 << 2 << 2; m_vehicleElementIds << "aileron-single" << "ail2-frame" << "ail2-motor" << "ail2-aileron" << "ail2-elevator" << "ail2-rudder"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4; m_channelIndex << 0 << 2 << 0 << 1 << 3; setupActuatorMinMaxAndNeutral(2, 2, 4); getWizard()->setActuatorSettings(m_actuatorSettings); break; case SetupWizard::FIXED_WING_ELEVON: loadSVGFile(FIXEDWING_SVG_FILE); m_wizardIndexes << 0 << 1 << 2 << 2; m_vehicleElementIds << "elevon" << "elevon-frame" << "elevon-motor" << "elevon-left" << "elevon-right"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3; m_channelIndex << 0 << 2 << 0 << 1; setupActuatorMinMaxAndNeutral(2, 2, 3); getWizard()->setActuatorSettings(m_actuatorSettings); break; case SetupWizard::FIXED_WING_VTAIL: loadSVGFile(FIXEDWING_SVG_FILE); m_wizardIndexes << 0 << 1 << 2 << 2 << 2 << 2; m_vehicleElementIds << "vtail" << "vtail-frame" << "vtail-motor" << "vtail-ail-left" << "vtail-ail-right" << "vtail-rudder-left" << "vtail-rudder-right"; m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5; m_channelIndex << 0 << 2 << 0 << 5 << 3 << 1; setupActuatorMinMaxAndNeutral(2, 2, 6); // should be 5 instead 6 but output 5 is not used getWizard()->setActuatorSettings(m_actuatorSettings); break; // Ground vehicles case SetupWizard::GROUNDVEHICLE_CAR: loadSVGFile(GROUND_SVG_FILE); m_wizardIndexes << 0 << 1 << 2; m_vehicleElementIds << "car" << "car-frame" << "car-motor" << "car-steering"; m_vehicleHighlightElementIndexes << 0 << 1 << 2; m_channelIndex << 0 << 1 << 0; setupActuatorMinMaxAndNeutral(1, 1, 2); getWizard()->setActuatorSettings(m_actuatorSettings); break; case SetupWizard::GROUNDVEHICLE_DIFFERENTIAL: loadSVGFile(GROUND_SVG_FILE); m_wizardIndexes << 0 << 1 << 1; m_vehicleElementIds << "tank" << "tank-frame" << "tank-left-motor" << "tank-right-motor"; m_vehicleHighlightElementIndexes << 0 << 1 << 2; m_channelIndex << 0 << 0 << 1; setupActuatorMinMaxAndNeutral(0, 1, 2); getWizard()->setActuatorSettings(m_actuatorSettings); break; case SetupWizard::GROUNDVEHICLE_MOTORCYCLE: loadSVGFile(GROUND_SVG_FILE); m_wizardIndexes << 0 << 1 << 2; m_vehicleElementIds << "motorbike" << "motorbike-frame" << "motorbike-motor" << "motorbike-steering"; m_vehicleHighlightElementIndexes << 0 << 1 << 2; m_channelIndex << 0 << 1 << 0; setupActuatorMinMaxAndNeutral(1, 1, 2); getWizard()->setActuatorSettings(m_actuatorSettings); break; default: break; } setupVehicleItems(); } void OutputCalibrationPage::setupVehicleItems() { m_vehicleItems.clear(); m_vehicleBoundsItem = new QGraphicsSvgItem(); m_vehicleBoundsItem->setSharedRenderer(m_vehicleRenderer); m_vehicleBoundsItem->setElementId(m_vehicleElementIds[0]); m_vehicleBoundsItem->setZValue(-1); m_vehicleBoundsItem->setOpacity(0); m_vehicleScene->addItem(m_vehicleBoundsItem); QRectF parentBounds = m_vehicleRenderer->boundsOnElement(m_vehicleElementIds[0]); for (int i = 1; i < m_vehicleElementIds.size(); i++) { QGraphicsSvgItem *item = new QGraphicsSvgItem(); item->setSharedRenderer(m_vehicleRenderer); item->setElementId(m_vehicleElementIds[i]); item->setZValue(i); item->setOpacity(1.0); QRectF itemBounds = m_vehicleRenderer->boundsOnElement(m_vehicleElementIds[i]); item->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y()); m_vehicleScene->addItem(item); m_vehicleItems << item; } } void OutputCalibrationPage::startWizard() { ui->calibrationStack->setCurrentIndex(m_wizardIndexes[0]); setupVehicleHighlightedPart(); } void OutputCalibrationPage::setupVehicleHighlightedPart() { qreal dimOpaque = m_currentWizardIndex == 0 ? 1.0 : 0.3; qreal highlightOpaque = 1.0; int highlightedIndex = m_vehicleHighlightElementIndexes[m_currentWizardIndex]; for (int i = 0; i < m_vehicleItems.size(); i++) { QGraphicsSvgItem *item = m_vehicleItems[i]; item->setOpacity((highlightedIndex == i) ? highlightOpaque : dimOpaque); } } void OutputCalibrationPage::setWizardPage() { qDebug() << "Wizard index: " << m_currentWizardIndex; QApplication::processEvents(); int currentPageIndex = m_wizardIndexes[m_currentWizardIndex]; qDebug() << "Current page: " << currentPageIndex; ui->calibrationStack->setCurrentIndex(currentPageIndex); int currentChannel = getCurrentChannel(); qDebug() << "Current channel: " << currentChannel + 1; if (currentChannel >= 0) { if (currentPageIndex == 1) { ui->motorNeutralSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral); ui->motorPWMValue->setText(QString(tr("Output value : %1 µs")).arg(m_actuatorSettings[currentChannel].channelNeutral)); // Reversable motor found if (m_actuatorSettings[currentChannel].isReversableMotor) { ui->motorNeutralSlider->setMinimum(m_actuatorSettings[currentChannel].channelMin); ui->motorNeutralSlider->setMaximum(m_actuatorSettings[currentChannel].channelMax); ui->motorInfo->setText(tr("
To find the neutral rate for this reversable motor, press the Start button below and slide the slider to the right or left until you find the value where the motor doesn't start.
When done press button again to stop.