LP-56 - Better txpid option namings, fix tabs-spaces, tooltips. headers, variable...
[librepilot.git] / ground / openpilotgcs / src / plugins / config / configinputwidget.cpp
blob878d76a78dfce58ea7185ef9f92aaf423052daf3
1 /**
2 ******************************************************************************
4 * @file configinputwidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
7 * @{
8 * @addtogroup ConfigPlugin Config Plugin
9 * @{
10 * @brief Servo input/output configuration panel for the config gadget
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "configinputwidget.h"
30 #include <extensionsystem/pluginmanager.h>
31 #include <coreplugin/generalsettings.h>
33 #include <QDebug>
34 #include <QStringList>
35 #include <QWidget>
36 #include <QTextEdit>
37 #include <QVBoxLayout>
38 #include <QPushButton>
39 #include <QDesktopServices>
40 #include <QUrl>
41 #include <QMessageBox>
42 #include <utils/stylehelper.h>
43 #include <QMessageBox>
45 #define ACCESS_MIN_MOVE -3
46 #define ACCESS_MAX_MOVE 3
47 #define STICK_MIN_MOVE -8
48 #define STICK_MAX_MOVE 8
50 #define CHANNEL_NUMBER_NONE 0
51 #define DEFAULT_FLIGHT_MODE_NUMBER 3
53 ConfigInputWidget::ConfigInputWidget(QWidget *parent) :
54 ConfigTaskWidget(parent),
55 wizardStep(wizardNone),
56 // not currently stored in the settings UAVO
57 transmitterMode(mode2),
58 transmitterType(acro),
60 loop(NULL),
61 skipflag(false),
62 nextDelayedTimer(),
63 nextDelayedTick(0),
64 nextDelayedLatestActivityTick(0),
65 accessoryDesiredObj0(NULL),
66 accessoryDesiredObj1(NULL),
67 accessoryDesiredObj2(NULL)
69 manualCommandObj = ManualControlCommand::GetInstance(getObjectManager());
70 manualSettingsObj = ManualControlSettings::GetInstance(getObjectManager());
71 flightModeSettingsObj = FlightModeSettings::GetInstance(getObjectManager());
72 flightStatusObj = FlightStatus::GetInstance(getObjectManager());
73 receiverActivityObj = ReceiverActivity::GetInstance(getObjectManager());
74 accessoryDesiredObj0 = AccessoryDesired::GetInstance(getObjectManager(), 0);
75 accessoryDesiredObj1 = AccessoryDesired::GetInstance(getObjectManager(), 1);
76 accessoryDesiredObj2 = AccessoryDesired::GetInstance(getObjectManager(), 2);
77 actuatorSettingsObj = ActuatorSettings::GetInstance(getObjectManager());
78 systemSettingsObj = SystemSettings::GetInstance(getObjectManager());
80 // Only instance 0 is present if the board is not connected.
81 // The other instances are populated lazily.
82 Q_ASSERT(accessoryDesiredObj0);
84 ui = new Ui_InputWidget();
85 ui->setupUi(this);
87 wizardUi = new Ui_InputWizardWidget();
88 wizardUi->setupUi(ui->wizard);
90 addApplySaveButtons(ui->saveRCInputToRAM, ui->saveRCInputToSD);
92 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
93 Core::Internal::GeneralSettings *settings = pm->getObject<Core::Internal::GeneralSettings>();
94 if (!settings->useExpertMode()) {
95 ui->saveRCInputToRAM->setVisible(false);
98 addApplySaveButtons(ui->saveRCInputToRAM, ui->saveRCInputToSD);
100 // Generate the rows of buttons in the input channel form GUI
101 unsigned int index = 0;
102 unsigned int indexRT = 0;
103 foreach(QString name, manualSettingsObj->getField("ChannelNumber")->getElementNames()) {
104 Q_ASSERT(index < ManualControlSettings::CHANNELGROUPS_NUMELEM);
105 InputChannelForm *form = new InputChannelForm(index, this);
106 form->setName(name);
108 form->moveTo(*(ui->channelLayout));
110 // The order of the following binding calls is important. Since the values will be populated
111 // in reverse order of the binding order otherwise the 'Reversed' logic will floor the neutral value
112 // to the max value ( which is smaller than the neutral value when reversed ) and the channel number
113 // will not be set correctly.
114 addWidgetBinding("ManualControlSettings", "ChannelNumber", form->ui->channelNumber, index);
115 addWidgetBinding("ManualControlSettings", "ChannelGroups", form->ui->channelGroup, index);
116 // Slider position based on real time Rcinput (allow monitoring)
117 addWidgetBinding("ManualControlCommand", "Channel", form->ui->channelNeutral, index);
118 // Neutral value stored on board (SpinBox)
119 addWidgetBinding("ManualControlSettings", "ChannelNeutral", form->ui->neutralValue, index);
120 addWidgetBinding("ManualControlSettings", "ChannelMax", form->ui->channelMax, index);
121 addWidgetBinding("ManualControlSettings", "ChannelMin", form->ui->channelMin, index);
122 addWidgetBinding("ManualControlSettings", "ChannelMax", form->ui->channelMax, index);
124 addWidget(form->ui->channelRev);
126 // Reversing supported for some channels only
127 bool reversable = ((index == ManualControlSettings::CHANNELGROUPS_THROTTLE) ||
128 (index == ManualControlSettings::CHANNELGROUPS_ROLL) ||
129 (index == ManualControlSettings::CHANNELGROUPS_PITCH) ||
130 (index == ManualControlSettings::CHANNELGROUPS_YAW));
131 form->ui->channelRev->setVisible(reversable);
133 // Input filter response time fields supported for some channels only
134 switch (index) {
135 case ManualControlSettings::CHANNELGROUPS_ROLL:
136 case ManualControlSettings::CHANNELGROUPS_PITCH:
137 case ManualControlSettings::CHANNELGROUPS_YAW:
138 case ManualControlSettings::CHANNELGROUPS_COLLECTIVE:
139 case ManualControlSettings::CHANNELGROUPS_ACCESSORY0:
140 case ManualControlSettings::CHANNELGROUPS_ACCESSORY1:
141 case ManualControlSettings::CHANNELGROUPS_ACCESSORY2:
142 addWidgetBinding("ManualControlSettings", "ResponseTime", form->ui->channelResponseTime, indexRT);
143 ++indexRT;
144 break;
145 case ManualControlSettings::CHANNELGROUPS_THROTTLE:
146 case ManualControlSettings::CHANNELGROUPS_FLIGHTMODE:
147 form->ui->channelResponseTime->setVisible(false);
148 break;
149 default:
150 Q_ASSERT(0);
151 break;
154 ++index;
157 addWidgetBinding("ManualControlSettings", "Deadband", ui->deadband, 0, 0.01f);
158 addWidgetBinding("ManualControlSettings", "DeadbandAssistedControl", ui->assistedControlDeadband, 0, 0.01f);
160 connect(ui->configurationWizard, SIGNAL(clicked()), this, SLOT(goToWizard()));
161 connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(disableWizardButton(int)));
162 connect(ui->runCalibration, SIGNAL(toggled(bool)), this, SLOT(simpleCalibration(bool)));
164 connect(wizardUi->wzNext, SIGNAL(clicked()), this, SLOT(wzNext()));
165 connect(wizardUi->wzCancel, SIGNAL(clicked()), this, SLOT(wzCancel()));
166 connect(wizardUi->wzBack, SIGNAL(clicked()), this, SLOT(wzBack()));
168 ui->stackedWidget->setCurrentIndex(0);
169 addWidgetBinding("FlightModeSettings", "FlightModePosition", ui->fmsModePos1, 0, 1, true);
170 addWidgetBinding("FlightModeSettings", "FlightModePosition", ui->fmsModePos2, 1, 1, true);
171 addWidgetBinding("FlightModeSettings", "FlightModePosition", ui->fmsModePos3, 2, 1, true);
172 addWidgetBinding("FlightModeSettings", "FlightModePosition", ui->fmsModePos4, 3, 1, true);
173 addWidgetBinding("FlightModeSettings", "FlightModePosition", ui->fmsModePos5, 4, 1, true);
174 addWidgetBinding("FlightModeSettings", "FlightModePosition", ui->fmsModePos6, 5, 1, true);
175 addWidgetBinding("ManualControlSettings", "FlightModeNumber", ui->fmsPosNum);
177 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Roll, "Roll", 1, true);
178 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Roll, "Roll", 1, true);
179 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Roll, "Roll", 1, true);
180 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Roll, "Roll", 1, true);
181 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Roll, "Roll", 1, true);
182 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Roll, "Roll", 1, true);
183 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Pitch, "Pitch", 1, true);
184 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Pitch, "Pitch", 1, true);
185 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Pitch, "Pitch", 1, true);
186 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Pitch, "Pitch", 1, true);
187 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Pitch, "Pitch", 1, true);
188 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Pitch, "Pitch", 1, true);
189 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Yaw, "Yaw", 1, true);
190 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Yaw, "Yaw", 1, true);
191 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Yaw, "Yaw", 1, true);
192 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Yaw, "Yaw", 1, true);
193 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Yaw, "Yaw", 1, true);
194 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Yaw, "Yaw", 1, true);
195 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Thrust, "Thrust", 1, true);
196 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Thrust, "Thrust", 1, true);
197 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Thrust, "Thrust", 1, true);
198 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Thrust, "Thrust", 1, true);
199 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Thrust, "Thrust", 1, true);
200 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Thrust, "Thrust", 1, true);
202 addWidgetBinding("FlightModeSettings", "Arming", ui->armControl);
203 addWidgetBinding("FlightModeSettings", "ArmedTimeout", ui->armTimeout, 0, 1000);
204 connect(ManualControlCommand::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveFMSlider()));
205 connect(ManualControlSettings::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updatePositionSlider()));
207 addWidget(ui->configurationWizard);
208 addWidget(ui->runCalibration);
210 autoLoadWidgets();
212 populateWidgets();
213 refreshWidgetsValues();
214 // Connect the help button
215 connect(ui->inputHelp, SIGNAL(clicked()), this, SLOT(openHelp()));
217 wizardUi->graphicsView->setScene(new QGraphicsScene(this));
218 wizardUi->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
219 m_renderer = new QSvgRenderer();
220 QGraphicsScene *l_scene = wizardUi->graphicsView->scene();
221 wizardUi->graphicsView->setBackgroundBrush(QBrush(Utils::StyleHelper::baseColor()));
222 if (QFile::exists(":/configgadget/images/TX2.svg") && m_renderer->load(QString(":/configgadget/images/TX2.svg")) && m_renderer->isValid()) {
223 l_scene->clear(); // Deletes all items contained in the scene as well.
225 m_txBackground = new QGraphicsSvgItem();
226 // All other items will be clipped to the shape of the background
227 m_txBackground->setFlags(QGraphicsItem::ItemClipsChildrenToShape |
228 QGraphicsItem::ItemClipsToShape);
229 m_txBackground->setSharedRenderer(m_renderer);
230 m_txBackground->setElementId("background");
231 l_scene->addItem(m_txBackground);
233 m_txMainBody = new QGraphicsSvgItem();
234 m_txMainBody->setParentItem(m_txBackground);
235 m_txMainBody->setSharedRenderer(m_renderer);
236 m_txMainBody->setElementId("body");
237 l_scene->addItem(m_txMainBody);
239 m_txLeftStick = new QGraphicsSvgItem();
240 m_txLeftStick->setParentItem(m_txBackground);
241 m_txLeftStick->setSharedRenderer(m_renderer);
242 m_txLeftStick->setElementId("ljoy");
244 m_txRightStick = new QGraphicsSvgItem();
245 m_txRightStick->setParentItem(m_txBackground);
246 m_txRightStick->setSharedRenderer(m_renderer);
247 m_txRightStick->setElementId("rjoy");
249 m_txAccess0 = new QGraphicsSvgItem();
250 m_txAccess0->setParentItem(m_txBackground);
251 m_txAccess0->setSharedRenderer(m_renderer);
252 m_txAccess0->setElementId("access0");
254 m_txAccess1 = new QGraphicsSvgItem();
255 m_txAccess1->setParentItem(m_txBackground);
256 m_txAccess1->setSharedRenderer(m_renderer);
257 m_txAccess1->setElementId("access1");
259 m_txAccess2 = new QGraphicsSvgItem();
260 m_txAccess2->setParentItem(m_txBackground);
261 m_txAccess2->setSharedRenderer(m_renderer);
262 m_txAccess2->setElementId("access2");
264 m_txFlightMode = new QGraphicsSvgItem();
265 m_txFlightMode->setParentItem(m_txBackground);
266 m_txFlightMode->setSharedRenderer(m_renderer);
267 m_txFlightMode->setElementId("flightModeCenter");
268 m_txFlightMode->setZValue(-10);
270 m_txArrows = new QGraphicsSvgItem();
271 m_txArrows->setParentItem(m_txBackground);
272 m_txArrows->setSharedRenderer(m_renderer);
273 m_txArrows->setElementId("arrows");
274 m_txArrows->setVisible(false);
276 QRectF orig = m_renderer->boundsOnElement("ljoy");
277 QMatrix Matrix = m_renderer->matrixForElement("ljoy");
278 orig = Matrix.mapRect(orig);
279 m_txLeftStickOrig.translate(orig.x(), orig.y());
280 m_txLeftStick->setTransform(m_txLeftStickOrig, false);
282 orig = m_renderer->boundsOnElement("arrows");
283 Matrix = m_renderer->matrixForElement("arrows");
284 orig = Matrix.mapRect(orig);
285 m_txArrowsOrig.translate(orig.x(), orig.y());
286 m_txArrows->setTransform(m_txArrowsOrig, false);
288 orig = m_renderer->boundsOnElement("body");
289 Matrix = m_renderer->matrixForElement("body");
290 orig = Matrix.mapRect(orig);
291 m_txMainBodyOrig.translate(orig.x(), orig.y());
292 m_txMainBody->setTransform(m_txMainBodyOrig, false);
294 orig = m_renderer->boundsOnElement("flightModeCenter");
295 Matrix = m_renderer->matrixForElement("flightModeCenter");
296 orig = Matrix.mapRect(orig);
297 m_txFlightModeCOrig.translate(orig.x(), orig.y());
298 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
300 orig = m_renderer->boundsOnElement("flightModeLeft");
301 Matrix = m_renderer->matrixForElement("flightModeLeft");
302 orig = Matrix.mapRect(orig);
303 m_txFlightModeLOrig.translate(orig.x(), orig.y());
304 orig = m_renderer->boundsOnElement("flightModeRight");
305 Matrix = m_renderer->matrixForElement("flightModeRight");
306 orig = Matrix.mapRect(orig);
307 m_txFlightModeROrig.translate(orig.x(), orig.y());
309 orig = m_renderer->boundsOnElement("rjoy");
310 Matrix = m_renderer->matrixForElement("rjoy");
311 orig = Matrix.mapRect(orig);
312 m_txRightStickOrig.translate(orig.x(), orig.y());
313 m_txRightStick->setTransform(m_txRightStickOrig, false);
315 orig = m_renderer->boundsOnElement("access0");
316 Matrix = m_renderer->matrixForElement("access0");
317 orig = Matrix.mapRect(orig);
318 m_txAccess0Orig.translate(orig.x(), orig.y());
319 m_txAccess0->setTransform(m_txAccess0Orig, false);
321 orig = m_renderer->boundsOnElement("access1");
322 Matrix = m_renderer->matrixForElement("access1");
323 orig = Matrix.mapRect(orig);
324 m_txAccess1Orig.translate(orig.x(), orig.y());
325 m_txAccess1->setTransform(m_txAccess1Orig, false);
327 orig = m_renderer->boundsOnElement("access2");
328 Matrix = m_renderer->matrixForElement("access2");
329 orig = Matrix.mapRect(orig);
330 m_txAccess2Orig.translate(orig.x(), orig.y());
331 m_txAccess2->setTransform(m_txAccess2Orig, true);
333 wizardUi->graphicsView->fitInView(m_txMainBody, Qt::KeepAspectRatio);
334 animate = new QTimer(this);
335 connect(animate, SIGNAL(timeout()), this, SLOT(moveTxControls()));
337 heliChannelOrder << ManualControlSettings::CHANNELGROUPS_COLLECTIVE <<
338 ManualControlSettings::CHANNELGROUPS_THROTTLE <<
339 ManualControlSettings::CHANNELGROUPS_ROLL <<
340 ManualControlSettings::CHANNELGROUPS_PITCH <<
341 ManualControlSettings::CHANNELGROUPS_YAW <<
342 ManualControlSettings::CHANNELGROUPS_FLIGHTMODE <<
343 ManualControlSettings::CHANNELGROUPS_ACCESSORY0 <<
344 ManualControlSettings::CHANNELGROUPS_ACCESSORY1 <<
345 ManualControlSettings::CHANNELGROUPS_ACCESSORY2;
347 acroChannelOrder << ManualControlSettings::CHANNELGROUPS_THROTTLE <<
348 ManualControlSettings::CHANNELGROUPS_ROLL <<
349 ManualControlSettings::CHANNELGROUPS_PITCH <<
350 ManualControlSettings::CHANNELGROUPS_YAW <<
351 ManualControlSettings::CHANNELGROUPS_FLIGHTMODE <<
352 ManualControlSettings::CHANNELGROUPS_ACCESSORY0 <<
353 ManualControlSettings::CHANNELGROUPS_ACCESSORY1 <<
354 ManualControlSettings::CHANNELGROUPS_ACCESSORY2;
356 groundChannelOrder << ManualControlSettings::CHANNELGROUPS_THROTTLE <<
357 ManualControlSettings::CHANNELGROUPS_YAW <<
358 ManualControlSettings::CHANNELGROUPS_ACCESSORY0;
360 updateEnableControls();
363 void ConfigInputWidget::resetTxControls()
365 m_txLeftStick->setTransform(m_txLeftStickOrig, false);
366 m_txRightStick->setTransform(m_txRightStickOrig, false);
367 m_txAccess0->setTransform(m_txAccess0Orig, false);
368 m_txAccess1->setTransform(m_txAccess1Orig, false);
369 m_txAccess2->setTransform(m_txAccess2Orig, false);
370 m_txFlightMode->setElementId("flightModeCenter");
371 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
372 m_txArrows->setVisible(false);
375 ConfigInputWidget::~ConfigInputWidget()
378 void ConfigInputWidget::enableControls(bool enable)
380 ConfigTaskWidget::enableControls(enable);
382 if (enable) {
383 updatePositionSlider();
387 void ConfigInputWidget::resizeEvent(QResizeEvent *event)
389 QWidget::resizeEvent(event);
391 wizardUi->graphicsView->fitInView(m_txBackground, Qt::KeepAspectRatio);
394 void ConfigInputWidget::openHelp()
396 QDesktopServices::openUrl(QUrl(tr("http://wiki.openpilot.org/x/04Cf"), QUrl::StrictMode));
399 void ConfigInputWidget::goToWizard()
401 QMessageBox msgBox;
403 msgBox.setText(tr("Arming Settings are now set to 'Always Disarmed' for your safety."));
404 msgBox.setDetailedText(tr("You will have to reconfigure the arming settings manually "
405 "when the wizard is finished. After the last step of the "
406 "wizard you will be taken to the Arming Settings screen."));
407 msgBox.setStandardButtons(QMessageBox::Ok);
408 msgBox.setDefaultButton(QMessageBox::Ok);
409 msgBox.exec();
411 // Set correct tab visible before starting wizard.
412 if (ui->tabWidget->currentIndex() != 0) {
413 ui->tabWidget->setCurrentIndex(0);
416 // Stash current manual settings data in case the wizard is
417 // cancelled or the user proceeds far enough into the wizard such
418 // that the UAVO is changed, but then backs out to the start and
419 // chooses a different TX type (which could otherwise result in
420 // unexpected TX channels being enabled)
421 manualSettingsData = manualSettingsObj->getData();
422 memento.manualSettingsData = manualSettingsData;
423 flightModeSettingsData = flightModeSettingsObj->getData();
424 memento.flightModeSettingsData = flightModeSettingsData;
425 flightModeSettingsData.Arming = FlightModeSettings::ARMING_ALWAYSDISARMED;
426 flightModeSettingsObj->setData(flightModeSettingsData);
427 // Stash actuatorSettings
428 actuatorSettingsData = actuatorSettingsObj->getData();
429 memento.actuatorSettingsData = actuatorSettingsData;
431 // Stash systemSettings
432 systemSettingsData = systemSettingsObj->getData();
433 memento.systemSettingsData = systemSettingsData;
435 // Now reset channel and actuator settings (disable outputs)
436 resetChannelSettings();
437 resetActuatorSettings();
439 // Use faster input update rate.
440 fastMdata();
442 // start the wizard
443 wizardSetUpStep(wizardWelcome);
444 wizardUi->graphicsView->fitInView(m_txBackground, Qt::KeepAspectRatio);
447 void ConfigInputWidget::disableWizardButton(int value)
449 if (value != 0) {
450 ui->groupBox_3->setVisible(false);
451 } else {
452 ui->groupBox_3->setVisible(true);
456 void ConfigInputWidget::wzCancel()
458 dimOtherControls(false);
460 // Cancel any ongoing delayd next trigger.
461 wzNextDelayedCancel();
463 // Restore original input update rate.
464 restoreMdata();
466 ui->stackedWidget->setCurrentIndex(0);
468 if (wizardStep != wizardNone) {
469 wizardTearDownStep(wizardStep);
471 wizardStep = wizardNone;
472 ui->stackedWidget->setCurrentIndex(0);
474 // Load settings back from beginning of wizard
475 manualSettingsObj->setData(memento.manualSettingsData);
476 flightModeSettingsObj->setData(memento.flightModeSettingsData);
477 actuatorSettingsObj->setData(memento.actuatorSettingsData);
478 systemSettingsObj->setData(memento.systemSettingsData);
481 void ConfigInputWidget::registerControlActivity()
483 nextDelayedLatestActivityTick = nextDelayedTick;
486 void ConfigInputWidget::wzNextDelayed()
488 nextDelayedTick++;
490 // Call next after the full 2500 ms timeout has been reached,
491 // or if no input activity has occurred the last 500 ms.
492 if (nextDelayedTick == 25 ||
493 nextDelayedTick - nextDelayedLatestActivityTick >= 5) {
494 wzNext();
498 void ConfigInputWidget::wzNextDelayedStart()
500 // Call wzNextDelayed every 100 ms, to see if it's time to go to the next page.
501 connect(&nextDelayedTimer, SIGNAL(timeout()), this, SLOT(wzNextDelayed()));
502 nextDelayedTimer.start(100);
505 // Cancel the delayed next timer, if it's active.
506 void ConfigInputWidget::wzNextDelayedCancel()
508 nextDelayedTick = 0;
509 nextDelayedLatestActivityTick = 0;
510 if (nextDelayedTimer.isActive()) {
511 nextDelayedTimer.stop();
512 disconnect(&nextDelayedTimer, SIGNAL(timeout()), this, SLOT(wzNextDelayed()));
516 void ConfigInputWidget::wzNext()
518 wzNextDelayedCancel();
520 // In identify sticks mode the next button can indicate
521 // channel advance
522 if (wizardStep != wizardNone &&
523 wizardStep != wizardIdentifySticks) {
524 wizardTearDownStep(wizardStep);
527 // State transitions for next button
528 switch (wizardStep) {
529 case wizardWelcome:
530 wizardSetUpStep(wizardChooseType);
531 break;
532 case wizardChooseType:
533 wizardSetUpStep(wizardChooseMode);
534 break;
535 case wizardChooseMode:
536 wizardSetUpStep(wizardIdentifySticks);
537 break;
538 case wizardIdentifySticks:
539 nextChannel();
540 if (currentChannelNum == -1) { // Gone through all channels
541 wizardTearDownStep(wizardIdentifySticks);
542 wizardSetUpStep(wizardIdentifyCenter);
544 break;
545 case wizardIdentifyCenter:
546 wizardSetUpStep(wizardIdentifyLimits);
547 break;
548 case wizardIdentifyLimits:
549 wizardSetUpStep(wizardIdentifyInverted);
550 break;
551 case wizardIdentifyInverted:
552 wizardSetUpStep(wizardFinish);
553 break;
554 case wizardFinish:
555 wizardStep = wizardNone;
557 // Restore original input update rate.
558 restoreMdata();
560 // Load actuator settings back from beginning of wizard
561 actuatorSettingsObj->setData(memento.actuatorSettingsData);
563 // Force flight mode neutral to middle and Throttle neutral at 4%
564 adjustSpecialNeutrals();
565 throttleError = false;
566 checkThrottleRange();
568 // Force flight mode number to be 1 if 2 CH ground vehicle was selected
569 if (transmitterType == ground) {
570 forceOneFlightMode();
573 manualSettingsObj->setData(manualSettingsData);
574 // move to Arming Settings tab
575 ui->stackedWidget->setCurrentIndex(0);
576 ui->tabWidget->setCurrentIndex(2);
577 break;
578 default:
579 Q_ASSERT(0);
583 void ConfigInputWidget::wzBack()
585 wzNextDelayedCancel();
587 if (wizardStep != wizardNone &&
588 wizardStep != wizardIdentifySticks) {
589 wizardTearDownStep(wizardStep);
592 // State transitions for next button
593 switch (wizardStep) {
594 case wizardChooseType:
595 wizardSetUpStep(wizardWelcome);
596 break;
597 case wizardChooseMode:
598 wizardSetUpStep(wizardChooseType);
599 break;
600 case wizardIdentifySticks:
601 prevChannel();
602 if (currentChannelNum == -1) {
603 wizardTearDownStep(wizardIdentifySticks);
604 wizardSetUpStep(wizardChooseMode);
606 break;
607 case wizardIdentifyCenter:
608 wizardSetUpStep(wizardIdentifySticks);
609 break;
610 case wizardIdentifyLimits:
611 wizardSetUpStep(wizardIdentifyCenter);
612 break;
613 case wizardIdentifyInverted:
614 wizardSetUpStep(wizardIdentifyLimits);
615 break;
616 case wizardFinish:
617 wizardSetUpStep(wizardIdentifyInverted);
618 break;
619 default:
620 Q_ASSERT(0);
624 void ConfigInputWidget::wizardSetUpStep(enum wizardSteps step)
626 wizardUi->wzNext->setText(tr("Next"));
628 switch (step) {
629 case wizardWelcome:
630 foreach(QPointer<QWidget> wd, extraWidgets) {
631 if (!wd.isNull()) {
632 delete wd;
635 extraWidgets.clear();
636 wizardUi->graphicsView->setVisible(false);
637 setTxMovement(nothing);
638 wizardUi->wzBack->setEnabled(false);
639 wizardUi->pagesStack->setCurrentWidget(wizardUi->welcomePage);
640 ui->stackedWidget->setCurrentIndex(1);
641 break;
642 case wizardChooseType:
644 wizardUi->graphicsView->setVisible(true);
645 wizardUi->graphicsView->fitInView(m_txBackground, Qt::KeepAspectRatio);
646 setTxMovement(nothing);
647 wizardUi->wzBack->setEnabled(true);
648 if (transmitterType == heli) {
649 wizardUi->typeHeli->setChecked(true);
650 } else if (transmitterType == ground) {
651 wizardUi->typeGround->setChecked(true);
652 } else {
653 wizardUi->typeAcro->setChecked(true);
655 wizardUi->pagesStack->setCurrentWidget(wizardUi->chooseTypePage);
657 break;
658 case wizardChooseMode:
660 wizardUi->wzBack->setEnabled(true);
661 QRadioButton *modeButtons[] = {
662 wizardUi->mode1Button,
663 wizardUi->mode2Button,
664 wizardUi->mode3Button,
665 wizardUi->mode4Button
668 for (int i = 0; i <= mode4; ++i) {
669 QString label;
670 txMode mode = static_cast<txMode>(i);
671 if (transmitterType == heli) {
672 switch (mode) {
673 case mode1: label = tr("Mode 1: Fore/Aft Cyclic and Yaw on the left, Throttle/Collective and Left/Right Cyclic on the right"); break;
674 case mode2: label = tr("Mode 2: Throttle/Collective and Yaw on the left, Cyclic on the right"); break;
675 case mode3: label = tr("Mode 3: Cyclic on the left, Throttle/Collective and Yaw on the right"); break;
676 case mode4: label = tr("Mode 4: Throttle/Collective and Left/Right Cyclic on the left, Fore/Aft Cyclic and Yaw on the right"); break;
677 default: Q_ASSERT(0); break;
679 wizardUi->typePageFooter->setText(" ");
680 } else {
681 switch (mode) {
682 case mode1: label = tr("Mode 1: Elevator and Rudder on the left, Throttle and Ailerons on the right"); break;
683 case mode2: label = tr("Mode 2: Throttle and Rudder on the left, Elevator and Ailerons on the right"); break;
684 case mode3: label = tr("Mode 3: Elevator and Ailerons on the left, Throttle and Rudder on the right"); break;
685 case mode4: label = tr("Mode 4: Throttle and Ailerons on the left, Elevator and Rudder on the right"); break;
686 default: Q_ASSERT(0); break;
688 wizardUi->typePageFooter->setText(tr("For a Quad: Elevator is Pitch, Ailerons are Roll, and Rudder is Yaw."));
690 modeButtons[i]->setText(label);
691 if (transmitterMode == mode) {
692 modeButtons[i]->setChecked(true);
695 wizardUi->pagesStack->setCurrentWidget(wizardUi->chooseModePage);
697 break;
698 case wizardIdentifySticks:
699 usedChannels.clear();
700 currentChannelNum = -1;
701 nextChannel();
702 manualSettingsData = manualSettingsObj->getData();
703 connect(receiverActivityObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyControls()));
704 wizardUi->wzNext->setEnabled(false);
705 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifySticksPage);
706 break;
707 case wizardIdentifyCenter:
708 setTxMovement(centerAll);
709 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifyCenterPage);
710 if (transmitterType == ground) {
711 wizardUi->identifyCenterInstructions->setText(QString(tr("Please center all controls and trims and press Next when ready.\n\n"
712 "For a ground vehicle, this center position will be used as neutral value of each channel.")));
714 break;
715 case wizardIdentifyLimits:
717 setTxMovement(nothing);
718 manualSettingsData = manualSettingsObj->getData();
719 for (uint i = 0; i < ManualControlSettings::CHANNELMAX_NUMELEM; ++i) {
720 // Preserve the inverted status
721 if (manualSettingsData.ChannelMin[i] <= manualSettingsData.ChannelMax[i]) {
722 manualSettingsData.ChannelMin[i] = manualSettingsData.ChannelNeutral[i];
723 manualSettingsData.ChannelMax[i] = manualSettingsData.ChannelNeutral[i];
724 } else {
725 // Make this detect as still inverted
726 manualSettingsData.ChannelMin[i] = manualSettingsData.ChannelNeutral[i] + 1;
727 manualSettingsData.ChannelMax[i] = manualSettingsData.ChannelNeutral[i];
730 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyLimits()));
731 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
732 connect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
733 connect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
735 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifyLimitsPage);
737 break;
738 case wizardIdentifyInverted:
739 dimOtherControls(true);
740 setTxMovement(nothing);
741 extraWidgets.clear();
742 for (int index = 0; index < manualSettingsObj->getField("ChannelMax")->getElementNames().length(); index++) {
743 QString name = manualSettingsObj->getField("ChannelMax")->getElementNames().at(index);
744 if (!name.contains("Access") && !name.contains("Flight") &&
745 (!name.contains("Collective") || transmitterType == heli)) {
746 QCheckBox *cb = new QCheckBox(name, this);
747 // Make sure checked status matches current one
748 cb->setChecked(manualSettingsData.ChannelMax[index] < manualSettingsData.ChannelMin[index]);
749 wizardUi->checkBoxesLayout->addWidget(cb, extraWidgets.size() / 4, extraWidgets.size() % 4);
750 extraWidgets.append(cb);
751 connect(cb, SIGNAL(toggled(bool)), this, SLOT(invertControls()));
754 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
755 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifyInvertedPage);
756 break;
757 case wizardFinish:
758 dimOtherControls(false);
759 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
760 connect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
761 connect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
762 wizardUi->pagesStack->setCurrentWidget(wizardUi->finishPage);
763 break;
764 default:
765 Q_ASSERT(0);
767 wizardStep = step;
770 void ConfigInputWidget::wizardTearDownStep(enum wizardSteps step)
772 Q_ASSERT(step == wizardStep);
773 switch (step) {
774 case wizardWelcome:
775 break;
776 case wizardChooseType:
777 if (wizardUi->typeAcro->isChecked()) {
778 transmitterType = acro;
779 } else if (wizardUi->typeGround->isChecked()) {
780 transmitterType = ground;
781 /* Make sure to tell controller, this is really a ground vehicle. */
782 systemSettingsData = systemSettingsObj->getData();
783 systemSettingsData.AirframeType = SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR;
784 systemSettingsObj->setData(systemSettingsData);
785 } else {
786 transmitterType = heli;
788 break;
789 case wizardChooseMode:
791 QRadioButton *modeButtons[] = {
792 wizardUi->mode1Button,
793 wizardUi->mode2Button,
794 wizardUi->mode3Button,
795 wizardUi->mode4Button
797 for (int i = mode1; i <= mode4; ++i) {
798 if (modeButtons[i]->isChecked()) {
799 transmitterMode = static_cast<txMode>(i);
803 break;
804 case wizardIdentifySticks:
805 disconnect(receiverActivityObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyControls()));
806 wizardUi->wzNext->setEnabled(true);
807 setTxMovement(nothing);
808 /* If flight mode stick isn't identified, force flight mode number to be 1 */
809 manualSettingsData = manualSettingsObj->getData();
810 if (manualSettingsData.ChannelGroups[ManualControlSettings::CHANNELNUMBER_FLIGHTMODE] ==
811 ManualControlSettings::CHANNELGROUPS_NONE) {
812 forceOneFlightMode();
814 break;
815 case wizardIdentifyCenter:
816 manualCommandData = manualCommandObj->getData();
817 manualSettingsData = manualSettingsObj->getData();
818 for (unsigned int i = 0; i < ManualControlCommand::CHANNEL_NUMELEM; ++i) {
819 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
821 manualSettingsObj->setData(manualSettingsData);
822 setTxMovement(nothing);
823 break;
824 case wizardIdentifyLimits:
825 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyLimits()));
826 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
827 disconnect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
828 disconnect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
829 manualSettingsObj->setData(manualSettingsData);
830 setTxMovement(nothing);
831 break;
832 case wizardIdentifyInverted:
833 dimOtherControls(false);
834 foreach(QWidget * wd, extraWidgets) {
835 QCheckBox *cb = qobject_cast<QCheckBox *>(wd);
837 if (cb) {
838 disconnect(cb, SIGNAL(toggled(bool)), this, SLOT(invertControls()));
839 delete cb;
842 extraWidgets.clear();
843 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
844 break;
845 case wizardFinish:
846 dimOtherControls(false);
847 setTxMovement(nothing);
848 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
849 disconnect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
850 disconnect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
851 break;
852 default:
853 Q_ASSERT(0);
857 static void fastMdataSingle(UAVDataObject *object, UAVObject::Metadata *savedMdata)
859 *savedMdata = object->getMetadata();
860 UAVObject::Metadata mdata = *savedMdata;
861 UAVObject::SetFlightTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_PERIODIC);
862 mdata.flightTelemetryUpdatePeriod = 150;
863 object->setMetadata(mdata);
866 static void restoreMdataSingle(UAVDataObject *object, UAVObject::Metadata *savedMdata)
868 object->setMetadata(*savedMdata);
872 * Set manual control command to fast updates
874 void ConfigInputWidget::fastMdata()
876 fastMdataSingle(manualCommandObj, &manualControlMdata);
877 fastMdataSingle(accessoryDesiredObj0, &accessoryDesiredMdata0);
881 * Restore previous update settings for manual control data
883 void ConfigInputWidget::restoreMdata()
885 restoreMdataSingle(manualCommandObj, &manualControlMdata);
886 restoreMdataSingle(accessoryDesiredObj0, &accessoryDesiredMdata0);
890 * Set the display to indicate which channel the person should move
892 void ConfigInputWidget::setChannel(int newChan)
894 bool canBeSkipped = false;
896 if (newChan == ManualControlSettings::CHANNELGROUPS_COLLECTIVE) {
897 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please enable throttle hold mode.</p>"
898 "<p>Move the Collective Pitch stick.</p>")));
899 } else if (newChan == ManualControlSettings::CHANNELGROUPS_FLIGHTMODE) {
900 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please toggle the Flight Mode switch.</p>"
901 "<p>For switches you may have to repeat this rapidly.</p>"
902 "<p>Alternatively, you can click Next to skip this channel, but you will get only <b>ONE</b> Flight Mode.</p>")));
903 canBeSkipped = true;
904 } else if ((transmitterType == heli) && (newChan == ManualControlSettings::CHANNELGROUPS_THROTTLE)) {
905 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please disable throttle hold mode.</p>"
906 "<p>Move the Throttle stick.</p>")));
907 } else {
908 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please move each control one at a time according to the instructions and picture below.</p>"
909 "<p>Move the %1 stick.</p>")).arg(manualSettingsObj->getField("ChannelGroups")->getElementNames().at(newChan)));
912 if (manualSettingsObj->getField("ChannelGroups")->getElementNames().at(newChan).contains("Accessory")) {
913 wizardUi->identifyStickInstructions->setText(wizardUi->identifyStickInstructions->text() + tr("<p>Alternatively, click Next to skip this channel.</p>"));
914 canBeSkipped = true;
917 if (canBeSkipped) {
918 wizardUi->wzNext->setEnabled(true);
919 wizardUi->wzNext->setText(tr("Next / Skip"));
920 } else {
921 wizardUi->wzNext->setEnabled(false);
924 setMoveFromCommand(newChan);
925 currentChannelNum = newChan;
926 channelDetected = false;
930 * Unfortunately order of channel should be different in different conditions. Selects
931 * next channel based on heli or acro mode
933 void ConfigInputWidget::nextChannel()
935 QList <int> order;
936 switch (transmitterType) {
937 case heli:
938 order = heliChannelOrder;
939 break;
940 case ground:
941 order = groundChannelOrder;
942 break;
943 default:
944 order = acroChannelOrder;
945 break;
948 if (currentChannelNum == -1) {
949 setChannel(order[0]);
950 return;
952 for (int i = 0; i < order.length() - 1; i++) {
953 if (order[i] == currentChannelNum) {
954 setChannel(order[i + 1]);
955 return;
958 currentChannelNum = -1; // hit end of list
962 * Unfortunately order of channel should be different in different conditions. Selects
963 * previous channel based on heli or acro mode
965 void ConfigInputWidget::prevChannel()
967 QList <int> order;
968 switch (transmitterType) {
969 case heli:
970 order = heliChannelOrder;
971 break;
972 case ground:
973 order = groundChannelOrder;
974 break;
975 default:
976 order = acroChannelOrder;
977 break;
980 // No previous from unset channel or next state
981 if (currentChannelNum == -1) {
982 return;
985 for (int i = 1; i < order.length(); i++) {
986 if (order[i] == currentChannelNum) {
987 if (!usedChannels.isEmpty() &&
988 usedChannels.back().channelIndex == order[i - 1]) {
989 usedChannels.removeLast();
991 setChannel(order[i - 1]);
992 return;
995 currentChannelNum = -1; // hit end of list
998 void ConfigInputWidget::identifyControls()
1000 static const int DEBOUNCE_COUNT = 4;
1001 static int debounce = 0;
1003 receiverActivityData = receiverActivityObj->getData();
1005 if (receiverActivityData.ActiveChannel == 255) {
1006 return;
1009 if (channelDetected) {
1010 registerControlActivity();
1011 return;
1014 receiverActivityData = receiverActivityObj->getData();
1015 currentChannel.group = receiverActivityData.ActiveGroup;
1016 currentChannel.number = receiverActivityData.ActiveChannel;
1018 if (debounce == 0) {
1019 // Register a channel to be debounced.
1020 lastChannel.group = currentChannel.group;
1021 lastChannel.number = currentChannel.number;
1022 lastChannel.channelIndex = currentChannelNum;
1023 ++debounce;
1024 return;
1027 if (currentChannel != lastChannel) {
1028 // A new channel was seen. Only register it if we count down to 0.
1029 --debounce;
1030 return;
1033 if (debounce < DEBOUNCE_COUNT) {
1034 // We still haven't seen enough enough activity on this channel yet.
1035 ++debounce;
1036 return;
1039 // Channel has been debounced and it's enough record it.
1041 if (usedChannels.contains(lastChannel)) {
1042 // Channel is already recorded.
1043 return;
1046 // Record the channel.
1048 channelDetected = true;
1049 debounce = 0;
1050 usedChannels.append(lastChannel);
1051 manualSettingsData = manualSettingsObj->getData();
1052 manualSettingsData.ChannelGroups[currentChannelNum] = currentChannel.group;
1053 manualSettingsData.ChannelNumber[currentChannelNum] = currentChannel.number;
1054 manualSettingsObj->setData(manualSettingsData);
1056 // m_config->wzText->clear();
1057 setTxMovement(nothing);
1059 wzNextDelayedStart();
1062 void ConfigInputWidget::identifyLimits()
1064 manualCommandData = manualCommandObj->getData();
1065 for (uint i = 0; i < ManualControlSettings::CHANNELMAX_NUMELEM; ++i) {
1066 if (manualSettingsData.ChannelMin[i] <= manualSettingsData.ChannelMax[i]) {
1067 // Non inverted channel
1068 if (manualSettingsData.ChannelMin[i] > manualCommandData.Channel[i]) {
1069 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1071 if (manualSettingsData.ChannelMax[i] < manualCommandData.Channel[i]) {
1072 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1074 } else {
1075 // Inverted channel
1076 if (manualSettingsData.ChannelMax[i] > manualCommandData.Channel[i]) {
1077 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1079 if (manualSettingsData.ChannelMin[i] < manualCommandData.Channel[i]) {
1080 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1084 manualSettingsObj->setData(manualSettingsData);
1087 void ConfigInputWidget::setMoveFromCommand(int command)
1089 // ManualControlSettings::ChannelNumberElem:
1090 // CHANNELNUMBER_ROLL=0,
1091 // CHANNELNUMBER_PITCH=1,
1092 // CHANNELNUMBER_YAW=2,
1093 // CHANNELNUMBER_THROTTLE=3,
1094 // CHANNELNUMBER_FLIGHTMODE=4,
1095 // CHANNELNUMBER_ACCESSORY0=5,
1096 // CHANNELNUMBER_ACCESSORY1=6,
1097 // CHANNELNUMBER_ACCESSORY2=7
1099 txMovements movement = moveLeftVerticalStick;
1101 switch (command) {
1102 case ManualControlSettings::CHANNELNUMBER_ROLL:
1103 movement = ((transmitterMode == mode3 || transmitterMode == mode4) ?
1104 moveLeftHorizontalStick : moveRightHorizontalStick);
1105 break;
1106 case ManualControlSettings::CHANNELNUMBER_PITCH:
1107 movement = (transmitterMode == mode1 || transmitterMode == mode3) ?
1108 moveLeftVerticalStick : moveRightVerticalStick;
1109 break;
1110 case ManualControlSettings::CHANNELNUMBER_YAW:
1111 movement = ((transmitterMode == mode1 || transmitterMode == mode2) ?
1112 moveLeftHorizontalStick : moveRightHorizontalStick);
1113 break;
1114 case ManualControlSettings::CHANNELNUMBER_THROTTLE:
1115 movement = (transmitterMode == mode2 || transmitterMode == mode4) ?
1116 moveLeftVerticalStick : moveRightVerticalStick;
1117 break;
1118 case ManualControlSettings::CHANNELNUMBER_COLLECTIVE:
1119 movement = (transmitterMode == mode2 || transmitterMode == mode4) ?
1120 moveLeftVerticalStick : moveRightVerticalStick;
1121 break;
1122 case ManualControlSettings::CHANNELNUMBER_FLIGHTMODE:
1123 movement = moveFlightMode;
1124 break;
1125 case ManualControlSettings::CHANNELNUMBER_ACCESSORY0:
1126 movement = moveAccess0;
1127 break;
1128 case ManualControlSettings::CHANNELNUMBER_ACCESSORY1:
1129 movement = moveAccess1;
1130 break;
1131 case ManualControlSettings::CHANNELNUMBER_ACCESSORY2:
1132 movement = moveAccess2;
1133 break;
1134 default:
1135 Q_ASSERT(0);
1136 break;
1138 setTxMovement(movement);
1141 void ConfigInputWidget::setTxMovement(txMovements movement)
1143 resetTxControls();
1144 switch (movement) {
1145 case moveLeftVerticalStick:
1146 movePos = 0;
1147 growing = true;
1148 currentMovement = moveLeftVerticalStick;
1149 animate->start(100);
1150 break;
1151 case moveRightVerticalStick:
1152 movePos = 0;
1153 growing = true;
1154 currentMovement = moveRightVerticalStick;
1155 animate->start(100);
1156 break;
1157 case moveLeftHorizontalStick:
1158 movePos = 0;
1159 growing = true;
1160 currentMovement = moveLeftHorizontalStick;
1161 animate->start(100);
1162 break;
1163 case moveRightHorizontalStick:
1164 movePos = 0;
1165 growing = true;
1166 currentMovement = moveRightHorizontalStick;
1167 animate->start(100);
1168 break;
1169 case moveAccess0:
1170 movePos = 0;
1171 growing = true;
1172 currentMovement = moveAccess0;
1173 animate->start(100);
1174 break;
1175 case moveAccess1:
1176 movePos = 0;
1177 growing = true;
1178 currentMovement = moveAccess1;
1179 animate->start(100);
1180 break;
1181 case moveAccess2:
1182 movePos = 0;
1183 growing = true;
1184 currentMovement = moveAccess2;
1185 animate->start(100);
1186 break;
1187 case moveFlightMode:
1188 movePos = 0;
1189 growing = true;
1190 currentMovement = moveFlightMode;
1191 animate->start(1000);
1192 break;
1193 case centerAll:
1194 movePos = 0;
1195 currentMovement = centerAll;
1196 animate->start(1000);
1197 break;
1198 case moveAll:
1199 movePos = 0;
1200 growing = true;
1201 currentMovement = moveAll;
1202 animate->start(50);
1203 break;
1204 case nothing:
1205 movePos = 0;
1206 animate->stop();
1207 break;
1208 default:
1209 Q_ASSERT(0);
1210 break;
1214 void ConfigInputWidget::moveTxControls()
1216 QTransform trans;
1217 QGraphicsItem *item = NULL;
1218 txMovementType move = vertical;
1219 int limitMax = 0;
1220 int limitMin = 0;
1221 static bool auxFlag = false;
1223 switch (currentMovement) {
1224 case moveLeftVerticalStick:
1225 item = m_txLeftStick;
1226 trans = m_txLeftStickOrig;
1227 limitMax = STICK_MAX_MOVE;
1228 limitMin = STICK_MIN_MOVE;
1229 move = vertical;
1230 break;
1231 case moveRightVerticalStick:
1232 item = m_txRightStick;
1233 trans = m_txRightStickOrig;
1234 limitMax = STICK_MAX_MOVE;
1235 limitMin = STICK_MIN_MOVE;
1236 move = vertical;
1237 break;
1238 case moveLeftHorizontalStick:
1239 item = m_txLeftStick;
1240 trans = m_txLeftStickOrig;
1241 limitMax = STICK_MAX_MOVE;
1242 limitMin = STICK_MIN_MOVE;
1243 move = horizontal;
1244 break;
1245 case moveRightHorizontalStick:
1246 item = m_txRightStick;
1247 trans = m_txRightStickOrig;
1248 limitMax = STICK_MAX_MOVE;
1249 limitMin = STICK_MIN_MOVE;
1250 move = horizontal;
1251 break;
1252 case moveAccess0:
1253 item = m_txAccess0;
1254 trans = m_txAccess0Orig;
1255 limitMax = ACCESS_MAX_MOVE;
1256 limitMin = ACCESS_MIN_MOVE;
1257 move = horizontal;
1258 break;
1259 case moveAccess1:
1260 item = m_txAccess1;
1261 trans = m_txAccess1Orig;
1262 limitMax = ACCESS_MAX_MOVE;
1263 limitMin = ACCESS_MIN_MOVE;
1264 move = horizontal;
1265 break;
1266 case moveAccess2:
1267 item = m_txAccess2;
1268 trans = m_txAccess2Orig;
1269 limitMax = ACCESS_MAX_MOVE;
1270 limitMin = ACCESS_MIN_MOVE;
1271 move = horizontal;
1272 break;
1273 case moveFlightMode:
1274 item = m_txFlightMode;
1275 move = jump;
1276 break;
1277 case centerAll:
1278 item = m_txArrows;
1279 move = jump;
1280 break;
1281 case moveAll:
1282 limitMax = STICK_MAX_MOVE;
1283 limitMin = STICK_MIN_MOVE;
1284 move = mix;
1285 break;
1286 default:
1287 break;
1289 if (move == vertical) {
1290 item->setTransform(trans.translate(0, movePos * 10), false);
1291 } else if (move == horizontal) {
1292 item->setTransform(trans.translate(movePos * 10, 0), false);
1293 } else if (move == jump) {
1294 if (item == m_txArrows) {
1295 m_txArrows->setVisible(!m_txArrows->isVisible());
1296 } else if (item == m_txFlightMode) {
1297 QGraphicsSvgItem *svg;
1298 svg = (QGraphicsSvgItem *)item;
1299 if (svg) {
1300 if (svg->elementId() == "flightModeCenter") {
1301 if (growing) {
1302 svg->setElementId("flightModeRight");
1303 m_txFlightMode->setTransform(m_txFlightModeROrig, false);
1304 } else {
1305 svg->setElementId("flightModeLeft");
1306 m_txFlightMode->setTransform(m_txFlightModeLOrig, false);
1308 } else if (svg->elementId() == "flightModeRight") {
1309 growing = false;
1310 svg->setElementId("flightModeCenter");
1311 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1312 } else if (svg->elementId() == "flightModeLeft") {
1313 growing = true;
1314 svg->setElementId("flightModeCenter");
1315 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1319 } else if (move == mix) {
1320 trans = m_txAccess0Orig;
1321 m_txAccess0->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1322 trans = m_txAccess1Orig;
1323 m_txAccess1->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1324 trans = m_txAccess2Orig;
1325 m_txAccess2->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1327 if (auxFlag) {
1328 trans = m_txLeftStickOrig;
1329 m_txLeftStick->setTransform(trans.translate(0, movePos * 10), false);
1330 trans = m_txRightStickOrig;
1331 m_txRightStick->setTransform(trans.translate(0, movePos * 10), false);
1332 } else {
1333 trans = m_txLeftStickOrig;
1334 m_txLeftStick->setTransform(trans.translate(movePos * 10, 0), false);
1335 trans = m_txRightStickOrig;
1336 m_txRightStick->setTransform(trans.translate(movePos * 10, 0), false);
1339 if (movePos == 0) {
1340 m_txFlightMode->setElementId("flightModeCenter");
1341 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1342 } else if (movePos == ACCESS_MAX_MOVE / 2) {
1343 m_txFlightMode->setElementId("flightModeRight");
1344 m_txFlightMode->setTransform(m_txFlightModeROrig, false);
1345 } else if (movePos == ACCESS_MIN_MOVE / 2) {
1346 m_txFlightMode->setElementId("flightModeLeft");
1347 m_txFlightMode->setTransform(m_txFlightModeLOrig, false);
1350 if (move == horizontal || move == vertical || move == mix) {
1351 if (movePos == 0 && growing) {
1352 auxFlag = !auxFlag;
1354 if (growing) {
1355 ++movePos;
1356 } else {
1357 --movePos;
1359 if (movePos > limitMax) {
1360 movePos = movePos - 2;
1361 growing = false;
1363 if (movePos < limitMin) {
1364 movePos = movePos + 2;
1365 growing = true;
1370 AccessoryDesired *ConfigInputWidget::getAccessoryDesiredInstance(int instance)
1372 switch (instance) {
1373 case 0:
1374 if (accessoryDesiredObj0 == NULL) {
1375 accessoryDesiredObj0 = AccessoryDesired::GetInstance(getObjectManager(), 0);
1377 return accessoryDesiredObj0;
1379 case 1:
1380 if (accessoryDesiredObj1 == NULL) {
1381 accessoryDesiredObj1 = AccessoryDesired::GetInstance(getObjectManager(), 1);
1383 return accessoryDesiredObj1;
1385 case 2:
1386 if (accessoryDesiredObj2 == NULL) {
1387 accessoryDesiredObj2 = AccessoryDesired::GetInstance(getObjectManager(), 2);
1389 return accessoryDesiredObj2;
1391 default:
1392 Q_ASSERT(false);
1395 return NULL;
1398 float ConfigInputWidget::getAccessoryDesiredValue(int instance)
1400 AccessoryDesired *accessoryDesiredObj = getAccessoryDesiredInstance(instance);
1402 if (accessoryDesiredObj == NULL) {
1403 Q_ASSERT(false);
1404 return 0.0f;
1407 AccessoryDesired::DataFields data = accessoryDesiredObj->getData();
1409 return data.AccessoryVal;
1412 void ConfigInputWidget::moveSticks()
1414 QTransform trans;
1416 manualCommandData = manualCommandObj->getData();
1417 flightStatusData = flightStatusObj->getData();
1419 switch (transmitterMode) {
1420 case mode1:
1421 trans = m_txLeftStickOrig;
1422 m_txLeftStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1423 trans = m_txRightStickOrig;
1424 m_txRightStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1425 break;
1426 case mode2:
1427 trans = m_txLeftStickOrig;
1428 m_txLeftStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1429 trans = m_txRightStickOrig;
1430 m_txRightStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1431 break;
1432 case mode3:
1433 trans = m_txLeftStickOrig;
1434 m_txLeftStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1435 trans = m_txRightStickOrig;
1436 m_txRightStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1437 break;
1438 case mode4:
1439 trans = m_txLeftStickOrig;
1440 m_txLeftStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1441 trans = m_txRightStickOrig;
1442 m_txRightStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1443 break;
1444 default:
1445 Q_ASSERT(0);
1446 break;
1448 if (flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[0]) {
1449 m_txFlightMode->setElementId("flightModeLeft");
1450 m_txFlightMode->setTransform(m_txFlightModeLOrig, false);
1451 } else if (flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[1]) {
1452 m_txFlightMode->setElementId("flightModeCenter");
1453 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1454 } else if (flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[2]) {
1455 m_txFlightMode->setElementId("flightModeRight");
1456 m_txFlightMode->setTransform(m_txFlightModeROrig, false);
1459 m_txAccess0->setTransform(QTransform(m_txAccess0Orig).translate(getAccessoryDesiredValue(0) * ACCESS_MAX_MOVE * 10, 0), false);
1460 m_txAccess1->setTransform(QTransform(m_txAccess1Orig).translate(getAccessoryDesiredValue(1) * ACCESS_MAX_MOVE * 10, 0), false);
1461 m_txAccess2->setTransform(QTransform(m_txAccess2Orig).translate(getAccessoryDesiredValue(2) * ACCESS_MAX_MOVE * 10, 0), false);
1464 void ConfigInputWidget::dimOtherControls(bool value)
1466 qreal opac;
1468 if (value) {
1469 opac = 0.1;
1470 } else {
1471 opac = 1;
1473 m_txAccess0->setOpacity(opac);
1474 m_txAccess1->setOpacity(opac);
1475 m_txAccess2->setOpacity(opac);
1476 m_txFlightMode->setOpacity(opac);
1479 void ConfigInputWidget::invertControls()
1481 manualSettingsData = manualSettingsObj->getData();
1482 foreach(QWidget * wd, extraWidgets) {
1483 QCheckBox *cb = qobject_cast<QCheckBox *>(wd);
1485 if (cb) {
1486 int index = manualSettingsObj->getField("ChannelNumber")->getElementNames().indexOf(cb->text());
1487 if ((cb->isChecked() && (manualSettingsData.ChannelMax[index] > manualSettingsData.ChannelMin[index])) ||
1488 (!cb->isChecked() && (manualSettingsData.ChannelMax[index] < manualSettingsData.ChannelMin[index]))) {
1489 qint16 aux;
1490 aux = manualSettingsData.ChannelMax[index];
1491 manualSettingsData.ChannelMax[index] = manualSettingsData.ChannelMin[index];
1492 manualSettingsData.ChannelMin[index] = aux;
1496 manualSettingsObj->setData(manualSettingsData);
1499 void ConfigInputWidget::moveFMSlider()
1501 ManualControlSettings::DataFields manualSettingsDataPriv = manualSettingsObj->getData();
1502 ManualControlCommand::DataFields manualCommandDataPriv = manualCommandObj->getData();
1504 float valueScaled;
1505 int chMin = manualSettingsDataPriv.ChannelMin[ManualControlSettings::CHANNELMIN_FLIGHTMODE];
1506 int chMax = manualSettingsDataPriv.ChannelMax[ManualControlSettings::CHANNELMAX_FLIGHTMODE];
1507 int chNeutral = manualSettingsDataPriv.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_FLIGHTMODE];
1509 int value = manualCommandDataPriv.Channel[ManualControlSettings::CHANNELMIN_FLIGHTMODE];
1511 if ((chMax > chMin && value >= chNeutral) || (chMin > chMax && value <= chNeutral)) {
1512 if (chMax != chNeutral) {
1513 valueScaled = (float)(value - chNeutral) / (float)(chMax - chNeutral);
1514 } else {
1515 valueScaled = 0;
1517 } else {
1518 if (chMin != chNeutral) {
1519 valueScaled = (float)(value - chNeutral) / (float)(chNeutral - chMin);
1520 } else {
1521 valueScaled = 0;
1525 // Bound and scale FlightMode from [-1..+1] to [0..1] range
1526 if (valueScaled < -1.0) {
1527 valueScaled = -1.0;
1528 } else if (valueScaled > 1.0) {
1529 valueScaled = 1.0;
1532 // Convert flightMode value into the switch position in the range [0..N-1]
1533 // This uses the same optimized computation as flight code to be consistent
1534 uint8_t pos = ((int16_t)(valueScaled * 256) + 256) * manualSettingsDataPriv.FlightModeNumber >> 9;
1535 if (pos >= manualSettingsDataPriv.FlightModeNumber) {
1536 pos = manualSettingsDataPriv.FlightModeNumber - 1;
1538 ui->fmsSlider->setValue(pos);
1541 void ConfigInputWidget::updatePositionSlider()
1543 ManualControlSettings::DataFields manualSettingsDataPriv = manualSettingsObj->getData();
1545 switch (manualSettingsDataPriv.FlightModeNumber) {
1546 default:
1547 case 6:
1548 ui->fmsModePos6->setEnabled(true);
1549 ui->pidBankSs1_5->setEnabled(true);
1550 ui->assistControlPos6->setEnabled(true);
1551 // pass through
1552 case 5:
1553 ui->fmsModePos5->setEnabled(true);
1554 ui->pidBankSs1_4->setEnabled(true);
1555 ui->assistControlPos5->setEnabled(true);
1556 // pass through
1557 case 4:
1558 ui->fmsModePos4->setEnabled(true);
1559 ui->pidBankSs1_3->setEnabled(true);
1560 ui->assistControlPos4->setEnabled(true);
1561 // pass through
1562 case 3:
1563 ui->fmsModePos3->setEnabled(true);
1564 ui->pidBankSs1_2->setEnabled(true);
1565 ui->assistControlPos3->setEnabled(true);
1566 // pass through
1567 case 2:
1568 ui->fmsModePos2->setEnabled(true);
1569 ui->pidBankSs1_1->setEnabled(true);
1570 ui->assistControlPos2->setEnabled(true);
1571 // pass through
1572 case 1:
1573 ui->fmsModePos1->setEnabled(true);
1574 ui->pidBankSs1_0->setEnabled(true);
1575 ui->assistControlPos1->setEnabled(true);
1576 // pass through
1577 case 0:
1578 break;
1581 switch (manualSettingsDataPriv.FlightModeNumber) {
1582 case 0:
1583 ui->fmsModePos1->setEnabled(false);
1584 ui->pidBankSs1_0->setEnabled(false);
1585 ui->assistControlPos1->setEnabled(false);
1586 // pass through
1587 case 1:
1588 ui->fmsModePos2->setEnabled(false);
1589 ui->pidBankSs1_1->setEnabled(false);
1590 ui->assistControlPos2->setEnabled(false);
1591 // pass through
1592 case 2:
1593 ui->fmsModePos3->setEnabled(false);
1594 ui->pidBankSs1_2->setEnabled(false);
1595 ui->assistControlPos3->setEnabled(false);
1596 // pass through
1597 case 3:
1598 ui->fmsModePos4->setEnabled(false);
1599 ui->pidBankSs1_3->setEnabled(false);
1600 ui->assistControlPos4->setEnabled(false);
1601 // pass through
1602 case 4:
1603 ui->fmsModePos5->setEnabled(false);
1604 ui->pidBankSs1_4->setEnabled(false);
1605 ui->assistControlPos5->setEnabled(false);
1606 // pass through
1607 case 5:
1608 ui->fmsModePos6->setEnabled(false);
1609 ui->pidBankSs1_5->setEnabled(false);
1610 ui->assistControlPos6->setEnabled(false);
1611 // pass through
1612 case 6:
1613 default:
1614 break;
1617 QString fmNumber = QString().setNum(manualSettingsDataPriv.FlightModeNumber);
1618 int count = 0;
1619 foreach(QSlider * sp, findChildren<QSlider *>()) {
1620 // Find FlightMode slider and apply stylesheet
1621 if (sp->objectName() == "channelNeutral") {
1622 if (count == 4) {
1623 sp->setStyleSheet(
1624 "QSlider::groove:horizontal {border: 2px solid rgb(196, 196, 196); height: 12px; border-radius: 4px; "
1625 "border-image:url(:/configgadget/images/flightmode_bg" + fmNumber + ".png); }"
1626 "QSlider::add-page:horizontal { background: none; border: none; }"
1627 "QSlider::sub-page:horizontal { background: none; border: none; }"
1628 "QSlider::handle:horizontal { background: rgba(196, 196, 196, 255); width: 10px; height: 28px; "
1629 "margin: -3px -2px; border-radius: 3px; border: 1px solid #777; }");
1630 count++;
1631 } else {
1632 count++;
1638 void ConfigInputWidget::updateCalibration()
1640 manualCommandData = manualCommandObj->getData();
1641 for (uint i = 0; i < ManualControlSettings::CHANNELMAX_NUMELEM; ++i) {
1642 if ((!reverse[i] && manualSettingsData.ChannelMin[i] > manualCommandData.Channel[i]) ||
1643 (reverse[i] && manualSettingsData.ChannelMin[i] < manualCommandData.Channel[i])) {
1644 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1646 if ((!reverse[i] && manualSettingsData.ChannelMax[i] < manualCommandData.Channel[i]) ||
1647 (reverse[i] && manualSettingsData.ChannelMax[i] > manualCommandData.Channel[i])) {
1648 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1650 if ((i == ManualControlSettings::CHANNELNUMBER_FLIGHTMODE) || (i == ManualControlSettings::CHANNELNUMBER_THROTTLE)) {
1651 adjustSpecialNeutrals();
1652 } else {
1653 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
1657 manualSettingsObj->setData(manualSettingsData);
1658 manualSettingsObj->updated();
1661 void ConfigInputWidget::simpleCalibration(bool enable)
1663 if (enable) {
1664 ui->configurationWizard->setEnabled(false);
1665 ui->saveRCInputToRAM->setEnabled(false);
1666 ui->saveRCInputToSD->setEnabled(false);
1667 ui->runCalibration->setText(tr("Stop Manual Calibration"));
1668 throttleError = false;
1670 QMessageBox msgBox;
1671 msgBox.setText(tr("<p>Arming Settings are now set to 'Always Disarmed' for your safety.</p>"
1672 "<p>Be sure your receiver is powered with an external source and Transmitter is on.</p>"
1673 "<p align='center'><b>Stop Manual Calibration</b> when done</p>"));
1674 msgBox.setDetailedText(tr("You will have to reconfigure the arming settings manually when the manual calibration is finished."));
1675 msgBox.setStandardButtons(QMessageBox::Ok);
1676 msgBox.setDefaultButton(QMessageBox::Ok);
1677 msgBox.exec();
1679 manualCommandData = manualCommandObj->getData();
1681 manualSettingsData = manualSettingsObj->getData();
1682 flightModeSettingsData = flightModeSettingsObj->getData();
1683 flightModeSettingsData.Arming = FlightModeSettings::ARMING_ALWAYSDISARMED;
1684 flightModeSettingsObj->setData(flightModeSettingsData);
1686 for (unsigned int i = 0; i < ManualControlCommand::CHANNEL_NUMELEM; i++) {
1687 reverse[i] = manualSettingsData.ChannelMax[i] < manualSettingsData.ChannelMin[i];
1688 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1689 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
1690 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1693 fastMdataSingle(manualCommandObj, &manualControlMdata);
1695 // Stash actuatorSettings
1696 actuatorSettingsData = actuatorSettingsObj->getData();
1697 memento.actuatorSettingsData = actuatorSettingsData;
1699 // Disable all actuators
1700 resetActuatorSettings();
1702 connect(manualCommandObj, SIGNAL(objectUnpacked(UAVObject *)), this, SLOT(updateCalibration()));
1703 } else {
1704 manualCommandData = manualCommandObj->getData();
1705 manualSettingsData = manualSettingsObj->getData();
1706 systemSettingsData = systemSettingsObj->getData();
1708 if (systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR) {
1709 QMessageBox::warning(this, tr("Ground Vehicle"),
1710 tr("<p>Please <b>center</b> throttle control and press OK when ready.</p>"));
1712 transmitterType = ground;
1713 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_THROTTLE] =
1714 manualCommandData.Channel[ManualControlSettings::CHANNELNUMBER_THROTTLE];
1717 restoreMdataSingle(manualCommandObj, &manualControlMdata);
1719 // Force flight mode number to be 1 if 2 channel ground vehicle was confirmed
1720 if (transmitterType == ground) {
1721 forceOneFlightMode();
1724 for (unsigned int i = 0; i < ManualControlCommand::CHANNEL_NUMELEM; i++) {
1725 if ((i == ManualControlSettings::CHANNELNUMBER_FLIGHTMODE) || (i == ManualControlSettings::CHANNELNUMBER_THROTTLE)) {
1726 adjustSpecialNeutrals();
1727 checkThrottleRange();
1728 } else {
1729 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
1732 manualSettingsObj->setData(manualSettingsData);
1734 // Load actuator settings back from beginning of manual calibration
1735 actuatorSettingsObj->setData(memento.actuatorSettingsData);
1737 ui->configurationWizard->setEnabled(true);
1738 ui->saveRCInputToRAM->setEnabled(true);
1739 ui->saveRCInputToSD->setEnabled(true);
1740 ui->runCalibration->setText(tr("Start Manual Calibration"));
1742 disconnect(manualCommandObj, SIGNAL(objectUnpacked(UAVObject *)), this, SLOT(updateCalibration()));
1746 void ConfigInputWidget::adjustSpecialNeutrals()
1748 // FlightMode and Throttle need special neutral settings
1750 // Force flight mode neutral to middle
1751 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_FLIGHTMODE] =
1752 (manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_FLIGHTMODE] +
1753 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_FLIGHTMODE]) / 2;
1755 // A ground vehicle has a reversible motor, the center position of throttle is the neutral setting.
1756 // So do not have to set a special neutral value for it.
1757 if (transmitterType == ground) {
1758 return;
1761 // Force throttle to be near min, add 4% from total range to avoid arming issues
1762 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_THROTTLE] =
1763 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_THROTTLE] +
1764 ((manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_THROTTLE] -
1765 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_THROTTLE]) * 0.04);
1768 void ConfigInputWidget::checkThrottleRange()
1770 int throttleRange = abs(manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_THROTTLE] -
1771 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_THROTTLE]);
1773 if (!throttleError && (throttleRange < 300)) {
1774 throttleError = true;
1775 QMessageBox::warning(this, tr("Warning"), tr("<p>There is something wrong with Throttle range. Please redo calibration and move <b>ALL sticks</b>, Throttle stick included.</p>"), QMessageBox::Ok);
1777 // Set Throttle neutral to max value so Throttle can't be positive
1778 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_THROTTLE] =
1779 manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_THROTTLE];
1783 bool ConfigInputWidget::shouldObjectBeSaved(UAVObject *object)
1785 // ManualControlCommand no need to be saved
1786 return dynamic_cast<ManualControlCommand *>(object) == NULL;
1789 void ConfigInputWidget::resetChannelSettings()
1791 manualSettingsData = manualSettingsObj->getData();
1792 // Clear all channel data : Channel Type (PPM,PWM..) and Number
1793 for (unsigned int channel = 0; channel < ManualControlSettings::CHANNELNUMBER_NUMELEM; channel++) {
1794 manualSettingsData.ChannelGroups[channel] = ManualControlSettings::CHANNELGROUPS_NONE;
1795 manualSettingsData.ChannelNumber[channel] = CHANNEL_NUMBER_NONE;
1796 manualSettingsData.FlightModeNumber = DEFAULT_FLIGHT_MODE_NUMBER;
1797 manualSettingsObj->setData(manualSettingsData);
1801 void ConfigInputWidget::resetActuatorSettings()
1803 actuatorSettingsData = actuatorSettingsObj->getData();
1805 UAVDataObject *mixer = dynamic_cast<UAVDataObject *>(getObjectManager()->getObject(QString("MixerSettings")));
1806 Q_ASSERT(mixer);
1808 QString mixerType;
1810 // Clear all output data : Min, max, neutral at same value
1811 // 1000 for motors and 1500 for all others (Reversable motor included)
1812 for (unsigned int output = 0; output < 12; output++) {
1813 QString mixerNumType = QString("Mixer%1Type").arg(output + 1);
1814 UAVObjectField *field = mixer->getField(mixerNumType);
1815 Q_ASSERT(field);
1817 if (field) {
1818 mixerType = field->getValue().toString();
1820 if ((mixerType == "Motor") || (mixerType == "Disabled")) {
1821 actuatorSettingsData.ChannelMax[output] = 1000;
1822 actuatorSettingsData.ChannelMin[output] = 1000;
1823 actuatorSettingsData.ChannelNeutral[output] = 1000;
1824 } else {
1825 actuatorSettingsData.ChannelMax[output] = 1500;
1826 actuatorSettingsData.ChannelMin[output] = 1500;
1827 actuatorSettingsData.ChannelNeutral[output] = 1500;
1829 actuatorSettingsObj->setData(actuatorSettingsData);
1833 void ConfigInputWidget::forceOneFlightMode()
1835 manualSettingsData = manualSettingsObj->getData();
1836 manualSettingsData.FlightModeNumber = 1;
1837 manualSettingsObj->setData(manualSettingsData);