2 ******************************************************************************
4 * @file configoutputwidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
6 * E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
7 * @addtogroup GCSPlugins GCS Plugins
9 * @addtogroup ConfigPlugin Config Plugin
11 * @brief Servo 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
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 "configoutputwidget.h"
31 #include "ui_output.h"
32 #include "ui_outputchannelform.h"
34 #include "outputchannelform.h"
35 #include "configvehicletypewidget.h"
37 #include "uavsettingsimportexport/uavsettingsimportexportfactory.h"
38 #include <extensionsystem/pluginmanager.h>
39 #include <uavobjecthelper.h>
41 #include "mixersettings.h"
42 #include "actuatorcommand.h"
43 #include "actuatorsettings.h"
44 #include "flightmodesettings.h"
45 #include "flightstatus.h"
46 #include "systemsettings.h"
49 #include <QStringList>
52 #include <QMessageBox>
55 #define DSHOT_MAXOUTPUT_RANGE 2000
56 #define DSHOT_MINTOUTPUT_RANGE 0
57 #define PWMSYNC_MAXOUTPUT_RANGE 1900
58 #define DEFAULT_MAXOUTPUT_RANGE 2000
59 #define DEFAULT_MINOUTPUT_RANGE 900
60 #define DEFAULT_MINOUTPUT_VALUE 1000
61 #define REVMOTOR_NEUTRAL_TARGET_VALUE 1500
62 #define REVMOTOR_NEUTRAL_DIFF_VALUE 150
63 #define MOTOR_NEUTRAL_DIFF_VALUE 200
66 #define SERVO_MAXOUTPUT_RANGE 2500
67 #define SERVO_MINOUTPUT_RANGE 500
68 #define SERVO_MAXOUTPUT_VALUE 2000
69 #define SERVO_MINOUTPUT_VALUE 1000
70 #define SERVO_NEUTRAL_VALUE 1500
72 ConfigOutputWidget::ConfigOutputWidget(QWidget
*parent
) : ConfigTaskWidget(parent
)
74 m_ui
= new Ui_OutputWidget();
77 // must be done before auto binding !
78 setWikiURL("Output+Configuration");
82 m_ui
->boardWarningFrame
->setVisible(false);
83 m_ui
->configWarningFrame
->setVisible(false);
85 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
86 UAVSettingsImportExportFactory
*importexportplugin
= pm
->getObject
<UAVSettingsImportExportFactory
>();
87 connect(importexportplugin
, SIGNAL(importAboutToBegin()), this, SLOT(stopTests()));
89 connect(m_ui
->channelOutTest
, SIGNAL(clicked(bool)), this, SLOT(runChannelTests(bool)));
91 // Configure the task widget
93 // Track the ActuatorSettings object
94 addUAVObject("ActuatorSettings");
96 // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10.
97 // Register for ActuatorSettings changes:
98 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
99 OutputChannelForm
*form
= new OutputChannelForm(i
, this);
100 form
->moveTo(*(m_ui
->channelLayout
));
102 connect(m_ui
->channelOutTest
, SIGNAL(toggled(bool)), form
, SLOT(enableChannelTest(bool)));
103 connect(form
, SIGNAL(channelChanged(int, int)), this, SLOT(sendChannelTest(int, int)));
105 addWidget(form
->ui
->actuatorMin
);
106 addWidget(form
->ui
->actuatorNeutral
);
107 addWidget(form
->ui
->actuatorMax
);
108 addWidget(form
->ui
->actuatorRev
);
109 addWidget(form
->ui
->actuatorLink
);
112 // Associate the buttons with their UAVO fields
113 addWidget(m_ui
->spinningArmed
);
114 connect(m_ui
->spinningArmed
, SIGNAL(clicked(bool)), this, SLOT(updateSpinStabilizeCheckComboBoxes()));
116 addUAVObject("FlightModeSettings");
117 addWidgetBinding("FlightModeSettings", "AlwaysStabilizeWhenArmedSwitch", m_ui
->alwaysStabilizedSwitch
);
119 connect(FlightStatus::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateAlwaysStabilizeStatus()));
121 MixerSettings
*mixer
= MixerSettings::GetInstance(getObjectManager());
123 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank1
, QColor("#C6ECAE"), m_ui
->cb_outputRate1
, m_ui
->cb_outputMode1
);
124 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank2
, QColor("#91E5D3"), m_ui
->cb_outputRate2
, m_ui
->cb_outputMode2
);
125 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank3
, QColor("#FCEC52"), m_ui
->cb_outputRate3
, m_ui
->cb_outputMode3
);
126 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank4
, QColor("#C3A8FF"), m_ui
->cb_outputRate4
, m_ui
->cb_outputMode4
);
127 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank5
, QColor("#F7F7F2"), m_ui
->cb_outputRate5
, m_ui
->cb_outputMode5
);
128 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank6
, QColor("#FF9F51"), m_ui
->cb_outputRate6
, m_ui
->cb_outputMode6
);
131 rates
<< 50 << 60 << 125 << 165 << 270 << 330 << 400 << 490;
133 foreach(OutputBankControls controls
, m_banks
) {
134 addWidget(controls
.rateCombo());
136 controls
.rateCombo()->addItem(tr("-"), QVariant(0));
137 controls
.rateCombo()->model()->setData(controls
.rateCombo()->model()->index(0, 0), QVariant(0), Qt::UserRole
- 1);
138 foreach(int rate
, rates
) {
139 controls
.rateCombo()->addItem(tr("%1 Hz").arg(rate
), rate
);
142 addWidgetBinding("ActuatorSettings", "BankMode", controls
.modeCombo(), i
++, 0, true);
143 connect(controls
.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
146 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
147 connect(systemAlarmsObj
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateBoardWarnings(UAVObject
*)));
149 inputCalibrationStarted
= false;
150 channelTestsStarted
= false;
152 // TODO why do we do that ?
153 disconnect(this, SLOT(refreshWidgetsValues(UAVObject
*)));
156 ConfigOutputWidget::~ConfigOutputWidget()
158 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
160 disconnect(systemAlarmsObj
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateBoardWarnings(UAVObject
*)));
161 foreach(OutputBankControls controls
, m_banks
) {
162 disconnect(controls
.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
166 void ConfigOutputWidget::enableControls(bool enable
)
168 ConfigTaskWidget::enableControls(enable
);
171 m_ui
->channelOutTest
->setChecked(false);
172 channelTestsStarted
= false;
174 m_ui
->channelOutTest
->setEnabled(enable
);
178 Force update all channels with the values in the OutputChannelForms.
180 void ConfigOutputWidget::sendAllChannelTests()
182 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
183 OutputChannelForm
*form
= getOutputChannelForm(i
);
184 sendChannelTest(i
, form
->neutral());
189 Toggles the channel testing mode by making the GCS take over
190 the ActuatorCommand objects
192 void ConfigOutputWidget::runChannelTests(bool state
)
194 if (!checkOutputConfig()) {
195 m_ui
->channelOutTest
->setChecked(false);
196 QMessageBox::warning(this, tr("Warning"), tr("There is something wrong in the current config."
197 "<p>Please fix the issue before starting testing outputs.</p>"), QMessageBox::Ok
);
201 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
202 SystemAlarms::DataFields systemAlarms
= systemAlarmsObj
->getData();
204 if (state
&& systemAlarms
.Alarm
[SystemAlarms::ALARM_ACTUATOR
] != SystemAlarms::ALARM_OK
) {
206 mbox
.setText(QString(tr("The actuator module is in an error state. This can also occur because there are no inputs. "
207 "Please fix these before testing outputs.")));
208 mbox
.setStandardButtons(QMessageBox::Ok
);
211 // Unfortunately must cache this since callback will reoccur
212 m_accInitialData
= ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
214 m_ui
->channelOutTest
->setChecked(false);
218 // Confirm this is definitely what they want
221 mbox
.setText(QString(tr("This option will start your motors by the amount selected on the sliders regardless of transmitter."
222 "It is recommended to remove any blades from motors. Are you sure you want to do this?")));
223 mbox
.setStandardButtons(QMessageBox::Yes
| QMessageBox::No
);
224 int retval
= mbox
.exec();
225 if (retval
!= QMessageBox::Yes
) {
227 qDebug() << "Cancelled";
228 m_ui
->channelOutTest
->setChecked(false);
233 channelTestsStarted
= state
;
235 // Emit signal to be received by Input tab
236 emit
outputConfigSafeChanged(!state
);
238 m_ui
->spinningArmed
->setEnabled(!state
);
239 m_ui
->alwaysStabilizedSwitch
->setEnabled((m_ui
->spinningArmed
->isChecked()) && !state
);
240 m_ui
->alwayStabilizedLabel1
->setEnabled((m_ui
->spinningArmed
->isChecked()) && !state
);
241 m_ui
->alwayStabilizedLabel2
->setEnabled((m_ui
->spinningArmed
->isChecked()) && !state
);
242 setBanksEnabled(!state
);
244 ActuatorCommand
*obj
= ActuatorCommand::GetInstance(getObjectManager());
245 UAVObject::Metadata mdata
= obj
->getMetadata();
247 m_accInitialData
= mdata
;
248 UAVObject::SetFlightAccess(mdata
, UAVObject::ACCESS_READONLY
);
249 UAVObject::SetFlightTelemetryUpdateMode(mdata
, UAVObject::UPDATEMODE_ONCHANGE
);
250 UAVObject::SetGcsTelemetryAcked(mdata
, false);
251 UAVObject::SetGcsTelemetryUpdateMode(mdata
, UAVObject::UPDATEMODE_ONCHANGE
);
252 mdata
.gcsTelemetryUpdatePeriod
= 100;
254 mdata
= m_accInitialData
; // Restore metadata
256 obj
->setMetadata(mdata
);
259 // Setup the correct initial channel values when the channel testing mode is turned on.
261 sendAllChannelTests();
265 if (!state
&& isDirty()) {
267 mbox
.setText(QString(tr("You may want to save your neutral settings.")));
268 mbox
.setStandardButtons(QMessageBox::Ok
);
269 mbox
.setIcon(QMessageBox::Information
);
274 OutputChannelForm
*ConfigOutputWidget::getOutputChannelForm(const int index
) const
276 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
277 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
278 if (outputChannelForm
->index() == index
) {
279 return outputChannelForm
;
283 // no OutputChannelForm found with given index
288 * Set the label for a channel output assignement
290 void ConfigOutputWidget::assignOutputChannel(UAVDataObject
*obj
, QString
&str
)
292 // FIXME: use signal/ slot approach
293 UAVObjectField
*field
= obj
->getField(str
);
294 QStringList options
= field
->getOptions();
295 int index
= options
.indexOf(field
->getValue().toString());
297 OutputChannelForm
*outputChannelForm
= getOutputChannelForm(index
);
299 if (outputChannelForm
) {
300 outputChannelForm
->setName(str
);
305 Sends the channel value to the UAV to move the servo.
306 Returns immediately if we are not in testing mode
308 void ConfigOutputWidget::sendChannelTest(int index
, int value
)
310 if (!m_ui
->channelOutTest
->isChecked()) {
314 if (index
< 0 || (unsigned)index
>= ActuatorCommand::CHANNEL_NUMELEM
) {
318 ActuatorCommand
*actuatorCommand
= ActuatorCommand::GetInstance(getObjectManager());
319 Q_ASSERT(actuatorCommand
);
320 ActuatorCommand::DataFields actuatorCommandFields
= actuatorCommand
->getData();
321 actuatorCommandFields
.Channel
[index
] = value
;
322 actuatorCommand
->setData(actuatorCommandFields
);
325 void ConfigOutputWidget::setColor(QWidget
*widget
, const QColor color
)
327 QPalette
p(palette());
329 p
.setColor(QPalette::Background
, color
);
330 p
.setColor(QPalette::Base
, color
);
331 p
.setColor(QPalette::Active
, QPalette::Button
, color
);
332 p
.setColor(QPalette::Inactive
, QPalette::Button
, color
);
333 widget
->setAutoFillBackground(true);
334 widget
->setPalette(p
);
337 /********************************
339 *******************************/
342 Request the current config from the board (RC Output)
344 void ConfigOutputWidget::refreshWidgetsValuesImpl(UAVObject
*obj
)
348 // Get Actuator Settings
349 ActuatorSettings
*actuatorSettings
= ActuatorSettings::GetInstance(getObjectManager());
351 Q_ASSERT(actuatorSettings
);
352 ActuatorSettings::DataFields actuatorSettingsData
= actuatorSettings
->getData();
354 // Get channel descriptions
355 QStringList channelDesc
= ConfigVehicleTypeWidget::getChannelDescriptions();
357 // Initialize output forms
358 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
359 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
360 outputChannelForm
->setName(channelDesc
[outputChannelForm
->index()]);
362 // init min,max,neutral
363 int minValue
= actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()];
364 int maxValue
= actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()];
365 outputChannelForm
->setRange(minValue
, maxValue
);
367 int neutral
= actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()];
368 outputChannelForm
->setNeutral(neutral
);
371 // Get the SpinWhileArmed setting
372 m_ui
->spinningArmed
->setChecked(actuatorSettingsData
.MotorsSpinWhileArmed
== ActuatorSettings::MOTORSSPINWHILEARMED_TRUE
);
374 for (int i
= 0; i
< m_banks
.count(); i
++) {
375 OutputBankControls controls
= m_banks
.at(i
);
376 // Reset to all disabled
377 controls
.label()->setText("-");
379 controls
.rateCombo()->setEnabled(false);
380 setColor(controls
.rateCombo(), palette().color(QPalette::Background
));
381 controls
.rateCombo()->setCurrentIndex(0);
383 controls
.modeCombo()->setEnabled(false);
384 setColor(controls
.modeCombo(), palette().color(QPalette::Background
));
387 // Get connected board model
388 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
390 UAVObjectUtilManager
*utilMngr
= pm
->getObject
<UAVObjectUtilManager
>();
392 QStringList bankLabels
;
393 QList
<int> channelBanks
;
396 int board
= utilMngr
->getBoardModel();
397 // Setup labels and combos for banks according to board type
398 if ((board
& 0xff00) == 0x0400) {
399 // Coptercontrol family of boards 4 timer banks
400 bankLabels
<< "1 (1-3)" << "2 (4)" << "3 (5,7-8)" << "4 (6,9-10)";
401 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 4 << 3 << 3 << 4 << 4;
402 } else if (board
== 0x0903) {
403 // Revolution family of boards 6 timer banks
404 bankLabels
<< "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7,12)" << "6 (8-11)";
405 channelBanks
<< 1 << 1 << 2 << 3 << 4 << 4 << 5 << 6 << 6 << 6 << 6 << 5;
406 } else if (board
== 0x0905) {
408 bankLabels
<< "1 (1)" << "2 (2,7,11)" << "3 (3)" << "4 (4)" << "5 (5-6)" << "6 (8-10,12)";
409 channelBanks
<< 1 << 2 << 3 << 4 << 5 << 5 << 2 << 6 << 6 << 6 << 2 << 6;
410 } else if (board
== 0x9201) {
412 bankLabels
<< "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7-8)" << "6 (9-10)";
413 channelBanks
<< 1 << 1 << 2 << 3 << 4 << 4 << 5 << 5 << 6 << 6;
414 } else if (board
== 0x1001) {
416 bankLabels
<< "1 (1-3,7)" << "2 (4,8)" << "3 (5)" << "4 (6)";
417 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 4 << 1 << 2;
418 } else if (board
== 0x1002 || board
== 0x1003) {
419 // SPRacingF3_EVO, NucleoF303RE
420 bankLabels
<< "1 (1-3)" << "2 (4)" << "3 (5-8)";
421 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 3 << 3 << 3;
422 } else if (board
== 0x1005) { // PikoBLX
423 bankLabels
<< "1 (1-4)" << "2 (5-6)" << "3 (7)" << "4 (8)";
424 channelBanks
<< 1 << 1 << 1 << 1 << 2 << 2 << 3 << 4;
425 } else if (board
== 0x1006) { // tinyFISH
426 bankLabels
<< "1 (1)" << "2 (2)" << "3 (3-4)" << "4 (5-6)";
427 channelBanks
<< 1 << 2 << 3 << 3 << 4 << 4;
431 // Store how many banks are active according to the board
432 activeBanksCount
= bankLabels
.count();
435 foreach(QString banklabel
, bankLabels
) {
436 OutputBankControls controls
= m_banks
.at(i
);
438 controls
.label()->setText(banklabel
);
439 int index
= controls
.rateCombo()->findData(actuatorSettingsData
.BankUpdateFreq
[i
]);
441 controls
.rateCombo()->addItem(tr("%1 Hz").arg(actuatorSettingsData
.BankUpdateFreq
[i
]), actuatorSettingsData
.BankUpdateFreq
[i
]);
443 bool isPWM
= (controls
.modeCombo()->currentIndex() == ActuatorSettings::BANKMODE_PWM
);
444 controls
.rateCombo()->setCurrentIndex(index
);
445 controls
.rateCombo()->setEnabled(!inputCalibrationStarted
&& !channelTestsStarted
&& isPWM
);
446 setColor(controls
.rateCombo(), controls
.color());
447 controls
.modeCombo()->setEnabled(!inputCalibrationStarted
&& !channelTestsStarted
);
448 setColor(controls
.modeCombo(), controls
.color());
452 // Get Channel ranges:
454 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
455 int minValue
= actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()];
456 int maxValue
= actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()];
458 if (channelBanks
.count() > i
) {
459 int bankNumber
= channelBanks
.at(i
);
460 OutputBankControls bankControls
= m_banks
.at(bankNumber
- 1);
462 setChannelLimits(outputChannelForm
, &bankControls
);
464 outputChannelForm
->setBank(QString::number(bankNumber
));
465 outputChannelForm
->setColor(bankControls
.color());
469 outputChannelForm
->setRange(minValue
, maxValue
);
470 int neutral
= actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()];
471 outputChannelForm
->setNeutral(neutral
);
474 updateSpinStabilizeCheckComboBoxes();
479 * Sends the config to the board, without saving to the SD card (RC Output)
481 void ConfigOutputWidget::updateObjectsFromWidgetsImpl()
483 ActuatorSettings
*actuatorSettings
= ActuatorSettings::GetInstance(getObjectManager());
485 Q_ASSERT(actuatorSettings
);
486 if (actuatorSettings
) {
487 ActuatorSettings::DataFields actuatorSettingsData
= actuatorSettings
->getData();
489 // Set channel ranges
490 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
491 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
492 actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()] = outputChannelForm
->max();
493 actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()] = outputChannelForm
->min();
494 actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()] = outputChannelForm
->neutral();
498 actuatorSettingsData
.BankUpdateFreq
[0] = m_ui
->cb_outputRate1
->currentData().toUInt();
499 actuatorSettingsData
.BankUpdateFreq
[1] = m_ui
->cb_outputRate2
->currentData().toUInt();
500 actuatorSettingsData
.BankUpdateFreq
[2] = m_ui
->cb_outputRate3
->currentData().toUInt();
501 actuatorSettingsData
.BankUpdateFreq
[3] = m_ui
->cb_outputRate4
->currentData().toUInt();
502 actuatorSettingsData
.BankUpdateFreq
[4] = m_ui
->cb_outputRate5
->currentData().toUInt();
503 actuatorSettingsData
.BankUpdateFreq
[5] = m_ui
->cb_outputRate6
->currentData().toUInt();
505 actuatorSettingsData
.MotorsSpinWhileArmed
= m_ui
->spinningArmed
->isChecked() ?
506 ActuatorSettings::MOTORSSPINWHILEARMED_TRUE
:
507 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE
;
510 UAVObjectUpdaterHelper updateHelper
;
511 actuatorSettings
->setData(actuatorSettingsData
, false);
512 updateHelper
.doObjectAndWait(actuatorSettings
);
515 FlightModeSettings
*flightModeSettings
= FlightModeSettings::GetInstance(getObjectManager());
516 Q_ASSERT(flightModeSettings
);
518 if (flightModeSettings
) {
519 FlightModeSettings::DataFields flightModeSettingsData
= flightModeSettings
->getData();
520 flightModeSettingsData
.AlwaysStabilizeWhenArmedSwitch
= m_ui
->alwaysStabilizedSwitch
->currentIndex();
523 flightModeSettings
->setData(flightModeSettingsData
);
527 void ConfigOutputWidget::updateSpinStabilizeCheckComboBoxes()
529 m_ui
->alwayStabilizedLabel1
->setEnabled((m_ui
->spinningArmed
->isChecked()) && (m_ui
->spinningArmed
->isEnabled()));
530 m_ui
->alwayStabilizedLabel2
->setEnabled((m_ui
->spinningArmed
->isChecked()) && (m_ui
->spinningArmed
->isEnabled()));
531 m_ui
->alwaysStabilizedSwitch
->setEnabled((m_ui
->spinningArmed
->isChecked()) && (m_ui
->spinningArmed
->isEnabled()));
533 if (!m_ui
->spinningArmed
->isChecked()) {
534 m_ui
->alwaysStabilizedSwitch
->setCurrentIndex(FlightModeSettings::ALWAYSSTABILIZEWHENARMEDSWITCH_DISABLED
);
538 void ConfigOutputWidget::updateAlwaysStabilizeStatus()
540 FlightStatus
*flightStatusObj
= FlightStatus::GetInstance(getObjectManager());
541 FlightStatus::DataFields flightStatus
= flightStatusObj
->getData();
543 if (flightStatus
.AlwaysStabilizeWhenArmed
== FlightStatus::ALWAYSSTABILIZEWHENARMED_TRUE
) {
544 m_ui
->alwayStabilizedLabel2
->setText(tr("AlwaysStabilizeWhenArmed is <b>ACTIVE</b>. This prevents arming!."));
546 m_ui
->alwayStabilizedLabel2
->setText(tr("(Really be careful!)."));
550 void ConfigOutputWidget::setChannelLimits(OutputChannelForm
*channelForm
, OutputBankControls
*bankControls
)
552 // Set UI limits according to the bankmode and destination
553 switch (bankControls
->modeCombo()->currentIndex()) {
554 case ActuatorSettings::BANKMODE_DSHOT
:
555 // 0 - 2000 UI limits, DShot min value is fixed to zero
556 if (channelForm
->isServoOutput()) {
557 // Driving a servo using DShot doest not make sense so break
560 channelForm
->setLimits(DSHOT_MINTOUTPUT_RANGE
, DSHOT_MINTOUTPUT_RANGE
, DSHOT_MINTOUTPUT_RANGE
, DSHOT_MAXOUTPUT_RANGE
);
561 channelForm
->setRange(DSHOT_MINTOUTPUT_RANGE
, DSHOT_MAXOUTPUT_RANGE
);
562 channelForm
->setNeutral(DSHOT_MINTOUTPUT_RANGE
);
564 case ActuatorSettings::BANKMODE_PWMSYNC
:
565 // 900 - 1900 UI limits
566 // Default values 1000 - 1900
567 channelForm
->setLimits(DEFAULT_MINOUTPUT_RANGE
, PWMSYNC_MAXOUTPUT_RANGE
, DEFAULT_MINOUTPUT_RANGE
, PWMSYNC_MAXOUTPUT_RANGE
);
568 channelForm
->setRange(DEFAULT_MINOUTPUT_VALUE
, PWMSYNC_MAXOUTPUT_RANGE
);
569 channelForm
->setNeutral(DEFAULT_MINOUTPUT_VALUE
);
570 if (channelForm
->isServoOutput()) {
571 // Servo: Some of them can handle PWMSync, 500 - 1900 UI limits
572 // Default values 1000 - 1900 + neutral 1500
573 channelForm
->setRange(SERVO_MINOUTPUT_VALUE
, PWMSYNC_MAXOUTPUT_RANGE
);
574 channelForm
->setNeutral(SERVO_NEUTRAL_VALUE
);
577 case ActuatorSettings::BANKMODE_PWM
:
578 if (channelForm
->isServoOutput()) {
579 // Servo: 500 - 2500 UI limits
580 // Default values 1000 - 2000 + neutral 1500
581 channelForm
->setLimits(SERVO_MINOUTPUT_RANGE
, SERVO_MAXOUTPUT_RANGE
, SERVO_MINOUTPUT_RANGE
, SERVO_MAXOUTPUT_RANGE
);
582 channelForm
->setRange(SERVO_MINOUTPUT_VALUE
, SERVO_MAXOUTPUT_VALUE
);
583 channelForm
->setNeutral(SERVO_NEUTRAL_VALUE
);
586 // PWM motor outputs fall to default
587 case ActuatorSettings::BANKMODE_ONESHOT125
:
588 case ActuatorSettings::BANKMODE_ONESHOT42
:
589 case ActuatorSettings::BANKMODE_MULTISHOT
:
590 if (channelForm
->isServoOutput()) {
591 // Driving a servo using this mode does not make sense so break
595 // Motors 900 - 2000 UI limits
596 // Default values 1000 - 2000, neutral set to min
597 // This settings are used for PWM, OneShot125, OneShot42 and MultiShot
598 channelForm
->setLimits(DEFAULT_MINOUTPUT_RANGE
, DEFAULT_MAXOUTPUT_RANGE
, DEFAULT_MINOUTPUT_RANGE
, DEFAULT_MAXOUTPUT_RANGE
);
599 channelForm
->setRange(DEFAULT_MINOUTPUT_VALUE
, DEFAULT_MAXOUTPUT_RANGE
);
600 channelForm
->setNeutral(DEFAULT_MINOUTPUT_VALUE
);
605 ConfigOutputWidget::ChannelConfigWarning
ConfigOutputWidget::checkChannelConfig(OutputChannelForm
*channelForm
, OutputBankControls
*bankControls
)
607 ChannelConfigWarning warning
= None
;
608 int currentNeutralValue
= channelForm
->neutralValue();
610 // Check if RevMotor has neutral value around center
611 if (channelForm
->isReversibleMotorOutput()) {
612 warning
= IsReversibleMotorCheckNeutral
;
613 int neutralDiff
= qAbs(REVMOTOR_NEUTRAL_TARGET_VALUE
- currentNeutralValue
);
614 if (neutralDiff
< REVMOTOR_NEUTRAL_DIFF_VALUE
) {
620 // Check if NormalMotor neutral is not too high
621 if (channelForm
->isNormalMotorOutput()) {
622 warning
= IsNormalMotorCheckNeutral
;
623 int neutralDiff
= currentNeutralValue
- DEFAULT_MINOUTPUT_VALUE
;
624 if (neutralDiff
< MOTOR_NEUTRAL_DIFF_VALUE
) {
630 switch (bankControls
->modeCombo()->currentIndex()) {
631 case ActuatorSettings::BANKMODE_DSHOT
:
632 if (channelForm
->isServoOutput()) {
633 // Driving a servo using DShot doest not make sense
634 warning
= CannotDriveServo
;
635 } else if (channelForm
->isReversibleMotorOutput()) {
636 // Bi-directional DShot not yet supported
637 warning
= BiDirectionalDShotNotSupported
;
640 case ActuatorSettings::BANKMODE_PWMSYNC
:
641 case ActuatorSettings::BANKMODE_PWM
:
643 case ActuatorSettings::BANKMODE_ONESHOT125
:
644 case ActuatorSettings::BANKMODE_ONESHOT42
:
645 case ActuatorSettings::BANKMODE_MULTISHOT
:
646 if (channelForm
->isServoOutput()) {
647 warning
= CannotDriveServo
;
648 // Driving a servo using this mode does not make sense so break
658 void ConfigOutputWidget::onBankTypeChange()
660 QComboBox
*bankModeCombo
= qobject_cast
<QComboBox
*>(sender());
662 ChannelConfigWarning new_warning
= None
;
664 if (bankModeCombo
!= NULL
) {
666 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
667 foreach(OutputBankControls controls
, m_banks
) {
668 if (controls
.modeCombo() == bankModeCombo
) {
669 bool enabled
= bankModeCombo
->currentIndex() == ActuatorSettings::BANKMODE_PWM
;
670 controls
.rateCombo()->setEnabled(enabled
);
671 controls
.rateCombo()->setCurrentIndex(enabled
? 1 : 0);
672 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
673 if (outputChannelForm
->bank().toInt() == bankNumber
) {
674 setChannelLimits(outputChannelForm
, &controls
);
675 ChannelConfigWarning warning
= checkChannelConfig(outputChannelForm
, &controls
);
676 if (warning
> None
) {
677 new_warning
= warning
;
688 updateChannelConfigWarning(new_warning
);
691 bool ConfigOutputWidget::checkOutputConfig()
693 ChannelConfigWarning new_warning
= None
;
697 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
699 foreach(OutputBankControls controls
, m_banks
) {
700 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
701 if (!outputChannelForm
->isDisabledOutput() && (outputChannelForm
->bank().toInt() == bankNumber
)) {
702 ChannelConfigWarning warning
= checkChannelConfig(outputChannelForm
, &controls
);
703 if (warning
> None
) {
704 new_warning
= warning
;
712 updateChannelConfigWarning(new_warning
);
714 // Emit signal to be received by Input tab
715 emit
outputConfigSafeChanged(new_warning
== None
);
717 return new_warning
== None
;
720 void ConfigOutputWidget::stopTests()
722 m_ui
->channelOutTest
->setChecked(false);
723 channelTestsStarted
= false;
726 void ConfigOutputWidget::updateBoardWarnings(UAVObject
*)
728 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
729 SystemAlarms::DataFields systemAlarms
= systemAlarmsObj
->getData();
731 if (systemAlarms
.Alarm
[SystemAlarms::ALARM_SYSTEMCONFIGURATION
] > SystemAlarms::ALARM_WARNING
) {
732 switch (systemAlarms
.ExtendedAlarmStatus
[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION
]) {
733 case SystemAlarms::EXTENDEDALARMSTATUS_UNSUPPORTEDCONFIG_ONESHOT
:
734 setBoardWarning(tr("OneShot and PWMSync output only works with Receiver Port settings marked with '+OneShot'<br>"
735 "When using Receiver Port setting 'PPM_PIN8+OneShot' "
736 "<b><font color='%1'>Bank %2</font></b> must be set to PWM")
737 .arg(m_banks
.at(3).color().name()).arg(m_banks
.at(3).label()->text()));
741 setBoardWarning(NULL
);
744 void ConfigOutputWidget::updateChannelConfigWarning(ChannelConfigWarning warning
)
748 if (warning
== BiDirectionalDShotNotSupported
) {
749 // TODO: Implement bi-directional DShot
750 warning_str
= "There is <b>one reversible motor</b> using DShot is configured.<br>"
751 "Bi-directional DShot is currently not supported. Please use PWM, OneShotXXX or MultiShot.";
754 if (warning
== IsNormalMotorCheckNeutral
) {
755 warning_str
= "There is at least one pretty <b>high neutral value</b> set in your configuration.<br>"
756 "Make sure all ESCs are calibrated and no mechanical stress in all motors.";
759 if (warning
== IsReversibleMotorCheckNeutral
) {
760 warning_str
= "A least one <b>reversible motor</b> is configured.<br>"
761 "Make sure a appropriate neutral value is set before saving and applying power to the vehicule.";
764 if (warning
== CannotDriveServo
) {
765 warning_str
= "One bank cannot drive a <b>servo output</b>!<br>"
766 "You must use PWM for this bank or move the servo output to another compatible bank.";
769 setConfigWarning(warning_str
);
772 void ConfigOutputWidget::setBanksEnabled(bool state
)
774 // Disable/Enable banks
775 for (int i
= 0; i
< m_banks
.count(); i
++) {
776 OutputBankControls controls
= m_banks
.at(i
);
777 if (i
< activeBanksCount
) {
778 controls
.modeCombo()->setEnabled(state
);
779 controls
.rateCombo()->setEnabled(state
);
781 controls
.modeCombo()->setEnabled(false);
782 controls
.rateCombo()->setEnabled(false);
787 void ConfigOutputWidget::setBoardWarning(QString message
)
789 m_ui
->boardWarningFrame
->setVisible(!message
.isNull());
790 m_ui
->boardWarningPic
->setPixmap(message
.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
791 m_ui
->boardWarningTxt
->setText(message
);
794 void ConfigOutputWidget::setConfigWarning(QString message
)
796 m_ui
->configWarningFrame
->setVisible(!message
.isNull());
797 m_ui
->configWarningPic
->setPixmap(message
.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
798 m_ui
->configWarningTxt
->setText(message
);
801 void ConfigOutputWidget::setInputCalibrationState(bool started
)
803 inputCalibrationStarted
= started
;
805 // Disable UI when a input calibration is started
806 // so user cannot manipulate settings.
807 enableControls(!started
);
808 setBanksEnabled(!started
);
810 m_ui
->spinningArmed
->setEnabled(!started
);
811 m_ui
->alwaysStabilizedSwitch
->setEnabled((m_ui
->spinningArmed
->isChecked()) && !started
);
812 m_ui
->alwayStabilizedLabel1
->setEnabled((m_ui
->spinningArmed
->isChecked()) && !started
);
813 m_ui
->alwayStabilizedLabel2
->setEnabled((m_ui
->spinningArmed
->isChecked()) && !started
);
815 // Disable every channel form when needed
816 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
817 OutputChannelForm
*form
= getOutputChannelForm(i
);
818 form
->ui
->actuatorRev
->setChecked(false);
819 form
->ui
->actuatorLink
->setChecked(false);
820 form
->setChannelRangeEnabled(!started
);
821 form
->setControlsEnabled(!started
);
825 OutputBankControls::OutputBankControls(MixerSettings
*mixer
, QLabel
*label
, QColor color
, QComboBox
*rateCombo
, QComboBox
*modeCombo
) :
826 m_mixer(mixer
), m_label(label
), m_color(color
), m_rateCombo(rateCombo
), m_modeCombo(modeCombo
)
829 OutputBankControls::~OutputBankControls()