LP-106 Setup Wizard refresh : Add dual servo setup (dual aileron or
[librepilot.git] / ground / gcs / src / plugins / setupwizard / pages / outputcalibrationpage.cpp
blobd093a784ef1fa1ad0af80be54bef3fdbde14e0df
1 /**
2 ******************************************************************************
4 * @file outputcalibrationpage.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
7 * @addtogroup
8 * @{
9 * @addtogroup OutputCalibrationPage
10 * @{
11 * @brief
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "outputcalibrationpage.h"
30 #include "ui_outputcalibrationpage.h"
31 #include "systemalarms.h"
32 #include "uavobjectmanager.h"
34 const QString OutputCalibrationPage::MULTI_SVG_FILE = QString(":/setupwizard/resources/multirotor-shapes.svg");
35 const QString OutputCalibrationPage::FIXEDWING_SVG_FILE = QString(":/setupwizard/resources/fixedwing-shapes-wizard.svg");
36 const QString OutputCalibrationPage::GROUND_SVG_FILE = QString(":/setupwizard/resources/ground-shapes-wizard.svg");
38 OutputCalibrationPage::OutputCalibrationPage(SetupWizard *wizard, QWidget *parent) :
39 AbstractWizardPage(wizard, parent), ui(new Ui::OutputCalibrationPage), m_vehicleBoundsItem(0),
40 m_currentWizardIndex(-1), m_calibrationUtil(0)
42 ui->setupUi(this);
44 qDebug() << "calling output calibration page";
45 m_vehicleRenderer = new QSvgRenderer();
47 // move the code that was here to setupVehicle() so we can determine which image to use.
48 m_vehicleScene = new QGraphicsScene(this);
49 ui->vehicleView->setScene(m_vehicleScene);
52 OutputCalibrationPage::~OutputCalibrationPage()
54 if (m_calibrationUtil) {
55 delete m_calibrationUtil;
56 m_calibrationUtil = 0;
59 OutputCalibrationUtil::stopOutputCalibration();
60 delete ui;
63 void OutputCalibrationPage::loadSVGFile(QString file)
65 if (QFile::exists(file) && m_vehicleRenderer->load(file) && m_vehicleRenderer->isValid()) {
66 ui->vehicleView->setScene(m_vehicleScene);
70 void OutputCalibrationPage::setupActuatorMinMaxAndNeutral(int motorChannelStart, int motorChannelEnd, int totalUsedChannels)
72 // Default values for the actuator settings, extra important for
73 // servos since a value out of range can actually destroy the
74 // vehicle if unlucky.
75 // Motors are not that important. REMOVE propellers always!!
76 OutputCalibrationUtil::startOutputCalibration();
78 for (int servoid = 0; servoid < 12; servoid++) {
79 if (servoid >= motorChannelStart && servoid <= motorChannelEnd) {
80 // Set to motor safe values
81 m_actuatorSettings[servoid].channelMin = LOW_OUTPUT_RATE_MILLISECONDS;
82 m_actuatorSettings[servoid].channelNeutral = LOW_OUTPUT_RATE_MILLISECONDS;
83 m_actuatorSettings[servoid].channelMax = getHighOutputRate();
84 m_actuatorSettings[servoid].isReversableMotor = false;
85 // Car and Tank should use reversable Esc/motors
86 if ((getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_CAR)
87 || (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL)) {
88 m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
89 m_actuatorSettings[servoid].isReversableMotor = true;
90 // Set initial output value
91 m_calibrationUtil->startChannelOutput(servoid, NEUTRAL_OUTPUT_RATE_MILLISECONDS);
92 m_calibrationUtil->stopChannelOutput();
94 } else if (servoid < totalUsedChannels) {
95 // Set to servo safe values
96 m_actuatorSettings[servoid].channelMin = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
97 m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
98 m_actuatorSettings[servoid].channelMax = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
99 // Set initial servo output value
100 m_calibrationUtil->startChannelOutput(servoid, NEUTRAL_OUTPUT_RATE_MILLISECONDS);
101 m_calibrationUtil->stopChannelOutput();
102 } else {
103 // "Disable" these channels
104 m_actuatorSettings[servoid].channelMin = LOW_OUTPUT_RATE_MILLISECONDS;
105 m_actuatorSettings[servoid].channelNeutral = LOW_OUTPUT_RATE_MILLISECONDS;
106 m_actuatorSettings[servoid].channelMax = LOW_OUTPUT_RATE_MILLISECONDS;
111 void OutputCalibrationPage::setupVehicle()
113 m_actuatorSettings = getWizard()->getActuatorSettings();
114 m_wizardIndexes.clear();
115 m_vehicleElementIds.clear();
116 m_vehicleElementTypes.clear();
117 m_vehicleHighlightElementIndexes.clear();
118 m_channelIndex.clear();
119 m_currentWizardIndex = 0;
120 m_vehicleScene->clear();
122 resetOutputCalibrationUtil();
124 switch (getWizard()->getVehicleSubType()) {
125 case SetupWizard::MULTI_ROTOR_TRI_Y:
126 // Loads the SVG file resourse and sets the scene
127 loadSVGFile(MULTI_SVG_FILE);
129 // The m_wizardIndexes array contains the index of the QStackedWidget
130 // in the page to use for each step.
131 // 0 : Start
132 // 1 : Motor page
133 // 2 : single Servo page
134 // 3 : Dual servo page, followed with -1 : Blank page.
135 m_wizardIndexes << 0 << 1 << 1 << 1 << 2;
137 // All element ids to load from the svg file and manage.
138 m_vehicleElementIds << "tri" << "tri-frame" << "tri-m1" << "tri-m2" << "tri-m3" << "tri-s1";
140 // The type of each element.
141 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << SERVO;
143 // The index of the elementId to highlight ( not dim ) for each step
144 // this is the index in the m_vehicleElementIds - 1.
145 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
147 // The channel number to configure for each step.
148 m_channelIndex << 0 << 0 << 1 << 2 << 3;
150 setupActuatorMinMaxAndNeutral(0, 2, 4);
152 getWizard()->setActuatorSettings(m_actuatorSettings);
153 break;
154 case SetupWizard::MULTI_ROTOR_QUAD_X:
155 loadSVGFile(MULTI_SVG_FILE);
156 m_wizardIndexes << 0 << 1 << 1 << 1 << 1;
157 m_vehicleElementIds << "quad-x" << "quad-x-frame" << "quad-x-m1" << "quad-x-m2" << "quad-x-m3" << "quad-x-m4";
158 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR;
159 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
160 m_channelIndex << 0 << 0 << 1 << 2 << 3;
161 setupActuatorMinMaxAndNeutral(0, 3, 4);
162 break;
163 case SetupWizard::MULTI_ROTOR_QUAD_PLUS:
164 loadSVGFile(MULTI_SVG_FILE);
165 m_wizardIndexes << 0 << 1 << 1 << 1 << 1;
166 m_vehicleElementIds << "quad-p" << "quad-p-frame" << "quad-p-m1" << "quad-p-m2" << "quad-p-m3" << "quad-p-m4";
167 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR;
168 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
169 m_channelIndex << 0 << 0 << 1 << 2 << 3;
170 setupActuatorMinMaxAndNeutral(0, 3, 4);
171 break;
172 case SetupWizard::MULTI_ROTOR_HEXA:
173 loadSVGFile(MULTI_SVG_FILE);
174 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
175 m_vehicleElementIds << "hexa" << "hexa-frame" << "hexa-m1" << "hexa-m2" << "hexa-m3" << "hexa-m4" << "hexa-m5" << "hexa-m6";
176 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
177 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6;
178 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
179 setupActuatorMinMaxAndNeutral(0, 5, 6);
180 break;
181 case SetupWizard::MULTI_ROTOR_HEXA_COAX_Y:
182 loadSVGFile(MULTI_SVG_FILE);
183 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
184 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";
185 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
186 m_vehicleHighlightElementIndexes << 0 << 2 << 1 << 4 << 3 << 6 << 5;
187 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
188 setupActuatorMinMaxAndNeutral(0, 5, 6);
189 break;
190 case SetupWizard::MULTI_ROTOR_HEXA_H:
191 loadSVGFile(MULTI_SVG_FILE);
192 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
193 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";
194 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
195 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6;
196 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
197 setupActuatorMinMaxAndNeutral(0, 5, 6);
198 break;
199 case SetupWizard::MULTI_ROTOR_HEXA_X:
200 loadSVGFile(MULTI_SVG_FILE);
201 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
202 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";
203 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
204 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6;
205 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
206 setupActuatorMinMaxAndNeutral(0, 5, 6);
207 break;
208 // Fixed Wing
209 case SetupWizard::FIXED_WING_DUAL_AILERON:
210 loadSVGFile(FIXEDWING_SVG_FILE);
211 m_wizardIndexes << 0 << 1 << 3 << -1 << 2 << 2;
212 m_vehicleElementIds << "aileron" << "aileron-frame" << "aileron-motor" << "aileron-ail-left" << "aileron-ail-right" << "aileron-elevator" << "aileron-rudder";
213 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO << SERVO << SERVO;
214 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5;
215 m_channelIndex << 0 << 2 << 0 << 5 << 1 << 3;
217 setupActuatorMinMaxAndNeutral(2, 2, 6); // should be 5 instead 6 but output 5 is not used
219 getWizard()->setActuatorSettings(m_actuatorSettings);
220 break;
221 case SetupWizard::FIXED_WING_AILERON:
222 loadSVGFile(FIXEDWING_SVG_FILE);
223 m_wizardIndexes << 0 << 1 << 2 << 2 << 2;
224 m_vehicleElementIds << "singleaileron" << "singleaileron-frame" << "singleaileron-motor" << "singleaileron-aileron" << "singleaileron-elevator" << "singleaileron-rudder";
225 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO << SERVO;
226 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
227 m_channelIndex << 0 << 2 << 0 << 1 << 3;
229 setupActuatorMinMaxAndNeutral(2, 2, 4);
231 getWizard()->setActuatorSettings(m_actuatorSettings);
232 break;
233 case SetupWizard::FIXED_WING_ELEVON:
234 loadSVGFile(FIXEDWING_SVG_FILE);
235 m_wizardIndexes << 0 << 1 << 3 << -1;
236 m_vehicleElementIds << "elevon" << "elevon-frame" << "elevon-motor" << "elevon-left" << "elevon-right";
237 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO;
238 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3;
239 m_channelIndex << 0 << 2 << 0 << 1;
241 setupActuatorMinMaxAndNeutral(2, 2, 3);
243 getWizard()->setActuatorSettings(m_actuatorSettings);
244 break;
245 case SetupWizard::FIXED_WING_VTAIL:
246 loadSVGFile(FIXEDWING_SVG_FILE);
247 m_wizardIndexes << 0 << 1 << 3 << -1 << 3 << -1;
248 m_vehicleElementIds << "vtail" << "vtail-frame" << "vtail-motor" << "vtail-ail-left" << "vtail-ail-right" << "vtail-rudder-left" << "vtail-rudder-right";
249 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO << SERVO << SERVO;
250 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5;
251 m_channelIndex << 0 << 2 << 0 << 5 << 3 << 1;
253 setupActuatorMinMaxAndNeutral(2, 2, 6); // should be 5 instead 6 but output 5 is not used
255 getWizard()->setActuatorSettings(m_actuatorSettings);
256 break;
258 // Ground vehicles
259 case SetupWizard::GROUNDVEHICLE_CAR:
260 loadSVGFile(GROUND_SVG_FILE);
261 m_wizardIndexes << 0 << 1 << 2;
262 m_vehicleElementIds << "car" << "car-frame" << "car-motor" << "car-steering";
263 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO;
264 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
265 m_channelIndex << 0 << 1 << 0;
267 setupActuatorMinMaxAndNeutral(1, 1, 2);
269 getWizard()->setActuatorSettings(m_actuatorSettings);
270 break;
271 case SetupWizard::GROUNDVEHICLE_DIFFERENTIAL:
272 loadSVGFile(GROUND_SVG_FILE);
273 m_wizardIndexes << 0 << 1 << 1;
274 m_vehicleElementIds << "tank" << "tank-frame" << "tank-left-motor" << "tank-right-motor";
275 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR;
276 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
277 m_channelIndex << 0 << 0 << 1;
279 setupActuatorMinMaxAndNeutral(0, 1, 2);
281 getWizard()->setActuatorSettings(m_actuatorSettings);
282 break;
283 case SetupWizard::GROUNDVEHICLE_MOTORCYCLE:
284 loadSVGFile(GROUND_SVG_FILE);
285 m_wizardIndexes << 0 << 1 << 2;
286 m_vehicleElementIds << "motorbike" << "motorbike-frame" << "motorbike-motor" << "motorbike-steering";
287 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO;
288 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
289 m_channelIndex << 0 << 1 << 0;
291 setupActuatorMinMaxAndNeutral(1, 1, 2);
293 getWizard()->setActuatorSettings(m_actuatorSettings);
294 break;
296 default:
297 break;
300 setupVehicleItems();
303 void OutputCalibrationPage::setupVehicleItems()
305 m_vehicleItems.clear();
306 m_arrowsItems.clear();
307 m_vehicleBoundsItem = new QGraphicsSvgItem();
308 m_vehicleBoundsItem->setSharedRenderer(m_vehicleRenderer);
309 m_vehicleBoundsItem->setElementId(m_vehicleElementIds[0]);
310 m_vehicleBoundsItem->setZValue(-1);
311 m_vehicleBoundsItem->setOpacity(0);
312 m_vehicleScene->addItem(m_vehicleBoundsItem);
314 QRectF parentBounds = m_vehicleRenderer->boundsOnElement(m_vehicleElementIds[0]);
316 for (int i = 1; i < m_vehicleElementIds.size(); i++) {
317 QGraphicsSvgItem *item = new QGraphicsSvgItem();
318 item->setSharedRenderer(m_vehicleRenderer);
319 item->setElementId(m_vehicleElementIds[i]);
320 item->setZValue(i);
321 item->setOpacity(1.0);
323 QRectF itemBounds = m_vehicleRenderer->boundsOnElement(m_vehicleElementIds[i]);
324 item->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y());
326 m_vehicleScene->addItem(item);
327 m_vehicleItems << item;
329 bool addArrows = false;
331 if ((m_vehicleElementIds[i].contains("left")) || (m_vehicleElementIds[i].contains("right"))
332 || (m_vehicleElementIds[i].contains("elevator")) || (m_vehicleElementIds[i].contains("rudder"))
333 || (m_vehicleElementIds[i].contains("steering")) || (m_vehicleElementIds[i] == "singleaileron-aileron")) {
334 addArrows = true;
337 if (addArrows) {
338 QString arrowUp = "-up"; // right if rudder / steering
339 QString arrowDown = "-down"; // left
341 QGraphicsSvgItem *itemUp = new QGraphicsSvgItem();
343 itemUp->setSharedRenderer(m_vehicleRenderer);
344 QString elementUp = m_vehicleElementIds[i] + arrowUp;
345 itemUp->setElementId(elementUp);
346 itemUp->setZValue(i + 10);
347 itemUp->setOpacity(0);
349 QRectF itemBounds = m_vehicleRenderer->boundsOnElement(elementUp);
350 itemUp->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y());
351 m_vehicleScene->addItem(itemUp);
353 m_arrowsItems << itemUp;
355 QGraphicsSvgItem *itemDown = new QGraphicsSvgItem();
356 itemDown->setSharedRenderer(m_vehicleRenderer);
357 QString elementDown = m_vehicleElementIds[i] + arrowDown;
358 itemDown->setElementId(elementDown);
359 itemDown->setZValue(i + 10);
360 itemDown->setOpacity(0);
362 itemBounds = m_vehicleRenderer->boundsOnElement(elementDown);
363 itemDown->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y());
364 m_vehicleScene->addItem(itemDown);
366 m_arrowsItems << itemDown;
371 void OutputCalibrationPage::startWizard()
373 ui->calibrationStack->setCurrentIndex(m_wizardIndexes[0]);
374 enableAllMotorsCheckBox(true);
375 setupVehicleHighlightedPart();
378 void OutputCalibrationPage::setupVehicleHighlightedPart()
380 qreal dimOpaque = m_currentWizardIndex == 0 ? 1.0 : 0.3;
381 qreal highlightOpaque = 1.0;
382 int highlightedIndex = m_vehicleHighlightElementIndexes[m_currentWizardIndex];
384 bool isDualServoSetup = (m_wizardIndexes[m_currentWizardIndex] == 3);
386 for (int i = 0; i < m_vehicleItems.size(); i++) {
387 QGraphicsSvgItem *item = m_vehicleItems[i];
388 if (highlightedIndex == i || (isDualServoSetup && ((highlightedIndex + 1) == i)) ||
389 (ui->calibrateAllMotors->isChecked() && m_vehicleElementTypes[i + 1] == MOTOR)) {
390 item->setOpacity(highlightOpaque);
391 } else {
392 item->setOpacity(dimOpaque);
397 void OutputCalibrationPage::showElementMovement(bool isUp, bool firstServo, qreal value)
399 QString highlightedItemName;
401 if (firstServo) {
402 highlightedItemName = m_vehicleItems[m_currentWizardIndex]->elementId();
403 } else {
404 if ((m_currentWizardIndex + 1) < m_wizardIndexes.size()) {
405 highlightedItemName = m_vehicleItems[m_currentWizardIndex + 1]->elementId();
409 for (int i = 0; i < m_arrowsItems.size(); i++) {
410 QString upItemName = highlightedItemName + "-up";
411 QString downItemName = highlightedItemName + "-down";
412 if (m_arrowsItems[i]->elementId() == upItemName) {
413 QGraphicsSvgItem *itemUp = m_arrowsItems[i];
414 itemUp->setOpacity(isUp ? value : 0);
416 if (m_arrowsItems[i]->elementId() == downItemName) {
417 QGraphicsSvgItem *itemDown = m_arrowsItems[i];
418 itemDown->setOpacity(isUp ? 0 : value);
423 void OutputCalibrationPage::setWizardPage()
425 qDebug() << "Wizard index: " << m_currentWizardIndex;
427 QApplication::processEvents();
429 int currentPageIndex = m_wizardIndexes[m_currentWizardIndex];
430 qDebug() << "Current page: " << currentPageIndex;
431 ui->calibrationStack->setCurrentIndex(currentPageIndex);
433 QList<quint16> currentChannels;
434 getCurrentChannels(currentChannels);
435 int currentChannel = currentChannels[0];
436 qDebug() << "Current channel: " << currentChannel + 1;
437 if (currentChannel >= 0) {
438 if (currentPageIndex == 1) {
439 ui->motorNeutralSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
440 ui->motorPWMValue->setText(QString(tr("Output value : <b>%1</b> µs")).arg(m_actuatorSettings[currentChannel].channelNeutral));
441 // Reversable motor found
442 if (m_actuatorSettings[currentChannel].isReversableMotor) {
443 ui->motorNeutralSlider->setMinimum(m_actuatorSettings[currentChannel].channelMin);
444 ui->motorNeutralSlider->setMaximum(m_actuatorSettings[currentChannel].channelMax);
445 ui->motorInfo->setText(tr("<html><head/><body><p><span style=\" font-size:10pt;\">To find </span><span style=\" font-size:10pt; font-weight:600;\">the neutral rate for this reversable motor</span><span style=\" font-size:10pt;\">, 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. <br/><br/>When done press button again to stop.</span></p></body></html>"));
447 } else if (currentPageIndex == 2) {
448 ui->servoPWMValue->setText(tr("Output value : <b>%1</b> µs").arg(m_actuatorSettings[currentChannel].channelNeutral));
449 if (m_actuatorSettings[currentChannel].channelMax < m_actuatorSettings[currentChannel].channelMin &&
450 !ui->reverseCheckbox->isChecked()) {
451 ui->reverseCheckbox->setChecked(true);
452 } else {
453 ui->reverseCheckbox->setChecked(false);
455 enableServoSliders(false);
456 if (ui->reverseCheckbox->isChecked()) {
457 ui->servoMaxAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMax);
458 ui->servoCenterAngleSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
459 ui->servoMinAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
460 } else {
461 ui->servoMinAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
462 ui->servoCenterAngleSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
463 ui->servoMaxAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMax);
465 } else if (currentPageIndex == 3) {
466 // Dual channel setup : two ailerons or Vtail
467 // First channel
468 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelNeutral));
471 if (m_actuatorSettings[currentChannel].channelMax < m_actuatorSettings[currentChannel].channelMin &&
472 !ui->reverseCheckbox1->isChecked()) {
473 ui->reverseCheckbox1->setChecked(true);
474 } else {
475 ui->reverseCheckbox1->setChecked(false);
477 enableServoSliders(false);
478 if (ui->reverseCheckbox1->isChecked()) {
479 ui->servoMaxAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMax);
480 ui->servoCenterAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelNeutral);
481 ui->servoMinAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMin);
482 } else {
483 ui->servoMinAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMin);
484 ui->servoCenterAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelNeutral);
485 ui->servoMaxAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMax);
487 // Second channel
488 int nextChannel = currentChannels[1];
489 qDebug() << "Current channel: " << currentChannel + 1 << " and " << nextChannel + 1
491 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs").arg(nextChannel + 1).arg(m_actuatorSettings[nextChannel].channelNeutral));
493 if (m_actuatorSettings[nextChannel].channelMax < m_actuatorSettings[nextChannel].channelMin &&
494 !ui->reverseCheckbox2->isChecked()) {
495 ui->reverseCheckbox2->setChecked(true);
496 } else {
497 ui->reverseCheckbox2->setChecked(false);
499 enableServoSliders(false);
500 if (ui->reverseCheckbox2->isChecked()) {
501 ui->servoMaxAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMax);
502 ui->servoCenterAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelNeutral);
503 ui->servoMinAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMin);
504 } else {
505 ui->servoMinAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMin);
506 ui->servoCenterAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelNeutral);
507 ui->servoMaxAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMax);
511 setupVehicleHighlightedPart();
512 // Hide arrows
513 showElementMovement(true, true, 0);
514 showElementMovement(false, true, 0);
515 showElementMovement(true, false, 0);
516 showElementMovement(false, false, 0);
519 void OutputCalibrationPage::initializePage()
521 if (m_vehicleScene) {
522 setupVehicle();
523 startWizard();
527 bool OutputCalibrationPage::validatePage()
529 if (!isFinished()) {
530 m_currentWizardIndex++;
531 while (!isFinished() && m_wizardIndexes[m_currentWizardIndex] == -1) {
532 // Skip step, found a blank page
533 // Dual servo setup, a '3' page is followed with a '-1' page
534 m_currentWizardIndex++;
536 if (ui->calibrateAllMotors->isChecked() &&
537 m_currentWizardIndex > 0 &&
538 m_wizardIndexes[m_currentWizardIndex - 1] == 1) {
539 while (!isFinished() && m_wizardIndexes[m_currentWizardIndex] == 1) {
540 m_currentWizardIndex++;
545 if (isFinished()) {
546 getWizard()->setActuatorSettings(m_actuatorSettings);
547 return true;
548 } else {
549 setWizardPage();
550 return false;
554 void OutputCalibrationPage::showEvent(QShowEvent *event)
556 Q_UNUSED(event);
557 if (m_vehicleBoundsItem) {
558 ui->vehicleView->setSceneRect(m_vehicleBoundsItem->boundingRect());
559 ui->vehicleView->fitInView(m_vehicleBoundsItem, Qt::KeepAspectRatio);
563 void OutputCalibrationPage::resizeEvent(QResizeEvent *event)
565 Q_UNUSED(event);
566 if (m_vehicleBoundsItem) {
567 ui->vehicleView->setSceneRect(m_vehicleBoundsItem->boundingRect());
568 ui->vehicleView->fitInView(m_vehicleBoundsItem, Qt::KeepAspectRatio);
572 void OutputCalibrationPage::customBackClicked()
574 if (m_currentWizardIndex >= 0) {
575 m_currentWizardIndex--;
576 while (m_currentWizardIndex > 0 &&
577 m_wizardIndexes[m_currentWizardIndex] == -1 &&
578 m_wizardIndexes[m_currentWizardIndex - 1] == 3) {
579 // Skip step, found a blank page
580 // Dual servo setup, a '3' page is followed with a '-1' page
581 m_currentWizardIndex--;
583 if (ui->calibrateAllMotors->isChecked()) {
584 while (m_currentWizardIndex > 0 &&
585 m_wizardIndexes[m_currentWizardIndex] == 1 &&
586 m_wizardIndexes[m_currentWizardIndex - 1] == 1) {
587 m_currentWizardIndex--;
592 if (m_currentWizardIndex >= 0) {
593 setWizardPage();
594 } else {
595 getWizard()->back();
599 void OutputCalibrationPage::getCurrentChannels(QList<quint16> &channels)
601 if (ui->calibrateAllMotors->isChecked()) {
602 for (int i = 1; i < m_channelIndex.size(); i++) {
603 if (m_vehicleElementTypes[i + 1] == MOTOR) {
604 channels << m_channelIndex[i];
607 } else {
608 channels << m_channelIndex[m_currentWizardIndex];
609 // Add next channel for dual servo setup
610 if (m_wizardIndexes[m_currentWizardIndex] == 3) {
611 channels << m_channelIndex[m_currentWizardIndex + 1];
616 void OutputCalibrationPage::enableAllMotorsCheckBox(bool enable)
618 if (getWizard()->getVehicleType() == SetupWizard::VEHICLE_MULTI) {
619 ui->calibrateAllMotors->setVisible(true);
620 ui->calibrateAllMotors->setEnabled(enable);
621 } else {
622 ui->calibrateAllMotors->setChecked(false);
623 ui->calibrateAllMotors->setVisible(false);
627 void OutputCalibrationPage::enableButtons(bool enable)
629 getWizard()->button(QWizard::NextButton)->setEnabled(enable);
630 getWizard()->button(QWizard::CustomButton1)->setEnabled(enable);
631 getWizard()->button(QWizard::CancelButton)->setEnabled(enable);
632 getWizard()->button(QWizard::BackButton)->setEnabled(enable);
633 enableAllMotorsCheckBox(enable);
634 QApplication::processEvents();
637 void OutputCalibrationPage::on_motorNeutralButton_toggled(bool checked)
639 ui->motorNeutralButton->setText(checked ? tr("Stop") : tr("Start"));
640 ui->motorNeutralSlider->setEnabled(checked);
642 QList<quint16> currentChannels;
643 getCurrentChannels(currentChannels);
644 quint16 currentChannel = currentChannels[0];
646 quint16 safeValue = m_actuatorSettings[currentChannel].channelMin;
648 if (m_actuatorSettings[currentChannel].isReversableMotor) {
649 safeValue = m_actuatorSettings[currentChannel].channelNeutral;
652 onStartButtonToggle(ui->motorNeutralButton, currentChannels, m_actuatorSettings[currentChannel].channelNeutral, safeValue, ui->motorNeutralSlider);
655 void OutputCalibrationPage::onStartButtonToggle(QAbstractButton *button, QList<quint16> &channels,
656 quint16 value, quint16 safeValue, QSlider *slider)
658 if (button->isChecked()) {
659 // Start calibration
660 if (checkAlarms()) {
661 enableButtons(false);
662 enableServoSliders(true);
663 m_calibrationUtil->startChannelOutput(channels, safeValue);
664 slider->setValue(value);
665 m_calibrationUtil->setChannelOutputValue(value);
666 } else {
667 button->setChecked(false);
669 } else {
670 // Stop calibration
671 quint16 channel = channels[0];
672 if ((button == ui->motorNeutralButton) && !m_actuatorSettings[channel].isReversableMotor) {
673 // Normal motor
674 m_calibrationUtil->startChannelOutput(channels, m_actuatorSettings[channel].channelMin);
675 } else {
676 // Servos and ReversableMotors
677 m_calibrationUtil->startChannelOutput(channels, m_actuatorSettings[channel].channelNeutral);
680 m_calibrationUtil->stopChannelOutput();
682 enableServoSliders(false);
683 enableButtons(true);
685 debugLogChannelValues(true);
688 void OutputCalibrationPage::onStartButtonToggleDual(QAbstractButton *button, QList<quint16> &channels,
689 quint16 value1, quint16 value2,
690 quint16 safeValue,
691 QSlider *slider1, QSlider *slider2)
693 if (button->isChecked()) {
694 // Start calibration
695 if (checkAlarms()) {
696 enableButtons(false);
697 enableServoSliders(true);
698 m_calibrationUtil->startChannelOutput(channels, safeValue);
700 slider1->setValue(value1);
701 slider2->setValue(value2);
702 m_calibrationUtil->setChannelDualOutputValue(value1, value2);
703 } else {
704 button->setChecked(false);
706 } else {
707 // Stop calibration
708 quint16 channel1 = channels[0];
709 quint16 channel2 = channels[1];
711 m_calibrationUtil->startChannelOutput(channels, m_actuatorSettings[channel1].channelNeutral);
712 m_calibrationUtil->stopChannelDualOutput(m_actuatorSettings[channel1].channelNeutral, m_actuatorSettings[channel2].channelNeutral);
714 m_calibrationUtil->stopChannelOutput();
716 enableServoSliders(false);
717 enableButtons(true);
719 debugLogChannelValues(true);
722 void OutputCalibrationPage::enableServoSliders(bool enabled)
724 ui->servoCenterAngleSlider->setEnabled(enabled);
725 ui->servoMinAngleSlider->setEnabled(enabled);
726 ui->servoMaxAngleSlider->setEnabled(enabled);
727 ui->reverseCheckbox->setEnabled(!enabled);
729 ui->servoCenterAngleSlider1->setEnabled(enabled);
730 ui->servoMinAngleSlider1->setEnabled(enabled);
731 ui->servoMaxAngleSlider1->setEnabled(enabled);
732 ui->reverseCheckbox1->setEnabled(!enabled);
733 ui->servoCenterAngleSlider2->setEnabled(enabled);
734 ui->servoMinAngleSlider2->setEnabled(enabled);
735 ui->servoMaxAngleSlider2->setEnabled(enabled);
736 ui->reverseCheckbox2->setEnabled(!enabled);
737 // Hide arrows
738 showElementMovement(true, true, 0);
739 showElementMovement(false, true, 0);
742 bool OutputCalibrationPage::checkAlarms()
744 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
745 UAVObjectManager *uavObjectManager = pm->getObject<UAVObjectManager>();
747 Q_ASSERT(uavObjectManager);
748 SystemAlarms *systemAlarms = SystemAlarms::GetInstance(uavObjectManager);
749 Q_ASSERT(systemAlarms);
750 SystemAlarms::DataFields data = systemAlarms->getData();
752 if (data.Alarm[SystemAlarms::ALARM_ACTUATOR] != SystemAlarms::ALARM_OK) {
753 QMessageBox mbox(this);
754 mbox.setText(QString(tr("The actuator module is in an error state.\n\n"
755 "Please make sure the correct firmware version is used then "
756 "restart the wizard and try again. If the problem persists please "
757 "consult the librepilot.org support forum.")));
758 mbox.setStandardButtons(QMessageBox::Ok);
759 mbox.setIcon(QMessageBox::Critical);
761 getWizard()->setWindowFlags(getWizard()->windowFlags() & ~Qt::WindowStaysOnTopHint);
763 mbox.exec();
765 getWizard()->setWindowFlags(getWizard()->windowFlags() | Qt::WindowStaysOnTopHint);
766 getWizard()->setWindowIcon(qApp->windowIcon());
767 getWizard()->show();
768 return false;
770 return true;
773 void OutputCalibrationPage::debugLogChannelValues(bool showFirst)
775 QList<quint16> currentChannels;
776 quint16 currentChannel;
778 getCurrentChannels(currentChannels);
779 if (showFirst) {
780 currentChannel = currentChannels[0];
781 } else {
782 currentChannel = currentChannels[1];
784 qDebug() << "ChannelMin : " << m_actuatorSettings[currentChannel].channelMin;
785 qDebug() << "ChannelNeutral: " << m_actuatorSettings[currentChannel].channelNeutral;
786 qDebug() << "ChannelMax : " << m_actuatorSettings[currentChannel].channelMax;
789 int OutputCalibrationPage::getHighOutputRate()
791 if (getWizard()->getEscType() == SetupWizard::ESC_ONESHOT) {
792 return HIGH_OUTPUT_RATE_MILLISECONDS_ONESHOT125;
793 } else {
794 return HIGH_OUTPUT_RATE_MILLISECONDS_PWM;
798 void OutputCalibrationPage::on_motorNeutralSlider_valueChanged(int value)
800 Q_UNUSED(value);
801 ui->motorPWMValue->setText(tr("Output value : <b>%1</b> µs").arg(value));
803 if (ui->motorNeutralButton->isChecked()) {
804 quint16 value = ui->motorNeutralSlider->value();
805 m_calibrationUtil->setChannelOutputValue(value);
807 QList<quint16> currentChannels;
808 getCurrentChannels(currentChannels);
809 foreach(quint16 channel, currentChannels) {
810 m_actuatorSettings[channel].channelNeutral = value;
812 debugLogChannelValues(true);
816 void OutputCalibrationPage::on_servoButton_toggled(bool checked)
818 ui->servoButton->setText(checked ? tr("Stop") : tr("Start"));
819 // Now we set servos, motors are done (Tricopter fix)
820 ui->calibrateAllMotors->setChecked(false);
822 QList<quint16> currentChannels;
823 getCurrentChannels(currentChannels);
824 quint16 currentChannel = currentChannels[0];
826 quint16 safeValue = m_actuatorSettings[currentChannel].channelNeutral;
827 onStartButtonToggle(ui->servoButton, currentChannels, safeValue, safeValue, ui->servoCenterAngleSlider);
830 void OutputCalibrationPage::on_dualservoButton_toggled(bool checked)
832 ui->dualservoButton->setText(checked ? tr("Stop") : tr("Start"));
833 // Now we set servos, motors are done (Tricopter fix)
834 ui->calibrateAllMotors->setChecked(false);
836 QList<quint16> currentChannels;
837 getCurrentChannels(currentChannels);
838 quint16 currentChannel = currentChannels[0];
839 quint16 nextChannel = currentChannels[1];
841 quint16 safeValue1 = m_actuatorSettings[currentChannel].channelNeutral;
842 quint16 safeValue2 = m_actuatorSettings[nextChannel].channelNeutral;
843 onStartButtonToggleDual(ui->dualservoButton, currentChannels, safeValue1, safeValue2, safeValue1,
844 ui->servoCenterAngleSlider1, ui->servoCenterAngleSlider2);
848 // Single servo page (2)
850 void OutputCalibrationPage::on_servoCenterAngleSlider_valueChanged(int position)
852 Q_UNUSED(position);
853 quint16 value = ui->servoCenterAngleSlider->value();
854 m_calibrationUtil->setChannelOutputValue(value);
856 QList<quint16> currentChannels;
857 getCurrentChannels(currentChannels);
858 quint16 currentChannel = currentChannels[0];
860 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(value));
862 bool showFirst = true;
863 setSliderLimitsAndArrows(currentChannel, showFirst, value, ui->reverseCheckbox, ui->servoMinAngleSlider, ui->servoMaxAngleSlider);
865 debugLogChannelValues(showFirst);
868 void OutputCalibrationPage::on_servoMinAngleSlider_valueChanged(int position)
870 Q_UNUSED(position);
871 quint16 value = ui->servoMinAngleSlider->value();
872 m_calibrationUtil->setChannelOutputValue(value);
874 QList<quint16> currentChannels;
875 getCurrentChannels(currentChannels);
876 quint16 currentChannel = currentChannels[0];
877 m_actuatorSettings[currentChannel].channelMin = value;
878 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs (Min)").arg(currentChannel + 1).arg(value));
880 // Adjust neutral and max
881 if (ui->reverseCheckbox->isChecked()) {
882 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
883 ui->servoCenterAngleSlider->setValue(value);
885 if (value <= m_actuatorSettings[currentChannel].channelMax) {
886 ui->servoMaxAngleSlider->setValue(value);
888 } else {
889 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
890 ui->servoCenterAngleSlider->setValue(value);
892 if (value >= m_actuatorSettings[currentChannel].channelMax) {
893 ui->servoMaxAngleSlider->setValue(value);
896 debugLogChannelValues(true);
899 void OutputCalibrationPage::on_servoMaxAngleSlider_valueChanged(int position)
901 Q_UNUSED(position);
902 quint16 value = ui->servoMaxAngleSlider->value();
903 m_calibrationUtil->setChannelOutputValue(value);
905 QList<quint16> currentChannels;
906 getCurrentChannels(currentChannels);
907 quint16 currentChannel = currentChannels[0];
908 m_actuatorSettings[currentChannel].channelMax = value;
909 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs (Max)").arg(currentChannel + 1).arg(value));
911 // Adjust neutral and min
912 if (ui->reverseCheckbox->isChecked()) {
913 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
914 ui->servoCenterAngleSlider->setValue(value);
916 if (value >= m_actuatorSettings[currentChannel].channelMin) {
917 ui->servoMinAngleSlider->setValue(value);
919 } else {
920 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
921 ui->servoCenterAngleSlider->setValue(value);
923 if (value <= m_actuatorSettings[currentChannel].channelMin) {
924 ui->servoMinAngleSlider->setValue(value);
927 debugLogChannelValues(true);
930 void OutputCalibrationPage::on_reverseCheckbox_toggled(bool checked)
932 Q_UNUSED(checked);
933 QList<quint16> currentChannels;
934 getCurrentChannels(currentChannels);
935 quint16 currentChannel = currentChannels[0];
937 reverseCheckBoxIsToggled(currentChannel, ui->reverseCheckbox,
938 ui->servoCenterAngleSlider, ui->servoMinAngleSlider, ui->servoMaxAngleSlider);
940 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs (Max)")
941 .arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelMax));
945 // Dual servo page (3) - first channel
947 void OutputCalibrationPage::on_servoCenterAngleSlider1_valueChanged(int position)
949 Q_UNUSED(position);
950 quint16 value = ui->servoCenterAngleSlider1->value();
951 quint16 value2 = ui->servoCenterAngleSlider2->value();
952 m_calibrationUtil->setChannelDualOutputValue(value, value2);
954 QList<quint16> currentChannels;
955 getCurrentChannels(currentChannels);
956 quint16 currentChannel = currentChannels[0];
959 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(value));
961 bool showFirst = true;
962 setSliderLimitsAndArrows(currentChannel, showFirst, value, ui->reverseCheckbox1, ui->servoMinAngleSlider1, ui->servoMaxAngleSlider1);
964 debugLogChannelValues(showFirst);
967 void OutputCalibrationPage::on_servoMinAngleSlider1_valueChanged(int position)
969 Q_UNUSED(position);
970 quint16 value = ui->servoMinAngleSlider1->value();
971 quint16 value2 = ui->servoCenterAngleSlider2->value();
972 m_calibrationUtil->setChannelDualOutputValue(value, value2);
974 QList<quint16> currentChannels;
975 getCurrentChannels(currentChannels);
976 quint16 currentChannel = currentChannels[0];
977 m_actuatorSettings[currentChannel].channelMin = value;
978 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs (Min)").arg(currentChannel + 1).arg(value));
980 // Adjust neutral and max
981 if (ui->reverseCheckbox1->isChecked()) {
982 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
983 ui->servoCenterAngleSlider1->setValue(value);
985 if (value <= m_actuatorSettings[currentChannel].channelMax) {
986 ui->servoMaxAngleSlider1->setValue(value);
988 } else {
989 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
990 ui->servoCenterAngleSlider1->setValue(value);
992 if (value >= m_actuatorSettings[currentChannel].channelMax) {
993 ui->servoMaxAngleSlider1->setValue(value);
996 debugLogChannelValues(true);
999 void OutputCalibrationPage::on_servoMaxAngleSlider1_valueChanged(int position)
1001 Q_UNUSED(position);
1002 quint16 value = ui->servoMaxAngleSlider1->value();
1003 quint16 value2 = ui->servoCenterAngleSlider2->value();
1004 m_calibrationUtil->setChannelDualOutputValue(value, value2);
1006 QList<quint16> currentChannels;
1007 getCurrentChannels(currentChannels);
1008 quint16 currentChannel = currentChannels[0];
1009 m_actuatorSettings[currentChannel].channelMax = value;
1010 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs (Max)").arg(currentChannel + 1).arg(value));
1012 // Adjust neutral and min
1013 if (ui->reverseCheckbox1->isChecked()) {
1014 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1015 ui->servoCenterAngleSlider1->setValue(value);
1017 if (value >= m_actuatorSettings[currentChannel].channelMin) {
1018 ui->servoMinAngleSlider1->setValue(value);
1020 } else {
1021 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1022 ui->servoCenterAngleSlider1->setValue(value);
1024 if (value <= m_actuatorSettings[currentChannel].channelMin) {
1025 ui->servoMinAngleSlider1->setValue(value);
1028 debugLogChannelValues(true);
1031 void OutputCalibrationPage::on_reverseCheckbox1_toggled(bool checked)
1033 Q_UNUSED(checked);
1034 QList<quint16> currentChannels;
1035 getCurrentChannels(currentChannels);
1036 quint16 currentChannel = currentChannels[0];
1038 reverseCheckBoxIsToggled(currentChannel, ui->reverseCheckbox1,
1039 ui->servoCenterAngleSlider1, ui->servoMinAngleSlider1, ui->servoMaxAngleSlider1);
1041 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs (Max)")
1042 .arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelMax));
1046 // Dual servo page - second channel
1048 void OutputCalibrationPage::on_servoCenterAngleSlider2_valueChanged(int position)
1050 Q_UNUSED(position);
1051 quint16 value = ui->servoCenterAngleSlider2->value();
1052 quint16 value1 = ui->servoCenterAngleSlider1->value();
1053 m_calibrationUtil->setChannelDualOutputValue(value1, value);
1055 QList<quint16> currentChannels;
1056 getCurrentChannels(currentChannels);
1057 quint16 currentChannel = currentChannels[1];
1059 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(value));
1061 bool showFirst = false;
1062 setSliderLimitsAndArrows(currentChannel, showFirst, value, ui->reverseCheckbox2, ui->servoMinAngleSlider2, ui->servoMaxAngleSlider2);
1064 debugLogChannelValues(showFirst);
1067 void OutputCalibrationPage::on_servoMinAngleSlider2_valueChanged(int position)
1069 Q_UNUSED(position);
1070 quint16 value = ui->servoMinAngleSlider2->value();
1071 quint16 value1 = ui->servoCenterAngleSlider1->value();
1072 m_calibrationUtil->setChannelDualOutputValue(value1, value);
1074 QList<quint16> currentChannels;
1075 getCurrentChannels(currentChannels);
1076 quint16 currentChannel = currentChannels[1];
1077 m_actuatorSettings[currentChannel].channelMin = value;
1078 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs (Min)").arg(currentChannel + 1).arg(value));
1080 // Adjust neutral and max
1081 if (ui->reverseCheckbox2->isChecked()) {
1082 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1083 ui->servoCenterAngleSlider2->setValue(value);
1085 if (value <= m_actuatorSettings[currentChannel].channelMax) {
1086 ui->servoMaxAngleSlider2->setValue(value);
1088 } else {
1089 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1090 ui->servoCenterAngleSlider2->setValue(value);
1092 if (value >= m_actuatorSettings[currentChannel].channelMax) {
1093 ui->servoMaxAngleSlider2->setValue(value);
1096 debugLogChannelValues(false);
1099 void OutputCalibrationPage::on_servoMaxAngleSlider2_valueChanged(int position)
1101 Q_UNUSED(position);
1102 quint16 value = ui->servoMaxAngleSlider2->value();
1103 quint16 value1 = ui->servoCenterAngleSlider1->value();
1104 m_calibrationUtil->setChannelDualOutputValue(value1, value);
1106 QList<quint16> currentChannels;
1107 getCurrentChannels(currentChannels);
1108 quint16 currentChannel = currentChannels[1];
1109 m_actuatorSettings[currentChannel].channelMax = value;
1110 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs (Max)").arg(currentChannel + 1).arg(value));
1112 // Adjust neutral and min
1113 if (ui->reverseCheckbox2->isChecked()) {
1114 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1115 ui->servoCenterAngleSlider2->setValue(value);
1117 if (value >= m_actuatorSettings[currentChannel].channelMin) {
1118 ui->servoMinAngleSlider2->setValue(value);
1120 } else {
1121 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1122 ui->servoCenterAngleSlider2->setValue(value);
1124 if (value <= m_actuatorSettings[currentChannel].channelMin) {
1125 ui->servoMinAngleSlider2->setValue(value);
1128 debugLogChannelValues(false);
1131 void OutputCalibrationPage::on_reverseCheckbox2_toggled(bool checked)
1133 Q_UNUSED(checked);
1134 QList<quint16> currentChannels;
1135 getCurrentChannels(currentChannels);
1136 quint16 currentChannel = currentChannels[1];
1139 reverseCheckBoxIsToggled(currentChannel, ui->reverseCheckbox2,
1140 ui->servoCenterAngleSlider2, ui->servoMinAngleSlider2, ui->servoMaxAngleSlider2);
1142 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs (Max)")
1143 .arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelMax));
1146 void OutputCalibrationPage::on_calibrateAllMotors_toggled(bool checked)
1148 Q_UNUSED(checked);
1149 setupVehicleHighlightedPart();
1152 void OutputCalibrationPage::resetOutputCalibrationUtil()
1154 if (m_calibrationUtil) {
1155 delete m_calibrationUtil;
1156 m_calibrationUtil = 0;
1158 m_calibrationUtil = new OutputCalibrationUtil();
1162 // Set Min/Max slider values and display servo movement with arrow
1164 void OutputCalibrationPage::setSliderLimitsAndArrows(quint16 currentChannel, bool showFirst, quint16 value,
1165 QCheckBox *revCheckbox, QSlider *minSlider, QSlider *maxSlider)
1167 m_actuatorSettings[currentChannel].channelNeutral = value;
1169 // Adjust min and max
1170 if (revCheckbox->isChecked()) {
1171 if (value >= m_actuatorSettings[currentChannel].channelMin) {
1172 minSlider->setValue(value);
1174 if (value <= m_actuatorSettings[currentChannel].channelMax) {
1175 maxSlider->setValue(value);
1177 } else {
1178 if (value <= m_actuatorSettings[currentChannel].channelMin) {
1179 minSlider->setValue(value);
1181 if (value >= m_actuatorSettings[currentChannel].channelMax) {
1182 maxSlider->setValue(value);
1186 quint16 minValue = (revCheckbox->isChecked()) ? maxSlider->value() : minSlider->value();
1187 quint16 maxValue = (revCheckbox->isChecked()) ? minSlider->value() : maxSlider->value();
1188 quint16 range = maxValue - minValue;
1190 // Reset all arrows
1191 showElementMovement(true, showFirst, 0);
1192 showElementMovement(false, showFirst, 0);
1193 showElementMovement(true, !showFirst, 0);
1194 showElementMovement(false, !showFirst, 0);
1196 // 35% "Dead band" : no arrow display
1197 quint16 limitLow = minValue + (range * 0.35);
1199 quint16 limitHigh = maxValue - (range * 0.35);
1200 quint16 middle = minValue + (range / 2);
1201 qreal arrowOpacity = 0;
1202 if (value < limitLow) {
1203 arrowOpacity = (qreal)(middle - value) / (qreal)(middle - minValue);
1205 showElementMovement(revCheckbox->isChecked(), showFirst, arrowOpacity);
1206 } else if (value > limitHigh) {
1207 arrowOpacity = (qreal)(value - middle) / (qreal)(maxValue - middle);
1208 showElementMovement(!revCheckbox->isChecked(), showFirst, arrowOpacity);
1213 // Set Center/Min/Max slider limits per reverse checkbox status
1215 void OutputCalibrationPage::reverseCheckBoxIsToggled(quint16 currentChannel,
1216 QCheckBox *checkBox, QSlider *centerSlider, QSlider *minSlider, QSlider *maxSlider)
1218 bool checked = checkBox->isChecked();
1220 if (checked && m_actuatorSettings[currentChannel].channelMax > m_actuatorSettings[currentChannel].channelMin) {
1221 quint16 oldMax = m_actuatorSettings[currentChannel].channelMax;
1222 m_actuatorSettings[currentChannel].channelMax = m_actuatorSettings[currentChannel].channelMin;
1223 m_actuatorSettings[currentChannel].channelMin = oldMax;
1224 } else if (!checkBox->isChecked() && m_actuatorSettings[currentChannel].channelMax < m_actuatorSettings[currentChannel].channelMin) {
1225 quint16 oldMax = m_actuatorSettings[currentChannel].channelMax;
1226 m_actuatorSettings[currentChannel].channelMax = m_actuatorSettings[currentChannel].channelMin;
1227 m_actuatorSettings[currentChannel].channelMin = oldMax;
1229 centerSlider->setInvertedAppearance(checked);
1230 centerSlider->setInvertedControls(checked);
1231 minSlider->setInvertedAppearance(checked);
1232 minSlider->setInvertedControls(checked);
1233 maxSlider->setInvertedAppearance(checked);
1234 maxSlider->setInvertedControls(checked);
1236 if (checkBox->isChecked()) {
1237 maxSlider->setValue(m_actuatorSettings[currentChannel].channelMax);
1238 centerSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
1239 minSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
1240 } else {
1241 minSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
1242 centerSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
1243 maxSlider->setValue(m_actuatorSettings[currentChannel].channelMax);