LP-311 Remove basic/advanced stabilization tab auto-switch (autotune/txpid lock issues)
[librepilot.git] / ground / gcs / src / plugins / config / configinputwidget.cpp
blobf350afcd35319d58ee0d6debc751b560fcd40a57
1 /**
2 ******************************************************************************
4 * @file configinputwidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup ConfigPlugin Config Plugin
10 * @{
11 * @brief Servo input/output configuration panel for the config gadget
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 "configinputwidget.h"
31 #include <extensionsystem/pluginmanager.h>
32 #include <coreplugin/generalsettings.h>
33 #include <utils/stylehelper.h>
35 #include "ui_input.h"
36 #include "ui_input_wizard.h"
38 #include "inputchannelform.h"
39 #include "ui_inputchannelform.h"
41 #include "failsafechannelform.h"
42 #include "ui_failsafechannelform.h"
44 #include <systemalarms.h>
46 #include <QDebug>
47 #include <QStringList>
48 #include <QWidget>
49 #include <QTextEdit>
50 #include <QVBoxLayout>
51 #include <QPushButton>
52 #include <QDesktopServices>
53 #include <QUrl>
54 #include <QMessageBox>
55 #include <QEventLoop>
56 #include <QGraphicsSvgItem>
57 #include <QSvgRenderer>
59 #define ACCESS_MIN_MOVE -3
60 #define ACCESS_MAX_MOVE 3
61 #define STICK_MIN_MOVE -8
62 #define STICK_MAX_MOVE 8
64 #define CHANNEL_NUMBER_NONE 0
65 #define DEFAULT_FLIGHT_MODE_NUMBER 0
67 ConfigInputWidget::ConfigInputWidget(QWidget *parent) :
68 ConfigTaskWidget(parent),
69 wizardStep(wizardNone),
70 // not currently stored in the settings UAVO
71 transmitterMode(mode2),
72 transmitterType(acro),
74 loop(NULL),
75 skipflag(false),
76 nextDelayedTimer(),
77 nextDelayedTick(0),
78 nextDelayedLatestActivityTick(0),
79 accessoryDesiredObj0(NULL),
80 accessoryDesiredObj1(NULL),
81 accessoryDesiredObj2(NULL),
82 accessoryDesiredObj3(NULL)
84 manualCommandObj = ManualControlCommand::GetInstance(getObjectManager());
85 manualSettingsObj = ManualControlSettings::GetInstance(getObjectManager());
86 flightModeSettingsObj = FlightModeSettings::GetInstance(getObjectManager());
87 flightStatusObj = FlightStatus::GetInstance(getObjectManager());
88 receiverActivityObj = ReceiverActivity::GetInstance(getObjectManager());
89 accessoryDesiredObj0 = AccessoryDesired::GetInstance(getObjectManager(), 0);
90 accessoryDesiredObj1 = AccessoryDesired::GetInstance(getObjectManager(), 1);
91 accessoryDesiredObj2 = AccessoryDesired::GetInstance(getObjectManager(), 2);
92 accessoryDesiredObj3 = AccessoryDesired::GetInstance(getObjectManager(), 3);
93 actuatorSettingsObj = ActuatorSettings::GetInstance(getObjectManager());
94 systemSettingsObj = SystemSettings::GetInstance(getObjectManager());
96 // Only instance 0 is present if the board is not connected.
97 // The other instances are populated lazily.
98 Q_ASSERT(accessoryDesiredObj0);
100 ui = new Ui_InputWidget();
101 ui->setupUi(this);
103 wizardUi = new Ui_InputWizardWidget();
104 wizardUi->setupUi(ui->wizard);
106 addApplySaveButtons(ui->saveRCInputToRAM, ui->saveRCInputToSD);
108 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
109 Core::Internal::GeneralSettings *settings = pm->getObject<Core::Internal::GeneralSettings>();
110 if (!settings->useExpertMode()) {
111 ui->saveRCInputToRAM->setVisible(false);
114 addApplySaveButtons(ui->saveRCInputToRAM, ui->saveRCInputToSD);
116 // Generate the rows of buttons in the input channel form GUI
117 quint32 index = 0;
118 quint32 indexRT = 0;
119 foreach(QString name, manualSettingsObj->getField("ChannelNumber")->getElementNames()) {
120 Q_ASSERT(index < ManualControlSettings::CHANNELGROUPS_NUMELEM);
122 // Input channel setup
123 InputChannelForm *inputChannelForm = new InputChannelForm(index, this);
124 inputChannelForm->setName(name);
126 inputChannelForm->moveTo(*(ui->channelLayout));
128 // The order of the following binding calls is important. Since the values will be populated
129 // in reverse order of the binding order otherwise the 'Reversed' logic will floor the neutral value
130 // to the max value ( which is smaller than the neutral value when reversed ) and the channel number
131 // will not be set correctly.
132 addWidgetBinding("ManualControlSettings", "ChannelNumber", inputChannelForm->ui->channelNumber, index);
133 addWidgetBinding("ManualControlSettings", "ChannelGroups", inputChannelForm->ui->channelGroup, index);
134 // Slider position based on real time Rcinput (allow monitoring)
135 addWidgetBinding("ManualControlCommand", "Channel", inputChannelForm->ui->channelNeutral, index);
136 // Neutral value stored on board (SpinBox)
137 addWidgetBinding("ManualControlSettings", "ChannelNeutral", inputChannelForm->ui->neutralValue, index);
138 addWidgetBinding("ManualControlSettings", "ChannelMax", inputChannelForm->ui->channelMax, index);
139 addWidgetBinding("ManualControlSettings", "ChannelMin", inputChannelForm->ui->channelMin, index);
140 addWidgetBinding("ManualControlSettings", "ChannelMax", inputChannelForm->ui->channelMax, index);
142 addWidget(inputChannelForm->ui->channelRev);
144 // Reversing supported for some channels only
145 bool reversable = ((index == ManualControlSettings::CHANNELGROUPS_THROTTLE) ||
146 (index == ManualControlSettings::CHANNELGROUPS_ROLL) ||
147 (index == ManualControlSettings::CHANNELGROUPS_PITCH) ||
148 (index == ManualControlSettings::CHANNELGROUPS_YAW));
149 inputChannelForm->ui->channelRev->setVisible(reversable);
151 // Input filter response time fields supported for some channels only
152 switch (index) {
153 case ManualControlSettings::CHANNELGROUPS_ROLL:
154 case ManualControlSettings::CHANNELGROUPS_PITCH:
155 case ManualControlSettings::CHANNELGROUPS_YAW:
156 case ManualControlSettings::CHANNELGROUPS_COLLECTIVE:
157 case ManualControlSettings::CHANNELGROUPS_ACCESSORY0:
158 case ManualControlSettings::CHANNELGROUPS_ACCESSORY1:
159 case ManualControlSettings::CHANNELGROUPS_ACCESSORY2:
160 case ManualControlSettings::CHANNELGROUPS_ACCESSORY3:
161 addWidgetBinding("ManualControlSettings", "ResponseTime", inputChannelForm->ui->channelResponseTime, indexRT);
162 ++indexRT;
163 break;
164 case ManualControlSettings::CHANNELGROUPS_THROTTLE:
165 case ManualControlSettings::CHANNELGROUPS_FLIGHTMODE:
166 inputChannelForm->ui->channelResponseTime->setVisible(false);
167 break;
168 default:
169 Q_ASSERT(0);
170 break;
172 ++index;
175 QList<int> failsafeReloadGroup;
176 failsafeReloadGroup.append(555);
178 addWidgetBinding("ManualControlSettings", "FailsafeFlightModeSwitchPosition", ui->failsafeFlightMode, 0, 1, true, new QList<int>(failsafeReloadGroup));
180 // Generate the rows for the failsafe channel form GUI
181 index = 0;
182 foreach(QString name, manualSettingsObj->getField("FailsafeChannel")->getElementNames()) {
183 Q_ASSERT(index < ManualControlSettings::FAILSAFECHANNEL_NUMELEM);
185 // Failsafe channels setup
186 FailsafeChannelForm *failsafeChannelForm = new FailsafeChannelForm(index, this);
187 addWidget(failsafeChannelForm->ui->channelValueSpinner);
188 failsafeChannelForm->setName(name);
189 failsafeChannelForm->moveTo(*(ui->failsafeChannelsLayout));
190 addWidgetBinding("ManualControlSettings", "FailsafeChannel", failsafeChannelForm->ui->channelValue, index, 0.01, true, new QList<int>(failsafeReloadGroup));
191 ++index;
193 addWidget(ui->failsafeDefault);
195 addWidgetBinding("ManualControlSettings", "Deadband", ui->deadband, 0, 1);
196 addWidgetBinding("ManualControlSettings", "DeadbandAssistedControl", ui->assistedControlDeadband, 0, 1);
198 connect(ui->configurationWizard, SIGNAL(clicked()), this, SLOT(goToWizard()));
199 connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(disableWizardButton(int)));
200 connect(ui->runCalibration, SIGNAL(toggled(bool)), this, SLOT(simpleCalibration(bool)));
202 connect(wizardUi->wzNext, SIGNAL(clicked()), this, SLOT(wzNext()));
203 connect(wizardUi->wzCancel, SIGNAL(clicked()), this, SLOT(wzCancel()));
204 connect(wizardUi->wzBack, SIGNAL(clicked()), this, SLOT(wzBack()));
206 ui->stackedWidget->setCurrentIndex(0);
207 QList<QWidget *> widgets = QList<QWidget *>() << ui->fmsModePos1 << ui->fmsModePos2 << ui->fmsModePos3 <<
208 ui->fmsModePos4 << ui->fmsModePos5 << ui->fmsModePos6;
209 index = 0;
210 foreach(QWidget * widget, widgets) {
211 addWidgetBinding("FlightModeSettings", "FlightModePosition", widget, index++, 1, true);
214 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Roll, "Roll", 1, true);
215 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Roll, "Roll", 1, true);
216 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Roll, "Roll", 1, true);
217 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Roll, "Roll", 1, true);
218 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Roll, "Roll", 1, true);
219 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Roll, "Roll", 1, true);
221 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Pitch, "Pitch", 1, true);
222 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Pitch, "Pitch", 1, true);
223 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Pitch, "Pitch", 1, true);
224 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Pitch, "Pitch", 1, true);
225 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Pitch, "Pitch", 1, true);
226 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Pitch, "Pitch", 1, true);
228 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Yaw, "Yaw", 1, true);
229 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Yaw, "Yaw", 1, true);
230 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Yaw, "Yaw", 1, true);
231 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Yaw, "Yaw", 1, true);
232 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Yaw, "Yaw", 1, true);
233 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Yaw, "Yaw", 1, true);
235 addWidgetBinding("FlightModeSettings", "Stabilization1Settings", ui->fmsSsPos1Thrust, "Thrust", 1, true);
236 addWidgetBinding("FlightModeSettings", "Stabilization2Settings", ui->fmsSsPos2Thrust, "Thrust", 1, true);
237 addWidgetBinding("FlightModeSettings", "Stabilization3Settings", ui->fmsSsPos3Thrust, "Thrust", 1, true);
238 addWidgetBinding("FlightModeSettings", "Stabilization4Settings", ui->fmsSsPos4Thrust, "Thrust", 1, true);
239 addWidgetBinding("FlightModeSettings", "Stabilization5Settings", ui->fmsSsPos5Thrust, "Thrust", 1, true);
240 addWidgetBinding("FlightModeSettings", "Stabilization6Settings", ui->fmsSsPos6Thrust, "Thrust", 1, true);
242 addWidgetBinding("ManualControlSettings", "FlightModeNumber", ui->fmsPosNum);
244 addWidgetBinding("FlightModeSettings", "Arming", ui->armControl);
245 addWidgetBinding("FlightModeSettings", "ArmedTimeout", ui->armTimeout, 0, 1000);
246 connect(ManualControlCommand::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveFMSlider()));
247 connect(ManualControlSettings::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updatePositionSlider()));
248 connect(SystemAlarms::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateConfigAlarmStatus()));
250 connect(ui->failsafeFlightMode, SIGNAL(currentIndexChanged(int)), this, SLOT(failsafeFlightModeChanged(int)));
251 connect(ui->failsafeFlightModeCb, SIGNAL(toggled(bool)), this, SLOT(failsafeFlightModeCbToggled(bool)));
253 connect(this, SIGNAL(enableControlsChanged(bool)), this, SLOT(enableControlsChanged(bool)));
255 addWidget(ui->configurationWizard);
256 addWidget(ui->runCalibration);
257 addWidget(ui->failsafeFlightModeCb);
259 autoLoadWidgets();
261 populateWidgets();
262 refreshWidgetsValues();
264 // Connect the help button
265 connect(ui->inputHelp, SIGNAL(clicked()), this, SLOT(openHelp()));
267 wizardUi->graphicsView->setScene(new QGraphicsScene(this));
268 wizardUi->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
269 m_renderer = new QSvgRenderer();
270 QGraphicsScene *l_scene = wizardUi->graphicsView->scene();
271 wizardUi->graphicsView->setBackgroundBrush(QBrush(Utils::StyleHelper::baseColor()));
272 if (QFile::exists(":/configgadget/images/TX2.svg") && m_renderer->load(QString(":/configgadget/images/TX2.svg")) && m_renderer->isValid()) {
273 l_scene->clear(); // Deletes all items contained in the scene as well.
275 m_txBackground = new QGraphicsSvgItem();
276 // All other items will be clipped to the shape of the background
277 m_txBackground->setFlags(QGraphicsItem::ItemClipsChildrenToShape |
278 QGraphicsItem::ItemClipsToShape);
279 m_txBackground->setSharedRenderer(m_renderer);
280 m_txBackground->setElementId("background");
281 l_scene->addItem(m_txBackground);
283 m_txMainBody = new QGraphicsSvgItem();
284 m_txMainBody->setParentItem(m_txBackground);
285 m_txMainBody->setSharedRenderer(m_renderer);
286 m_txMainBody->setElementId("body");
287 l_scene->addItem(m_txMainBody);
289 m_txLeftStick = new QGraphicsSvgItem();
290 m_txLeftStick->setParentItem(m_txBackground);
291 m_txLeftStick->setSharedRenderer(m_renderer);
292 m_txLeftStick->setElementId("ljoy");
294 m_txRightStick = new QGraphicsSvgItem();
295 m_txRightStick->setParentItem(m_txBackground);
296 m_txRightStick->setSharedRenderer(m_renderer);
297 m_txRightStick->setElementId("rjoy");
299 m_txAccess0 = new QGraphicsSvgItem();
300 m_txAccess0->setParentItem(m_txBackground);
301 m_txAccess0->setSharedRenderer(m_renderer);
302 m_txAccess0->setElementId("access0");
304 m_txAccess1 = new QGraphicsSvgItem();
305 m_txAccess1->setParentItem(m_txBackground);
306 m_txAccess1->setSharedRenderer(m_renderer);
307 m_txAccess1->setElementId("access1");
309 m_txAccess2 = new QGraphicsSvgItem();
310 m_txAccess2->setParentItem(m_txBackground);
311 m_txAccess2->setSharedRenderer(m_renderer);
312 m_txAccess2->setElementId("access2");
314 m_txAccess3 = new QGraphicsSvgItem();
315 m_txAccess3->setParentItem(m_txBackground);
316 m_txAccess3->setSharedRenderer(m_renderer);
317 m_txAccess3->setElementId("access3");
319 m_txFlightMode = new QGraphicsSvgItem();
320 m_txFlightMode->setParentItem(m_txBackground);
321 m_txFlightMode->setSharedRenderer(m_renderer);
322 m_txFlightMode->setElementId("flightModeCenter");
323 m_txFlightMode->setZValue(-10);
325 m_txFlightModeCountBG = new QGraphicsSvgItem();
326 m_txFlightModeCountBG->setParentItem(m_txBackground);
327 m_txFlightModeCountBG->setSharedRenderer(m_renderer);
328 m_txFlightModeCountBG->setElementId("fm_count_bg");
329 l_scene->addItem(m_txFlightModeCountBG);
331 m_txFlightModeCountText = new QGraphicsSimpleTextItem("?", m_txFlightModeCountBG);
332 m_txFlightModeCountText->setBrush(QColor(40, 40, 40));
333 m_txFlightModeCountText->setFont(QFont("Arial Bold"));
335 m_txArrows = new QGraphicsSvgItem();
336 m_txArrows->setParentItem(m_txBackground);
337 m_txArrows->setSharedRenderer(m_renderer);
338 m_txArrows->setElementId("arrows");
339 m_txArrows->setVisible(false);
341 QRectF orig = m_renderer->boundsOnElement("ljoy");
342 QMatrix Matrix = m_renderer->matrixForElement("ljoy");
343 orig = Matrix.mapRect(orig);
344 m_txLeftStickOrig.translate(orig.x(), orig.y());
345 m_txLeftStick->setTransform(m_txLeftStickOrig, false);
347 orig = m_renderer->boundsOnElement("arrows");
348 Matrix = m_renderer->matrixForElement("arrows");
349 orig = Matrix.mapRect(orig);
350 m_txArrowsOrig.translate(orig.x(), orig.y());
351 m_txArrows->setTransform(m_txArrowsOrig, false);
353 orig = m_renderer->boundsOnElement("body");
354 Matrix = m_renderer->matrixForElement("body");
355 orig = Matrix.mapRect(orig);
356 m_txMainBodyOrig.translate(orig.x(), orig.y());
357 m_txMainBody->setTransform(m_txMainBodyOrig, false);
359 orig = m_renderer->boundsOnElement("flightModeCenter");
360 Matrix = m_renderer->matrixForElement("flightModeCenter");
361 orig = Matrix.mapRect(orig);
362 m_txFlightModeCOrig.translate(orig.x(), orig.y());
363 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
365 orig = m_renderer->boundsOnElement("flightModeLeft");
366 Matrix = m_renderer->matrixForElement("flightModeLeft");
367 orig = Matrix.mapRect(orig);
368 m_txFlightModeLOrig.translate(orig.x(), orig.y());
369 orig = m_renderer->boundsOnElement("flightModeRight");
370 Matrix = m_renderer->matrixForElement("flightModeRight");
371 orig = Matrix.mapRect(orig);
372 m_txFlightModeROrig.translate(orig.x(), orig.y());
374 orig = m_renderer->boundsOnElement("fm_count_bg");
375 Matrix = m_renderer->matrixForElement("fm_count_bg");
376 orig = Matrix.mapRect(orig);
377 m_txFlightModeCountBGOrig.translate(orig.x(), orig.y());
378 m_txFlightModeCountBG->setTransform(m_txFlightModeCountBGOrig, false);
380 QRectF flightModeBGRect = m_txFlightModeCountBG->boundingRect();
381 QRectF flightModeTextRect = m_txFlightModeCountText->boundingRect();
382 qreal scale = 2.5;
383 m_txFlightModeCountTextOrig.translate(flightModeBGRect.width() - (flightModeBGRect.height() / 2), flightModeBGRect.height() / 2);
384 m_txFlightModeCountTextOrig.scale(scale, scale);
385 m_txFlightModeCountTextOrig.translate(-flightModeTextRect.width() / 2, -flightModeTextRect.height() / 2);
386 m_txFlightModeCountText->setTransform(m_txFlightModeCountTextOrig, false);
388 orig = m_renderer->boundsOnElement("rjoy");
389 Matrix = m_renderer->matrixForElement("rjoy");
390 orig = Matrix.mapRect(orig);
391 m_txRightStickOrig.translate(orig.x(), orig.y());
392 m_txRightStick->setTransform(m_txRightStickOrig, false);
394 orig = m_renderer->boundsOnElement("access0");
395 Matrix = m_renderer->matrixForElement("access0");
396 orig = Matrix.mapRect(orig);
397 m_txAccess0Orig.translate(orig.x(), orig.y());
398 m_txAccess0->setTransform(m_txAccess0Orig, false);
400 orig = m_renderer->boundsOnElement("access1");
401 Matrix = m_renderer->matrixForElement("access1");
402 orig = Matrix.mapRect(orig);
403 m_txAccess1Orig.translate(orig.x(), orig.y());
404 m_txAccess1->setTransform(m_txAccess1Orig, false);
406 orig = m_renderer->boundsOnElement("access2");
407 Matrix = m_renderer->matrixForElement("access2");
408 orig = Matrix.mapRect(orig);
409 m_txAccess2Orig.translate(orig.x(), orig.y());
410 m_txAccess2->setTransform(m_txAccess2Orig, true);
412 orig = m_renderer->boundsOnElement("access3");
413 Matrix = m_renderer->matrixForElement("access3");
414 orig = Matrix.mapRect(orig);
415 m_txAccess3Orig.translate(orig.x(), orig.y());
416 m_txAccess3->setTransform(m_txAccess3Orig, true);
418 wizardUi->graphicsView->fitInView(m_txMainBody, Qt::KeepAspectRatio);
419 animate = new QTimer(this);
420 connect(animate, SIGNAL(timeout()), this, SLOT(moveTxControls()));
422 heliChannelOrder << ManualControlSettings::CHANNELGROUPS_COLLECTIVE <<
423 ManualControlSettings::CHANNELGROUPS_THROTTLE <<
424 ManualControlSettings::CHANNELGROUPS_ROLL <<
425 ManualControlSettings::CHANNELGROUPS_PITCH <<
426 ManualControlSettings::CHANNELGROUPS_YAW <<
427 ManualControlSettings::CHANNELGROUPS_FLIGHTMODE <<
428 ManualControlSettings::CHANNELGROUPS_ACCESSORY0 <<
429 ManualControlSettings::CHANNELGROUPS_ACCESSORY1 <<
430 ManualControlSettings::CHANNELGROUPS_ACCESSORY2 <<
431 ManualControlSettings::CHANNELGROUPS_ACCESSORY3;
433 acroChannelOrder << ManualControlSettings::CHANNELGROUPS_THROTTLE <<
434 ManualControlSettings::CHANNELGROUPS_ROLL <<
435 ManualControlSettings::CHANNELGROUPS_PITCH <<
436 ManualControlSettings::CHANNELGROUPS_YAW <<
437 ManualControlSettings::CHANNELGROUPS_FLIGHTMODE <<
438 ManualControlSettings::CHANNELGROUPS_ACCESSORY0 <<
439 ManualControlSettings::CHANNELGROUPS_ACCESSORY1 <<
440 ManualControlSettings::CHANNELGROUPS_ACCESSORY2 <<
441 ManualControlSettings::CHANNELGROUPS_ACCESSORY3;
443 groundChannelOrder << ManualControlSettings::CHANNELGROUPS_THROTTLE <<
444 ManualControlSettings::CHANNELGROUPS_YAW <<
445 ManualControlSettings::CHANNELGROUPS_ACCESSORY0;
447 updateEnableControls();
450 void ConfigInputWidget::buildOptionComboBox(QComboBox *combo, UAVObjectField *field, int index, bool applyLimits)
452 if (combo == ui->failsafeFlightMode) {
453 for (quint32 i = 0; i < FlightModeSettings::FLIGHTMODEPOSITION_NUMELEM; i++) {
454 ui->failsafeFlightMode->addItem(QString("Position %1").arg(i + 1), QVariant(i));
456 } else {
457 ConfigTaskWidget::buildOptionComboBox(combo, field, index, applyLimits);
461 void ConfigInputWidget::resetTxControls()
463 m_txLeftStick->setTransform(m_txLeftStickOrig, false);
464 m_txRightStick->setTransform(m_txRightStickOrig, false);
465 m_txAccess0->setTransform(m_txAccess0Orig, false);
466 m_txAccess1->setTransform(m_txAccess1Orig, false);
467 m_txAccess2->setTransform(m_txAccess2Orig, false);
468 m_txAccess3->setTransform(m_txAccess3Orig, false);
469 m_txFlightMode->setElementId("flightModeCenter");
470 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
471 m_txArrows->setVisible(false);
472 m_txFlightModeCountText->setText("?");
473 m_txFlightModeCountText->setVisible(false);
474 m_txFlightModeCountBG->setVisible(false);
477 ConfigInputWidget::~ConfigInputWidget()
480 void ConfigInputWidget::enableControls(bool enable)
482 ConfigTaskWidget::enableControls(enable);
484 if (enable) {
485 updatePositionSlider();
486 } else {
487 // Hide configAlarmStatus when disconnected
488 ui->configAlarmStatus->setVisible(false);
492 void ConfigInputWidget::resizeEvent(QResizeEvent *event)
494 QWidget::resizeEvent(event);
496 wizardUi->graphicsView->fitInView(m_txBackground, Qt::KeepAspectRatio);
499 void ConfigInputWidget::openHelp()
501 QDesktopServices::openUrl(QUrl(QString(WIKI_URL_ROOT) + QString("Input+Configuration"),
502 QUrl::StrictMode));
505 void ConfigInputWidget::goToWizard()
507 QMessageBox msgBox;
509 msgBox.setText(tr("Arming Settings are now set to 'Always Disarmed' for your safety."));
510 msgBox.setDetailedText(tr("You will have to reconfigure the arming settings manually "
511 "when the wizard is finished. After the last step of the "
512 "wizard you will be taken to the Arming Settings screen."));
513 msgBox.setStandardButtons(QMessageBox::Ok);
514 msgBox.setDefaultButton(QMessageBox::Ok);
515 msgBox.exec();
517 // Set correct tab visible before starting wizard.
518 if (ui->tabWidget->currentIndex() != 0) {
519 ui->tabWidget->setCurrentIndex(0);
522 // Stash current manual settings data in case the wizard is
523 // cancelled or the user proceeds far enough into the wizard such
524 // that the UAVO is changed, but then backs out to the start and
525 // chooses a different TX type (which could otherwise result in
526 // unexpected TX channels being enabled)
527 manualSettingsData = manualSettingsObj->getData();
528 memento.manualSettingsData = manualSettingsData;
529 flightModeSettingsData = flightModeSettingsObj->getData();
530 memento.flightModeSettingsData = flightModeSettingsData;
531 flightModeSettingsData.Arming = FlightModeSettings::ARMING_ALWAYSDISARMED;
532 flightModeSettingsObj->setData(flightModeSettingsData);
533 // Stash actuatorSettings
534 actuatorSettingsData = actuatorSettingsObj->getData();
535 memento.actuatorSettingsData = actuatorSettingsData;
537 // Stash systemSettings
538 systemSettingsData = systemSettingsObj->getData();
539 memento.systemSettingsData = systemSettingsData;
541 // Now reset channel and actuator settings (disable outputs)
542 resetChannelSettings();
543 resetActuatorSettings();
545 // Use faster input update rate.
546 fastMdata();
548 // start the wizard
549 wizardSetUpStep(wizardWelcome);
550 wizardUi->graphicsView->fitInView(m_txBackground, Qt::KeepAspectRatio);
553 void ConfigInputWidget::disableWizardButton(int value)
555 if (value != 0) {
556 ui->groupBox_3->setVisible(false);
557 } else {
558 ui->groupBox_3->setVisible(true);
562 void ConfigInputWidget::wzCancel()
564 dimOtherControls(false);
566 // Cancel any ongoing delayd next trigger.
567 wzNextDelayedCancel();
569 // Restore original input update rate.
570 restoreMdata();
572 ui->stackedWidget->setCurrentIndex(0);
574 if (wizardStep != wizardNone) {
575 wizardTearDownStep(wizardStep);
577 wizardStep = wizardNone;
578 ui->stackedWidget->setCurrentIndex(0);
580 // Load settings back from beginning of wizard
581 manualSettingsObj->setData(memento.manualSettingsData);
582 flightModeSettingsObj->setData(memento.flightModeSettingsData);
583 actuatorSettingsObj->setData(memento.actuatorSettingsData);
584 systemSettingsObj->setData(memento.systemSettingsData);
587 void ConfigInputWidget::registerControlActivity()
589 nextDelayedLatestActivityTick = nextDelayedTick;
592 void ConfigInputWidget::wzNextDelayed()
594 nextDelayedTick++;
596 // Call next after the full 2500 ms timeout has been reached,
597 // or if no input activity has occurred the last 500 ms.
598 if (nextDelayedTick == 25 ||
599 nextDelayedTick - nextDelayedLatestActivityTick >= 5) {
600 wzNext();
604 void ConfigInputWidget::wzNextDelayedStart()
606 // Call wzNextDelayed every 100 ms, to see if it's time to go to the next page.
607 connect(&nextDelayedTimer, SIGNAL(timeout()), this, SLOT(wzNextDelayed()));
608 nextDelayedTimer.start(100);
611 // Cancel the delayed next timer, if it's active.
612 void ConfigInputWidget::wzNextDelayedCancel()
614 nextDelayedTick = 0;
615 nextDelayedLatestActivityTick = 0;
616 if (nextDelayedTimer.isActive()) {
617 nextDelayedTimer.stop();
618 disconnect(&nextDelayedTimer, SIGNAL(timeout()), this, SLOT(wzNextDelayed()));
622 void ConfigInputWidget::wzNext()
624 wzNextDelayedCancel();
626 // In identify sticks mode the next button can indicate
627 // channel advance
628 if (wizardStep != wizardNone &&
629 wizardStep != wizardIdentifySticks) {
630 wizardTearDownStep(wizardStep);
633 // State transitions for next button
634 switch (wizardStep) {
635 case wizardWelcome:
636 wizardSetUpStep(wizardChooseType);
637 break;
638 case wizardChooseType:
639 wizardSetUpStep(wizardChooseMode);
640 break;
641 case wizardChooseMode:
642 wizardSetUpStep(wizardIdentifySticks);
643 break;
644 case wizardIdentifySticks:
645 nextChannel();
646 if (currentChannelNum == -1) { // Gone through all channels
647 wizardTearDownStep(wizardIdentifySticks);
648 wizardSetUpStep(wizardIdentifyCenter);
650 break;
651 case wizardIdentifyCenter:
652 resetFlightModeSettings();
653 wizardSetUpStep(wizardIdentifyLimits);
654 break;
655 case wizardIdentifyLimits:
656 wizardSetUpStep(wizardIdentifyInverted);
657 break;
658 case wizardIdentifyInverted:
659 wizardSetUpStep(wizardFinish);
660 break;
661 case wizardFinish:
662 wizardStep = wizardNone;
664 // Restore original input update rate.
665 restoreMdata();
667 // Load actuator settings back from beginning of wizard
668 actuatorSettingsObj->setData(memento.actuatorSettingsData);
670 // Force flight mode neutral to middle and Throttle neutral at 4%
671 adjustSpecialNeutrals();
672 throttleError = false;
673 checkThrottleRange();
675 // Force flight mode number to be 1 if 2 CH ground vehicle was selected
676 if (transmitterType == ground) {
677 forceOneFlightMode();
680 manualSettingsObj->setData(manualSettingsData);
681 // move to Arming Settings tab
682 ui->stackedWidget->setCurrentIndex(0);
683 ui->tabWidget->setCurrentIndex(3);
684 break;
685 default:
686 Q_ASSERT(0);
690 void ConfigInputWidget::wzBack()
692 wzNextDelayedCancel();
694 if (wizardStep != wizardNone &&
695 wizardStep != wizardIdentifySticks) {
696 wizardTearDownStep(wizardStep);
699 // State transitions for back button
700 switch (wizardStep) {
701 case wizardChooseType:
702 wizardSetUpStep(wizardWelcome);
703 break;
704 case wizardChooseMode:
705 wizardSetUpStep(wizardChooseType);
706 break;
707 case wizardIdentifySticks:
708 prevChannel();
709 if (currentChannelNum == -1) {
710 wizardTearDownStep(wizardIdentifySticks);
711 wizardSetUpStep(wizardChooseMode);
713 break;
714 case wizardIdentifyCenter:
715 wizardSetUpStep(wizardIdentifySticks);
716 break;
717 case wizardIdentifyLimits:
718 wizardSetUpStep(wizardIdentifyCenter);
719 break;
720 case wizardIdentifyInverted:
721 resetFlightModeSettings();
722 wizardSetUpStep(wizardIdentifyLimits);
723 break;
724 case wizardFinish:
725 wizardSetUpStep(wizardIdentifyInverted);
726 break;
727 default:
728 Q_ASSERT(0);
732 void ConfigInputWidget::wizardSetUpStep(enum wizardSteps step)
734 wizardUi->wzNext->setText(tr("Next"));
736 switch (step) {
737 case wizardWelcome:
738 foreach(QPointer<QWidget> wd, extraWidgets) {
739 if (!wd.isNull()) {
740 delete wd;
743 extraWidgets.clear();
744 wizardUi->graphicsView->setVisible(false);
745 setTxMovement(nothing);
746 wizardUi->wzBack->setEnabled(false);
747 wizardUi->pagesStack->setCurrentWidget(wizardUi->welcomePage);
748 ui->stackedWidget->setCurrentIndex(1);
749 break;
750 case wizardChooseType:
752 wizardUi->graphicsView->setVisible(true);
753 wizardUi->graphicsView->fitInView(m_txBackground, Qt::KeepAspectRatio);
754 setTxMovement(nothing);
755 wizardUi->wzBack->setEnabled(true);
756 if (transmitterType == heli) {
757 wizardUi->typeHeli->setChecked(true);
758 } else if (transmitterType == ground) {
759 wizardUi->typeGround->setChecked(true);
760 } else {
761 wizardUi->typeAcro->setChecked(true);
763 wizardUi->pagesStack->setCurrentWidget(wizardUi->chooseTypePage);
765 break;
766 case wizardChooseMode:
768 wizardUi->wzBack->setEnabled(true);
769 QRadioButton *modeButtons[] = {
770 wizardUi->mode1Button,
771 wizardUi->mode2Button,
772 wizardUi->mode3Button,
773 wizardUi->mode4Button
776 for (int i = 0; i <= mode4; ++i) {
777 QString label;
778 txMode mode = static_cast<txMode>(i);
779 if (transmitterType == heli) {
780 switch (mode) {
781 case mode1: label = tr("Mode 1: Fore/Aft Cyclic and Yaw on the left, Throttle/Collective and Left/Right Cyclic on the right"); break;
782 case mode2: label = tr("Mode 2: Throttle/Collective and Yaw on the left, Cyclic on the right"); break;
783 case mode3: label = tr("Mode 3: Cyclic on the left, Throttle/Collective and Yaw on the right"); break;
784 case mode4: label = tr("Mode 4: Throttle/Collective and Left/Right Cyclic on the left, Fore/Aft Cyclic and Yaw on the right"); break;
785 default: Q_ASSERT(0); break;
787 wizardUi->typePageFooter->setText(" ");
788 } else {
789 switch (mode) {
790 case mode1: label = tr("Mode 1: Elevator and Rudder on the left, Throttle and Ailerons on the right"); break;
791 case mode2: label = tr("Mode 2: Throttle and Rudder on the left, Elevator and Ailerons on the right"); break;
792 case mode3: label = tr("Mode 3: Elevator and Ailerons on the left, Throttle and Rudder on the right"); break;
793 case mode4: label = tr("Mode 4: Throttle and Ailerons on the left, Elevator and Rudder on the right"); break;
794 default: Q_ASSERT(0); break;
796 wizardUi->typePageFooter->setText(tr("For a Quad: Elevator is Pitch, Ailerons are Roll, and Rudder is Yaw."));
798 modeButtons[i]->setText(label);
799 if (transmitterMode == mode) {
800 modeButtons[i]->setChecked(true);
803 wizardUi->pagesStack->setCurrentWidget(wizardUi->chooseModePage);
805 break;
806 case wizardIdentifySticks:
807 usedChannels.clear();
808 currentChannelNum = -1;
809 nextChannel();
810 manualSettingsData = manualSettingsObj->getData();
811 connect(receiverActivityObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyControls()));
812 wizardUi->wzNext->setEnabled(false);
813 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifySticksPage);
814 break;
815 case wizardIdentifyCenter:
816 setTxMovement(centerAll);
817 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifyCenterPage);
818 if (transmitterType == ground) {
819 wizardUi->identifyCenterInstructions->setText(QString(tr("Please center all controls and trims and press Next when ready.\n\n"
820 "For a ground vehicle, this center position will be used as neutral value of each channel.")));
822 break;
823 case wizardIdentifyLimits:
825 setTxMovement(nothing);
826 manualSettingsData = manualSettingsObj->getData();
827 for (uint i = 0; i < ManualControlSettings::CHANNELMAX_NUMELEM; ++i) {
828 // Preserve the inverted status
829 if (manualSettingsData.ChannelMin[i] <= manualSettingsData.ChannelMax[i]) {
830 manualSettingsData.ChannelMin[i] = manualSettingsData.ChannelNeutral[i];
831 manualSettingsData.ChannelMax[i] = manualSettingsData.ChannelNeutral[i];
832 } else {
833 // Make this detect as still inverted
834 manualSettingsData.ChannelMin[i] = manualSettingsData.ChannelNeutral[i] + 1;
835 manualSettingsData.ChannelMax[i] = manualSettingsData.ChannelNeutral[i];
838 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyLimits()));
839 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
840 connect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
841 connect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
843 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifyLimitsPage);
845 break;
846 case wizardIdentifyInverted:
847 dimOtherControls(true);
848 setTxMovement(nothing);
849 extraWidgets.clear();
850 for (int index = 0; index < manualSettingsObj->getField("ChannelMax")->getElementNames().length(); index++) {
851 QString name = manualSettingsObj->getField("ChannelMax")->getElementNames().at(index);
852 if (!name.contains("Access") && !name.contains("Flight") &&
853 (!name.contains("Collective") || transmitterType == heli)) {
854 QCheckBox *cb = new QCheckBox(name, this);
855 // Make sure checked status matches current one
856 cb->setChecked(manualSettingsData.ChannelMax[index] < manualSettingsData.ChannelMin[index]);
857 wizardUi->checkBoxesLayout->addWidget(cb, extraWidgets.size() / 4, extraWidgets.size() % 4);
858 extraWidgets.append(cb);
859 connect(cb, SIGNAL(toggled(bool)), this, SLOT(invertControls()));
862 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
863 wizardUi->pagesStack->setCurrentWidget(wizardUi->identifyInvertedPage);
864 break;
865 case wizardFinish:
866 dimOtherControls(false);
867 connect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
868 connect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
869 connect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
870 wizardUi->pagesStack->setCurrentWidget(wizardUi->finishPage);
871 break;
872 default:
873 Q_ASSERT(0);
875 wizardStep = step;
878 void ConfigInputWidget::wizardTearDownStep(enum wizardSteps step)
880 Q_ASSERT(step == wizardStep);
881 switch (step) {
882 case wizardWelcome:
883 break;
884 case wizardChooseType:
885 if (wizardUi->typeAcro->isChecked()) {
886 transmitterType = acro;
887 } else if (wizardUi->typeGround->isChecked()) {
888 transmitterType = ground;
889 /* Make sure to tell controller, this is really a ground vehicle. */
890 systemSettingsData = systemSettingsObj->getData();
891 systemSettingsData.AirframeType = SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR;
892 systemSettingsObj->setData(systemSettingsData);
893 } else {
894 transmitterType = heli;
896 break;
897 case wizardChooseMode:
899 QRadioButton *modeButtons[] = {
900 wizardUi->mode1Button,
901 wizardUi->mode2Button,
902 wizardUi->mode3Button,
903 wizardUi->mode4Button
905 for (int i = mode1; i <= mode4; ++i) {
906 if (modeButtons[i]->isChecked()) {
907 transmitterMode = static_cast<txMode>(i);
911 break;
912 case wizardIdentifySticks:
913 disconnect(receiverActivityObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyControls()));
914 wizardUi->wzNext->setEnabled(true);
915 setTxMovement(nothing);
916 /* If flight mode stick isn't identified, force flight mode number to be 1 */
917 manualSettingsData = manualSettingsObj->getData();
918 if (manualSettingsData.ChannelGroups[ManualControlSettings::CHANNELNUMBER_FLIGHTMODE] ==
919 ManualControlSettings::CHANNELGROUPS_NONE) {
920 forceOneFlightMode();
922 break;
923 case wizardIdentifyCenter:
924 manualCommandData = manualCommandObj->getData();
925 manualSettingsData = manualSettingsObj->getData();
926 for (unsigned int i = 0; i < ManualControlCommand::CHANNEL_NUMELEM; ++i) {
927 // Set Accessory neutral to middle range
928 if (i >= ManualControlSettings::CHANNELNUMBER_ACCESSORY0) {
929 manualSettingsData.ChannelNeutral[i] = manualSettingsData.ChannelMin[i] + ((manualSettingsData.ChannelMax[i] - manualSettingsData.ChannelMin[i]) / 2);
930 } else {
931 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
934 manualSettingsObj->setData(manualSettingsData);
935 setTxMovement(nothing);
936 break;
937 case wizardIdentifyLimits:
938 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(identifyLimits()));
939 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
940 disconnect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
941 disconnect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
942 manualSettingsObj->setData(manualSettingsData);
943 setTxMovement(nothing);
944 break;
945 case wizardIdentifyInverted:
946 dimOtherControls(false);
947 foreach(QWidget * wd, extraWidgets) {
948 QCheckBox *cb = qobject_cast<QCheckBox *>(wd);
950 if (cb) {
951 disconnect(cb, SIGNAL(toggled(bool)), this, SLOT(invertControls()));
952 delete cb;
955 extraWidgets.clear();
956 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
957 break;
958 case wizardFinish:
959 dimOtherControls(false);
960 setTxMovement(nothing);
961 disconnect(manualCommandObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
962 disconnect(flightStatusObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
963 disconnect(accessoryDesiredObj0, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(moveSticks()));
964 break;
965 default:
966 Q_ASSERT(0);
970 static void fastMdataSingle(UAVDataObject *object, UAVObject::Metadata *savedMdata)
972 *savedMdata = object->getMetadata();
973 UAVObject::Metadata mdata = *savedMdata;
974 UAVObject::SetFlightTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_PERIODIC);
975 mdata.flightTelemetryUpdatePeriod = 150;
976 object->setMetadata(mdata);
979 static void restoreMdataSingle(UAVDataObject *object, UAVObject::Metadata *savedMdata)
981 object->setMetadata(*savedMdata);
985 * Set manual control command to fast updates
987 void ConfigInputWidget::fastMdata()
989 fastMdataSingle(manualCommandObj, &manualControlMdata);
990 fastMdataSingle(accessoryDesiredObj0, &accessoryDesiredMdata0);
994 * Restore previous update settings for manual control data
996 void ConfigInputWidget::restoreMdata()
998 restoreMdataSingle(manualCommandObj, &manualControlMdata);
999 restoreMdataSingle(accessoryDesiredObj0, &accessoryDesiredMdata0);
1003 * Set the display to indicate which channel the person should move
1005 void ConfigInputWidget::setChannel(int newChan)
1007 bool canBeSkipped = false;
1009 if (newChan == ManualControlSettings::CHANNELGROUPS_COLLECTIVE) {
1010 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please enable throttle hold mode.</p>"
1011 "<p>Move the Collective Pitch stick.</p>")));
1012 } else if (newChan == ManualControlSettings::CHANNELGROUPS_FLIGHTMODE) {
1013 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please toggle the Flight Mode switch.</p>"
1014 "<p>For switches you may have to repeat this rapidly.</p>"
1015 "<p>Alternatively, you can click Next to skip this channel, but you will get only <b>ONE</b> Flight Mode.</p>")));
1016 canBeSkipped = true;
1017 } else if ((transmitterType == heli) && (newChan == ManualControlSettings::CHANNELGROUPS_THROTTLE)) {
1018 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please disable throttle hold mode.</p>"
1019 "<p>Move the Throttle stick.</p>")));
1020 } else {
1021 wizardUi->identifyStickInstructions->setText(QString(tr("<p>Please move each control one at a time according to the instructions and picture below.</p>"
1022 "<p>Move the %1 stick.</p>")).arg(manualSettingsObj->getField("ChannelGroups")->getElementNames().at(newChan)));
1025 if (manualSettingsObj->getField("ChannelGroups")->getElementNames().at(newChan).contains("Accessory")) {
1026 wizardUi->identifyStickInstructions->setText(wizardUi->identifyStickInstructions->text() + tr("<p>Alternatively, click Next to skip this channel.</p>"));
1027 canBeSkipped = true;
1030 if (canBeSkipped) {
1031 wizardUi->wzNext->setEnabled(true);
1032 wizardUi->wzNext->setText(tr("Next / Skip"));
1033 } else {
1034 wizardUi->wzNext->setEnabled(false);
1037 setMoveFromCommand(newChan);
1038 currentChannelNum = newChan;
1039 channelDetected = false;
1043 * Unfortunately order of channel should be different in different conditions. Selects
1044 * next channel based on heli or acro mode
1046 void ConfigInputWidget::nextChannel()
1048 QList <int> order;
1049 switch (transmitterType) {
1050 case heli:
1051 order = heliChannelOrder;
1052 break;
1053 case ground:
1054 order = groundChannelOrder;
1055 break;
1056 default:
1057 order = acroChannelOrder;
1058 break;
1061 if (currentChannelNum == -1) {
1062 setChannel(order[0]);
1063 return;
1065 for (int i = 0; i < order.length() - 1; i++) {
1066 if (order[i] == currentChannelNum) {
1067 setChannel(order[i + 1]);
1068 return;
1071 currentChannelNum = -1; // hit end of list
1075 * Unfortunately order of channel should be different in different conditions. Selects
1076 * previous channel based on heli or acro mode
1078 void ConfigInputWidget::prevChannel()
1080 QList <int> order;
1081 switch (transmitterType) {
1082 case heli:
1083 order = heliChannelOrder;
1084 break;
1085 case ground:
1086 order = groundChannelOrder;
1087 break;
1088 default:
1089 order = acroChannelOrder;
1090 break;
1093 // No previous from unset channel or next state
1094 if (currentChannelNum == -1) {
1095 return;
1098 for (int i = 1; i < order.length(); i++) {
1099 if (order[i] == currentChannelNum) {
1100 if (!usedChannels.isEmpty() &&
1101 usedChannels.back().channelIndex == order[i - 1]) {
1102 usedChannels.removeLast();
1104 setChannel(order[i - 1]);
1105 return;
1108 currentChannelNum = -1; // hit end of list
1111 void ConfigInputWidget::identifyControls()
1113 static const int DEBOUNCE_COUNT = 4;
1114 static int debounce = 0;
1116 receiverActivityData = receiverActivityObj->getData();
1118 if (receiverActivityData.ActiveChannel == 255) {
1119 return;
1122 if (channelDetected) {
1123 registerControlActivity();
1124 return;
1127 receiverActivityData = receiverActivityObj->getData();
1128 currentChannel.group = receiverActivityData.ActiveGroup;
1129 currentChannel.number = receiverActivityData.ActiveChannel;
1131 if (debounce == 0) {
1132 // Register a channel to be debounced.
1133 lastChannel.group = currentChannel.group;
1134 lastChannel.number = currentChannel.number;
1135 lastChannel.channelIndex = currentChannelNum;
1136 ++debounce;
1137 return;
1140 if (currentChannel != lastChannel) {
1141 // A new channel was seen. Only register it if we count down to 0.
1142 --debounce;
1143 return;
1146 if (debounce < DEBOUNCE_COUNT) {
1147 // We still haven't seen enough enough activity on this channel yet.
1148 ++debounce;
1149 return;
1152 // Channel has been debounced and it's enough record it.
1154 if (usedChannels.contains(lastChannel)) {
1155 // Channel is already recorded.
1156 return;
1159 // Record the channel.
1161 channelDetected = true;
1162 debounce = 0;
1163 usedChannels.append(lastChannel);
1164 manualSettingsData = manualSettingsObj->getData();
1165 manualSettingsData.ChannelGroups[currentChannelNum] = currentChannel.group;
1166 manualSettingsData.ChannelNumber[currentChannelNum] = currentChannel.number;
1167 manualSettingsObj->setData(manualSettingsData);
1169 // m_config->wzText->clear();
1170 setTxMovement(nothing);
1172 wzNextDelayedStart();
1175 void ConfigInputWidget::identifyLimits()
1177 manualCommandData = manualCommandObj->getData();
1178 for (uint i = 0; i < ManualControlSettings::CHANNELMAX_NUMELEM; ++i) {
1179 if (manualSettingsData.ChannelMin[i] <= manualSettingsData.ChannelMax[i]) {
1180 // Non inverted channel
1181 if (manualSettingsData.ChannelMin[i] > manualCommandData.Channel[i]) {
1182 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1184 if (manualSettingsData.ChannelMax[i] < manualCommandData.Channel[i]) {
1185 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1187 } else {
1188 // Inverted channel
1189 if (manualSettingsData.ChannelMax[i] > manualCommandData.Channel[i]) {
1190 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1192 if (manualSettingsData.ChannelMin[i] < manualCommandData.Channel[i]) {
1193 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1196 // Flightmode channel
1197 if (i == ManualControlSettings::CHANNELGROUPS_FLIGHTMODE) {
1198 bool newFlightModeValue = true;
1199 // Avoid duplicate values too close and error due to RcTx drift
1200 int minSpacing = 100; // 100µs
1201 for (int pos = 0; pos < manualSettingsData.FlightModeNumber + 1; ++pos) {
1202 if (flightModeSignalValue[pos] == 0) {
1203 // A new flightmode value can be set now
1204 for (int checkpos = 0; checkpos < manualSettingsData.FlightModeNumber + 1; ++checkpos) {
1205 // Check if value is already used, MinSpacing needed between values.
1206 if ((flightModeSignalValue[checkpos] < manualCommandData.Channel[i] + minSpacing) &&
1207 (flightModeSignalValue[checkpos] > manualCommandData.Channel[i] - minSpacing)) {
1208 newFlightModeValue = false;
1211 // Be sure FlightModeNumber is < FlightModeSettings::FLIGHTMODEPOSITION_NUMELEM (6)
1212 if ((manualSettingsData.FlightModeNumber < FlightModeSettings::FLIGHTMODEPOSITION_NUMELEM) && newFlightModeValue) {
1213 // Start from 0, erase previous count
1214 if (pos == 0) {
1215 manualSettingsData.FlightModeNumber = 0;
1217 // Store new value and increase FlightModeNumber
1218 flightModeSignalValue[pos] = manualCommandData.Channel[i];
1219 manualSettingsData.FlightModeNumber++;
1220 // Show flight mode number
1221 m_txFlightModeCountText->setText(QString().number(manualSettingsData.FlightModeNumber));
1222 m_txFlightModeCountText->setVisible(true);
1223 m_txFlightModeCountBG->setVisible(true);
1229 manualSettingsObj->setData(manualSettingsData);
1232 void ConfigInputWidget::setMoveFromCommand(int command)
1234 // ManualControlSettings::ChannelNumberElem:
1235 // CHANNELNUMBER_ROLL=0,
1236 // CHANNELNUMBER_PITCH=1,
1237 // CHANNELNUMBER_YAW=2,
1238 // CHANNELNUMBER_THROTTLE=3,
1239 // CHANNELNUMBER_FLIGHTMODE=4,
1240 // CHANNELNUMBER_ACCESSORY0=5,
1241 // CHANNELNUMBER_ACCESSORY1=6,
1242 // CHANNELNUMBER_ACCESSORY2=7
1244 txMovements movement = moveLeftVerticalStick;
1246 switch (command) {
1247 case ManualControlSettings::CHANNELNUMBER_ROLL:
1248 movement = ((transmitterMode == mode3 || transmitterMode == mode4) ?
1249 moveLeftHorizontalStick : moveRightHorizontalStick);
1250 break;
1251 case ManualControlSettings::CHANNELNUMBER_PITCH:
1252 movement = (transmitterMode == mode1 || transmitterMode == mode3) ?
1253 moveLeftVerticalStick : moveRightVerticalStick;
1254 break;
1255 case ManualControlSettings::CHANNELNUMBER_YAW:
1256 movement = ((transmitterMode == mode1 || transmitterMode == mode2) ?
1257 moveLeftHorizontalStick : moveRightHorizontalStick);
1258 break;
1259 case ManualControlSettings::CHANNELNUMBER_THROTTLE:
1260 movement = (transmitterMode == mode2 || transmitterMode == mode4) ?
1261 moveLeftVerticalStick : moveRightVerticalStick;
1262 break;
1263 case ManualControlSettings::CHANNELNUMBER_COLLECTIVE:
1264 movement = (transmitterMode == mode2 || transmitterMode == mode4) ?
1265 moveLeftVerticalStick : moveRightVerticalStick;
1266 break;
1267 case ManualControlSettings::CHANNELNUMBER_FLIGHTMODE:
1268 movement = moveFlightMode;
1269 break;
1270 case ManualControlSettings::CHANNELNUMBER_ACCESSORY0:
1271 movement = moveAccess0;
1272 break;
1273 case ManualControlSettings::CHANNELNUMBER_ACCESSORY1:
1274 movement = moveAccess1;
1275 break;
1276 case ManualControlSettings::CHANNELNUMBER_ACCESSORY2:
1277 movement = moveAccess2;
1278 break;
1279 case ManualControlSettings::CHANNELNUMBER_ACCESSORY3:
1280 movement = moveAccess3;
1281 break;
1282 default:
1283 Q_ASSERT(0);
1284 break;
1286 setTxMovement(movement);
1289 void ConfigInputWidget::setTxMovement(txMovements movement)
1291 resetTxControls();
1292 switch (movement) {
1293 case moveLeftVerticalStick:
1294 movePos = 0;
1295 growing = true;
1296 currentMovement = moveLeftVerticalStick;
1297 animate->start(100);
1298 break;
1299 case moveRightVerticalStick:
1300 movePos = 0;
1301 growing = true;
1302 currentMovement = moveRightVerticalStick;
1303 animate->start(100);
1304 break;
1305 case moveLeftHorizontalStick:
1306 movePos = 0;
1307 growing = true;
1308 currentMovement = moveLeftHorizontalStick;
1309 animate->start(100);
1310 break;
1311 case moveRightHorizontalStick:
1312 movePos = 0;
1313 growing = true;
1314 currentMovement = moveRightHorizontalStick;
1315 animate->start(100);
1316 break;
1317 case moveAccess0:
1318 movePos = 0;
1319 growing = true;
1320 currentMovement = moveAccess0;
1321 animate->start(100);
1322 break;
1323 case moveAccess1:
1324 movePos = 0;
1325 growing = true;
1326 currentMovement = moveAccess1;
1327 animate->start(100);
1328 break;
1329 case moveAccess2:
1330 movePos = 0;
1331 growing = true;
1332 currentMovement = moveAccess2;
1333 animate->start(100);
1334 break;
1335 case moveAccess3:
1336 movePos = 0;
1337 growing = true;
1338 currentMovement = moveAccess3;
1339 animate->start(100);
1340 break;
1341 case moveFlightMode:
1342 movePos = 0;
1343 growing = true;
1344 currentMovement = moveFlightMode;
1345 animate->start(1000);
1346 break;
1347 case centerAll:
1348 movePos = 0;
1349 currentMovement = centerAll;
1350 animate->start(1000);
1351 break;
1352 case moveAll:
1353 movePos = 0;
1354 growing = true;
1355 currentMovement = moveAll;
1356 animate->start(50);
1357 break;
1358 case nothing:
1359 movePos = 0;
1360 animate->stop();
1361 break;
1362 default:
1363 Q_ASSERT(0);
1364 break;
1368 void ConfigInputWidget::moveTxControls()
1370 QTransform trans;
1371 QGraphicsItem *item = NULL;
1372 txMovementType move = vertical;
1373 int limitMax = 0;
1374 int limitMin = 0;
1375 static bool auxFlag = false;
1377 switch (currentMovement) {
1378 case moveLeftVerticalStick:
1379 item = m_txLeftStick;
1380 trans = m_txLeftStickOrig;
1381 limitMax = STICK_MAX_MOVE;
1382 limitMin = STICK_MIN_MOVE;
1383 move = vertical;
1384 break;
1385 case moveRightVerticalStick:
1386 item = m_txRightStick;
1387 trans = m_txRightStickOrig;
1388 limitMax = STICK_MAX_MOVE;
1389 limitMin = STICK_MIN_MOVE;
1390 move = vertical;
1391 break;
1392 case moveLeftHorizontalStick:
1393 item = m_txLeftStick;
1394 trans = m_txLeftStickOrig;
1395 limitMax = STICK_MAX_MOVE;
1396 limitMin = STICK_MIN_MOVE;
1397 move = horizontal;
1398 break;
1399 case moveRightHorizontalStick:
1400 item = m_txRightStick;
1401 trans = m_txRightStickOrig;
1402 limitMax = STICK_MAX_MOVE;
1403 limitMin = STICK_MIN_MOVE;
1404 move = horizontal;
1405 break;
1406 case moveAccess0:
1407 item = m_txAccess0;
1408 trans = m_txAccess0Orig;
1409 limitMax = ACCESS_MAX_MOVE;
1410 limitMin = ACCESS_MIN_MOVE;
1411 move = horizontal;
1412 break;
1413 case moveAccess1:
1414 item = m_txAccess1;
1415 trans = m_txAccess1Orig;
1416 limitMax = ACCESS_MAX_MOVE;
1417 limitMin = ACCESS_MIN_MOVE;
1418 move = horizontal;
1419 break;
1420 case moveAccess2:
1421 item = m_txAccess2;
1422 trans = m_txAccess2Orig;
1423 limitMax = ACCESS_MAX_MOVE;
1424 limitMin = ACCESS_MIN_MOVE;
1425 move = horizontal;
1426 break;
1427 case moveAccess3:
1428 item = m_txAccess3;
1429 trans = m_txAccess3Orig;
1430 limitMax = ACCESS_MAX_MOVE;
1431 limitMin = ACCESS_MIN_MOVE;
1432 move = horizontal;
1433 break;
1434 case moveFlightMode:
1435 item = m_txFlightMode;
1436 move = jump;
1437 break;
1438 case centerAll:
1439 item = m_txArrows;
1440 move = jump;
1441 break;
1442 case moveAll:
1443 limitMax = STICK_MAX_MOVE;
1444 limitMin = STICK_MIN_MOVE;
1445 move = mix;
1446 break;
1447 default:
1448 break;
1450 if (move == vertical) {
1451 item->setTransform(trans.translate(0, movePos * 10), false);
1452 } else if (move == horizontal) {
1453 item->setTransform(trans.translate(movePos * 10, 0), false);
1454 } else if (move == jump) {
1455 if (item == m_txArrows) {
1456 m_txArrows->setVisible(!m_txArrows->isVisible());
1457 } else if (item == m_txFlightMode) {
1458 QGraphicsSvgItem *svg;
1459 svg = (QGraphicsSvgItem *)item;
1460 if (svg) {
1461 if (svg->elementId() == "flightModeCenter") {
1462 if (growing) {
1463 svg->setElementId("flightModeRight");
1464 m_txFlightMode->setTransform(m_txFlightModeROrig, false);
1465 } else {
1466 svg->setElementId("flightModeLeft");
1467 m_txFlightMode->setTransform(m_txFlightModeLOrig, false);
1469 } else if (svg->elementId() == "flightModeRight") {
1470 growing = false;
1471 svg->setElementId("flightModeCenter");
1472 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1473 } else if (svg->elementId() == "flightModeLeft") {
1474 growing = true;
1475 svg->setElementId("flightModeCenter");
1476 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1480 } else if (move == mix) {
1481 trans = m_txAccess0Orig;
1482 m_txAccess0->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1483 trans = m_txAccess1Orig;
1484 m_txAccess1->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1485 trans = m_txAccess2Orig;
1486 m_txAccess2->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1487 trans = m_txAccess3Orig;
1488 m_txAccess3->setTransform(trans.translate(movePos * 10 * ACCESS_MAX_MOVE / STICK_MAX_MOVE, 0), false);
1490 if (auxFlag) {
1491 trans = m_txLeftStickOrig;
1492 m_txLeftStick->setTransform(trans.translate(0, movePos * 10), false);
1493 trans = m_txRightStickOrig;
1494 m_txRightStick->setTransform(trans.translate(0, movePos * 10), false);
1495 } else {
1496 trans = m_txLeftStickOrig;
1497 m_txLeftStick->setTransform(trans.translate(movePos * 10, 0), false);
1498 trans = m_txRightStickOrig;
1499 m_txRightStick->setTransform(trans.translate(movePos * 10, 0), false);
1502 if (movePos == 0) {
1503 m_txFlightMode->setElementId("flightModeCenter");
1504 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1505 } else if (movePos == ACCESS_MAX_MOVE / 2) {
1506 m_txFlightMode->setElementId("flightModeRight");
1507 m_txFlightMode->setTransform(m_txFlightModeROrig, false);
1508 } else if (movePos == ACCESS_MIN_MOVE / 2) {
1509 m_txFlightMode->setElementId("flightModeLeft");
1510 m_txFlightMode->setTransform(m_txFlightModeLOrig, false);
1513 if (move == horizontal || move == vertical || move == mix) {
1514 if (movePos == 0 && growing) {
1515 auxFlag = !auxFlag;
1517 if (growing) {
1518 ++movePos;
1519 } else {
1520 --movePos;
1522 if (movePos > limitMax) {
1523 movePos = movePos - 2;
1524 growing = false;
1526 if (movePos < limitMin) {
1527 movePos = movePos + 2;
1528 growing = true;
1533 AccessoryDesired *ConfigInputWidget::getAccessoryDesiredInstance(int instance)
1535 switch (instance) {
1536 case 0:
1537 if (accessoryDesiredObj0 == NULL) {
1538 accessoryDesiredObj0 = AccessoryDesired::GetInstance(getObjectManager(), 0);
1540 return accessoryDesiredObj0;
1542 case 1:
1543 if (accessoryDesiredObj1 == NULL) {
1544 accessoryDesiredObj1 = AccessoryDesired::GetInstance(getObjectManager(), 1);
1546 return accessoryDesiredObj1;
1548 case 2:
1549 if (accessoryDesiredObj2 == NULL) {
1550 accessoryDesiredObj2 = AccessoryDesired::GetInstance(getObjectManager(), 2);
1552 return accessoryDesiredObj2;
1554 case 3:
1555 if (accessoryDesiredObj3 == NULL) {
1556 accessoryDesiredObj3 = AccessoryDesired::GetInstance(getObjectManager(), 3);
1558 return accessoryDesiredObj3;
1560 default:
1561 Q_ASSERT(false);
1564 return NULL;
1567 float ConfigInputWidget::getAccessoryDesiredValue(int instance)
1569 AccessoryDesired *accessoryDesiredObj = getAccessoryDesiredInstance(instance);
1571 if (accessoryDesiredObj == NULL) {
1572 Q_ASSERT(false);
1573 return 0.0f;
1576 AccessoryDesired::DataFields data = accessoryDesiredObj->getData();
1578 return data.AccessoryVal;
1581 void ConfigInputWidget::moveSticks()
1583 QTransform trans;
1585 manualCommandData = manualCommandObj->getData();
1586 flightStatusData = flightStatusObj->getData();
1588 switch (transmitterMode) {
1589 case mode1:
1590 trans = m_txLeftStickOrig;
1591 m_txLeftStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1592 trans = m_txRightStickOrig;
1593 m_txRightStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1594 break;
1595 case mode2:
1596 trans = m_txLeftStickOrig;
1597 m_txLeftStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1598 trans = m_txRightStickOrig;
1599 m_txRightStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1600 break;
1601 case mode3:
1602 trans = m_txLeftStickOrig;
1603 m_txLeftStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1604 trans = m_txRightStickOrig;
1605 m_txRightStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1606 break;
1607 case mode4:
1608 trans = m_txLeftStickOrig;
1609 m_txLeftStick->setTransform(trans.translate(manualCommandData.Roll * STICK_MAX_MOVE * 10, -manualCommandData.Throttle * STICK_MAX_MOVE * 10), false);
1610 trans = m_txRightStickOrig;
1611 m_txRightStick->setTransform(trans.translate(manualCommandData.Yaw * STICK_MAX_MOVE * 10, manualCommandData.Pitch * STICK_MAX_MOVE * 10), false);
1612 break;
1613 default:
1614 Q_ASSERT(0);
1615 break;
1617 if ((flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[0]) ||
1618 (flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[5])) {
1619 m_txFlightMode->setElementId("flightModeLeft");
1620 m_txFlightMode->setTransform(m_txFlightModeLOrig, false);
1621 } else if ((flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[1]) ||
1622 (flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[4])) {
1623 m_txFlightMode->setElementId("flightModeCenter");
1624 m_txFlightMode->setTransform(m_txFlightModeCOrig, false);
1625 } else if ((flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[2]) ||
1626 (flightStatusData.FlightMode == flightModeSettingsData.FlightModePosition[3])) {
1627 m_txFlightMode->setElementId("flightModeRight");
1628 m_txFlightMode->setTransform(m_txFlightModeROrig, false);
1631 m_txAccess0->setTransform(QTransform(m_txAccess0Orig).translate(getAccessoryDesiredValue(0) * ACCESS_MAX_MOVE * 10, 0), false);
1632 m_txAccess1->setTransform(QTransform(m_txAccess1Orig).translate(getAccessoryDesiredValue(1) * ACCESS_MAX_MOVE * 10, 0), false);
1633 m_txAccess2->setTransform(QTransform(m_txAccess2Orig).translate(getAccessoryDesiredValue(2) * ACCESS_MAX_MOVE * 10, 0), false);
1634 m_txAccess3->setTransform(QTransform(m_txAccess3Orig).translate(getAccessoryDesiredValue(3) * ACCESS_MAX_MOVE * 10, 0), false);
1637 void ConfigInputWidget::dimOtherControls(bool value)
1639 qreal opac;
1641 if (value) {
1642 opac = 0.1;
1643 } else {
1644 opac = 1;
1646 m_txAccess0->setOpacity(opac);
1647 m_txAccess1->setOpacity(opac);
1648 m_txAccess2->setOpacity(opac);
1649 m_txAccess3->setOpacity(opac);
1650 m_txFlightMode->setOpacity(opac);
1653 void ConfigInputWidget::invertControls()
1655 manualSettingsData = manualSettingsObj->getData();
1656 foreach(QWidget * wd, extraWidgets) {
1657 QCheckBox *cb = qobject_cast<QCheckBox *>(wd);
1659 if (cb) {
1660 int index = manualSettingsObj->getField("ChannelNumber")->getElementNames().indexOf(cb->text());
1661 if ((cb->isChecked() && (manualSettingsData.ChannelMax[index] > manualSettingsData.ChannelMin[index])) ||
1662 (!cb->isChecked() && (manualSettingsData.ChannelMax[index] < manualSettingsData.ChannelMin[index]))) {
1663 qint16 aux;
1664 aux = manualSettingsData.ChannelMax[index];
1665 manualSettingsData.ChannelMax[index] = manualSettingsData.ChannelMin[index];
1666 manualSettingsData.ChannelMin[index] = aux;
1670 manualSettingsObj->setData(manualSettingsData);
1673 void ConfigInputWidget::moveFMSlider()
1675 ManualControlSettings::DataFields manualSettingsDataPriv = manualSettingsObj->getData();
1676 ManualControlCommand::DataFields manualCommandDataPriv = manualCommandObj->getData();
1678 float valueScaled;
1679 int chMin = manualSettingsDataPriv.ChannelMin[ManualControlSettings::CHANNELMIN_FLIGHTMODE];
1680 int chMax = manualSettingsDataPriv.ChannelMax[ManualControlSettings::CHANNELMAX_FLIGHTMODE];
1681 int chNeutral = manualSettingsDataPriv.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_FLIGHTMODE];
1683 int value = manualCommandDataPriv.Channel[ManualControlSettings::CHANNELMIN_FLIGHTMODE];
1685 if ((chMax > chMin && value >= chNeutral) || (chMin > chMax && value <= chNeutral)) {
1686 if (chMax != chNeutral) {
1687 valueScaled = (float)(value - chNeutral) / (float)(chMax - chNeutral);
1688 } else {
1689 valueScaled = 0;
1691 } else {
1692 if (chMin != chNeutral) {
1693 valueScaled = (float)(value - chNeutral) / (float)(chNeutral - chMin);
1694 } else {
1695 valueScaled = 0;
1699 // Bound and scale FlightMode from [-1..+1] to [0..1] range
1700 if (valueScaled < -1.0) {
1701 valueScaled = -1.0;
1702 } else if (valueScaled > 1.0) {
1703 valueScaled = 1.0;
1706 // Convert flightMode value into the switch position in the range [0..N-1]
1707 // This uses the same optimized computation as flight code to be consistent
1708 uint8_t pos = ((int16_t)(valueScaled * 256) + 256) * manualSettingsDataPriv.FlightModeNumber >> 9;
1709 if (pos >= manualSettingsDataPriv.FlightModeNumber) {
1710 pos = manualSettingsDataPriv.FlightModeNumber - 1;
1712 ui->fmsSlider->setValue(pos);
1713 highlightStabilizationMode(pos);
1716 void ConfigInputWidget::highlightStabilizationMode(int pos)
1718 QComboBox *comboboxFm = this->findChild<QComboBox *>("fmsModePos" + QString::number(pos + 1));
1719 QString customStyleSheet = "QComboBox:editable:!on{background: #feb103;}";
1721 if (comboboxFm) {
1722 QString flightModeText = comboboxFm->currentText();
1723 comboboxFm->setStyleSheet("");
1724 for (uint8_t i = 0; i < FlightModeSettings::FLIGHTMODEPOSITION_NUMELEM; i++) {
1725 QLabel *label = this->findChild<QLabel *>("stab" + QString::number(i + 1) + "_label");
1726 QComboBox *comboRoll = this->findChild<QComboBox *>("fmsSsPos" + QString::number(i + 1) + "Roll");
1727 QComboBox *comboPitch = this->findChild<QComboBox *>("fmsSsPos" + QString::number(i + 1) + "Pitch");
1728 QComboBox *comboYaw = this->findChild<QComboBox *>("fmsSsPos" + QString::number(i + 1) + "Yaw");
1729 QComboBox *comboThrust = this->findChild<QComboBox *>("fmsSsPos" + QString::number(i + 1) + "Thrust");
1730 QComboBox *comboboxFm2 = this->findChild<QComboBox *>("fmsModePos" + QString::number(i + 1));
1731 comboboxFm2->setStyleSheet("");
1733 // Highlight current stabilization mode if any.
1734 if ((flightModeText.contains("Stabilized", Qt::CaseInsensitive)) && (flightModeText.contains(QString::number(i + 1), Qt::CaseInsensitive))) {
1735 label->setStyleSheet("border-radius: 4px; border:3px solid #feb103;");
1736 comboRoll->setStyleSheet(customStyleSheet);
1737 comboPitch->setStyleSheet(customStyleSheet);
1738 comboYaw->setStyleSheet(customStyleSheet);
1739 comboThrust->setStyleSheet(customStyleSheet);
1740 } else {
1741 label->setStyleSheet("");
1742 comboRoll->setStyleSheet("");
1743 comboPitch->setStyleSheet("");
1744 comboYaw->setStyleSheet("");
1745 comboThrust->setStyleSheet("");
1746 if (!flightModeText.contains("Stabilized", Qt::CaseInsensitive)) {
1747 // Highlight PosHold, Return to Base, ... flightmodes
1748 comboboxFm->setStyleSheet(customStyleSheet);
1755 void setComboBoxItemEnabled(QComboBox *combo, int index, bool enabled = true)
1757 combo->setItemData(index, enabled ? QVariant(1 | 32) : QVariant(0), Qt::UserRole - 1);
1760 void ConfigInputWidget::updatePositionSlider()
1762 ManualControlSettings::DataFields manualSettingsDataPriv = manualSettingsObj->getData();
1764 switch (manualSettingsDataPriv.FlightModeNumber) {
1765 default:
1766 case 6:
1767 ui->fmsModePos6->setEnabled(true);
1768 ui->pidBankSs1_5->setEnabled(true);
1769 ui->assistControlPos6->setEnabled(true);
1770 setComboBoxItemEnabled(ui->failsafeFlightMode, 5);
1771 // pass through
1772 case 5:
1773 ui->fmsModePos5->setEnabled(true);
1774 ui->pidBankSs1_4->setEnabled(true);
1775 ui->assistControlPos5->setEnabled(true);
1776 setComboBoxItemEnabled(ui->failsafeFlightMode, 4);
1777 // pass through
1778 case 4:
1779 ui->fmsModePos4->setEnabled(true);
1780 ui->pidBankSs1_3->setEnabled(true);
1781 ui->assistControlPos4->setEnabled(true);
1782 setComboBoxItemEnabled(ui->failsafeFlightMode, 3);
1783 // pass through
1784 case 3:
1785 ui->fmsModePos3->setEnabled(true);
1786 ui->pidBankSs1_2->setEnabled(true);
1787 ui->assistControlPos3->setEnabled(true);
1788 setComboBoxItemEnabled(ui->failsafeFlightMode, 2);
1789 // pass through
1790 case 2:
1791 ui->fmsModePos2->setEnabled(true);
1792 ui->pidBankSs1_1->setEnabled(true);
1793 ui->assistControlPos2->setEnabled(true);
1794 setComboBoxItemEnabled(ui->failsafeFlightMode, 1);
1795 // pass through
1796 case 1:
1797 ui->fmsModePos1->setEnabled(true);
1798 ui->pidBankSs1_0->setEnabled(true);
1799 ui->assistControlPos1->setEnabled(true);
1800 setComboBoxItemEnabled(ui->failsafeFlightMode, 0);
1801 // pass through
1802 case 0:
1803 break;
1806 switch (manualSettingsDataPriv.FlightModeNumber) {
1807 case 0:
1808 ui->fmsModePos1->setEnabled(false);
1809 ui->pidBankSs1_0->setEnabled(false);
1810 ui->assistControlPos1->setEnabled(false);
1811 setComboBoxItemEnabled(ui->failsafeFlightMode, 0, false);
1812 // pass through
1813 case 1:
1814 ui->fmsModePos2->setEnabled(false);
1815 ui->pidBankSs1_1->setEnabled(false);
1816 ui->assistControlPos2->setEnabled(false);
1817 setComboBoxItemEnabled(ui->failsafeFlightMode, 1, false);
1818 // pass through
1819 case 2:
1820 ui->fmsModePos3->setEnabled(false);
1821 ui->pidBankSs1_2->setEnabled(false);
1822 ui->assistControlPos3->setEnabled(false);
1823 setComboBoxItemEnabled(ui->failsafeFlightMode, 2, false);
1824 // pass through
1825 case 3:
1826 ui->fmsModePos4->setEnabled(false);
1827 ui->pidBankSs1_3->setEnabled(false);
1828 ui->assistControlPos4->setEnabled(false);
1829 setComboBoxItemEnabled(ui->failsafeFlightMode, 3, false);
1830 // pass through
1831 case 4:
1832 ui->fmsModePos5->setEnabled(false);
1833 ui->pidBankSs1_4->setEnabled(false);
1834 ui->assistControlPos5->setEnabled(false);
1835 setComboBoxItemEnabled(ui->failsafeFlightMode, 4, false);
1836 // pass through
1837 case 5:
1838 ui->fmsModePos6->setEnabled(false);
1839 ui->pidBankSs1_5->setEnabled(false);
1840 ui->assistControlPos6->setEnabled(false);
1841 setComboBoxItemEnabled(ui->failsafeFlightMode, 5, false);
1842 // pass through
1843 case 6:
1844 default:
1845 break;
1848 QString fmNumber = QString().setNum(manualSettingsDataPriv.FlightModeNumber);
1849 int count = 0;
1850 foreach(QSlider * sp, findChildren<QSlider *>()) {
1851 // Find FlightMode slider and apply stylesheet
1852 if (sp->objectName() == "channelNeutral") {
1853 if (count == 4) {
1854 sp->setStyleSheet(
1855 "QSlider::groove:horizontal {border: 2px solid rgb(196, 196, 196); margin: 0px 23px 0px 23px; height: 12px; border-radius: 5px; "
1856 "border-image:url(:/configgadget/images/flightmode_bg" + fmNumber + ".png); }"
1857 "QSlider::add-page:horizontal { background: none; border: none; }"
1858 "QSlider::sub-page:horizontal { background: none; border: none; }"
1859 "QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:0, "
1860 "stop: 0 rgba(196, 196, 196, 180), stop: 0.45 rgba(196, 196, 196, 180), "
1861 "stop: 0.46 rgba(255,0,0,100), stop: 0.54 rgba(255,0,0,100), "
1862 "stop: 0.55 rgba(196, 196, 196, 180), stop: 1 rgba(196, 196, 196, 180)); "
1863 "width: 46px; height: 28px; margin: -6px -23px -6px -23px; border-radius: 5px; border: 1px solid #777; }");
1864 count++;
1865 } else {
1866 count++;
1872 void ConfigInputWidget::updateConfigAlarmStatus()
1874 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
1875 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
1877 QString message = tr("Config OK");
1878 QString tooltipMessage = tr("All fine, no config alarm!");
1879 QString bgColor = "green";
1881 if (systemAlarms.Alarm[SystemAlarms::ALARM_SYSTEMCONFIGURATION] > SystemAlarms::ALARM_WARNING) {
1882 switch (systemAlarms.ExtendedAlarmStatus[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION]) {
1883 case SystemAlarms::EXTENDEDALARMSTATUS_FLIGHTMODE:
1884 message = tr("Config error");
1885 tooltipMessage = tr("There is something wrong with your config,\nusually a Thrust mode or Assisted mode not supported.\n\n"
1886 "Tip: Reduce the Flight Mode Count to find the culprit.");
1887 bgColor = "red";
1890 ui->configAlarmStatus->setVisible(true);
1891 ui->configAlarmStatus->setStyleSheet(
1892 "QLabel { background-color: " + bgColor + ";"
1893 "color: rgb(255, 255, 255); border-radius: 5; margin:1px; font:bold; }");
1894 ui->configAlarmStatus->setText(message);
1895 ui->configAlarmStatus->setToolTip(tooltipMessage);
1898 void ConfigInputWidget::updateCalibration()
1900 manualCommandData = manualCommandObj->getData();
1901 for (uint i = 0; i < ManualControlSettings::CHANNELMAX_NUMELEM; ++i) {
1902 if ((!reverse[i] && manualSettingsData.ChannelMin[i] > manualCommandData.Channel[i]) ||
1903 (reverse[i] && manualSettingsData.ChannelMin[i] < manualCommandData.Channel[i])) {
1904 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1906 if ((!reverse[i] && manualSettingsData.ChannelMax[i] < manualCommandData.Channel[i]) ||
1907 (reverse[i] && manualSettingsData.ChannelMax[i] > manualCommandData.Channel[i])) {
1908 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1910 if ((i == ManualControlSettings::CHANNELNUMBER_FLIGHTMODE) || (i == ManualControlSettings::CHANNELNUMBER_THROTTLE)) {
1911 adjustSpecialNeutrals();
1912 } else {
1913 // Set Accessory neutral to middle range
1914 if (i >= ManualControlSettings::CHANNELNUMBER_ACCESSORY0) {
1915 manualSettingsData.ChannelNeutral[i] = manualSettingsData.ChannelMin[i] + ((manualSettingsData.ChannelMax[i] - manualSettingsData.ChannelMin[i]) / 2);
1916 } else {
1917 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
1922 manualSettingsObj->setData(manualSettingsData);
1923 manualSettingsObj->updated();
1926 void ConfigInputWidget::simpleCalibration(bool enable)
1928 if (enable) {
1929 ui->configurationWizard->setEnabled(false);
1930 ui->saveRCInputToRAM->setEnabled(false);
1931 ui->saveRCInputToSD->setEnabled(false);
1932 ui->runCalibration->setText(tr("Stop Manual Calibration"));
1933 throttleError = false;
1935 QMessageBox msgBox;
1936 msgBox.setText(tr("<p>Arming Settings are now set to 'Always Disarmed' for your safety.</p>"
1937 "<p>Be sure your receiver is powered with an external source and Transmitter is on.</p>"
1938 "<p align='center'><b>Stop Manual Calibration</b> when done</p>"));
1939 msgBox.setDetailedText(tr("You will have to reconfigure the arming settings manually when the manual calibration is finished."));
1940 msgBox.setStandardButtons(QMessageBox::Ok);
1941 msgBox.setDefaultButton(QMessageBox::Ok);
1942 msgBox.exec();
1944 manualCommandData = manualCommandObj->getData();
1946 manualSettingsData = manualSettingsObj->getData();
1947 flightModeSettingsData = flightModeSettingsObj->getData();
1948 flightModeSettingsData.Arming = FlightModeSettings::ARMING_ALWAYSDISARMED;
1949 flightModeSettingsObj->setData(flightModeSettingsData);
1951 for (unsigned int i = 0; i < ManualControlCommand::CHANNEL_NUMELEM; i++) {
1952 reverse[i] = manualSettingsData.ChannelMax[i] < manualSettingsData.ChannelMin[i];
1953 manualSettingsData.ChannelMin[i] = manualCommandData.Channel[i];
1954 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
1955 manualSettingsData.ChannelMax[i] = manualCommandData.Channel[i];
1958 fastMdataSingle(manualCommandObj, &manualControlMdata);
1960 // Stash actuatorSettings
1961 actuatorSettingsData = actuatorSettingsObj->getData();
1962 memento.actuatorSettingsData = actuatorSettingsData;
1964 // Disable all actuators
1965 resetActuatorSettings();
1967 connect(manualCommandObj, SIGNAL(objectUnpacked(UAVObject *)), this, SLOT(updateCalibration()));
1968 } else {
1969 manualCommandData = manualCommandObj->getData();
1970 manualSettingsData = manualSettingsObj->getData();
1971 systemSettingsData = systemSettingsObj->getData();
1973 if (systemSettingsData.AirframeType == SystemSettings::AIRFRAMETYPE_GROUNDVEHICLECAR) {
1974 QMessageBox::warning(this, tr("Ground Vehicle"),
1975 tr("<p>Please <b>center</b> throttle control and press OK when ready.</p>"));
1977 transmitterType = ground;
1978 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_THROTTLE] =
1979 manualCommandData.Channel[ManualControlSettings::CHANNELNUMBER_THROTTLE];
1982 restoreMdataSingle(manualCommandObj, &manualControlMdata);
1984 // Force flight mode number to be 1 if 2 channel ground vehicle was confirmed
1985 if (transmitterType == ground) {
1986 forceOneFlightMode();
1989 for (unsigned int i = 0; i < ManualControlCommand::CHANNEL_NUMELEM; i++) {
1990 if ((i == ManualControlSettings::CHANNELNUMBER_FLIGHTMODE) || (i == ManualControlSettings::CHANNELNUMBER_THROTTLE)) {
1991 adjustSpecialNeutrals();
1992 checkThrottleRange();
1993 } else {
1994 // Set Accessory neutral to middle range
1995 if (i >= ManualControlSettings::CHANNELNUMBER_ACCESSORY0) {
1996 manualSettingsData.ChannelNeutral[i] = manualSettingsData.ChannelMin[i] + ((manualSettingsData.ChannelMax[i] - manualSettingsData.ChannelMin[i]) / 2);
1997 } else {
1998 manualSettingsData.ChannelNeutral[i] = manualCommandData.Channel[i];
2002 manualSettingsObj->setData(manualSettingsData);
2004 // Load actuator settings back from beginning of manual calibration
2005 actuatorSettingsObj->setData(memento.actuatorSettingsData);
2007 ui->configurationWizard->setEnabled(true);
2008 ui->saveRCInputToRAM->setEnabled(true);
2009 ui->saveRCInputToSD->setEnabled(true);
2010 ui->runCalibration->setText(tr("Start Manual Calibration"));
2012 disconnect(manualCommandObj, SIGNAL(objectUnpacked(UAVObject *)), this, SLOT(updateCalibration()));
2016 void ConfigInputWidget::adjustSpecialNeutrals()
2018 // FlightMode and Throttle need special neutral settings
2020 // Force flight mode neutral to middle
2021 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_FLIGHTMODE] =
2022 (manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_FLIGHTMODE] +
2023 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_FLIGHTMODE]) / 2;
2025 // A ground vehicle has a reversible motor, the center position of throttle is the neutral setting.
2026 // So do not have to set a special neutral value for it.
2027 if (transmitterType == ground) {
2028 return;
2031 // Force throttle to be near min, add 4% from total range to avoid arming issues
2032 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_THROTTLE] =
2033 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_THROTTLE] +
2034 ((manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_THROTTLE] -
2035 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_THROTTLE]) * 0.04);
2038 void ConfigInputWidget::checkThrottleRange()
2040 int throttleRange = abs(manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_THROTTLE] -
2041 manualSettingsData.ChannelMin[ManualControlSettings::CHANNELMIN_THROTTLE]);
2043 if (!throttleError && (throttleRange < 300)) {
2044 throttleError = true;
2045 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);
2047 // Set Throttle neutral to max value so Throttle can't be positive
2048 manualSettingsData.ChannelNeutral[ManualControlSettings::CHANNELNEUTRAL_THROTTLE] =
2049 manualSettingsData.ChannelMax[ManualControlSettings::CHANNELMAX_THROTTLE];
2053 bool ConfigInputWidget::shouldObjectBeSaved(UAVObject *object)
2055 // ManualControlCommand no need to be saved
2056 return dynamic_cast<ManualControlCommand *>(object) == NULL;
2059 void ConfigInputWidget::resetChannelSettings()
2061 manualSettingsData = manualSettingsObj->getData();
2062 // Clear all channel data : Channel Type (PPM,PWM..) and Number
2063 for (unsigned int channel = 0; channel < ManualControlSettings::CHANNELNUMBER_NUMELEM; channel++) {
2064 manualSettingsData.ChannelGroups[channel] = ManualControlSettings::CHANNELGROUPS_NONE;
2065 manualSettingsData.ChannelNumber[channel] = CHANNEL_NUMBER_NONE;
2066 manualSettingsObj->setData(manualSettingsData);
2068 resetFlightModeSettings();
2071 void ConfigInputWidget::resetFlightModeSettings()
2073 // Reset FlightMode settings
2074 manualSettingsData.FlightModeNumber = DEFAULT_FLIGHT_MODE_NUMBER;
2075 manualSettingsObj->setData(manualSettingsData);
2076 for (uint8_t pos = 0; pos < FlightModeSettings::FLIGHTMODEPOSITION_NUMELEM; pos++) {
2077 flightModeSignalValue[pos] = 0;
2081 void ConfigInputWidget::resetActuatorSettings()
2083 actuatorSettingsData = actuatorSettingsObj->getData();
2085 UAVDataObject *mixer = dynamic_cast<UAVDataObject *>(getObjectManager()->getObject(QString("MixerSettings")));
2086 Q_ASSERT(mixer);
2088 QString mixerType;
2090 // Clear all output data : Min, max, neutral at same value
2091 // 1000 for motors and 1500 for all others (Reversable motor included)
2092 for (unsigned int output = 0; output < ActuatorSettings::CHANNELMAX_NUMELEM; output++) {
2093 QString mixerNumType = QString("Mixer%1Type").arg(output + 1);
2094 UAVObjectField *field = mixer->getField(mixerNumType);
2095 Q_ASSERT(field);
2097 if (field) {
2098 mixerType = field->getValue().toString();
2100 if ((mixerType == "Motor") || (mixerType == "Disabled")) {
2101 actuatorSettingsData.ChannelMax[output] = 1000;
2102 actuatorSettingsData.ChannelMin[output] = 1000;
2103 actuatorSettingsData.ChannelNeutral[output] = 1000;
2104 } else {
2105 actuatorSettingsData.ChannelMax[output] = 1500;
2106 actuatorSettingsData.ChannelMin[output] = 1500;
2107 actuatorSettingsData.ChannelNeutral[output] = 1500;
2109 actuatorSettingsObj->setData(actuatorSettingsData);
2113 void ConfigInputWidget::forceOneFlightMode()
2115 manualSettingsData = manualSettingsObj->getData();
2116 manualSettingsData.FlightModeNumber = 1;
2117 manualSettingsObj->setData(manualSettingsData);
2120 void ConfigInputWidget::failsafeFlightModeChanged(int index)
2122 ui->failsafeFlightMode->setEnabled(index != -1);
2123 ui->failsafeFlightModeCb->setChecked(index != -1);
2126 void ConfigInputWidget::failsafeFlightModeCbToggled(bool checked)
2128 ui->failsafeFlightMode->setCurrentIndex(checked ? 0 : -1);
2131 void ConfigInputWidget::enableControlsChanged(bool enabled)
2133 ui->failsafeFlightMode->setEnabled(enabled && ui->failsafeFlightMode->currentIndex() != -1);