LP-514 Add DShot150/600/1200 and OneShot42/MultiShot Esc support to Wizard
[librepilot.git] / ground / gcs / src / plugins / setupwizard / pages / outputcalibrationpage.cpp
blobd23abac223f76e682f8fba682850e0153f0380ac
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"
31 #include "ui_outputcalibrationpage.h"
33 #include "systemalarms.h"
35 #include "extensionsystem/pluginmanager.h"
36 #include "uavobjectmanager.h"
38 const QString OutputCalibrationPage::MULTI_SVG_FILE = QString(":/setupwizard/resources/multirotor-shapes.svg");
39 const QString OutputCalibrationPage::FIXEDWING_SVG_FILE = QString(":/setupwizard/resources/fixedwing-shapes-wizard.svg");
40 const QString OutputCalibrationPage::GROUND_SVG_FILE = QString(":/setupwizard/resources/ground-shapes-wizard.svg");
42 OutputCalibrationPage::OutputCalibrationPage(SetupWizard *wizard, QWidget *parent) :
43 AbstractWizardPage(wizard, parent), ui(new Ui::OutputCalibrationPage), m_vehicleBoundsItem(0),
44 m_currentWizardIndex(-1), m_calibrationUtil(0)
46 ui->setupUi(this);
48 qDebug() << "calling output calibration page";
49 m_vehicleRenderer = new QSvgRenderer();
51 // move the code that was here to setupVehicle() so we can determine which image to use.
52 m_vehicleScene = new QGraphicsScene(this);
53 ui->vehicleView->setScene(m_vehicleScene);
56 OutputCalibrationPage::~OutputCalibrationPage()
58 if (m_calibrationUtil) {
59 delete m_calibrationUtil;
60 m_calibrationUtil = 0;
63 OutputCalibrationUtil::stopOutputCalibration();
64 delete ui;
67 void OutputCalibrationPage::loadSVGFile(QString file)
69 if (QFile::exists(file) && m_vehicleRenderer->load(file) && m_vehicleRenderer->isValid()) {
70 ui->vehicleView->setScene(m_vehicleScene);
74 void OutputCalibrationPage::setupActuatorMinMaxAndNeutral(int motorChannelStart, int motorChannelEnd, int totalUsedChannels)
76 // Default values for the actuator settings, extra important for
77 // servos since a value out of range can actually destroy the
78 // vehicle if unlucky.
79 // Motors are not that important. REMOVE propellers always!!
80 OutputCalibrationUtil::startOutputCalibration();
82 for (int servoid = 0; servoid < 12; servoid++) {
83 if (servoid >= motorChannelStart && servoid <= motorChannelEnd) {
84 // Set to motor safe values
85 m_actuatorSettings[servoid].channelMin = getLowOutputRate();
86 m_actuatorSettings[servoid].channelNeutral = getLowOutputRate();
87 m_actuatorSettings[servoid].channelMax = getHighOutputRate();
88 m_actuatorSettings[servoid].isReversableMotor = false;
89 // Car, Tank, Boat and Boat differential should use reversable Esc/motors
90 if ((getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_CAR)
91 || (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL)
92 || (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_BOAT)
93 || (getWizard()->getVehicleSubType() == SetupWizard::GROUNDVEHICLE_DIFFERENTIAL_BOAT)) {
94 m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
95 m_actuatorSettings[servoid].isReversableMotor = true;
97 // Set initial output value
98 m_calibrationUtil->startChannelOutput(servoid, m_actuatorSettings[servoid].channelNeutral);
99 m_calibrationUtil->stopChannelOutput();
100 } else if (servoid < totalUsedChannels) {
101 // Set to servo safe values
102 m_actuatorSettings[servoid].channelMin = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
103 m_actuatorSettings[servoid].channelNeutral = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
104 m_actuatorSettings[servoid].channelMax = NEUTRAL_OUTPUT_RATE_MILLISECONDS;
105 // Set initial servo output value
106 m_calibrationUtil->startChannelOutput(servoid, NEUTRAL_OUTPUT_RATE_MILLISECONDS);
107 m_calibrationUtil->stopChannelOutput();
108 } else {
109 // "Disable" these channels
110 m_actuatorSettings[servoid].channelMin = LOW_OUTPUT_RATE_MILLISECONDS;
111 m_actuatorSettings[servoid].channelNeutral = LOW_OUTPUT_RATE_MILLISECONDS;
112 m_actuatorSettings[servoid].channelMax = LOW_OUTPUT_RATE_MILLISECONDS;
117 void OutputCalibrationPage::setupVehicle()
119 m_actuatorSettings = getWizard()->getActuatorSettings();
120 m_wizardIndexes.clear();
121 m_vehicleElementIds.clear();
122 m_vehicleElementTypes.clear();
123 m_vehicleHighlightElementIndexes.clear();
124 m_channelIndex.clear();
125 m_currentWizardIndex = 0;
126 m_vehicleScene->clear();
128 resetOutputCalibrationUtil();
130 switch (getWizard()->getVehicleSubType()) {
131 case SetupWizard::MULTI_ROTOR_TRI_Y:
132 // Loads the SVG file resourse and sets the scene
133 loadSVGFile(MULTI_SVG_FILE);
135 // The m_wizardIndexes array contains the index of the QStackedWidget
136 // in the page to use for each step.
137 // 0 : Start
138 // 1 : Motor page
139 // 2 : single Servo page
140 // 3 : Dual servo page, followed with -1 : Blank page.
141 m_wizardIndexes << 0 << 1 << 1 << 1 << 2;
143 // All element ids to load from the svg file and manage.
144 m_vehicleElementIds << "tri" << "tri-frame" << "tri-m1" << "tri-m2" << "tri-m3" << "tri-s1";
146 // The type of each element.
147 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << SERVO;
149 // The index of the elementId to highlight ( not dim ) for each step
150 // this is the index in the m_vehicleElementIds - 1.
151 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
153 // The channel number to configure for each step.
154 m_channelIndex << 0 << 0 << 1 << 2 << 3;
156 setupActuatorMinMaxAndNeutral(0, 2, 4);
158 getWizard()->setActuatorSettings(m_actuatorSettings);
159 break;
160 case SetupWizard::MULTI_ROTOR_QUAD_X:
161 loadSVGFile(MULTI_SVG_FILE);
162 m_wizardIndexes << 0 << 1 << 1 << 1 << 1;
163 m_vehicleElementIds << "quad-x" << "quad-x-frame" << "quad-x-m1" << "quad-x-m2" << "quad-x-m3" << "quad-x-m4";
164 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR;
165 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
166 m_channelIndex << 0 << 0 << 1 << 2 << 3;
167 setupActuatorMinMaxAndNeutral(0, 3, 4);
168 break;
169 case SetupWizard::MULTI_ROTOR_QUAD_PLUS:
170 loadSVGFile(MULTI_SVG_FILE);
171 m_wizardIndexes << 0 << 1 << 1 << 1 << 1;
172 m_vehicleElementIds << "quad-p" << "quad-p-frame" << "quad-p-m1" << "quad-p-m2" << "quad-p-m3" << "quad-p-m4";
173 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR;
174 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
175 m_channelIndex << 0 << 0 << 1 << 2 << 3;
176 setupActuatorMinMaxAndNeutral(0, 3, 4);
177 break;
178 case SetupWizard::MULTI_ROTOR_HEXA:
179 loadSVGFile(MULTI_SVG_FILE);
180 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
181 m_vehicleElementIds << "hexa" << "hexa-frame" << "hexa-m1" << "hexa-m2" << "hexa-m3" << "hexa-m4" << "hexa-m5" << "hexa-m6";
182 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
183 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6;
184 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
185 setupActuatorMinMaxAndNeutral(0, 5, 6);
186 break;
187 case SetupWizard::MULTI_ROTOR_HEXA_COAX_Y:
188 loadSVGFile(MULTI_SVG_FILE);
189 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
190 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";
191 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
192 m_vehicleHighlightElementIndexes << 0 << 2 << 1 << 4 << 3 << 6 << 5;
193 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
194 setupActuatorMinMaxAndNeutral(0, 5, 6);
195 break;
196 case SetupWizard::MULTI_ROTOR_HEXA_H:
197 loadSVGFile(MULTI_SVG_FILE);
198 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
199 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";
200 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
201 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6;
202 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
203 setupActuatorMinMaxAndNeutral(0, 5, 6);
204 break;
205 case SetupWizard::MULTI_ROTOR_HEXA_X:
206 loadSVGFile(MULTI_SVG_FILE);
207 m_wizardIndexes << 0 << 1 << 1 << 1 << 1 << 1 << 1;
208 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";
209 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR << MOTOR;
210 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5 << 6;
211 m_channelIndex << 0 << 0 << 1 << 2 << 3 << 4 << 5;
212 setupActuatorMinMaxAndNeutral(0, 5, 6);
213 break;
214 // Fixed Wing
215 case SetupWizard::FIXED_WING_DUAL_AILERON:
216 loadSVGFile(FIXEDWING_SVG_FILE);
217 m_wizardIndexes << 0 << 1 << 3 << -1 << 2 << 2;
218 m_vehicleElementIds << "aileron" << "aileron-frame" << "aileron-motor" << "aileron-ail-left" << "aileron-ail-right" << "aileron-elevator" << "aileron-rudder";
219 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO << SERVO << SERVO;
220 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5;
221 m_channelIndex << 0 << 3 << 0 << 5 << 1 << 2;
223 setupActuatorMinMaxAndNeutral(3, 3, 6); // should be 5 instead 6 but output 5 is not used
225 getWizard()->setActuatorSettings(m_actuatorSettings);
226 break;
227 case SetupWizard::FIXED_WING_AILERON:
228 loadSVGFile(FIXEDWING_SVG_FILE);
229 m_wizardIndexes << 0 << 1 << 2 << 2 << 2;
230 m_vehicleElementIds << "singleaileron" << "singleaileron-frame" << "singleaileron-motor" << "singleaileron-aileron" << "singleaileron-elevator" << "singleaileron-rudder";
231 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO << SERVO;
232 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4;
233 m_channelIndex << 0 << 3 << 0 << 1 << 2;
235 setupActuatorMinMaxAndNeutral(3, 3, 4);
237 getWizard()->setActuatorSettings(m_actuatorSettings);
238 break;
239 case SetupWizard::FIXED_WING_ELEVON:
240 loadSVGFile(FIXEDWING_SVG_FILE);
241 m_wizardIndexes << 0 << 1 << 3 << -1;
242 m_vehicleElementIds << "elevon" << "elevon-frame" << "elevon-motor" << "elevon-left" << "elevon-right";
243 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO;
244 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3;
245 m_channelIndex << 0 << 3 << 0 << 1;
247 setupActuatorMinMaxAndNeutral(3, 3, 3);
249 getWizard()->setActuatorSettings(m_actuatorSettings);
250 break;
251 case SetupWizard::FIXED_WING_VTAIL:
252 loadSVGFile(FIXEDWING_SVG_FILE);
253 m_wizardIndexes << 0 << 1 << 3 << -1 << 3 << -1;
254 m_vehicleElementIds << "vtail" << "vtail-frame" << "vtail-motor" << "vtail-ail-left" << "vtail-ail-right" << "vtail-rudder-left" << "vtail-rudder-right";
255 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO << SERVO << SERVO << SERVO;
256 m_vehicleHighlightElementIndexes << 0 << 1 << 2 << 3 << 4 << 5;
257 m_channelIndex << 0 << 3 << 0 << 5 << 2 << 1;
259 setupActuatorMinMaxAndNeutral(3, 3, 6); // should be 5 instead 6 but output 5 is not used
261 getWizard()->setActuatorSettings(m_actuatorSettings);
262 break;
264 // Ground vehicles
265 case SetupWizard::GROUNDVEHICLE_CAR:
266 loadSVGFile(GROUND_SVG_FILE);
267 m_wizardIndexes << 0 << 1 << 2;
268 m_vehicleElementIds << "car" << "car-frame" << "car-motor" << "car-steering";
269 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO;
270 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
271 m_channelIndex << 0 << 3 << 0;
273 setupActuatorMinMaxAndNeutral(3, 3, 2);
275 getWizard()->setActuatorSettings(m_actuatorSettings);
276 break;
277 case SetupWizard::GROUNDVEHICLE_DIFFERENTIAL:
278 loadSVGFile(GROUND_SVG_FILE);
279 m_wizardIndexes << 0 << 1 << 1;
280 m_vehicleElementIds << "tank" << "tank-frame" << "tank-left-motor" << "tank-right-motor";
281 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR;
282 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
283 m_channelIndex << 0 << 0 << 1;
285 setupActuatorMinMaxAndNeutral(0, 1, 2);
287 getWizard()->setActuatorSettings(m_actuatorSettings);
288 break;
289 case SetupWizard::GROUNDVEHICLE_MOTORCYCLE:
290 loadSVGFile(GROUND_SVG_FILE);
291 m_wizardIndexes << 0 << 1 << 2;
292 m_vehicleElementIds << "motorbike" << "motorbike-frame" << "motorbike-motor" << "motorbike-steering";
293 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO;
294 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
295 m_channelIndex << 0 << 3 << 0;
297 setupActuatorMinMaxAndNeutral(3, 3, 2);
299 getWizard()->setActuatorSettings(m_actuatorSettings);
300 break;
301 case SetupWizard::GROUNDVEHICLE_BOAT:
302 loadSVGFile(GROUND_SVG_FILE);
303 m_wizardIndexes << 0 << 1 << 2;
304 m_vehicleElementIds << "boat" << "boat-frame" << "boat-motor" << "boat-rudder";
305 m_vehicleElementTypes << FULL << FRAME << MOTOR << SERVO;
306 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
307 m_channelIndex << 0 << 3 << 0;
309 setupActuatorMinMaxAndNeutral(3, 3, 2);
311 getWizard()->setActuatorSettings(m_actuatorSettings);
312 break;
313 case SetupWizard::GROUNDVEHICLE_DIFFERENTIAL_BOAT:
314 loadSVGFile(GROUND_SVG_FILE);
315 m_wizardIndexes << 0 << 1 << 1;
316 m_vehicleElementIds << "boat_diff" << "boat_diff-frame" << "boat_diff-left-motor" << "boat_diff-right-motor";
317 m_vehicleElementTypes << FULL << FRAME << MOTOR << MOTOR;
318 m_vehicleHighlightElementIndexes << 0 << 1 << 2;
319 m_channelIndex << 0 << 0 << 1;
321 setupActuatorMinMaxAndNeutral(0, 1, 2);
323 getWizard()->setActuatorSettings(m_actuatorSettings);
324 break;
326 default:
327 break;
330 setupVehicleItems();
333 void OutputCalibrationPage::setupVehicleItems()
335 m_vehicleItems.clear();
336 m_arrowsItems.clear();
337 m_vehicleBoundsItem = new QGraphicsSvgItem();
338 m_vehicleBoundsItem->setSharedRenderer(m_vehicleRenderer);
339 m_vehicleBoundsItem->setElementId(m_vehicleElementIds[0]);
340 m_vehicleBoundsItem->setZValue(-1);
341 m_vehicleBoundsItem->setOpacity(0);
342 m_vehicleScene->addItem(m_vehicleBoundsItem);
344 QRectF parentBounds = m_vehicleRenderer->boundsOnElement(m_vehicleElementIds[0]);
346 for (int i = 1; i < m_vehicleElementIds.size(); i++) {
347 QGraphicsSvgItem *item = new QGraphicsSvgItem();
348 item->setSharedRenderer(m_vehicleRenderer);
349 item->setElementId(m_vehicleElementIds[i]);
350 item->setZValue(i);
351 item->setOpacity(1.0);
353 QRectF itemBounds = m_vehicleRenderer->boundsOnElement(m_vehicleElementIds[i]);
354 item->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y());
356 m_vehicleScene->addItem(item);
357 m_vehicleItems << item;
359 bool addArrows = false;
361 if ((m_vehicleElementIds[i].contains("left")) || (m_vehicleElementIds[i].contains("right"))
362 || (m_vehicleElementIds[i].contains("elevator")) || (m_vehicleElementIds[i].contains("rudder"))
363 || (m_vehicleElementIds[i].contains("steering")) || (m_vehicleElementIds[i] == "singleaileron-aileron")) {
364 addArrows = true;
367 if (addArrows) {
368 QString arrowUp = "-up"; // right if rudder / steering
369 QString arrowDown = "-down"; // left
371 QGraphicsSvgItem *itemUp = new QGraphicsSvgItem();
373 itemUp->setSharedRenderer(m_vehicleRenderer);
374 QString elementUp = m_vehicleElementIds[i] + arrowUp;
375 itemUp->setElementId(elementUp);
376 itemUp->setZValue(i + 10);
377 itemUp->setOpacity(0);
379 QRectF itemBounds = m_vehicleRenderer->boundsOnElement(elementUp);
380 itemUp->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y());
381 m_vehicleScene->addItem(itemUp);
383 m_arrowsItems << itemUp;
385 QGraphicsSvgItem *itemDown = new QGraphicsSvgItem();
386 itemDown->setSharedRenderer(m_vehicleRenderer);
387 QString elementDown = m_vehicleElementIds[i] + arrowDown;
388 itemDown->setElementId(elementDown);
389 itemDown->setZValue(i + 10);
390 itemDown->setOpacity(0);
392 itemBounds = m_vehicleRenderer->boundsOnElement(elementDown);
393 itemDown->setPos(itemBounds.x() - parentBounds.x(), itemBounds.y() - parentBounds.y());
394 m_vehicleScene->addItem(itemDown);
396 m_arrowsItems << itemDown;
401 void OutputCalibrationPage::startWizard()
403 ui->calibrationStack->setCurrentIndex(m_wizardIndexes[0]);
404 enableAllMotorsCheckBox(true);
405 setupVehicleHighlightedPart();
408 void OutputCalibrationPage::setupVehicleHighlightedPart()
410 qreal dimOpaque = m_currentWizardIndex == 0 ? 1.0 : 0.3;
411 qreal highlightOpaque = 1.0;
412 int highlightedIndex = m_vehicleHighlightElementIndexes[m_currentWizardIndex];
414 bool isDualServoSetup = (m_wizardIndexes[m_currentWizardIndex] == 3);
416 for (int i = 0; i < m_vehicleItems.size(); i++) {
417 QGraphicsSvgItem *item = m_vehicleItems[i];
418 if (highlightedIndex == i || (isDualServoSetup && ((highlightedIndex + 1) == i)) ||
419 (ui->calibrateAllMotors->isChecked() && m_vehicleElementTypes[i + 1] == MOTOR)) {
420 item->setOpacity(highlightOpaque);
421 } else {
422 item->setOpacity(dimOpaque);
427 void OutputCalibrationPage::showElementMovement(bool isUp, bool firstServo, qreal value)
429 QString highlightedItemName;
431 if (firstServo) {
432 highlightedItemName = m_vehicleItems[m_currentWizardIndex]->elementId();
433 } else {
434 if ((m_currentWizardIndex + 1) < m_wizardIndexes.size()) {
435 highlightedItemName = m_vehicleItems[m_currentWizardIndex + 1]->elementId();
439 for (int i = 0; i < m_arrowsItems.size(); i++) {
440 QString upItemName = highlightedItemName + "-up";
441 QString downItemName = highlightedItemName + "-down";
442 if (m_arrowsItems[i]->elementId() == upItemName) {
443 QGraphicsSvgItem *itemUp = m_arrowsItems[i];
444 itemUp->setOpacity(isUp ? value : 0);
446 if (m_arrowsItems[i]->elementId() == downItemName) {
447 QGraphicsSvgItem *itemDown = m_arrowsItems[i];
448 itemDown->setOpacity(isUp ? 0 : value);
453 void OutputCalibrationPage::setWizardPage()
455 qDebug() << "Wizard index: " << m_currentWizardIndex;
457 QApplication::processEvents();
459 int currentPageIndex = m_wizardIndexes[m_currentWizardIndex];
460 qDebug() << "Current page: " << currentPageIndex;
461 ui->calibrationStack->setCurrentIndex(currentPageIndex);
463 QList<quint16> currentChannels;
464 getCurrentChannels(currentChannels);
465 int currentChannel = currentChannels[0];
466 qDebug() << "Current channel: " << currentChannel + 1;
467 if (currentChannel >= 0) {
468 if (currentPageIndex == 1) {
469 // Set Min, Neutral and Max for slider in all cases, needed for DShot.
470 ui->motorNeutralSlider->setMinimum(m_actuatorSettings[currentChannel].channelMin);
471 ui->motorNeutralSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
472 ui->motorNeutralSlider->setMaximum(m_actuatorSettings[currentChannel].channelMin + NEUTRAL_OUTPUT_RATE_RANGE);
473 if (ui->motorNeutralSlider->minimum() == LOW_OUTPUT_RATE_DSHOT) {
474 // DShot output
475 ui->motorPWMValue->setText(QString(tr("Digital output value : <b>%1</b>")).arg(m_actuatorSettings[currentChannel].channelNeutral));
476 } else {
477 ui->motorPWMValue->setText(QString(tr("Output value : <b>%1</b> µs")).arg(m_actuatorSettings[currentChannel].channelNeutral));
479 // Reversable motor found
480 if (m_actuatorSettings[currentChannel].isReversableMotor) {
481 ui->motorNeutralSlider->setMaximum(m_actuatorSettings[currentChannel].channelMax);
482 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>"));
484 } else if (currentPageIndex == 2) {
485 ui->servoPWMValue->setText(tr("Output value : <b>%1</b> µs").arg(m_actuatorSettings[currentChannel].channelNeutral));
486 if (m_actuatorSettings[currentChannel].channelMax < m_actuatorSettings[currentChannel].channelMin &&
487 !ui->reverseCheckbox->isChecked()) {
488 ui->reverseCheckbox->setChecked(true);
489 } else {
490 ui->reverseCheckbox->setChecked(false);
492 enableServoSliders(false);
493 if (ui->reverseCheckbox->isChecked()) {
494 ui->servoMaxAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMax);
495 ui->servoCenterAngleSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
496 ui->servoMinAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
497 } else {
498 ui->servoMinAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
499 ui->servoCenterAngleSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
500 ui->servoMaxAngleSlider->setValue(m_actuatorSettings[currentChannel].channelMax);
502 } else if (currentPageIndex == 3) {
503 // Dual channel setup : two ailerons or Vtail
504 // First channel
505 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelNeutral));
508 if (m_actuatorSettings[currentChannel].channelMax < m_actuatorSettings[currentChannel].channelMin &&
509 !ui->reverseCheckbox1->isChecked()) {
510 ui->reverseCheckbox1->setChecked(true);
511 } else {
512 ui->reverseCheckbox1->setChecked(false);
514 enableServoSliders(false);
515 if (ui->reverseCheckbox1->isChecked()) {
516 ui->servoMaxAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMax);
517 ui->servoCenterAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelNeutral);
518 ui->servoMinAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMin);
519 } else {
520 ui->servoMinAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMin);
521 ui->servoCenterAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelNeutral);
522 ui->servoMaxAngleSlider1->setValue(m_actuatorSettings[currentChannel].channelMax);
524 // Second channel
525 int nextChannel = currentChannels[1];
526 qDebug() << "Current channel: " << currentChannel + 1 << " and " << nextChannel + 1
528 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs").arg(nextChannel + 1).arg(m_actuatorSettings[nextChannel].channelNeutral));
530 if (m_actuatorSettings[nextChannel].channelMax < m_actuatorSettings[nextChannel].channelMin &&
531 !ui->reverseCheckbox2->isChecked()) {
532 ui->reverseCheckbox2->setChecked(true);
533 } else {
534 ui->reverseCheckbox2->setChecked(false);
536 enableServoSliders(false);
537 if (ui->reverseCheckbox2->isChecked()) {
538 ui->servoMaxAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMax);
539 ui->servoCenterAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelNeutral);
540 ui->servoMinAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMin);
541 } else {
542 ui->servoMinAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMin);
543 ui->servoCenterAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelNeutral);
544 ui->servoMaxAngleSlider2->setValue(m_actuatorSettings[nextChannel].channelMax);
548 setupVehicleHighlightedPart();
549 // Hide arrows
550 showElementMovement(true, true, 0);
551 showElementMovement(false, true, 0);
552 showElementMovement(true, false, 0);
553 showElementMovement(false, false, 0);
556 void OutputCalibrationPage::initializePage()
558 if (m_vehicleScene) {
559 setupVehicle();
560 startWizard();
564 bool OutputCalibrationPage::validatePage()
566 if (!isFinished()) {
567 m_currentWizardIndex++;
568 while (!isFinished() && m_wizardIndexes[m_currentWizardIndex] == -1) {
569 // Skip step, found a blank page
570 // Dual servo setup, a '3' page is followed with a '-1' page
571 m_currentWizardIndex++;
573 if (ui->calibrateAllMotors->isChecked() &&
574 m_currentWizardIndex > 0 &&
575 m_wizardIndexes[m_currentWizardIndex - 1] == 1) {
576 while (!isFinished() && m_wizardIndexes[m_currentWizardIndex] == 1) {
577 m_currentWizardIndex++;
582 if (isFinished()) {
583 getWizard()->setActuatorSettings(m_actuatorSettings);
584 return true;
585 } else {
586 setWizardPage();
587 return false;
591 void OutputCalibrationPage::showEvent(QShowEvent *event)
593 Q_UNUSED(event);
594 if (m_vehicleBoundsItem) {
595 ui->vehicleView->setSceneRect(m_vehicleBoundsItem->boundingRect());
596 ui->vehicleView->fitInView(m_vehicleBoundsItem, Qt::KeepAspectRatio);
600 void OutputCalibrationPage::resizeEvent(QResizeEvent *event)
602 Q_UNUSED(event);
603 if (m_vehicleBoundsItem) {
604 ui->vehicleView->setSceneRect(m_vehicleBoundsItem->boundingRect());
605 ui->vehicleView->fitInView(m_vehicleBoundsItem, Qt::KeepAspectRatio);
609 void OutputCalibrationPage::customBackClicked()
611 if (m_currentWizardIndex >= 0) {
612 m_currentWizardIndex--;
613 while (m_currentWizardIndex > 0 &&
614 m_wizardIndexes[m_currentWizardIndex] == -1 &&
615 m_wizardIndexes[m_currentWizardIndex - 1] == 3) {
616 // Skip step, found a blank page
617 // Dual servo setup, a '3' page is followed with a '-1' page
618 m_currentWizardIndex--;
620 if (ui->calibrateAllMotors->isChecked()) {
621 while (m_currentWizardIndex > 0 &&
622 m_wizardIndexes[m_currentWizardIndex] == 1 &&
623 m_wizardIndexes[m_currentWizardIndex - 1] == 1) {
624 m_currentWizardIndex--;
629 if (m_currentWizardIndex >= 0) {
630 setWizardPage();
631 } else {
632 getWizard()->back();
636 void OutputCalibrationPage::getCurrentChannels(QList<quint16> &channels)
638 if (ui->calibrateAllMotors->isChecked()) {
639 for (int i = 1; i < m_channelIndex.size(); i++) {
640 if (m_vehicleElementTypes[i + 1] == MOTOR) {
641 channels << m_channelIndex[i];
644 } else {
645 channels << m_channelIndex[m_currentWizardIndex];
646 // Add next channel for dual servo setup
647 if (m_wizardIndexes[m_currentWizardIndex] == 3) {
648 channels << m_channelIndex[m_currentWizardIndex + 1];
653 void OutputCalibrationPage::enableAllMotorsCheckBox(bool enable)
655 if (getWizard()->getVehicleType() == SetupWizard::VEHICLE_MULTI) {
656 ui->calibrateAllMotors->setVisible(true);
657 ui->calibrateAllMotors->setEnabled(enable);
658 } else {
659 ui->calibrateAllMotors->setChecked(false);
660 ui->calibrateAllMotors->setVisible(false);
664 void OutputCalibrationPage::enableButtons(bool enable)
666 getWizard()->button(QWizard::NextButton)->setEnabled(enable);
667 getWizard()->button(QWizard::CustomButton1)->setEnabled(enable);
668 getWizard()->button(QWizard::CancelButton)->setEnabled(enable);
669 getWizard()->button(QWizard::BackButton)->setEnabled(enable);
670 enableAllMotorsCheckBox(enable);
671 QApplication::processEvents();
674 void OutputCalibrationPage::on_motorNeutralButton_toggled(bool checked)
676 ui->motorNeutralButton->setText(checked ? tr("Stop") : tr("Start"));
677 ui->motorNeutralSlider->setEnabled(checked);
679 QList<quint16> currentChannels;
680 getCurrentChannels(currentChannels);
681 quint16 currentChannel = currentChannels[0];
683 quint16 safeValue = m_actuatorSettings[currentChannel].channelMin;
685 if (m_actuatorSettings[currentChannel].isReversableMotor) {
686 safeValue = m_actuatorSettings[currentChannel].channelNeutral;
689 onStartButtonToggle(ui->motorNeutralButton, currentChannels, m_actuatorSettings[currentChannel].channelNeutral, safeValue, ui->motorNeutralSlider);
692 void OutputCalibrationPage::onStartButtonToggle(QAbstractButton *button, QList<quint16> &channels,
693 quint16 value, quint16 safeValue, QSlider *slider)
695 if (button->isChecked()) {
696 // Start calibration
697 if (checkAlarms()) {
698 enableButtons(false);
699 enableServoSliders(true);
700 m_calibrationUtil->startChannelOutput(channels, safeValue);
701 slider->setValue(value);
702 m_calibrationUtil->setChannelOutputValue(value);
703 } else {
704 button->setChecked(false);
706 } else {
707 // Stop calibration
708 quint16 channel = channels[0];
709 if ((button == ui->motorNeutralButton) && !m_actuatorSettings[channel].isReversableMotor) {
710 // Normal motor
711 m_calibrationUtil->startChannelOutput(channels, m_actuatorSettings[channel].channelMin);
712 } else {
713 // Servos and ReversableMotors
714 m_calibrationUtil->startChannelOutput(channels, m_actuatorSettings[channel].channelNeutral);
717 m_calibrationUtil->stopChannelOutput();
719 enableServoSliders(false);
720 enableButtons(true);
722 debugLogChannelValues(true);
725 void OutputCalibrationPage::onStartButtonToggleDual(QAbstractButton *button, QList<quint16> &channels,
726 quint16 value1, quint16 value2,
727 quint16 safeValue,
728 QSlider *slider1, QSlider *slider2)
730 if (button->isChecked()) {
731 // Start calibration
732 if (checkAlarms()) {
733 enableButtons(false);
734 enableServoSliders(true);
735 m_calibrationUtil->startChannelOutput(channels, safeValue);
737 slider1->setValue(value1);
738 slider2->setValue(value2);
739 m_calibrationUtil->setChannelDualOutputValue(value1, value2);
740 } else {
741 button->setChecked(false);
743 } else {
744 // Stop calibration
745 quint16 channel1 = channels[0];
746 quint16 channel2 = channels[1];
748 m_calibrationUtil->startChannelOutput(channels, m_actuatorSettings[channel1].channelNeutral);
749 m_calibrationUtil->stopChannelDualOutput(m_actuatorSettings[channel1].channelNeutral, m_actuatorSettings[channel2].channelNeutral);
751 m_calibrationUtil->stopChannelOutput();
753 enableServoSliders(false);
754 enableButtons(true);
756 debugLogChannelValues(true);
759 void OutputCalibrationPage::enableServoSliders(bool enabled)
761 ui->servoCenterAngleSlider->setEnabled(enabled);
762 ui->servoMinAngleSlider->setEnabled(enabled);
763 ui->servoMaxAngleSlider->setEnabled(enabled);
764 ui->reverseCheckbox->setEnabled(!enabled);
766 ui->servoCenterAngleSlider1->setEnabled(enabled);
767 ui->servoMinAngleSlider1->setEnabled(enabled);
768 ui->servoMaxAngleSlider1->setEnabled(enabled);
769 ui->reverseCheckbox1->setEnabled(!enabled);
770 ui->servoCenterAngleSlider2->setEnabled(enabled);
771 ui->servoMinAngleSlider2->setEnabled(enabled);
772 ui->servoMaxAngleSlider2->setEnabled(enabled);
773 ui->reverseCheckbox2->setEnabled(!enabled);
774 // Hide arrows
775 showElementMovement(true, true, 0);
776 showElementMovement(false, true, 0);
779 bool OutputCalibrationPage::checkAlarms()
781 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
782 UAVObjectManager *uavObjectManager = pm->getObject<UAVObjectManager>();
784 Q_ASSERT(uavObjectManager);
785 SystemAlarms *systemAlarms = SystemAlarms::GetInstance(uavObjectManager);
786 Q_ASSERT(systemAlarms);
787 SystemAlarms::DataFields data = systemAlarms->getData();
789 if (data.Alarm[SystemAlarms::ALARM_ACTUATOR] != SystemAlarms::ALARM_OK) {
790 QMessageBox mbox(this);
791 mbox.setText(QString(tr("The actuator module is in an error state.\n\n"
792 "Please make sure the correct firmware version is used then "
793 "restart the wizard and try again. If the problem persists please "
794 "consult the librepilot.org support forum.")));
795 mbox.setStandardButtons(QMessageBox::Ok);
796 mbox.setIcon(QMessageBox::Critical);
798 getWizard()->setWindowFlags(getWizard()->windowFlags() & ~Qt::WindowStaysOnTopHint);
800 mbox.exec();
802 getWizard()->setWindowFlags(getWizard()->windowFlags() | Qt::WindowStaysOnTopHint);
803 getWizard()->setWindowIcon(qApp->windowIcon());
804 getWizard()->show();
805 return false;
807 return true;
810 void OutputCalibrationPage::debugLogChannelValues(bool showFirst)
812 QList<quint16> currentChannels;
813 quint16 currentChannel;
815 getCurrentChannels(currentChannels);
816 if (showFirst) {
817 currentChannel = currentChannels[0];
818 } else {
819 currentChannel = currentChannels[1];
821 qDebug() << "ChannelMin : " << m_actuatorSettings[currentChannel].channelMin;
822 qDebug() << "ChannelNeutral: " << m_actuatorSettings[currentChannel].channelNeutral;
823 qDebug() << "ChannelMax : " << m_actuatorSettings[currentChannel].channelMax;
826 int OutputCalibrationPage::getLowOutputRate()
828 if (getWizard()->getEscType() == VehicleConfigurationSource::ESC_DSHOT150 ||
829 getWizard()->getEscType() == VehicleConfigurationSource::ESC_DSHOT600 ||
830 getWizard()->getEscType() == VehicleConfigurationSource::ESC_DSHOT1200) {
831 return LOW_OUTPUT_RATE_DSHOT;
832 } else {
833 return LOW_OUTPUT_RATE_MILLISECONDS_PWM;
837 int OutputCalibrationPage::getHighOutputRate()
839 if (getWizard()->getEscType() == VehicleConfigurationSource::ESC_ONESHOT125 ||
840 getWizard()->getEscType() == VehicleConfigurationSource::ESC_ONESHOT42 ||
841 getWizard()->getEscType() == VehicleConfigurationSource::ESC_MULTISHOT) {
842 return HIGH_OUTPUT_RATE_MILLISECONDS_ONESHOT_MULTISHOT;
843 } else if (getWizard()->getEscType() == VehicleConfigurationSource::ESC_DSHOT150 ||
844 getWizard()->getEscType() == VehicleConfigurationSource::ESC_DSHOT600 ||
845 getWizard()->getEscType() == VehicleConfigurationSource::ESC_DSHOT1200) {
846 return HIGH_OUTPUT_RATE_DSHOT;
847 } else {
848 return HIGH_OUTPUT_RATE_MILLISECONDS_PWM;
852 void OutputCalibrationPage::on_motorNeutralSlider_valueChanged(int value)
854 Q_UNUSED(value);
856 if (ui->motorNeutralSlider->minimum() == LOW_OUTPUT_RATE_DSHOT) {
857 // DShot output
858 ui->motorPWMValue->setText(QString(tr("Digital output value : <b>%1</b>")).arg(value));
859 } else {
860 ui->motorPWMValue->setText(QString(tr("Output value : <b>%1</b> µs")).arg(value));
862 if (ui->motorNeutralButton->isChecked()) {
863 quint16 value = ui->motorNeutralSlider->value();
864 m_calibrationUtil->setChannelOutputValue(value);
866 QList<quint16> currentChannels;
867 getCurrentChannels(currentChannels);
868 foreach(quint16 channel, currentChannels) {
869 m_actuatorSettings[channel].channelNeutral = value;
871 debugLogChannelValues(true);
875 void OutputCalibrationPage::on_servoButton_toggled(bool checked)
877 ui->servoButton->setText(checked ? tr("Stop") : tr("Start"));
878 // Now we set servos, motors are done (Tricopter fix)
879 ui->calibrateAllMotors->setChecked(false);
881 QList<quint16> currentChannels;
882 getCurrentChannels(currentChannels);
883 quint16 currentChannel = currentChannels[0];
885 quint16 safeValue = m_actuatorSettings[currentChannel].channelNeutral;
886 onStartButtonToggle(ui->servoButton, currentChannels, safeValue, safeValue, ui->servoCenterAngleSlider);
889 void OutputCalibrationPage::on_dualservoButton_toggled(bool checked)
891 ui->dualservoButton->setText(checked ? tr("Stop") : tr("Start"));
892 // Now we set servos, motors are done (Tricopter fix)
893 ui->calibrateAllMotors->setChecked(false);
895 QList<quint16> currentChannels;
896 getCurrentChannels(currentChannels);
897 quint16 currentChannel = currentChannels[0];
898 quint16 nextChannel = currentChannels[1];
900 quint16 safeValue1 = m_actuatorSettings[currentChannel].channelNeutral;
901 quint16 safeValue2 = m_actuatorSettings[nextChannel].channelNeutral;
902 onStartButtonToggleDual(ui->dualservoButton, currentChannels, safeValue1, safeValue2, safeValue1,
903 ui->servoCenterAngleSlider1, ui->servoCenterAngleSlider2);
907 // Single servo page (2)
909 void OutputCalibrationPage::on_servoCenterAngleSlider_valueChanged(int position)
911 Q_UNUSED(position);
912 quint16 value = ui->servoCenterAngleSlider->value();
913 m_calibrationUtil->setChannelOutputValue(value);
915 QList<quint16> currentChannels;
916 getCurrentChannels(currentChannels);
917 quint16 currentChannel = currentChannels[0];
919 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(value));
921 bool showFirst = true;
922 setSliderLimitsAndArrows(currentChannel, showFirst, value, ui->reverseCheckbox, ui->servoMinAngleSlider, ui->servoMaxAngleSlider);
924 debugLogChannelValues(showFirst);
927 void OutputCalibrationPage::on_servoMinAngleSlider_valueChanged(int position)
929 Q_UNUSED(position);
930 quint16 value = ui->servoMinAngleSlider->value();
931 m_calibrationUtil->setChannelOutputValue(value);
933 QList<quint16> currentChannels;
934 getCurrentChannels(currentChannels);
935 quint16 currentChannel = currentChannels[0];
936 m_actuatorSettings[currentChannel].channelMin = value;
937 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs (Min)").arg(currentChannel + 1).arg(value));
939 // Adjust neutral and max
940 if (ui->reverseCheckbox->isChecked()) {
941 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
942 ui->servoCenterAngleSlider->setValue(value);
944 if (value <= m_actuatorSettings[currentChannel].channelMax) {
945 ui->servoMaxAngleSlider->setValue(value);
947 } else {
948 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
949 ui->servoCenterAngleSlider->setValue(value);
951 if (value >= m_actuatorSettings[currentChannel].channelMax) {
952 ui->servoMaxAngleSlider->setValue(value);
955 debugLogChannelValues(true);
958 void OutputCalibrationPage::on_servoMaxAngleSlider_valueChanged(int position)
960 Q_UNUSED(position);
961 quint16 value = ui->servoMaxAngleSlider->value();
962 m_calibrationUtil->setChannelOutputValue(value);
964 QList<quint16> currentChannels;
965 getCurrentChannels(currentChannels);
966 quint16 currentChannel = currentChannels[0];
967 m_actuatorSettings[currentChannel].channelMax = value;
968 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs (Max)").arg(currentChannel + 1).arg(value));
970 // Adjust neutral and min
971 if (ui->reverseCheckbox->isChecked()) {
972 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
973 ui->servoCenterAngleSlider->setValue(value);
975 if (value >= m_actuatorSettings[currentChannel].channelMin) {
976 ui->servoMinAngleSlider->setValue(value);
978 } else {
979 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
980 ui->servoCenterAngleSlider->setValue(value);
982 if (value <= m_actuatorSettings[currentChannel].channelMin) {
983 ui->servoMinAngleSlider->setValue(value);
986 debugLogChannelValues(true);
989 void OutputCalibrationPage::on_reverseCheckbox_toggled(bool checked)
991 Q_UNUSED(checked);
992 QList<quint16> currentChannels;
993 getCurrentChannels(currentChannels);
994 quint16 currentChannel = currentChannels[0];
996 reverseCheckBoxIsToggled(currentChannel, ui->reverseCheckbox,
997 ui->servoCenterAngleSlider, ui->servoMinAngleSlider, ui->servoMaxAngleSlider);
999 ui->servoPWMValue->setText(tr("Output %1 value : <b>%2</b> µs (Max)")
1000 .arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelMax));
1004 // Dual servo page (3) - first channel
1006 void OutputCalibrationPage::on_servoCenterAngleSlider1_valueChanged(int position)
1008 Q_UNUSED(position);
1009 quint16 value = ui->servoCenterAngleSlider1->value();
1010 quint16 value2 = ui->servoCenterAngleSlider2->value();
1011 m_calibrationUtil->setChannelDualOutputValue(value, value2);
1013 QList<quint16> currentChannels;
1014 getCurrentChannels(currentChannels);
1015 quint16 currentChannel = currentChannels[0];
1018 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(value));
1020 bool showFirst = true;
1021 setSliderLimitsAndArrows(currentChannel, showFirst, value, ui->reverseCheckbox1, ui->servoMinAngleSlider1, ui->servoMaxAngleSlider1);
1023 debugLogChannelValues(showFirst);
1026 void OutputCalibrationPage::on_servoMinAngleSlider1_valueChanged(int position)
1028 Q_UNUSED(position);
1029 quint16 value = ui->servoMinAngleSlider1->value();
1030 quint16 value2 = ui->servoCenterAngleSlider2->value();
1031 m_calibrationUtil->setChannelDualOutputValue(value, value2);
1033 QList<quint16> currentChannels;
1034 getCurrentChannels(currentChannels);
1035 quint16 currentChannel = currentChannels[0];
1036 m_actuatorSettings[currentChannel].channelMin = value;
1037 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs (Min)").arg(currentChannel + 1).arg(value));
1039 // Adjust neutral and max
1040 if (ui->reverseCheckbox1->isChecked()) {
1041 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1042 ui->servoCenterAngleSlider1->setValue(value);
1044 if (value <= m_actuatorSettings[currentChannel].channelMax) {
1045 ui->servoMaxAngleSlider1->setValue(value);
1047 } else {
1048 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1049 ui->servoCenterAngleSlider1->setValue(value);
1051 if (value >= m_actuatorSettings[currentChannel].channelMax) {
1052 ui->servoMaxAngleSlider1->setValue(value);
1055 debugLogChannelValues(true);
1058 void OutputCalibrationPage::on_servoMaxAngleSlider1_valueChanged(int position)
1060 Q_UNUSED(position);
1061 quint16 value = ui->servoMaxAngleSlider1->value();
1062 quint16 value2 = ui->servoCenterAngleSlider2->value();
1063 m_calibrationUtil->setChannelDualOutputValue(value, value2);
1065 QList<quint16> currentChannels;
1066 getCurrentChannels(currentChannels);
1067 quint16 currentChannel = currentChannels[0];
1068 m_actuatorSettings[currentChannel].channelMax = value;
1069 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs (Max)").arg(currentChannel + 1).arg(value));
1071 // Adjust neutral and min
1072 if (ui->reverseCheckbox1->isChecked()) {
1073 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1074 ui->servoCenterAngleSlider1->setValue(value);
1076 if (value >= m_actuatorSettings[currentChannel].channelMin) {
1077 ui->servoMinAngleSlider1->setValue(value);
1079 } else {
1080 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1081 ui->servoCenterAngleSlider1->setValue(value);
1083 if (value <= m_actuatorSettings[currentChannel].channelMin) {
1084 ui->servoMinAngleSlider1->setValue(value);
1087 debugLogChannelValues(true);
1090 void OutputCalibrationPage::on_reverseCheckbox1_toggled(bool checked)
1092 Q_UNUSED(checked);
1093 QList<quint16> currentChannels;
1094 getCurrentChannels(currentChannels);
1095 quint16 currentChannel = currentChannels[0];
1097 reverseCheckBoxIsToggled(currentChannel, ui->reverseCheckbox1,
1098 ui->servoCenterAngleSlider1, ui->servoMinAngleSlider1, ui->servoMaxAngleSlider1);
1100 ui->servoPWMValue1->setText(tr("Output %1 value : <b>%2</b> µs (Max)")
1101 .arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelMax));
1105 // Dual servo page - second channel
1107 void OutputCalibrationPage::on_servoCenterAngleSlider2_valueChanged(int position)
1109 Q_UNUSED(position);
1110 quint16 value = ui->servoCenterAngleSlider2->value();
1111 quint16 value1 = ui->servoCenterAngleSlider1->value();
1112 m_calibrationUtil->setChannelDualOutputValue(value1, value);
1114 QList<quint16> currentChannels;
1115 getCurrentChannels(currentChannels);
1116 quint16 currentChannel = currentChannels[1];
1118 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs").arg(currentChannel + 1).arg(value));
1120 bool showFirst = false;
1121 setSliderLimitsAndArrows(currentChannel, showFirst, value, ui->reverseCheckbox2, ui->servoMinAngleSlider2, ui->servoMaxAngleSlider2);
1123 debugLogChannelValues(showFirst);
1126 void OutputCalibrationPage::on_servoMinAngleSlider2_valueChanged(int position)
1128 Q_UNUSED(position);
1129 quint16 value = ui->servoMinAngleSlider2->value();
1130 quint16 value1 = ui->servoCenterAngleSlider1->value();
1131 m_calibrationUtil->setChannelDualOutputValue(value1, value);
1133 QList<quint16> currentChannels;
1134 getCurrentChannels(currentChannels);
1135 quint16 currentChannel = currentChannels[1];
1136 m_actuatorSettings[currentChannel].channelMin = value;
1137 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs (Min)").arg(currentChannel + 1).arg(value));
1139 // Adjust neutral and max
1140 if (ui->reverseCheckbox2->isChecked()) {
1141 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1142 ui->servoCenterAngleSlider2->setValue(value);
1144 if (value <= m_actuatorSettings[currentChannel].channelMax) {
1145 ui->servoMaxAngleSlider2->setValue(value);
1147 } else {
1148 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1149 ui->servoCenterAngleSlider2->setValue(value);
1151 if (value >= m_actuatorSettings[currentChannel].channelMax) {
1152 ui->servoMaxAngleSlider2->setValue(value);
1155 debugLogChannelValues(false);
1158 void OutputCalibrationPage::on_servoMaxAngleSlider2_valueChanged(int position)
1160 Q_UNUSED(position);
1161 quint16 value = ui->servoMaxAngleSlider2->value();
1162 quint16 value1 = ui->servoCenterAngleSlider1->value();
1163 m_calibrationUtil->setChannelDualOutputValue(value1, value);
1165 QList<quint16> currentChannels;
1166 getCurrentChannels(currentChannels);
1167 quint16 currentChannel = currentChannels[1];
1168 m_actuatorSettings[currentChannel].channelMax = value;
1169 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs (Max)").arg(currentChannel + 1).arg(value));
1171 // Adjust neutral and min
1172 if (ui->reverseCheckbox2->isChecked()) {
1173 if (value >= m_actuatorSettings[currentChannel].channelNeutral) {
1174 ui->servoCenterAngleSlider2->setValue(value);
1176 if (value >= m_actuatorSettings[currentChannel].channelMin) {
1177 ui->servoMinAngleSlider2->setValue(value);
1179 } else {
1180 if (value <= m_actuatorSettings[currentChannel].channelNeutral) {
1181 ui->servoCenterAngleSlider2->setValue(value);
1183 if (value <= m_actuatorSettings[currentChannel].channelMin) {
1184 ui->servoMinAngleSlider2->setValue(value);
1187 debugLogChannelValues(false);
1190 void OutputCalibrationPage::on_reverseCheckbox2_toggled(bool checked)
1192 Q_UNUSED(checked);
1193 QList<quint16> currentChannels;
1194 getCurrentChannels(currentChannels);
1195 quint16 currentChannel = currentChannels[1];
1198 reverseCheckBoxIsToggled(currentChannel, ui->reverseCheckbox2,
1199 ui->servoCenterAngleSlider2, ui->servoMinAngleSlider2, ui->servoMaxAngleSlider2);
1201 ui->servoPWMValue2->setText(tr("Output %1 value : <b>%2</b> µs (Max)")
1202 .arg(currentChannel + 1).arg(m_actuatorSettings[currentChannel].channelMax));
1205 void OutputCalibrationPage::on_calibrateAllMotors_toggled(bool checked)
1207 Q_UNUSED(checked);
1208 setupVehicleHighlightedPart();
1211 void OutputCalibrationPage::resetOutputCalibrationUtil()
1213 if (m_calibrationUtil) {
1214 delete m_calibrationUtil;
1215 m_calibrationUtil = 0;
1217 m_calibrationUtil = new OutputCalibrationUtil();
1221 // Set Min/Max slider values and display servo movement with arrow
1223 void OutputCalibrationPage::setSliderLimitsAndArrows(quint16 currentChannel, bool showFirst, quint16 value,
1224 QCheckBox *revCheckbox, QSlider *minSlider, QSlider *maxSlider)
1226 m_actuatorSettings[currentChannel].channelNeutral = value;
1228 // Adjust min and max
1229 if (revCheckbox->isChecked()) {
1230 if (value >= m_actuatorSettings[currentChannel].channelMin) {
1231 minSlider->setValue(value);
1233 if (value <= m_actuatorSettings[currentChannel].channelMax) {
1234 maxSlider->setValue(value);
1236 } else {
1237 if (value <= m_actuatorSettings[currentChannel].channelMin) {
1238 minSlider->setValue(value);
1240 if (value >= m_actuatorSettings[currentChannel].channelMax) {
1241 maxSlider->setValue(value);
1245 quint16 minValue = (revCheckbox->isChecked()) ? maxSlider->value() : minSlider->value();
1246 quint16 maxValue = (revCheckbox->isChecked()) ? minSlider->value() : maxSlider->value();
1247 quint16 range = maxValue - minValue;
1249 // Reset all arrows
1250 showElementMovement(true, showFirst, 0);
1251 showElementMovement(false, showFirst, 0);
1252 showElementMovement(true, !showFirst, 0);
1253 showElementMovement(false, !showFirst, 0);
1255 // 35% "Dead band" : no arrow display
1256 quint16 limitLow = minValue + (range * 0.35);
1258 quint16 limitHigh = maxValue - (range * 0.35);
1259 quint16 middle = minValue + (range / 2);
1260 qreal arrowOpacity = 0;
1261 if (value < limitLow) {
1262 arrowOpacity = (qreal)(middle - value) / (qreal)(middle - minValue);
1264 showElementMovement(revCheckbox->isChecked(), showFirst, arrowOpacity);
1265 } else if (value > limitHigh) {
1266 arrowOpacity = (qreal)(value - middle) / (qreal)(maxValue - middle);
1267 showElementMovement(!revCheckbox->isChecked(), showFirst, arrowOpacity);
1272 // Set Center/Min/Max slider limits per reverse checkbox status
1274 void OutputCalibrationPage::reverseCheckBoxIsToggled(quint16 currentChannel,
1275 QCheckBox *checkBox, QSlider *centerSlider, QSlider *minSlider, QSlider *maxSlider)
1277 bool checked = checkBox->isChecked();
1279 if (checked && m_actuatorSettings[currentChannel].channelMax > m_actuatorSettings[currentChannel].channelMin) {
1280 quint16 oldMax = m_actuatorSettings[currentChannel].channelMax;
1281 m_actuatorSettings[currentChannel].channelMax = m_actuatorSettings[currentChannel].channelMin;
1282 m_actuatorSettings[currentChannel].channelMin = oldMax;
1283 } else if (!checkBox->isChecked() && m_actuatorSettings[currentChannel].channelMax < m_actuatorSettings[currentChannel].channelMin) {
1284 quint16 oldMax = m_actuatorSettings[currentChannel].channelMax;
1285 m_actuatorSettings[currentChannel].channelMax = m_actuatorSettings[currentChannel].channelMin;
1286 m_actuatorSettings[currentChannel].channelMin = oldMax;
1288 centerSlider->setInvertedAppearance(checked);
1289 centerSlider->setInvertedControls(checked);
1290 minSlider->setInvertedAppearance(checked);
1291 minSlider->setInvertedControls(checked);
1292 maxSlider->setInvertedAppearance(checked);
1293 maxSlider->setInvertedControls(checked);
1295 if (checkBox->isChecked()) {
1296 maxSlider->setValue(m_actuatorSettings[currentChannel].channelMax);
1297 centerSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
1298 minSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
1299 } else {
1300 minSlider->setValue(m_actuatorSettings[currentChannel].channelMin);
1301 centerSlider->setValue(m_actuatorSettings[currentChannel].channelNeutral);
1302 maxSlider->setValue(m_actuatorSettings[currentChannel].channelMax);