/* * Copyright (C) EdgeTX * * Based on code named * opentx - https://github.com/opentx/opentx * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "setup_module.h" #include "ui_setup_module.h" #include "namevalidator.h" #include "autolineedit.h" #include "multiprotocols.h" #include "helpers.h" #define FAILSAFE_CHANNEL_HOLD 2000 #define FAILSAFE_CHANNEL_NOPULSE 2001 #define MASK_PROTOCOL (1<<0) #define MASK_CHANNELS_COUNT (1<<1) #define MASK_RX_NUMBER (1<<2) #define MASK_CHANNELS_RANGE (1<<3) #define MASK_PPM_FIELDS (1<<4) #define MASK_FAILSAFES (1<<5) #define MASK_OPEN_DRAIN (1<<6) #define MASK_MULTIMODULE (1<<7) #define MASK_ANTENNA (1<<8) #define MASK_MULTIOPTION (1<<9) #define MASK_R9M (1<<10) #define MASK_SBUSPPM_FIELDS (1<<11) #define MASK_SUBTYPES (1<<12) #define MASK_ACCESS (1<<13) #define MASK_RX_FREQ (1<<14) #define MASK_RF_POWER (1<<15) #define MASK_RF_RACING_MODE (1<<16) #define MASK_GHOST (1<<17) #define MASK_BAUDRATE (1<<18) #define MASK_MULTI_DSM_OPT (1<<19) #define MASK_CHANNELMAP (1<<20) #define MASK_MULTI_BAYANG_OPT (1<<21) #define MASK_AFHDS (1<<22) #define MASK_CSRF_ARMING_MODE (1<<23) #define MASK_CSRF_ARMING_TRIGGER (1<<24) quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx, FilteredItemModelFactory * panelFilteredItemModels, CompoundItemModelFactory * panelItemModels): ModelPanel(parent, model, generalSettings, firmware), module(module), moduleIdx(moduleIdx), ui(new Ui::Module), trainerModeItemModel(nullptr) { lock = true; ui->setupUi(this); ui->label_module->setText(ModuleData::indexToString(moduleIdx, firmware)); if (isTrainerModule(moduleIdx)) { ui->formLayout_col1->setSpacing(0); if (!IS_HORUS_OR_TARANIS(firmware->getBoard())) { ui->label_trainerMode->hide(); ui->trainerMode->hide(); } else { updateTrainerModeItemModel(); ui->trainerMode->setField(model.trainerMode); connect(ui->trainerMode, &AutoComboBox::currentDataChanged, this, [=] () { update(); emit updateItemModels(); emit modified(); }); } } else { ui->label_trainerMode->hide(); ui->trainerMode->hide(); } if (panelFilteredItemModels) { if (!isTrainerModule(moduleIdx)) { int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(ModuleData::protocolItemModel(generalSettings), moduleIdx + 1/*flag cannot be 0*/), QString("Module Protocol %1").arg(moduleIdx)); panelFilteredItemModels->getItemModel(id)->setSortCaseSensitivity(Qt::CaseInsensitive); panelFilteredItemModels->getItemModel(id)->sort(0); ui->protocol->setModel(panelFilteredItemModels->getItemModel(id)); if (ui->protocol->findData(module.protocol) < 0) { const QString moduleIdxDesc = isInternalModule(moduleIdx) ? tr("internal") : tr("external"); const QString compareDesc = isInternalModule(moduleIdx) ? tr("hardware") : tr("profile"); const QString intModuleDesc = isInternalModule(moduleIdx) ? ModuleData::typeToString(generalSettings.internalModule) : ""; QString msg = tr("Warning: The %1 module protocol %2 is incompatible with the %3 %1 module %4 and has been set to OFF!"); msg = msg.arg(moduleIdxDesc).arg(module.protocolToString(module.protocol)).arg(compareDesc).arg(intModuleDesc); QMessageBox *msgBox = new QMessageBox(this); msgBox->setIcon( QMessageBox::Warning ); msgBox->setText(msg); msgBox->addButton( "Ok", QMessageBox::AcceptRole ); msgBox->setWindowFlag(Qt::WindowStaysOnTopHint); msgBox->setAttribute(Qt::WA_DeleteOnClose); // delete pointer after close msgBox->setModal(false); msgBox->show(); module.clear(); } ui->protocol->setField(module.protocol, this); } if (isInternalModule(moduleIdx)) { int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(GeneralSettings::antennaModeItemModel(true)), FIM_ANTENNAMODE); ui->antennaMode->setModel(panelFilteredItemModels->getItemModel(id)); } } ui->multiProtocol->setModel(Multiprotocols::protocolItemModel()); ui->btnGrpValueType->setId(ui->optPercent, FAILSAFE_DISPLAY_PERCENT); ui->btnGrpValueType->setId(ui->optUs, FAILSAFE_DISPLAY_USEC); ui->btnGrpValueType->button(failsafesValueDisplayType)->setChecked(true); ui->registrationId->setText(model.registrationId); if (panelItemModels) { ui->crsfArmingMode->setModel(panelItemModels->getItemModel(AIM_MODULE_CRSFARMINGMODE)); ui->crsfArmingTrigger->setModel(panelFilteredItemModels->getItemModel(FIM_CRSFARMSWITCH)); } setupFailsafes(); disableMouseScrolling(); update(); connect(ui->protocol, &AutoComboBox::currentDataChanged, this, &ModulePanel::onProtocolChanged); connect(ui->multiSubType, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onSubTypeChanged); connect(ui->multiProtocol, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onMultiProtocolChanged); connect(this, &ModulePanel::channelsRangeChanged, this, &ModulePanel::setupFailsafes); connect(ui->btnGrpValueType, static_cast(&QButtonGroup::idClicked), this, &ModulePanel::onFailsafesDisplayValueTypeChanged); connect(ui->rxFreq, static_cast(&QSpinBox::valueChanged), this, &ModulePanel::onRfFreqChanged); connect(ui->clearRx1, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); connect(ui->clearRx2, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); connect(ui->clearRx3, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); connect(ui->cboAfhdsOpt1, static_cast(&QComboBox::currentIndexChanged), [=] (int index) { if (lock) return; if (this->module.protocol == PULSES_FLYSKY_AFHDS2A) Helpers::setBitmappedValue(this->module.flysky.mode, ui->cboAfhdsOpt1->currentData().toInt(), 1); else this->module.afhds3.phyMode = ui->cboAfhdsOpt1->currentData().toInt(); emit modified(); }); connect(ui->cboAfhdsOpt2, static_cast(&QComboBox::currentIndexChanged), [=] (int index) { if (lock) return; if (this->module.protocol == PULSES_FLYSKY_AFHDS2A) Helpers::setBitmappedValue(this->module.flysky.mode, ui->cboAfhdsOpt2->currentData().toInt(), 0); else this->module.afhds3.emi = ui->cboAfhdsOpt2->currentData().toInt(); emit modified(); }); connect(ui->crsfArmingMode, static_cast(&QComboBox::currentIndexChanged), [=] (int index) { this->module.crsf.crsfArmingMode = ui->crsfArmingMode->currentData().toInt(); if (this->module.crsf.crsfArmingMode != ModuleData::CRSF_ARMING_MODE_SWITCH) this->module.crsf.crsfArmingTrigger = RawSwitch(SWITCH_TYPE_NONE); update(); emit modified(); }); connect(ui->crsfArmingTrigger, static_cast(&QComboBox::currentIndexChanged), [=] (int index) { this->module.crsf.crsfArmingTrigger = RawSwitch(ui->crsfArmingTrigger->currentData().toInt()); emit modified(); }); lock = false; } ModulePanel::~ModulePanel() { delete ui; } void ModulePanel::setupFailsafes() { ChannelFailsafeWidgetsGroup grp; const int start = module.channelsStart; const int end = start + module.channelsCount; const bool hasFailsafe = module.hasFailsafes(firmware); lock = true; QMutableMapIterator i(failsafeGroupsMap); while (i.hasNext()) { i.next(); grp = i.value(); ui->failsafesLayout->removeWidget(grp.label); ui->failsafesLayout->removeWidget(grp.combo); ui->failsafesLayout->removeWidget(grp.sbPercent); ui->failsafesLayout->removeWidget(grp.sbUsec); if (i.key() < start || i.key() >= end || !hasFailsafe) { grp.label->deleteLater(); grp.combo->deleteLater(); grp.sbPercent->deleteLater(); grp.sbUsec->deleteLater(); i.remove(); } } if (!hasFailsafe) { lock = false; return; } int row = 0; int col = 0; int channelMax = model->getChannelsMax(); int channelMaxUs = 512 * channelMax / 100 * 2; for (int i = start; i < end; ++i) { if (failsafeGroupsMap.contains(i)) { grp = failsafeGroupsMap.value(i); } else { QLabel * label = new QLabel(this); label->setProperty("index", i); label->setText(QString::number(i + 1)); QComboBox * combo = new QComboBox(this); combo->setProperty("index", i); combo->addItem(tr("Value"), 0); combo->addItem(tr("Hold"), FAILSAFE_CHANNEL_HOLD); combo->addItem(tr("No Pulse"), FAILSAFE_CHANNEL_NOPULSE); QDoubleSpinBox * sbDbl = new QDoubleSpinBox(this); sbDbl->setProperty("index", i); sbDbl->setMinimumSize(QSize(20, 0)); sbDbl->setRange(-channelMax, channelMax); sbDbl->setSingleStep(0.1); sbDbl->setDecimals(1); QSpinBox * sbInt = new QSpinBox(this); sbInt->setProperty("index", i); sbInt->setMinimumSize(QSize(20, 0)); sbInt->setRange(-channelMaxUs, channelMaxUs); sbInt->setSingleStep(1); grp = ChannelFailsafeWidgetsGroup(); grp.combo = combo; grp.sbPercent = sbDbl; grp.sbUsec = sbInt; grp.label = label; failsafeGroupsMap.insert(i, grp); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFailsafeComboIndexChanged(int))); connect(sbInt, SIGNAL(valueChanged(int)), this, SLOT(onFailsafeUsecChanged(int))); connect(sbDbl, SIGNAL(valueChanged(double)), this, SLOT(onFailsafePercentChanged(double))); } ui->failsafesLayout->addWidget(grp.label, row, col, Qt::AlignHCenter); ui->failsafesLayout->addWidget(grp.combo, row + 1, col, Qt::AlignHCenter); ui->failsafesLayout->addWidget(grp.sbPercent, row + 2, col, Qt::AlignHCenter); ui->failsafesLayout->addWidget(grp.sbUsec, row + 3, col, Qt::AlignHCenter); grp.sbPercent->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_PERCENT); grp.sbUsec->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_USEC); updateFailsafe(i); if (++col > 7) { row += 4; col = 0; } } lock = false; } void ModulePanel::update() { lock = true; const auto protocol = (PulsesProtocol)module.protocol; const auto board = firmware->getBoard(); const auto & pdef = multiProtocols.getProtocol(module.multi.rfProtocol); unsigned int mask = 0; unsigned int max_rx_num = 63; if (!isTrainerModule(moduleIdx)) { mask |= MASK_PROTOCOL; switch (protocol) { case PULSES_PXX_R9M: mask |= MASK_R9M | MASK_RF_POWER | MASK_SUBTYPES; case PULSES_ACCESS_R9M: case PULSES_ACCESS_R9M_LITE: case PULSES_ACCESS_R9M_LITE_PRO: case PULSES_ACCESS_ISRM: case PULSES_ACCST_ISRM_D16: case PULSES_XJT_LITE_X16: case PULSES_XJT_LITE_D8: case PULSES_XJT_LITE_LR12: case PULSES_PXX_XJT_X16: case PULSES_PXX_XJT_D8: case PULSES_PXX_XJT_LR12: case PULSES_PXX_DJT: mask |= MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; // ACCST Rx ID if (protocol==PULSES_PXX_XJT_X16 || protocol==PULSES_PXX_XJT_LR12 || protocol==PULSES_PXX_R9M || protocol==PULSES_ACCST_ISRM_D16 || protocol==PULSES_XJT_LITE_X16 || protocol==PULSES_XJT_LITE_LR12) mask |= MASK_RX_NUMBER; // ACCESS else if (protocol==PULSES_ACCESS_ISRM || protocol==PULSES_ACCESS_R9M || protocol==PULSES_ACCESS_R9M_LITE || protocol==PULSES_ACCESS_R9M_LITE_PRO) mask |= MASK_RX_NUMBER | MASK_ACCESS; if (isInternalModule(moduleIdx) && (protocol==PULSES_PXX_XJT_X16 || protocol==PULSES_PXX_XJT_D8 || protocol==PULSES_PXX_XJT_LR12) && HAS_EXTERNAL_ANTENNA(board) && generalSettings.antennaMode == GeneralSettings::ANTENNA_MODE_PER_MODEL) mask |= MASK_ANTENNA; if (protocol == PULSES_ACCESS_ISRM && module.channelsCount == 8) mask |= MASK_RF_RACING_MODE; break; case PULSES_LP45: case PULSES_DSM2: case PULSES_DSMX: mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER; module.channelsCount = 6; max_rx_num = 20; break; case PULSES_CROSSFIRE: mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER | MASK_BAUDRATE | MASK_CSRF_ARMING_MODE; module.channelsCount = 16; ui->telemetryBaudrate->setModel(ModuleData::telemetryBaudrateItemModel(protocol)); ui->telemetryBaudrate->setField(module.crsf.telemetryBaudrate); ui->crsfArmingMode->setCurrentIndex(module.crsf.crsfArmingMode); if (module.crsf.crsfArmingMode == ModuleData::CRSF_ARMING_MODE_SWITCH) { mask |= MASK_CSRF_ARMING_TRIGGER; ui->crsfArmingTrigger->setCurrentIndex(ui->crsfArmingTrigger->findData(RawSwitch(module.crsf.crsfArmingTrigger).toValue())); } break; case PULSES_GHOST: mask |= MASK_CHANNELS_RANGE | MASK_GHOST | MASK_BAUDRATE; module.channelsCount = 16; ui->telemetryBaudrate->setModel(ModuleData::telemetryBaudrateItemModel(protocol)); ui->telemetryBaudrate->setField(module.ghost.telemetryBaudrate); break; case PULSES_PPM: mask |= MASK_SUBTYPES | MASK_PPM_FIELDS | MASK_SBUSPPM_FIELDS| MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT; break; case PULSES_SBUS: module.channelsCount = 16; mask |= MASK_SUBTYPES | MASK_SBUSPPM_FIELDS| MASK_CHANNELS_RANGE; break; case PULSES_MULTIMODULE: mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER | MASK_MULTIMODULE | MASK_SUBTYPES; switch (module.multi.rfProtocol) { case MODULE_SUBTYPE_MULTI_OLRS: max_rx_num = MODULE_SUBTYPE_MULTI_OLRS_RXNUM; case MODULE_SUBTYPE_MULTI_BUGS: max_rx_num = MODULE_SUBTYPE_MULTI_BUGS_RXNUM; case MODULE_SUBTYPE_MULTI_BUGS_MINI: max_rx_num = MODULE_SUBTYPE_MULTI_BUGS_MINI_RXNUM; default: max_rx_num = 63; } if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) mask |= MASK_CHANNELS_COUNT; else module.channelsCount = 16; if (pdef.optionsstr != nullptr) { if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) mask |= MASK_MULTI_DSM_OPT; else if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_BAYANG) mask |= MASK_MULTI_BAYANG_OPT; else mask |= MASK_MULTIOPTION; } if (pdef.hasFailsafe || (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FRSKY && (module.subType == 0 || module.subType == 2 || module.subType > 3 ))) mask |= MASK_FAILSAFES; if (pdef.disableChannelMap) mask |= MASK_CHANNELMAP; break; case PULSES_FLYSKY_AFHDS3: mask |= MASK_RX_NUMBER; case PULSES_FLYSKY_AFHDS2A: mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES | MASK_AFHDS; break; case PULSES_LEMON_DSMP: mask |= MASK_CHANNELS_RANGE; break; default: break; } if (protocol != PULSES_MULTIMODULE && module.hasFailsafes(firmware)) mask |= MASK_FAILSAFES; } else if (IS_HORUS_OR_TARANIS(board)) { if (model->trainerMode == TRAINER_MODE_SLAVE_JACK) { mask |= MASK_PPM_FIELDS | MASK_SBUSPPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; } } else if (model->trainerMode != TRAINER_MODE_MASTER_JACK) { mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; } if (isExternalModule(moduleIdx)) ui->telemetryBaudrate->setVisible(mask & MASK_BAUDRATE); else ui->telemetryBaudrate->setVisible(false); ui->label_protocol->setVisible(mask & MASK_PROTOCOL); ui->protocol->setVisible(mask & MASK_PROTOCOL); ui->label_rxNumber->setVisible(mask & MASK_RX_NUMBER); ui->rxNumber->setVisible(mask & MASK_RX_NUMBER); ui->rxNumber->setMaximum(max_rx_num); ui->rxNumber->setValue(module.modelId); ui->label_channelsStart->setVisible(mask & MASK_CHANNELS_RANGE); ui->channelsStart->setVisible(mask & MASK_CHANNELS_RANGE); ui->channelsStart->setMaximum(33 - module.channelsCount); ui->channelsStart->setValue(module.channelsStart+1); ui->label_channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); ui->channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); ui->channelsCount->setEnabled(mask & MASK_CHANNELS_COUNT); ui->channelsCount->setMaximum(module.getMaxChannelCount()); ui->channelsCount->setValue(module.channelsCount); ui->channelsCount->setSingleStep(firmware->getCapability(HasPPMStart) ? 1 : 2); ui->label_crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); ui->crsfArmingMode->setVisible(mask & MASK_CSRF_ARMING_MODE); ui->crsfArmingTrigger->setVisible(mask & MASK_CSRF_ARMING_TRIGGER); // PPM settings fields ui->label_ppmPolarity->setVisible(mask & MASK_SBUSPPM_FIELDS); ui->ppmPolarity->setVisible(mask & MASK_SBUSPPM_FIELDS); ui->ppmPolarity->setCurrentIndex(module.ppm.pulsePol); ui->label_ppmOutputType->setVisible(mask & MASK_OPEN_DRAIN); ui->ppmOutputType->setVisible(mask & MASK_OPEN_DRAIN); ui->ppmOutputType->setCurrentIndex(module.ppm.outputType); ui->label_ppmDelay->setVisible(mask & MASK_PPM_FIELDS); ui->ppmDelay->setVisible(mask & MASK_PPM_FIELDS); ui->ppmDelay->setValue(module.ppm.delay); ui->label_ppmFrameLength->setVisible(mask & MASK_SBUSPPM_FIELDS); ui->ppmFrameLength->setVisible(mask & MASK_SBUSPPM_FIELDS); ui->ppmFrameLength->setMinimum(module.channelsCount * (model->extendedLimits ? 2.250 : 2)+3.5); ui->ppmFrameLength->setMaximum(firmware->getCapability(PPMFrameLength)); ui->ppmFrameLength->setValue(22.5 + ((double)module.ppm.frameLength) * 0.5); if (mask & MASK_ANTENNA) { if (module.pxx.antennaMode == GeneralSettings::ANTENNA_MODE_PER_MODEL) module.pxx.antennaMode = GeneralSettings::ANTENNA_MODE_INTERNAL; ui->antennaMode->setField(module.pxx.antennaMode, this); ui->antennaLabel->show(); ui->antennaMode->show(); } else { ui->antennaLabel->hide(); ui->antennaMode->hide(); } if (mask & MASK_RF_RACING_MODE) { ui->racingMode->show(); ui->racingMode->setChecked(module.access.racingMode); } else { ui->racingMode->hide(); } // R9M options ui->r9mPower->setVisible(mask & MASK_RF_POWER); ui->label_r9mPower->setVisible(mask & MASK_RF_POWER); ui->warning_r9mPower->setVisible((mask & MASK_R9M) && module.subType == MODULE_SUBTYPE_R9M_EU); ui->warning_r9mFlex->setVisible((mask & MASK_R9M) && module.subType > MODULE_SUBTYPE_R9M_EU); if (mask & MASK_RF_POWER) { const QSignalBlocker blocker(ui->r9mPower); ui->r9mPower->clear(); ui->r9mPower->addItems(ModuleData::powerValueStrings(protocol, module.subType, firmware)); ui->r9mPower->setCurrentIndex(mask & MASK_R9M ? module.pxx.power : module.afhds3.rfPower); } // module subtype ui->label_multiSubType->setVisible(mask & MASK_SUBTYPES); ui->multiSubType->setVisible(mask & MASK_SUBTYPES); if (mask & MASK_SUBTYPES) { unsigned numEntries = 2; // R9M FCC/EU unsigned i = 0; switch(protocol){ case PULSES_MULTIMODULE: numEntries = (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST ? 8 : pdef.numSubTypes()); break; case PULSES_PXX_R9M: if (firmware->getCapability(HasModuleR9MFlex)) i = 2; break; case PULSES_PPM: numEntries = PPM_NUM_SUBTYPES; break; case PULSES_SBUS: numEntries = SBUS_NUM_SUBTYPES; break; default: break; } numEntries += i; const QSignalBlocker blocker(ui->multiSubType); ui->multiSubType->clear(); for ( ; i < numEntries; i++) ui->multiSubType->addItem(module.subTypeToString(i), i); ui->multiSubType->setCurrentIndex(ui->multiSubType->findData(module.subType)); } // Multi settings fields ui->label_multiProtocol->setVisible(mask & MASK_MULTIMODULE); ui->multiProtocol->setVisible(mask & MASK_MULTIMODULE); ui->label_option->setVisible(mask & MASK_MULTIOPTION); ui->optionValue->setVisible(mask & MASK_MULTIOPTION); ui->lblChkOption->setVisible(mask & MASK_MULTI_DSM_OPT); ui->chkOption->setVisible(mask & MASK_MULTI_DSM_OPT); ui->lblCboOption->setVisible(mask & MASK_MULTI_DSM_OPT || mask & MASK_MULTI_BAYANG_OPT); ui->cboOption->setVisible(mask & MASK_MULTI_DSM_OPT || mask & MASK_MULTI_BAYANG_OPT); ui->disableTelem->setVisible(mask & MASK_MULTIMODULE); ui->disableChMap->setVisible(mask & MASK_CHANNELMAP); ui->lowPower->setVisible(mask & MASK_MULTIMODULE); ui->autoBind->setVisible(mask & MASK_MULTIMODULE); if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) ui->autoBind->setVisible(false); else ui->autoBind->setText(tr("Bind on channel")); if (mask & MASK_MULTIMODULE) { ui->multiProtocol->setCurrentIndex(ui->multiProtocol->findData(module.multi.rfProtocol)); ui->disableTelem->setChecked(module.multi.disableTelemetry); ui->disableChMap->setChecked(module.multi.disableMapping); ui->autoBind->setChecked(module.multi.autoBindMode); ui->lowPower->setChecked(module.multi.lowPowerMode); } if (mask & MASK_MULTIOPTION) { ui->label_option->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); int8_t min, max; getMultiOptionValues(module.multi.rfProtocol, min, max); auto lineEdit = ui->optionValue->findChild(); if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { ui->optionValue->setMinimum(50 + 5 * min); ui->optionValue->setMaximum(50 + 5 * max); ui->optionValue->setSingleStep(5); ui->optionValue->setValue(50 + 5 * module.multi.optionValue); if (lineEdit) { lineEdit->setReadOnly(true); lineEdit->setFocusPolicy(Qt::NoFocus); } } else { ui->optionValue->setMinimum(min); ui->optionValue->setMaximum(max); ui->optionValue->setSingleStep(1); ui->optionValue->setValue(module.multi.optionValue); if (lineEdit) { lineEdit->setReadOnly(false); lineEdit->setFocusPolicy(Qt::WheelFocus); } } } if (mask & MASK_MULTI_DSM_OPT) { ui->lblChkOption->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); ui->chkOption->setChecked(Helpers::getBitmappedValue(module.multi.optionValue, 0)); ui->lblCboOption->setText(qApp->translate("Multiprotocols", "Servo update rate")); ui->cboOption->clear(); ui->cboOption->addItems({ DSM2_OPTION_SERVOFREQ_NAMES }); ui->cboOption->setSizeAdjustPolicy(QComboBox::AdjustToContents); ui->cboOption->setCurrentIndex(Helpers::getBitmappedValue(module.multi.optionValue, 1)); } if (mask & MASK_MULTI_BAYANG_OPT) { ui->lblCboOption->setText(qApp->translate("Multiprotocols", qPrintable(pdef.optionsstr))); ui->cboOption->clear(); ui->cboOption->addItems({ BAYANG_OPTION_TELEMETRY_NAMES }); ui->cboOption->setSizeAdjustPolicy(QComboBox::AdjustToContents); ui->cboOption->setCurrentIndex(Helpers::getBitmappedValue(module.multi.optionValue, 1)); } // Ghost settings fields ui->raw12bits->setVisible(mask & MASK_GHOST); if (mask & MASK_GHOST) { ui->raw12bits->setChecked(module.ghost.raw12bits); } if (mask & MASK_ACCESS) { ui->rx1->setText(module.access.receiverName[0]); ui->rx2->setText(module.access.receiverName[1]); ui->rx3->setText(module.access.receiverName[2]); } ui->registrationIdLabel->setVisible(mask & MASK_ACCESS); ui->registrationId->setVisible(mask & MASK_ACCESS); ui->rx1Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); ui->clearRx1->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); ui->rx1->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 0))); ui->rx2Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); ui->clearRx2->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); ui->rx2->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 1))); ui->rx3Label->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); ui->clearRx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); ui->rx3->setVisible((mask & MASK_ACCESS) && (module.access.receivers & (1 << 2))); // AFHFS if (mask & MASK_AFHDS) { if (protocol == PULSES_FLYSKY_AFHDS2A) { ui->label_afhds->setText(tr("Options")); ui->cboAfhdsOpt1->setModel(ModuleData::afhds2aMode1ItemModel()); ui->cboAfhdsOpt1->setCurrentIndex(Helpers::getBitmappedValue(module.flysky.mode, 1)); ui->cboAfhdsOpt2->setModel(ModuleData::afhds2aMode2ItemModel()); ui->cboAfhdsOpt2->setCurrentIndex(Helpers::getBitmappedValue(module.flysky.mode, 0)); } else { ui->label_afhds->setText(tr("Type")); ui->cboAfhdsOpt1->setModel(ModuleData::afhds3PhyModeItemModel()); ui->cboAfhdsOpt1->setCurrentIndex(ui->cboAfhdsOpt1->findData(module.afhds3.phyMode)); ui->cboAfhdsOpt2->setModel(ModuleData::afhds3EmiItemModel()); ui->cboAfhdsOpt2->setCurrentIndex(ui->cboAfhdsOpt2->findData(module.afhds3.emi)); } } ui->label_afhds->setVisible(mask & MASK_AFHDS); ui->cboAfhdsOpt1->setVisible(mask & MASK_AFHDS); ui->cboAfhdsOpt2->setVisible(mask & MASK_AFHDS); // Failsafes ui->label_failsafeMode->setVisible(mask & MASK_FAILSAFES); ui->failsafeMode->setVisible(mask & MASK_FAILSAFES); if ((mask & MASK_FAILSAFES) && module.failsafeMode == FAILSAFE_CUSTOM) { if (ui->failsafesGroupBox->isHidden()) { setupFailsafes(); ui->failsafesGroupBox->setVisible(true); } } else { ui->failsafesGroupBox->setVisible(false); } if (mask & MASK_FAILSAFES) { ui->failsafeMode->setCurrentIndex(module.failsafeMode); if (firmware->getCapability(ChannelsName)) { int chan; QString name; QMapIterator i(failsafeGroupsMap); while (i.hasNext()) { i.next(); chan = i.key(); name = QString(model->limitData[chan + module.channelsStart].name).trimmed(); if (name.isEmpty()) i.value().label->setText(QString::number(chan + 1)); else i.value().label->setText(name); } } } ui->label_rxFreq->setVisible((mask & MASK_RX_FREQ)); ui->rxFreq->setVisible((mask & MASK_RX_FREQ)); lock = false; } void ModulePanel::onProtocolChanged(int index) { if (!lock) { module.channelsCount = module.getMaxChannelCount(); update(); if (module.protocol == PULSES_GHOST || module.protocol == PULSES_CROSSFIRE) { if (Boards::getCapability(getCurrentFirmware()->getBoard(), Board::SportMaxBaudRate) < 400000) { // default to 115k ui->telemetryBaudrate->setCurrentIndex(0); } else { // default to 400k ui->telemetryBaudrate->setCurrentIndex(1); } } else if (module.protocol == PULSES_FLYSKY_AFHDS2A) { module.flysky.setDefault(); } else if (module.protocol == PULSES_FLYSKY_AFHDS3) { module.afhds3.setDefault(); } emit protocolChanged(); emit updateItemModels(); emit modified(); } } void ModulePanel::on_ppmPolarity_currentIndexChanged(int index) { if (!lock && module.ppm.pulsePol != (bool)index) { module.ppm.pulsePol = index; emit modified(); } } void ModulePanel::on_r9mPower_currentIndexChanged(int index) { if (!lock) { if (module.protocol == PULSES_FLYSKY_AFHDS2A && module.flysky.rfPower != (unsigned int)index) { module.flysky.rfPower = index; emit modified(); } else if (module.protocol == PULSES_FLYSKY_AFHDS3 && module.afhds3.rfPower != (unsigned int)index) { module.afhds3.rfPower = index; emit modified(); } else if(module.pxx.power != (unsigned int)index) module.pxx.power = index; } } void ModulePanel::on_ppmOutputType_currentIndexChanged(int index) { if (!lock && module.ppm.outputType != (bool)index) { module.ppm.outputType = index; emit modified(); } } void ModulePanel::on_channelsCount_editingFinished() { if (!lock && module.channelsCount != ui->channelsCount->value()) { module.channelsCount = ui->channelsCount->value(); emit channelsRangeChanged(); update(); emit modified(); } } void ModulePanel::on_channelsStart_editingFinished() { if (!lock && module.channelsStart != (unsigned)ui->channelsStart->value() - 1) { module.channelsStart = (unsigned)ui->channelsStart->value() - 1; emit channelsRangeChanged(); update(); emit modified(); } } void ModulePanel::on_ppmDelay_editingFinished() { if (!lock && module.ppm.delay != ui->ppmDelay->value()) { // TODO only accept valid values module.ppm.delay = ui->ppmDelay->value(); emit modified(); } } void ModulePanel::on_rxNumber_editingFinished() { if (module.modelId != (unsigned)ui->rxNumber->value()) { module.modelId = (unsigned)ui->rxNumber->value(); emit modified(); } } void ModulePanel::on_ppmFrameLength_editingFinished() { int val = (ui->ppmFrameLength->value() - 22.5) / 0.5; if (module.ppm.frameLength != val) { module.ppm.frameLength = val; emit modified(); } } void ModulePanel::on_failsafeMode_currentIndexChanged(int value) { if (!lock && module.failsafeMode != (unsigned)value) { module.failsafeMode = value; update(); emit modified(); } } void ModulePanel::onMultiProtocolChanged(int index) { int rfProtocol = ui->multiProtocol->itemData(index).toInt(); if (!lock && module.multi.rfProtocol != (unsigned)rfProtocol) { lock=true; module.multi.rfProtocol = (unsigned int)rfProtocol; module.multi.optionValue = 0; unsigned int maxSubTypes = multiProtocols.getProtocol(index).numSubTypes(); if (rfProtocol > MODULE_SUBTYPE_MULTI_LAST) maxSubTypes = 8; module.subType = std::min(module.subType, maxSubTypes - 1); module.channelsCount = module.getMaxChannelCount(); update(); emit updateItemModels(); emit modified(); lock = false; } } void ModulePanel::on_optionValue_valueChanged(int value) { if (!lock) { if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { module.multi.optionValue = (value - 50) / 5; } else { module.multi.optionValue = value; } emit modified(); } } void ModulePanel::on_chkOption_stateChanged(int state) { if (!lock) { if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) { unsigned int opt = (unsigned int)module.multi.optionValue; if (Helpers::getBitmappedValue(opt, 0) != (state == Qt::Checked)) { Helpers::setBitmappedValue(opt, (state == Qt::Checked), 0); module.multi.optionValue = opt; emit modified(); } } else if (module.multi.optionValue != (state == Qt::Checked)) { module.multi.optionValue = (state == Qt::Checked); emit modified(); } } } void ModulePanel::on_cboOption_currentIndexChanged(int value) { if (!lock && value >= 0) { if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) { unsigned int opt = (unsigned int)module.multi.optionValue; if (Helpers::getBitmappedValue(opt, 1) != (unsigned int)value) { Helpers::setBitmappedValue(opt, value, 1); module.multi.optionValue = opt; emit modified(); } } else if (module.multi.optionValue != value) { module.multi.optionValue = value; emit modified(); } } } void ModulePanel::onSubTypeChanged() { const unsigned type = ui->multiSubType->currentData().toUInt(); if (!lock && module.subType != type) { lock=true; module.subType = type; emit modified(); lock = false; } } void ModulePanel::onRfFreqChanged(int freq) { // TODO fix for AFHDS2A //if (module.afhds3.rxFreq != (unsigned int)freq) { // module.afhds3.rxFreq = (unsigned int)freq; // emit modified(); //} } void ModulePanel::on_disableTelem_stateChanged(int state) { module.multi.disableTelemetry = (state == Qt::Checked); } void ModulePanel::on_disableChMap_stateChanged(int state) { module.multi.disableMapping = (state == Qt::Checked); } void ModulePanel::on_raw12bits_stateChanged(int state) { module.ghost.raw12bits = (state == Qt::Checked); } void ModulePanel::on_racingMode_stateChanged(int state) { module.access.racingMode = (state == Qt::Checked); } void ModulePanel::on_autoBind_stateChanged(int state) { module.multi.autoBindMode = (state == Qt::Checked); } void ModulePanel::on_lowPower_stateChanged(int state) { module.multi.lowPowerMode = (state == Qt::Checked); } void ModulePanel::onFailsafeModified(unsigned channel) { updateFailsafeUI(channel, FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC); } void ModulePanel::updateFailsafeUI(unsigned channel, quint8 updtSb) { int value = model->limitData[channel].failsafe; double pctVal = divRoundClosest(value * 1000, 1024) / 10.0; if (failsafeGroupsMap.contains(channel)) { const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); bool disable = (value == FAILSAFE_CHANNEL_HOLD || value == FAILSAFE_CHANNEL_NOPULSE); if (grp.combo) { grp.combo->setCurrentIndex(grp.combo->findData(disable ? value : 0)); } if ((updtSb & FAILSAFE_DISPLAY_PERCENT) && grp.sbPercent) { grp.sbPercent->blockSignals(true); grp.sbPercent->setValue(pctVal); grp.sbPercent->blockSignals(false); } if ((updtSb & FAILSAFE_DISPLAY_USEC) && grp.sbUsec) { grp.sbUsec->blockSignals(true); grp.sbUsec->setValue(value); grp.sbUsec->blockSignals(false); } } } // updtSb (update spin box(es)): 0=none or bitmask of FailsafeValueDisplayTypes void ModulePanel::setChannelFailsafeValue(const int channel, const int value, quint8 updtSb) { if (channel < 0 || channel >= CPN_MAX_CHNOUT) return; model->limitData[channel].failsafe = value; updateFailsafeUI(channel, updtSb); if (!lock) { emit failsafeModified(channel); emit modified(); } } void ModulePanel::onFailsafeUsecChanged(int value) { if (!sender()) return; bool ok = false; int channel = sender()->property("index").toInt(&ok); if (ok) setChannelFailsafeValue(channel, value, FAILSAFE_DISPLAY_PERCENT); } void ModulePanel::onFailsafePercentChanged(double value) { if (!sender()) return; bool ok = false; int channel = sender()->property("index").toInt(&ok); if (ok) setChannelFailsafeValue(channel, divRoundClosest(int(value * 1024), 100), FAILSAFE_DISPLAY_USEC); } void ModulePanel::onFailsafeComboIndexChanged(int index) { if (!sender()) return; QComboBox * cb = qobject_cast(sender()); if (cb && !lock) { lock = true; bool ok = false; int channel = sender()->property("index").toInt(&ok); if (ok) { model->limitData[channel].failsafe = cb->itemData(index).toInt(); updateFailsafe(channel); emit failsafeModified(channel); emit modified(); } lock = false; } } void ModulePanel::onFailsafesDisplayValueTypeChanged(int type) { if (failsafesValueDisplayType != type) { failsafesValueDisplayType = type; foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { if (grp.sbPercent) grp.sbPercent->setVisible(type == FAILSAFE_DISPLAY_PERCENT); if (grp.sbUsec) grp.sbUsec->setVisible(type == FAILSAFE_DISPLAY_USEC); } } } void ModulePanel::onExtendedLimitsToggled() { double channelMaxPct = double(model->getChannelsMax()); int channelMaxUs = 512 * channelMaxPct / 100 * 2; foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { if (grp.sbPercent) grp.sbPercent->setRange(-channelMaxPct, channelMaxPct); if (grp.sbUsec) grp.sbUsec->setRange(-channelMaxUs, channelMaxUs); } } void ModulePanel::updateFailsafe(unsigned channel) { if (channel >= CPN_MAX_CHNOUT || !failsafeGroupsMap.contains(channel)) return; const int failsafeValue = model->limitData[channel].failsafe; const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); const bool valDisable = (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE); if (grp.combo) grp.combo->setCurrentIndex(grp.combo->findData(valDisable ? failsafeValue : 0)); if (grp.sbPercent) grp.sbPercent->setDisabled(valDisable); if (grp.sbUsec) grp.sbUsec->setDisabled(valDisable); if (!valDisable) setChannelFailsafeValue(channel, failsafeValue, FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC); } void ModulePanel::onClearAccessRxClicked() { QPushButton *button = qobject_cast(sender()); if (button == ui->clearRx1) { module.access.receivers &= ~(1 << 0); ui->rx1->clear(); update(); emit modified(); } else if (button == ui->clearRx2) { module.access.receivers &= ~(1 << 1); ui->rx2->clear(); update(); emit modified(); } else if (button == ui->clearRx3) { module.access.receivers &= ~(1 << 2); ui->rx3->clear(); update(); emit modified(); } } void ModulePanel::updateTrainerModeItemModel() { if (isTrainerModule(moduleIdx)) { if (trainerModeItemModel) delete trainerModeItemModel; trainerModeItemModel = new FilteredItemModel(model->trainerModeItemModel(generalSettings, firmware)); ui->trainerMode->setModel(trainerModeItemModel); ui->trainerMode->updateValue(); } }