diff --git a/CMakeLists.txt b/CMakeLists.txt index 870252da4..ccd04d375 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "0") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) -set(SDCARD_REVISION "0036") +set(SDCARD_REVISION "0037") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) diff --git a/CREDITS.txt b/CREDITS.txt index 3b31f5604..dc9dc238d 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2237,3 +2237,33 @@ Glenn Noonan Michael Jakob Dean Berg David Hankins +Gary Cripps +Devin Crumb +Daniel Naude +James Miller +Pascal Cayol +eDefine +Mario Sette +Hans-Georg Böhm +Robert Oats +Helmut Suhrbier +Johannes Vorster +Deon du Rand +Stephen Hill-Harriss +Regi Pradeeswaran +Gary Busler +Stefano Boggia +Vernon Martin +Alexander W Wilson Jr +Adam Bowersock +Gary Burrows +Mark Gaskievicz +James Miller +Donald Taylor +Paul Makepeace +Thomas Fowler +Walter Szempruch +Charles Starck +Fabian Mächler +David Van Dyne +Karim el Mawardi diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index a201d50ed..5a44d246e 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -20,6 +20,7 @@ #include "boards.h" #include "macros.h" +#include "compounditemmodels.h" // TODO remove all those constants // Update: These are now all only used within this class. @@ -374,7 +375,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case MultiposPotsPositions: return IS_HORUS_OR_TARANIS(board) ? 6 : 0; - case Switches: + case Board::Switches: if (IS_TARANIS_X9E(board)) return 18; else if (board == Board::BOARD_TARANIS_X9LITE) @@ -410,11 +411,11 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else if (IS_HORUS_X12S(board)) return 8; else - return getCapability(board, Switches); + return getCapability(board, Board::Switches); case SwitchPositions: if (IS_HORUS_OR_TARANIS(board)) - return getCapability(board, Switches) * 3; + return getCapability(board, Board::Switches) * 3; else return 9; @@ -425,10 +426,14 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 4; case NumTrimSwitches: - return getCapability(board, NumTrims) * 2; - } + return getCapability(board, Board::NumTrims) * 2; - return 0; + case HasRTC: + return IS_STM32(board) ? true : false; + + default: + return 0; + } } QString Boards::getAxisName(int index) @@ -444,7 +449,7 @@ QString Boards::getAxisName(int index) if (index < (int)DIM(axes)) return axes[index]; else - return tr("Unknown"); + return CPN_STR_UNKNOWN_ITEM; } QString Boards::getAnalogInputName(Board::Type board, int index) @@ -595,6 +600,96 @@ QString Boards::getBoardName(Board::Type board) case BOARD_RADIOMASTER_T8: return "Radiomaster T8"; default: - return tr("Unknown"); + return CPN_STR_UNKNOWN_ITEM; } } + +// static +QString Boards::potTypeToString(int value) +{ + switch(value) { + case POT_NONE: + return tr("None"); + case POT_WITH_DETENT: + return tr("Pot with detent"); + case POT_MULTIPOS_SWITCH: + return tr("Multi pos switch"); + case POT_WITHOUT_DETENT: + return tr("Pot without detent"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + + +// static +QString Boards::sliderTypeToString(int value) +{ + switch(value) { + case SLIDER_NONE: + return tr("None"); + case SLIDER_WITH_DETENT: + return tr("Slider with detent"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString Boards::switchTypeToString(int value) +{ + switch(value) { + case SWITCH_NOT_AVAILABLE: + return tr("None"); + case SWITCH_TOGGLE: + return tr("2 Positions Toggle"); + case SWITCH_2POS: + return tr("2 Positions"); + case SWITCH_3POS: + return tr("3 Positions"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +AbstractStaticItemModel * Boards::potTypeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_BOARDS_POT_TYPE); + + for (int i = 0; i < POT_TYPE_COUNT; i++) { + mdl->appendToItemList(potTypeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * Boards::sliderTypeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_BOARDS_SLIDER_TYPE); + + for (int i = 0; i < SLIDER_TYPE_COUNT; i++) { + mdl->appendToItemList(sliderTypeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * Boards::switchTypeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_BOARDS_SWITCH_TYPE); + + for (int i = 0; i < SWITCH_TYPE_COUNT; i++) { + mdl->appendToItemList(switchTypeToString(i), i, true, 0, (i < SWITCH_3POS ? SwitchTypeFlag2Pos : SwitchTypeFlag3Pos)); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index e55aeae37..33ac887d9 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -18,13 +18,19 @@ * GNU General Public License for more details. */ -#ifndef _BOARDS_H_ -#define _BOARDS_H_ +#pragma once #include #include #include +class AbstractStaticItemModel; + +// identiying names of static abstract item models +constexpr char AIM_BOARDS_POT_TYPE[] {"boards.pottype"}; +constexpr char AIM_BOARDS_SLIDER_TYPE[] {"boards.slidertype"}; +constexpr char AIM_BOARDS_SWITCH_TYPE[] {"boards.switchtype"}; + // TODO create a Board class with all these functions namespace Board { @@ -64,13 +70,15 @@ namespace Board { POT_NONE, POT_WITH_DETENT, POT_MULTIPOS_SWITCH, - POT_WITHOUT_DETENT + POT_WITHOUT_DETENT, + POT_TYPE_COUNT }; enum SliderType { SLIDER_NONE, - SLIDER_WITH_DETENT + SLIDER_WITH_DETENT, + SLIDER_TYPE_COUNT }; enum SwitchType @@ -78,7 +86,8 @@ namespace Board { SWITCH_NOT_AVAILABLE, SWITCH_TOGGLE, SWITCH_2POS, - SWITCH_3POS + SWITCH_3POS, + SWITCH_TYPE_COUNT }; enum StickAxes { @@ -130,7 +139,8 @@ namespace Board { SwitchPositions, FactoryInstalledSwitches, NumTrims, - NumTrimSwitches + NumTrimSwitches, + HasRTC }; struct SwitchInfo @@ -149,6 +159,13 @@ namespace Board { unsigned int position; }; + enum SwitchTypeMasks { + SwitchTypeFlag2Pos = 0x01, + SwitchTypeFlag3Pos = 0x02, + SwitchTypeContext2Pos = SwitchTypeFlag2Pos, + SwitchTypeContext3Pos = SwitchTypeFlag2Pos | SwitchTypeFlag3Pos + }; + } class Boards @@ -182,6 +199,12 @@ class Boards static QString getAnalogInputName(Board::Type board, int index); static bool isBoardCompatible(Board::Type board1, Board::Type board2); static QString getBoardName(Board::Type board); + static QString potTypeToString(int value); + static QString sliderTypeToString(int value); + static QString switchTypeToString(int value); + static AbstractStaticItemModel * potTypeItemModel(); + static AbstractStaticItemModel * sliderTypeItemModel(); + static AbstractStaticItemModel * switchTypeItemModel(); protected: @@ -361,5 +384,3 @@ inline bool IS_ACCESS_RADIO(Board::Type board, const QString & id) return (IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || board == Board::BOARD_TARANIS_X9DP_2019 || board == Board::BOARD_X10_EXPRESS || IS_TARANIS_X7_ACCESS(board) || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess"))); } - -#endif // _BOARDS_H_ diff --git a/companion/src/firmwares/eeprominterface.h b/companion/src/firmwares/eeprominterface.h index 6cd62f287..00980339c 100644 --- a/companion/src/firmwares/eeprominterface.h +++ b/companion/src/firmwares/eeprominterface.h @@ -134,7 +134,6 @@ enum Capability { VirtualInputs, InputsLength, TrainerInputs, - RtcTime, SportTelemetry, LuaScripts, LuaInputsPerScript, @@ -153,7 +152,13 @@ enum Capability { HasSwitchableJack, HasSportConnector, PwrButtonPress, - Sensors + Sensors, + HasAuxSerialMode, + HasAux2SerialMode, + HasBluetooth, + HasAntennaChoice, + HasADCJitterFilter, + HasTelemetryBaudrate }; class EEPROMInterface diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index f9df851b2..d1575d3e2 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -23,6 +23,7 @@ #include "appdata.h" #include "eeprominterface.h" #include "radiodataconversionstate.h" +#include "compounditemmodels.h" const uint8_t chout_ar[] = { // First number is 0..23 -> template setup, Second is relevant channel out 1,2,3,4 , 1,2,4,3 , 1,3,2,4 , 1,3,4,2 , 1,4,2,3 , 1,4,3,2, @@ -36,7 +37,7 @@ bool GeneralSettings::switchPositionAllowedTaranis(int index) const if (index == 0) return true; - div_t qr = div(abs(index)-1, 3); + div_t qr = div(abs(index) - 1, 3); if (index < 0 && switchConfig[qr.quot] != Board::SWITCH_3POS) return false; @@ -53,14 +54,14 @@ bool GeneralSettings::switchSourceAllowedTaranis(int index) const bool GeneralSettings::isPotAvailable(int index) const { - if (index < 0 || index > getBoardCapability(getCurrentBoard(), Board::Pots)) + if (index < 0 || index > Boards::getCapability(getCurrentBoard(), Board::Pots)) return false; return potConfig[index] != Board::POT_NONE; } bool GeneralSettings::isSliderAvailable(int index) const { - if (index < 0 || index > getBoardCapability(getCurrentBoard(), Board::Sliders)) + if (index < 0 || index > Boards::getCapability(getCurrentBoard(), Board::Sliders)) return false; return sliderConfig[index] != Board::SLIDER_NONE; } @@ -71,7 +72,7 @@ GeneralSettings::GeneralSettings() contrast = 25; - for (int i=0; i < CPN_MAX_ANALOGS; ++i) { + for (int i = 0; i < CPN_MAX_ANALOGS; ++i) { calibMid[i] = 0x200; calibSpanNeg[i] = 0x180; calibSpanPos[i] = 0x180; @@ -128,7 +129,7 @@ GeneralSettings::GeneralSettings() } for (uint8_t i = 0; i < 4; i++) { - trainer.mix[i].mode = 2; // replace (:=) + trainer.mix[i].mode = TrainerMix::TRN_MIX_MODE_SUBST; trainer.mix[i].src = i; trainer.mix[i].weight = 100; } @@ -387,3 +388,265 @@ void GeneralSettings::convert(RadioDataConversionState & cstate) } } + +QString GeneralSettings::antennaModeToString() const +{ + return antennaModeToString(antennaMode); +} + +QString GeneralSettings::bluetoothModeToString() const +{ + return bluetoothModeToString(bluetoothMode); +} + +QString GeneralSettings::auxSerialModeToString() const +{ + return auxSerialModeToString(auxSerialMode); +} + +QString GeneralSettings::telemetryBaudrateToString() const +{ + return telemetryBaudrateToString(telemetryBaudrate); +} + +// static +QString GeneralSettings::antennaModeToString(int value) +{ + Board::Type board = getCurrentBoard(); + + switch(value) { + case ANTENNA_MODE_INTERNAL: + return tr("Internal"); + case ANTENNA_MODE_ASK: + return tr("Ask"); + case ANTENNA_MODE_PER_MODEL: + return tr("Per model"); + case ANTENNA_MODE_EXTERNAL: + // case ANTENNA_MODE_INTERNAL_EXTERNAL: + return IS_HORUS_X12S(board) ? tr("Internal + External") : tr("External"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString GeneralSettings::bluetoothModeToString(int value) +{ + Board::Type board = getCurrentBoard(); + + switch(value) { + case BLUETOOTH_MODE_OFF: + return tr("OFF"); + case BLUETOOTH_MODE_ENABLED: + // case BLUETOOTH_MODE_TELEMETRY: + return IS_TARANIS_X9E(board) ? tr("Enabled") : tr("Telemetry"); + case BLUETOOTH_MODE_TRAINER: + return tr("Trainer"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString GeneralSettings::auxSerialModeToString(int value) +{ + switch(value) { + case AUX_SERIAL_OFF: + return tr("OFF"); + case AUX_SERIAL_TELE_MIRROR: + return tr("Telemetry Mirror"); + case AUX_SERIAL_TELE_IN: + return tr("Telemetry In"); + case AUX_SERIAL_SBUS_TRAINER: + return tr("SBUS Trainer"); + case AUX_SERIAL_LUA: + return tr("LUA"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString GeneralSettings::telemetryBaudrateToString(int value) +{ + switch(value) { + case 0: + return "400000"; + case 1: + return "115200"; + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +FieldRange GeneralSettings::getPPM_MultiplierRange() +{ + FieldRange result; + + result.min = 0; + result.max = 5; + result.decimals = 1; + result.step = 0.1; + result.offset = 10; + + return result; +} + +// static +FieldRange GeneralSettings::getTxVoltageCalibrationRange() +{ + FieldRange result; + + result.decimals = 1; + result.max = 9.9; + result.min = -result.max; + result.step = 0.1; + result.unit = tr("v"); + + return result; +} + +// static +FieldRange GeneralSettings::getTxCurrentCalibration() +{ + FieldRange result; + + result.max = 49; + result.min = -result.max; + result.unit = tr("mA"); + + return result; +} + +// static +AbstractStaticItemModel * GeneralSettings::antennaModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_ANTENNAMODE); + + for (int i = ANTENNA_MODE_FIRST; i <= ANTENNA_MODE_LAST; i++) { + mdl->appendToItemList(antennaModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * GeneralSettings::bluetoothModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_BLUETOOTHMODE); + + for (int i = 0; i < BLUETOOTH_MODE_COUNT; i++) { + mdl->appendToItemList(bluetoothModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * GeneralSettings::auxSerialModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_AUXSERIALMODE); + + for (int i = 0; i < AUX_SERIAL_COUNT; i++) { + mdl->appendToItemList(auxSerialModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * GeneralSettings::telemetryBaudrateItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_GS_TELEMETRYBAUDRATE); + + for (int i = 0; i <= 1; i++) { + mdl->appendToItemList(telemetryBaudrateToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +/* + TrainerMix +*/ + +QString TrainerMix::modeToString() const +{ + return modeToString(mode); +} + +QString TrainerMix::srcToString() const +{ + return srcToString(src); +} + +// static +FieldRange TrainerMix::getWeightRange() +{ + FieldRange result; + + result.decimals = 0; + result.max = 125; + result.min = -result.max; + result.step = 1; + + return result; +} + +// static +QString TrainerMix::modeToString(int value) +{ + switch(value) { + case TRN_MIX_MODE_OFF: + return tr("OFF"); + case TRN_MIX_MODE_ADD: + return tr("+= (Sum)"); + case TRN_MIX_MODE_SUBST: + return tr(":= (Replace)"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString TrainerMix::srcToString(int value) +{ + return tr("CH%1").arg(value + 1); +} + +// static +AbstractStaticItemModel * TrainerMix::modeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_TRAINERMIX_MODE); + + for (int i = 0; i < TRN_MIX_MODE_COUNT; i++) { + mdl->appendToItemList(modeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * TrainerMix::srcItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName(AIM_TRAINERMIX_SRC); + + for (int i = 0; i < Boards::getCapability(getCurrentBoard(), Board::Sticks); i++) { + mdl->appendToItemList(srcToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/generalsettings.h b/companion/src/firmwares/generalsettings.h index f7b28931d..466d630af 100644 --- a/companion/src/firmwares/generalsettings.h +++ b/companion/src/firmwares/generalsettings.h @@ -18,13 +18,13 @@ * GNU General Public License for more details. */ -#ifndef GENERALSETTINGS_H -#define GENERALSETTINGS_H +#pragma once #include "boards.h" #include "constants.h" #include "customfunctiondata.h" #include "rawsource.h" +#include "datahelpers.h" #include @@ -32,6 +32,15 @@ class Firmware; class ModelData; class GeneralSettings; class RadioDataConversionState; +class AbstractStaticItemModel; + +// identiying names of static abstract item models +constexpr char AIM_GS_ANTENNAMODE[] {"gs.antennamode"}; +constexpr char AIM_GS_BLUETOOTHMODE[] {"gs.bluetoothmode"}; +constexpr char AIM_GS_AUXSERIALMODE[] {"gs.auxserialmode"}; +constexpr char AIM_GS_TELEMETRYBAUDRATE[] {"gs.telemetrybaudrate"}; +constexpr char AIM_TRAINERMIX_MODE[] {"trainermix.mode"}; +constexpr char AIM_TRAINERMIX_SRC[] {"trainermix.src"}; enum UartModes { UART_MODE_NONE, @@ -42,23 +51,52 @@ enum UartModes { }; class TrainerMix { + Q_DECLARE_TR_FUNCTIONS(TrainerMix) + public: + enum TrainerMixModes { + TRN_MIX_MODE_OFF, + TRN_MIX_MODE_ADD, + TRN_MIX_MODE_SUBST, + TRN_MIX_MODE_COUNT + }; + TrainerMix() { clear(); } + unsigned int src; // 0-7 = ch1-8 RawSwitch swtch; int weight; unsigned int mode; // off, add-mode, subst-mode + void clear() { memset(reinterpret_cast(this), 0, sizeof(TrainerMix)); } + QString modeToString() const; + QString srcToString() const; + + static FieldRange getWeightRange(); + static QString modeToString(int value); + static QString srcToString(int value); + static AbstractStaticItemModel * modeItemModel(); + static AbstractStaticItemModel * srcItemModel(); }; class TrainerData { public: TrainerData() { clear(); } - int calib[4]; - TrainerMix mix[4]; + + int calib[4]; + TrainerMix mix[4]; + void clear() { memset(reinterpret_cast(this), 0, sizeof(TrainerData)); } }; +constexpr int CURR_MODEL_FILENAME_LEN {16}; +constexpr int OWNER_NAME_LEN {10}; +constexpr int BLUETOOTH_NAME_LEN {10}; +constexpr int TTS_LANGUAGE_LEN {2}; +constexpr int HARDWARE_NAME_LEN {3}; +constexpr int THEME_NAME_LEN {8}; +constexpr int REGISTRATION_ID_LEN {8}; + class GeneralSettings { Q_DECLARE_TR_FUNCTIONS(GeneralSettings) @@ -71,6 +109,36 @@ class GeneralSettings { BEEPER_ALL = 1 }; + enum BluetoothMode { + BLUETOOTH_MODE_OFF, + BLUETOOTH_MODE_ENABLED, + BLUETOOTH_MODE_TELEMETRY = BLUETOOTH_MODE_ENABLED, + BLUETOOTH_MODE_TRAINER, + BLUETOOTH_MODE_COUNT + }; + + enum AntennaMode { + ANTENNA_MODE_INTERNAL = -2, + ANTENNA_MODE_FIRST = ANTENNA_MODE_INTERNAL, + ANTENNA_MODE_ASK, + ANTENNA_MODE_PER_MODEL, + ANTENNA_MODE_EXTERNAL, + ANTENNA_MODE_INTERNAL_EXTERNAL = ANTENNA_MODE_EXTERNAL, + ANTENNA_MODE_LAST = ANTENNA_MODE_INTERNAL_EXTERNAL + }; + + enum AuxSerialMode { + AUX_SERIAL_OFF, + AUX_SERIAL_TELE_MIRROR, + AUX_SERIAL_TELE_IN, + AUX_SERIAL_SBUS_TRAINER, + AUX_SERIAL_LUA, + AUX_SERIAL_COUNT + }; + + enum TelemetryBaudrate { + }; + GeneralSettings(); void convert(RadioDataConversionState & cstate); @@ -81,61 +149,61 @@ class GeneralSettings { unsigned int version; unsigned int variant; - int calibMid[CPN_MAX_ANALOGS]; - int calibSpanNeg[CPN_MAX_ANALOGS]; - int calibSpanPos[CPN_MAX_ANALOGS]; - unsigned int currModelIndex; - char currModelFilename[16+1]; - unsigned int contrast; - unsigned int vBatWarn; - int txVoltageCalibration; - int txCurrentCalibration; - int vBatMin; - int vBatMax; - int backlightMode; + int calibMid[CPN_MAX_ANALOGS]; + int calibSpanNeg[CPN_MAX_ANALOGS]; + int calibSpanPos[CPN_MAX_ANALOGS]; + unsigned int currModelIndex; + char currModelFilename[CURR_MODEL_FILENAME_LEN + 1]; + unsigned int contrast; + unsigned int vBatWarn; + int txVoltageCalibration; + int txCurrentCalibration; + int vBatMin; + int vBatMax; + int backlightMode; TrainerData trainer; - unsigned int view; // main screen view // TODO enum - bool disableThrottleWarning; - bool fai; - bool disableMemoryWarning; + unsigned int view; // main screen view // TODO enum + bool disableThrottleWarning; + bool fai; + bool disableMemoryWarning; BeeperMode beeperMode; - bool disableAlarmWarning; - bool disableRssiPoweroffAlarm; - unsigned int usbMode; - unsigned int jackMode; - bool sportPower; + bool disableAlarmWarning; + bool disableRssiPoweroffAlarm; + unsigned int usbMode; + unsigned int jackMode; + bool sportPower; BeeperMode hapticMode; - unsigned int stickMode; // TODO enum - int timezone; - bool adjustRTC; - bool optrexDisplay; - unsigned int inactivityTimer; - unsigned int telemetryBaudrate; - bool minuteBeep; - bool preBeep; - bool flashBeep; - unsigned int splashMode; + unsigned int stickMode; // TODO enum + int timezone; + bool adjustRTC; + bool optrexDisplay; + unsigned int inactivityTimer; + unsigned int telemetryBaudrate; + bool minuteBeep; + bool preBeep; + bool flashBeep; + unsigned int splashMode; int splashDuration; - unsigned int backlightDelay; - unsigned int templateSetup; //RETA order according to chout_ar array - int PPM_Multiplier; - int hapticLength; - unsigned int reNavigation; + unsigned int backlightDelay; + unsigned int templateSetup; //RETA order according to chout_ar array + int PPM_Multiplier; + int hapticLength; + unsigned int reNavigation; unsigned int stickReverse; - unsigned int speakerPitch; - int hapticStrength; - unsigned int speakerMode; - char ownerName[10+1]; - int beeperLength; - unsigned int gpsFormat; - int speakerVolume; - unsigned int backlightBright; - unsigned int backlightOffBright; + unsigned int speakerPitch; + int hapticStrength; + unsigned int speakerMode; + char ownerName[OWNER_NAME_LEN + 1]; + int beeperLength; + unsigned int gpsFormat; + int speakerVolume; + unsigned int backlightBright; + unsigned int backlightOffBright; int switchesDelay; unsigned int mAhWarn; unsigned int mAhUsed; unsigned int globalTimer; - char bluetoothName[10+1]; + char bluetoothName[BLUETOOTH_NAME_LEN + 1]; unsigned int bluetoothBaudrate; unsigned int bluetoothMode; unsigned int sticksGain; @@ -145,7 +213,7 @@ class GeneralSettings { bool rtcCheckDisable; bool keysBacklight; unsigned int imperial; - char ttsLanguage[2+1]; + char ttsLanguage[TTS_LANGUAGE_LEN + 1]; int beepVolume; int wavVolume; int varioVolume; @@ -156,33 +224,47 @@ class GeneralSettings { unsigned int mavbaud; unsigned int switchUnlockStates; unsigned int auxSerialMode; + unsigned int aux2SerialMode; int antennaMode; unsigned int backlightColor; CustomFunctionData customFn[CPN_MAX_SPECIAL_FUNCTIONS]; - char switchName[CPN_MAX_SWITCHES][3+1]; + char switchName[CPN_MAX_SWITCHES][HARDWARE_NAME_LEN + 1]; unsigned int switchConfig[CPN_MAX_SWITCHES]; - char stickName[CPN_MAX_STICKS][3+1]; - char potName[CPN_MAX_KNOBS][3+1]; + char stickName[CPN_MAX_STICKS][HARDWARE_NAME_LEN + 1]; + char potName[CPN_MAX_KNOBS][HARDWARE_NAME_LEN + 1]; unsigned int potConfig[CPN_MAX_KNOBS]; - char sliderName[CPN_MAX_SLIDERS][3+1]; + char sliderName[CPN_MAX_SLIDERS][HARDWARE_NAME_LEN + 1]; unsigned int sliderConfig[CPN_MAX_SLIDERS]; - char themeName[8+1]; - typedef uint8_t ThemeOptionData[8+1]; + char themeName[THEME_NAME_LEN + 1]; + typedef uint8_t ThemeOptionData[8 + 1]; ThemeOptionData themeOptionValue[5]; - - char registrationId[8+1]; + + char registrationId[REGISTRATION_ID_LEN + 1]; int gyroMax; int gyroOffset; int pwrOnSpeed; int pwrOffSpeed; - + bool switchPositionAllowedTaranis(int index) const; bool switchSourceAllowedTaranis(int index) const; bool isPotAvailable(int index) const; bool isSliderAvailable(int index) const; + QString antennaModeToString() const; + QString bluetoothModeToString() const; + QString auxSerialModeToString() const; + QString telemetryBaudrateToString() const; + + static QString antennaModeToString(int value); + static QString bluetoothModeToString(int value); + static QString auxSerialModeToString(int value); + static QString telemetryBaudrateToString(int value); + static FieldRange getPPM_MultiplierRange(); + static FieldRange getTxVoltageCalibrationRange(); + static FieldRange getTxCurrentCalibration(); + static AbstractStaticItemModel * antennaModeItemModel(); + static AbstractStaticItemModel * bluetoothModeItemModel(); + static AbstractStaticItemModel * auxSerialModeItemModel(); + static AbstractStaticItemModel * telemetryBaudrateItemModel(); }; - - -#endif // GENERALSETTINGS_H diff --git a/companion/src/firmwares/moduledata.cpp b/companion/src/firmwares/moduledata.cpp index b36d88e04..49a079d79 100644 --- a/companion/src/firmwares/moduledata.cpp +++ b/companion/src/firmwares/moduledata.cpp @@ -45,19 +45,6 @@ bool ModuleData::isPxx2Module() const } } -bool ModuleData::isPxx1Module() const -{ - switch(protocol){ - case PULSES_PXX_XJT_X16: - case PULSES_PXX_R9M: - case PULSES_PXX_R9M_LITE: - case PULSES_PXX_R9M_LITE_PRO: - return true; - default: - return false; - } -} - bool ModuleData::supportRxNum() const { switch (protocol) { diff --git a/companion/src/firmwares/moduledata.h b/companion/src/firmwares/moduledata.h index 119f44eb8..8acec18b0 100644 --- a/companion/src/firmwares/moduledata.h +++ b/companion/src/firmwares/moduledata.h @@ -215,12 +215,12 @@ class ModuleData { struct Access { unsigned int receivers; char receiverName[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME+1]; + unsigned int racingMode; } access; void clear() { memset(this, 0, sizeof(ModuleData)); } void convert(RadioDataConversionState & cstate); bool isPxx2Module() const; - bool isPxx1Module() const; bool supportRxNum() const; QString polarityToString() const { return ppm.pulsePol ? tr("Positive") : tr("Negative"); } QString rfProtocolToString() const; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index f9fc8c8f5..b82e76795 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -2158,15 +2158,16 @@ class ModuleUnionField: public UnionField { module(module) { internalField.Append(new UnsignedField<3>(this, module.access.receivers)); - internalField.Append(new SpareBitsField<5>(this)); + internalField.Append(new SpareBitsField<4>(this)); + internalField.Append(new UnsignedField<1>(this, module.access.racingMode)); - for (int i=0; i(this, receiverName[i])); memset(receiverName, 0, sizeof(receiverName)); } - bool select(const unsigned int& attr) const override + bool select(const unsigned int & attr) const override { return attr >= PULSES_ACCESS_ISRM && attr <= PULSES_ACCESS_R9M_LITE_PRO; } @@ -2759,7 +2760,7 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type if (version >= 218) { internalField.Append(new UnsignedField<4>(this, generalData.auxSerialMode)); if (IS_FAMILY_HORUS_OR_T16(board) && version >= 219) { - internalField.Append(new SpareBitsField<4>(this)); + internalField.Append(new UnsignedField<4>(this, generalData.aux2SerialMode)); } else { for (uint8_t i=0; isetName("sensordata.unit"); for (int i = 0; i <= UNIT_MAX; i++) { - QString str = unitToString(i); + QString str = unitToString(i, false); if (str != CPN_STR_UNKNOWN_ITEM) mdl->appendToItemList(str, i); } diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 8a032cec5..1581b444b 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -188,7 +188,7 @@ class SensorData { QString typeToString() const; QString formulaToString() const; QString cellIndexToString() const; - QString unitToString() const; + QString unitToString(bool hideRaw = true) const; QString precToString() const; int getMask() const; QString paramsToString(const ModelData * model) const; @@ -203,7 +203,7 @@ class SensorData { static QString typeToString(const int value); static QString formulaToString(const int value); static QString cellIndexToString(const int value); - static QString unitToString(const int value); + static QString unitToString(const int value, bool hideRaw = true); static QString precToString(const int value); static bool isRssiSensorAvailable(const ModelData * model, const int value); static QString rssiSensorToString(const ModelData * model, const int value); diff --git a/companion/src/generaledit/CMakeLists.txt b/companion/src/generaledit/CMakeLists.txt index 2c458c719..a9c12997a 100644 --- a/companion/src/generaledit/CMakeLists.txt +++ b/companion/src/generaledit/CMakeLists.txt @@ -3,16 +3,18 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) set(generaledit_NAMES generaledit generalsetup - trainer - hardware ) set(generaledit_SRCS calibration.cpp + hardware.cpp + trainer.cpp ) set(generaledit_HDRS calibration.h + hardware.h + trainer.h ) set(generaledit_UIS diff --git a/companion/src/generaledit/calibration.cpp b/companion/src/generaledit/calibration.cpp index 05fb30a58..e43b61a53 100644 --- a/companion/src/generaledit/calibration.cpp +++ b/companion/src/generaledit/calibration.cpp @@ -18,93 +18,45 @@ * GNU General Public License for more details. */ -#include -#include -#include #include "calibration.h" - +#include "autospinbox.h" +#include "helpers.h" CalibrationPanel::CalibrationPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware): GeneralPanel(parent, generalSettings, firmware) { + int rows = Boards::getCapability(getCurrentBoard(), Board::MaxAnalogs); - tableWidget = new QTableWidget(); - QVBoxLayout * layout = new QVBoxLayout(); - layout->addWidget(tableWidget); - layout->setContentsMargins(0, 0, 0, 0); - this->setLayout(layout); - - tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - tableWidget->setColumnCount(3); - tableWidget->setShowGrid(false); - tableWidget->setSelectionMode(QAbstractItemView::NoSelection); - tableWidget->setFrameStyle(QFrame::NoFrame | QFrame::Plain); - tableWidget->setStyleSheet("QTableWidget {background-color: transparent;}"); QStringList headerLabels; - headerLabels << tr("Negative span") << tr("Mid value") << tr("Positive span"); - tableWidget->setHorizontalHeaderLabels(headerLabels); + headerLabels << "" << tr("Negative span") << tr("Mid value") << tr("Positive span"); - int rows = getBoardCapability(getCurrentBoard(), Board::MaxAnalogs); - tableWidget->setRowCount(rows); + TableLayout * tableLayout = new TableLayout(this, rows, headerLabels); - for(int i = 0; i < rows; ++i) { + for (int i = 0; i < rows; i++) { + int col = 0; + QLabel * label = new QLabel(this); + label->setText(firmware->getAnalogInputName(i)); + tableLayout->addWidget(i, col++, label); - QTableWidgetItem *newItem = new QTableWidgetItem(firmware->getAnalogInputName(i)); - newItem->setTextAlignment(Qt::AlignLeft); - tableWidget->setVerticalHeaderItem(i, newItem); + QLineEdit * leNeg = new QLineEdit(this); + leNeg->setText(QString("%1").arg(generalSettings.calibSpanNeg[i])); + leNeg->setReadOnly(true); + tableLayout->addWidget(i, col++, leNeg); - for(int j = 0; j < 3; ++j) { - QSpinBox * newItem = new QSpinBox(); - newItem->setMinimum(-9999); - newItem->setMaximum(9999); - newItem->setSingleStep(1); - newItem->setValue(getCalibrationValue(i, j)); - newItem->setProperty("row", i); - newItem->setProperty("column", j); - tableWidget->setCellWidget(i, j, newItem); - connect(newItem, SIGNAL(valueChanged(int)), this, SLOT(onCellChanged(int))); - } + QLineEdit * leMid = new QLineEdit(this); + leMid->setText(QString("%1").arg(generalSettings.calibMid[i])); + leMid->setReadOnly(true); + tableLayout->addWidget(i, col++, leMid); + + QLineEdit * lePos = new QLineEdit(this); + lePos->setText(QString("%1").arg(generalSettings.calibSpanPos[i])); + lePos->setReadOnly(true); + tableLayout->addWidget(i, col++, lePos); } + disableMouseScrolling(); -} - -void CalibrationPanel::onCellChanged(int value) -{ - QSpinBox * sb = qobject_cast(sender()); - int row = sb->property("row").toInt(); - int column = sb->property("column").toInt(); - - if (value != getCalibrationValue(row, column)) { - setCalibrationValue(row,column, value); - qDebug() << "CalibrationPanel::onCellChanged" << row << column << "to" << value; - emit modified(); - } -} - -int CalibrationPanel::getCalibrationValue(int row, int column) -{ - switch(column) { - case 0: - return generalSettings.calibSpanNeg[row]; - case 1: - return generalSettings.calibMid[row]; - case 2: - return generalSettings.calibSpanPos[row]; - } - return 0; -} - -void CalibrationPanel::setCalibrationValue(int row, int column, int value) -{ - switch(column) { - case 0: - generalSettings.calibSpanNeg[row] = value; - break; - case 1: - generalSettings.calibMid[row] = value; - break; - case 2: - generalSettings.calibSpanPos[row] = value; - break; - } + tableLayout->resizeColumnsToContents(); + tableLayout->setColumnWidth(0, QString(15, ' ')); + tableLayout->pushColumnsLeft(headerLabels.count()); + tableLayout->pushRowsUp(rows + 1); } diff --git a/companion/src/generaledit/calibration.h b/companion/src/generaledit/calibration.h index fd28e492b..279b7cf80 100644 --- a/companion/src/generaledit/calibration.h +++ b/companion/src/generaledit/calibration.h @@ -18,20 +18,10 @@ * GNU General Public License for more details. */ -#ifndef _CALIBRATION_H_ -#define _CALIBRATION_H_ +#pragma once -#include #include "generaledit.h" -namespace Ui { - class Calibration; -} - -class QLabel; -class AutoLineEdit; -class AutoComboBox; - class CalibrationPanel : public GeneralPanel { Q_OBJECT @@ -40,14 +30,5 @@ class CalibrationPanel : public GeneralPanel CalibrationPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware); virtual ~CalibrationPanel() {}; - private slots: - void onCellChanged(int value); - private: - QTableWidget * tableWidget; - - int getCalibrationValue(int row, int column); - void setCalibrationValue(int row, int column, int value); }; - -#endif // _CALIBRATION_H_ diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index e80038f9f..99884ed43 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -46,26 +46,27 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } ui->profile_CB->clear(); - for (int i=0; iprofile_CB->addItem(name, i); - if (i==g.id()) { - ui->profile_CB->setCurrentIndex(ui->profile_CB->count()-1); + if (i == g.id()) { + ui->profile_CB->setCurrentIndex(ui->profile_CB->count() - 1); } } } - sharedItemModels = new CompoundItemModelFactory(&generalSettings, nullptr); - sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSource); - sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); - sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); - sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); + editorItemModels = new CompoundItemModelFactory(&generalSettings, nullptr); + // tabs created below expect these item models to be pre-registered + editorItemModels->addItemModel(AbstractItemModel::IMID_RawSource); + editorItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); + editorItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); + editorItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); - addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, sharedItemModels), tr("Global Functions")); - addTab(new TrainerPanel(this, generalSettings, firmware), tr("Trainer")); - addTab(new HardwarePanel(this, generalSettings, firmware), tr("Hardware")); + addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, editorItemModels), tr("Global Functions")); + addTab(new TrainerPanel(this, generalSettings, firmware, editorItemModels), tr("Trainer")); + addTab(new HardwarePanel(this, generalSettings, firmware, editorItemModels), tr("Hardware")); addTab(new CalibrationPanel(this, generalSettings, firmware), tr("Calibration")); ui->tabWidget->setCurrentIndex( g.generalEditTab() ); @@ -74,7 +75,7 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir GeneralEdit::~GeneralEdit() { delete ui; - delete sharedItemModels; + delete editorItemModels; // cleans up item models registered by the tabs } void GeneralEdit::closeEvent(QCloseEvent *event) @@ -90,7 +91,7 @@ void GeneralEdit::addTab(GenericPanel *panel, QString text) VerticalScrollArea * area = new VerticalScrollArea(widget, panel); baseLayout->addWidget(area); ui->tabWidget->addTab(widget, text); - connect(panel, SIGNAL(modified()), this, SLOT(onTabModified())); + connect(panel, &GenericPanel::modified, this, &GeneralEdit::onTabModified); } void GeneralEdit::onTabModified() diff --git a/companion/src/generaledit/generaledit.h b/companion/src/generaledit/generaledit.h index b2e6caa82..2ad27cb1c 100644 --- a/companion/src/generaledit/generaledit.h +++ b/companion/src/generaledit/generaledit.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _GENERALEDIT_H_ -#define _GENERALEDIT_H_ +#pragma once #include #include "eeprominterface.h" @@ -35,7 +34,7 @@ class GeneralPanel : public GenericPanel { public: GeneralPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware): - GenericPanel(parent, NULL, generalSettings, firmware) + GenericPanel(parent, nullptr, generalSettings, firmware) { } }; @@ -73,7 +72,5 @@ class GeneralEdit : public QDialog QVector panels; void addTab(GenericPanel *panel, QString text); void closeEvent(QCloseEvent *event); - CompoundItemModelFactory *sharedItemModels; + CompoundItemModelFactory *editorItemModels; }; - -#endif // _GENERALEDIT_H_ diff --git a/companion/src/generaledit/hardware.cpp b/companion/src/generaledit/hardware.cpp index 9d2e8e452..4f7e52a7a 100644 --- a/companion/src/generaledit/hardware.cpp +++ b/companion/src/generaledit/hardware.cpp @@ -19,326 +19,268 @@ */ #include "hardware.h" -#include "ui_hardware.h" +#include "compounditemmodels.h" +#include "filtereditemmodels.h" +#include "autolineedit.h" +#include "autocombobox.h" +#include "autocheckbox.h" +#include "autospinbox.h" +#include "autodoublespinbox.h" -void HardwarePanel::setupSwitchType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type, bool threePos) -{ - Board::Type board = getCurrentBoard(); - if (IS_STM32(board) && index < getBoardCapability(board, Board::Switches)) { - type->addItem(tr("None"), Board::SWITCH_NOT_AVAILABLE); - type->addItem(tr("2 Positions Toggle"), Board::SWITCH_TOGGLE); - type->addItem(tr("2 Positions"), Board::SWITCH_2POS); - if (threePos) { - type->addItem(tr("3 Positions"), Board::SWITCH_3POS); - } - name->setField(generalSettings.switchName[index], 3, this); - type->setField(generalSettings.switchConfig[index], this); - if (IS_TARANIS_X7(board)) { - if (index == 4) { - label->setText("SF"); - } - else if (index == 5) { - label->setText("SH"); - } - if (index == 6) { - label->setText("SI"); - } - else if (index == 7) { - label->setText("SJ"); - } - } - else if (IS_RADIOMASTER_TX12(board)) { - if (index == 6) { - label->setText("SI"); - } - else if (index == 7) { - label->setText("SJ"); - } - } - else if (IS_FAMILY_T12(board)) { - if (index == 4) { - label->setText("SG"); - } - else if (index == 5) { - label->setText("SH"); - } - } - } - else { - label->hide(); - name->hide(); - type->hide(); - } -} +#include +#include +#include -void HardwarePanel::setupPotType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type) -{ - Board::Type board = firmware->getBoard(); +constexpr char FIM_SWITCHTYPE2POS[] {"Switch Type 2 Pos"}; +constexpr char FIM_SWITCHTYPE3POS[] {"Switch Type 3 Pos"}; - if (IS_STM32(board) && index < getBoardCapability(board, Board::Pots)) { - label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+index).toString()); - type->addItem(tr("None"), Board::POT_NONE); - type->addItem(tr("Pot with detent"), Board::POT_WITH_DETENT); - type->addItem(tr("Multipos switch"), Board::POT_MULTIPOS_SWITCH); - type->addItem(tr("Pot without detent"), Board::POT_WITHOUT_DETENT); - name->setField(generalSettings.potName[index], 3, this); - type->setField(generalSettings.potConfig[index], this); - } - else { - label->hide(); - name->hide(); - type->hide(); - } -} - -void HardwarePanel::setupSliderType(int index, QLabel *label, AutoLineEdit *name, AutoComboBox *type) -{ - Board::Type board = firmware->getBoard(); - - if (IS_STM32(board) && index < getBoardCapability(board, Board::Sliders)) { - label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+getBoardCapability(board, Board::Pots)+index).toString()); - type->addItem(tr("None"), Board::SLIDER_NONE); - type->addItem(tr("Slider with detent"), Board::SLIDER_WITH_DETENT); - name->setField(generalSettings.sliderName[index], 3, this); - type->setField(generalSettings.sliderConfig[index], this); - } - else { - label->hide(); - name->hide(); - type->hide(); - } -} - -bool HardwarePanel::isSwitch3Pos(int idx) -{ - Board::Type board = firmware->getBoard(); - Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board, idx); - - switchInfo.config = Board::SwitchType(generalSettings.switchConfig[idx]); - return switchInfo.config == Board::SWITCH_3POS; -}; - -HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware): +HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): GeneralPanel(parent, generalSettings, firmware), - ui(new Ui::Hardware) + board(firmware->getBoard()), + editorItemModels(sharedItemModels) { - ui->setupUi(this); + editorItemModels->registerItemModel(Boards::potTypeItemModel()); + editorItemModels->registerItemModel(Boards::sliderTypeItemModel()); + int id = editorItemModels->registerItemModel(Boards::switchTypeItemModel()); - Board::Type board = firmware->getBoard(); + tabFilteredModels = new FilteredItemModelFactory(); + tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), Board::SwitchTypeContext2Pos), FIM_SWITCHTYPE2POS); + tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id), Board::SwitchTypeContext3Pos), FIM_SWITCHTYPE3POS); - if (IS_STM32(board)) { - ui->rudName->setField(generalSettings.stickName[0], 3, this); - ui->eleName->setField(generalSettings.stickName[1], 3, this); - ui->thrName->setField(generalSettings.stickName[2], 3, this); - ui->ailName->setField(generalSettings.stickName[3], 3, this); + int antmodelid = editorItemModels->registerItemModel(GeneralSettings::antennaModeItemModel()); + int btmodelid = editorItemModels->registerItemModel(GeneralSettings::bluetoothModeItemModel()); + int auxmodelid = editorItemModels->registerItemModel(GeneralSettings::auxSerialModeItemModel()); + int baudmodelid = editorItemModels->registerItemModel(GeneralSettings::telemetryBaudrateItemModel()); + + grid = new QGridLayout(this); + int count; + int row = 0; + + count = Boards::getCapability(board, Board::Sticks); + if (count) { + for (int i = 0; i < count; i++) { + addStick(i, row); + } } - else { - ui->rudLabel->hide(); - ui->rudName->hide(); - ui->eleLabel->hide(); - ui->eleName->hide(); - ui->thrLabel->hide(); - ui->thrName->hide(); - ui->ailLabel->hide(); - ui->ailName->hide(); - ui->potsTypeSeparator_1->hide(); - ui->potsTypeSeparator_2->hide(); + + count = Boards::getCapability(board, Board::Pots); + if (count) { + for (int i = 0; i < count; i++) { + addPot(i, row); + } } - + + count = Boards::getCapability(board, Board::Sliders); + if (count) { + for (int i = 0; i < count; i++) { + addSlider(i, row); + } + addLine(row); + } + + count = Boards::getCapability(board, Board::Switches); + if (count) { + for (int i = 0; i < count; i++) { + addSwitch(i, row); + } + addLine(row); + } + + addLabel(tr("Battery Offset"), row, 0); + AutoDoubleSpinBox *txVoltageCalibration = new AutoDoubleSpinBox(this); + FieldRange txVCRng = GeneralSettings::getTxVoltageCalibrationRange(); + txVoltageCalibration->setDecimals(txVCRng.decimals); + txVoltageCalibration->setSingleStep(txVCRng.step); + txVoltageCalibration->setSuffix(txVCRng.unit); + txVoltageCalibration->setField(generalSettings.txVoltageCalibration); + addParams(row, txVoltageCalibration); + + if (Boards::getCapability(board, Board::HasRTC)) { + addLabel(tr("RTC Battery Check"), row, 0); + AutoCheckBox *rtcCheckDisable = new AutoCheckBox(this); + rtcCheckDisable->setField(generalSettings.rtcCheckDisable, this, true); + addParams(row, rtcCheckDisable); + } + + if (firmware->getCapability(HasBluetooth)) { + addLabel(tr("Bluetooth"), row, 0); + + QGridLayout *btlayout = new QGridLayout(); + + AutoComboBox *bluetoothMode = new AutoComboBox(this); + bluetoothMode->setModel(editorItemModels->getItemModel(btmodelid)); + bluetoothMode->setField(generalSettings.bluetoothMode, this); + btlayout->addWidget(bluetoothMode, 0, 0); + + QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ); + btlayout->addItem(spacer, 0, 1); + + QLabel *btnamelabel = new QLabel(this); + btnamelabel->setText(tr("Device Name:")); + btlayout->addWidget(btnamelabel, 0, 2); + + AutoLineEdit *bluetoothName = new AutoLineEdit(this); + bluetoothName->setField(generalSettings.bluetoothName, BLUETOOTH_NAME_LEN, this); + btlayout->addWidget(bluetoothName, 0, 3); + + grid->addLayout(btlayout, row, 1); + row++; + } + + if (firmware->getCapability(HasAntennaChoice)) { + addLabel(tr("Antenna"), row, 0); + AutoComboBox *antennaMode = new AutoComboBox(this); + antennaMode->setModel(editorItemModels->getItemModel(antmodelid)); + antennaMode->setField(generalSettings.antennaMode, this); + addParams(row, antennaMode); + } + + if (firmware->getCapability(HasAuxSerialMode)) { + QString lbl = "Serial Port"; + if (IS_RADIOMASTER_TX16S(board)) + lbl.append(" (TTL)"); + addLabel(tr("%1").arg(lbl), row, 0); + AutoComboBox *serialPortMode = new AutoComboBox(this); + serialPortMode->setModel(editorItemModels->getItemModel(auxmodelid)); + serialPortMode->setField(generalSettings.auxSerialMode); + addParams(row, serialPortMode); + } + + if (firmware->getCapability(HasAux2SerialMode)) { + QString lbl = "Serial Port 2"; + if (IS_RADIOMASTER_TX16S(board)) + lbl.append(" (TTL)"); + addLabel(tr("%1").arg(lbl), row, 0); + AutoComboBox *serialPort2Mode = new AutoComboBox(this); + serialPort2Mode->setModel(editorItemModels->getItemModel(auxmodelid)); + serialPort2Mode->setField(generalSettings.aux2SerialMode); + addParams(row, serialPort2Mode); + } + + if (firmware->getCapability(HasADCJitterFilter)) { + addLabel(tr("ADC Filter"), row, 0); + AutoCheckBox *filterEnable = new AutoCheckBox(this); + filterEnable->setField(generalSettings.jitterFilter, this, true); + addParams(row, filterEnable); + } + if (firmware->getCapability(HasSportConnector)) { - ui->sportPower->setChecked(generalSettings.sportPower); - } - else { - ui->sportPower->hide(); - ui->sportPowerLabel->hide(); + addLabel(tr("S.Port Power"), row, 0); + AutoCheckBox *sportPower = new AutoCheckBox(this); + sportPower->setField(generalSettings.sportPower, this); + addParams(row, sportPower); } - setupPotType(0, ui->pot1Label, ui->pot1Name, ui->pot1Type); - setupPotType(1, ui->pot2Label, ui->pot2Name, ui->pot2Type); - setupPotType(2, ui->pot3Label, ui->pot3Name, ui->pot3Type); - setupPotType(3, ui->pot4Label, ui->pot4Name, ui->pot4Type); - setupPotType(4, ui->pot5Label, ui->pot5Name, ui->pot5Type); - - setupSliderType(0, ui->lsLabel, ui->lsName, ui->lsType); - setupSliderType(1, ui->rsLabel, ui->rsName, ui->rsType); - setupSliderType(2, ui->ls2Label, ui->ls2Name, ui->ls2Type); - setupSliderType(3, ui->rs2Label, ui->rs2Name, ui->rs2Type); - - setupSwitchType(0, ui->saLabel, ui->saName, ui->saType, isSwitch3Pos(0)); - setupSwitchType(1, ui->sbLabel, ui->sbName, ui->sbType, isSwitch3Pos(1)); - setupSwitchType(2, ui->scLabel, ui->scName, ui->scType, isSwitch3Pos(2)); - setupSwitchType(3, ui->sdLabel, ui->sdName, ui->sdType, isSwitch3Pos(3)); - setupSwitchType(4, ui->seLabel, ui->seName, ui->seType, isSwitch3Pos(4)); - setupSwitchType(5, ui->sfLabel, ui->sfName, ui->sfType, isSwitch3Pos(5)); - setupSwitchType(6, ui->sgLabel, ui->sgName, ui->sgType, isSwitch3Pos(6)); - setupSwitchType(7, ui->shLabel, ui->shName, ui->shType, isSwitch3Pos(7)); - setupSwitchType(8, ui->siLabel, ui->siName, ui->siType, isSwitch3Pos(8)); - setupSwitchType(9, ui->sjLabel, ui->sjName, ui->sjType, isSwitch3Pos(9)); - setupSwitchType(10, ui->skLabel, ui->skName, ui->skType); // Here starts X9E, only 3 switches - setupSwitchType(11, ui->slLabel, ui->slName, ui->slType); - setupSwitchType(12, ui->smLabel, ui->smName, ui->smType); - setupSwitchType(13, ui->snLabel, ui->snName, ui->snType); - setupSwitchType(14, ui->soLabel, ui->soName, ui->soType); - setupSwitchType(15, ui->spLabel, ui->spName, ui->spType); - setupSwitchType(16, ui->sqLabel, ui->sqName, ui->sqType); - setupSwitchType(17, ui->srLabel, ui->srName, ui->srType); - - if (IS_TARANIS(board) && !IS_TARANIS_SMALL(board)) { - ui->serialPortMode->setCurrentIndex(generalSettings.auxSerialMode); - } - else { - ui->serialPortMode->setCurrentIndex(0); - ui->serialPortMode->hide(); - ui->serialPortLabel->hide(); + if (firmware->getCapability(HasTelemetryBaudrate)) { + addLabel(tr("Maximum Baud"), row, 0); + AutoComboBox *maxBaudRate = new AutoComboBox(this); + maxBaudRate->setModel(editorItemModels->getItemModel(baudmodelid)); + maxBaudRate->setField(generalSettings.telemetryBaudrate, this); + addParams(row, maxBaudRate); } - if (!IS_SKY9X(board)) { - ui->txCurrentCalibration->hide(); - ui->txCurrentCalibrationLabel->hide(); - } - - if (IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_TARANIS_X9DP_2019(board) || IS_FAMILY_HORUS_OR_T16(board)) { - ui->bluetoothMode->addItem(tr("OFF"), 0); - if (IS_TARANIS_X9E(board)) { - ui->bluetoothMode->addItem(tr("Enabled"), 1); - } - else { - ui->bluetoothMode->addItem(tr("Telemetry"), 1); - ui->bluetoothMode->addItem(tr("Trainer"), 2); - } - ui->bluetoothMode->setField(generalSettings.bluetoothMode, this); - ui->bluetoothName->setField(generalSettings.bluetoothName, 10, this); - } - else { - ui->bluetoothLabel->hide(); - ui->bluetoothWidget->hide(); - } - - if ((IS_FAMILY_HORUS_OR_T16(board) && board != Board::BOARD_X10_EXPRESS) || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board))) { - ui->antennaMode->addItem(tr("Internal"), -2); - ui->antennaMode->addItem(tr("Ask"), -1); - ui->antennaMode->addItem(tr("Per model"), 0); - ui->antennaMode->addItem(IS_HORUS_X12S(board) ? tr("Internal + External") : tr("External"), 1); - ui->antennaMode->setField(generalSettings.antennaMode, this); - } - else { - ui->antennaLabel->hide(); - ui->antennaMode->hide(); - } - - if (IS_HORUS_OR_TARANIS(board)) { - ui->filterEnable->setChecked(!generalSettings.jitterFilter); - } - else { - ui->filterEnable->hide(); - ui->filterLabel->hide(); - } - - if (IS_STM32(board)) { - ui->rtcCheckDisable->setChecked(!generalSettings.rtcCheckDisable); - } - else { - ui->rtcCheckDisable->hide(); - ui->rtcCheckLabel->hide(); + if (firmware->getCapability(HastxCurrentCalibration)) { + addLabel(tr("Current Offset"), row, 0); + AutoSpinBox *txCurrentCalibration = new AutoSpinBox(this); + FieldRange txCCRng = GeneralSettings::getTxCurrentCalibration(); + txCurrentCalibration->setSuffix(txVCRng.unit); + txCurrentCalibration->setField(generalSettings.txCurrentCalibration); + addParams(row, txCurrentCalibration); } + addVSpring(grid, 0, grid->rowCount()); + addHSpring(grid, grid->columnCount(), 0); disableMouseScrolling(); - - setValues(); } HardwarePanel::~HardwarePanel() { - delete ui; + delete tabFilteredModels; } -void HardwarePanel::on_filterEnable_stateChanged() +void HardwarePanel::addStick(int index, int & row) { - generalSettings.jitterFilter = !ui->filterEnable->isChecked(); + int col = 0; + addLabel(Boards::getAnalogInputName(board, index), row, col++); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.stickName[index], HARDWARE_NAME_LEN, this); + + addParams(row, name); } -void HardwarePanel::on_sportPower_stateChanged() +void HardwarePanel::addPot(int index, int & row) { - generalSettings.sportPower = ui->sportPower->isChecked(); + addLabel(Boards::getAnalogInputName(board, Boards::getCapability(board, Board::Sticks) + index), row, 0); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.potName[index], HARDWARE_NAME_LEN, this); + + AutoComboBox *type = new AutoComboBox(this); + type->setModel(editorItemModels->getItemModel(AIM_BOARDS_POT_TYPE)); + type->setField(generalSettings.potConfig[index], this); + + addParams(row, name, type); } -void HardwarePanel::on_rtcCheckDisable_stateChanged() +void HardwarePanel::addSlider(int index, int & row) { - generalSettings.rtcCheckDisable = !ui->rtcCheckDisable->isChecked(); + addLabel(Boards::getAnalogInputName(board, Boards::getCapability(board, Board::Sticks) + + Boards::getCapability(board, Board::Pots) + index), row, 0); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.sliderName[index], HARDWARE_NAME_LEN, this); + + AutoComboBox *type = new AutoComboBox(this); + type->setModel(editorItemModels->getItemModel(AIM_BOARDS_SLIDER_TYPE)); + type->setField(generalSettings.sliderConfig[index], this); + + addParams(row, name, type); } -void HardwarePanel::on_PPM_MultiplierDSB_editingFinished() +void HardwarePanel::addSwitch(int index, int & row) { - int val = (int)(ui->PPM_MultiplierDSB->value()*10)-10; - if (generalSettings.PPM_Multiplier != val) { - generalSettings.PPM_Multiplier = val; - emit modified(); - } + addLabel(Boards::getSwitchInfo(board, index).name, row, 0); + + AutoLineEdit *name = new AutoLineEdit(this); + name->setField(generalSettings.switchName[index], HARDWARE_NAME_LEN, this); + + AutoComboBox *type = new AutoComboBox(this); + Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board, index); + type->setModel(switchInfo.config < Board::SWITCH_3POS ? tabFilteredModels->getItemModel(FIM_SWITCHTYPE2POS) : + tabFilteredModels->getItemModel(FIM_SWITCHTYPE3POS)); + type->setField(generalSettings.switchConfig[index], this); + + addParams(row, name, type); } -void HardwarePanel::on_PPM1_editingFinished() +void HardwarePanel::addLabel(QString text, int row, int col) { - if (generalSettings.trainer.calib[0] != ui->PPM1->value()) { - generalSettings.trainer.calib[0] = ui->PPM1->value(); - emit modified(); - } + QLabel *label = new QLabel(this); + label->setText(text); + grid->addWidget(label, row, col); } -void HardwarePanel::on_PPM2_editingFinished() +void HardwarePanel::addLine(int & row) { - if (generalSettings.trainer.calib[1] != ui->PPM2->value()) { - generalSettings.trainer.calib[1] = ui->PPM2->value(); - emit modified(); - } + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + line->setLineWidth(1); + line->setMidLineWidth(0); + grid->addWidget(line, row, 0, 1, grid->columnCount()); + row++; } -void HardwarePanel::on_PPM3_editingFinished() +void HardwarePanel::addParams(int & row, QWidget * widget1, QWidget * widget2) { - if (generalSettings.trainer.calib[2] != ui->PPM3->value()) { - generalSettings.trainer.calib[2] = ui->PPM3->value(); - emit modified(); - } -} - -void HardwarePanel::on_PPM4_editingFinished() -{ - if (generalSettings.trainer.calib[3] != ui->PPM4->value()) { - generalSettings.trainer.calib[3] = ui->PPM4->value(); - emit modified(); - } -} - - -void HardwarePanel::on_txCurrentCalibration_editingFinished() -{ - if (generalSettings.txCurrentCalibration != ui->txCurrentCalibration->value()) { - generalSettings.txCurrentCalibration = ui->txCurrentCalibration->value(); - emit modified(); - } -} - -void HardwarePanel::setValues() -{ - ui->txVoltageCalibration->setValue((double)generalSettings.txVoltageCalibration/10); - ui->txCurrentCalibration->setValue((double)generalSettings.txCurrentCalibration); - - ui->PPM1->setValue(generalSettings.trainer.calib[0]); - ui->PPM2->setValue(generalSettings.trainer.calib[1]); - ui->PPM3->setValue(generalSettings.trainer.calib[2]); - ui->PPM4->setValue(generalSettings.trainer.calib[3]); - ui->PPM_MultiplierDSB->setValue((qreal)(generalSettings.PPM_Multiplier+10)/10); -} - -void HardwarePanel::on_txVoltageCalibration_editingFinished() -{ - if (generalSettings.txVoltageCalibration != ui->txVoltageCalibration->value()*10) { - generalSettings.txVoltageCalibration = ui->txVoltageCalibration->value()*10; - emit modified(); - } -} - -void HardwarePanel::on_serialPortMode_currentIndexChanged(int index) -{ - generalSettings.auxSerialMode = index; - emit modified(); + QGridLayout *subgrid = new QGridLayout(); + subgrid->addWidget(widget1, 0, 0); + if (widget2) + subgrid->addWidget(widget2, 0, 1); + else + addHSpring(subgrid, 1, 0); + addHSpring(subgrid, 2, 0); + grid->addLayout(subgrid, row, 1); + row++; } diff --git a/companion/src/generaledit/hardware.h b/companion/src/generaledit/hardware.h index 3b63a1d77..71636a2cc 100644 --- a/companion/src/generaledit/hardware.h +++ b/companion/src/generaledit/hardware.h @@ -18,52 +18,33 @@ * GNU General Public License for more details. */ -#ifndef _HARDWARE_H_ -#define _HARDWARE_H_ +#pragma once #include "generaledit.h" -namespace Ui { - class Hardware; -} - -class QLabel; -class AutoLineEdit; -class AutoComboBox; +class CompoundItemModelFactory; +class FilteredItemModelFactory; +class QGridLayout; class HardwarePanel : public GeneralPanel { Q_OBJECT public: - HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware); + HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * editorItemModels); virtual ~HardwarePanel(); - private slots: - void on_PPM1_editingFinished(); - void on_PPM2_editingFinished(); - void on_PPM3_editingFinished(); - void on_PPM4_editingFinished(); - void on_PPM_MultiplierDSB_editingFinished(); - - void on_txCurrentCalibration_editingFinished(); - void on_txVoltageCalibration_editingFinished(); - void on_filterEnable_stateChanged(); - void on_rtcCheckDisable_stateChanged(); - void on_sportPower_stateChanged(); - - void on_serialPortMode_currentIndexChanged(int index); - - protected: - void setupPotType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type); - void setupSliderType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type); - void setupSwitchType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type, bool threePos = true); - private: - Ui::Hardware *ui; + Board::Type board; + CompoundItemModelFactory *editorItemModels; + FilteredItemModelFactory *tabFilteredModels; + QGridLayout *grid; - void setValues(); - bool isSwitch3Pos(int idx); + void addStick(int index, int & row); + void addPot(int index, int & row); + void addSlider(int index, int & row); + void addSwitch(int index, int & row); + void addLabel(QString text, int row, int col); + void addLine(int & row); + void addParams(int & row, QWidget * widget1, QWidget * widget2 = nullptr); }; - -#endif // _HARDWARE_H_ diff --git a/companion/src/generaledit/hardware.ui b/companion/src/generaledit/hardware.ui deleted file mode 100644 index 5224ed785..000000000 --- a/companion/src/generaledit/hardware.ui +++ /dev/null @@ -1,1160 +0,0 @@ - - - Hardware - - - - 0 - 0 - 470 - 1618 - - - - - 0 - 0 - - - - Form - - - - 6 - - - - - - - - - - - - - - - - - - - OFF - - - - - S-Port Mirror - - - - - Telemetry - - - - - SBUS Trainer - - - - - Debug - - - - - - - - - - - - - - - - - - - - - Thr - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - 3 - - - - - - - SD - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 10 - - - - Qt::Horizontal - - - - - - - SO - - - - - - - -49 - - - 49 - - - - - - - 3 - - - - - - - RS - - - - - - - 3 - - - - - - - Ail - - - - - - - S4 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - LS2 - - - - - - - Antenna - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - PPM 1 - - - - - - - LS - - - - - - - Rud - - - - - - - Serial Port - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - SP - - - - - - - 3 - - - - - - - RS2 - - - - - - - - - - - - - - - - - SH - - - - - - - - - - - - - - - - - SM - - - - - - - SE - - - - - - - - - - - - - - - - - - - - 1 - - - 5.000000000000000 - - - 1.000000000000000 - - - - - - - S2 - - - - - - - 3 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - SJ - - - - - - - PPM 2 - - - - - - - 3 - - - - - - - SG - - - - - - - S5 - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 10 - - - - Qt::Horizontal - - - - - - - Current Offset - - - - - - - SK - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - ADC Filter - - - - - - - 3 - - - - - - - 3 - - - - - - - SF - - - - - - - - - - - - - - - - - - - - - - - - - - - S3 - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - SB - - - - - - - - - - - - - - - - - - - - SI - - - - - - - SQ - - - - - - - Ele - - - - - - - 3 - - - - - - - 3 - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - 3 - - - - - - - - - - SA - - - - - - - 3 - - - - - - - - - - - - - - SR - - - - - - - 3 - - - - - - - PPM Multiplier - - - - - - - S1 - - - - - - - - 0 - 0 - - - - Bluetooth - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - PPM 4 - - - - - - - - - - - - - - - - - v - - - 1 - - - -9.900000000000000 - - - 9.900000000000000 - - - 0.100000000000000 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - 3 - - - - - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - - - - - Device Name: - - - - - - - 10 - - - - - - - - - - PPM 3 - - - - - - - RTC Batt Check - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SL - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - 3 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - 3 - - - - - - - SC - - - - - - - 3 - - - - - - - - - - - - - - - - -9999 - - - 9999 - - - - - - - - 0 - 10 - - - - Qt::Horizontal - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - SN - - - - - - - Battery Offset - - - - - - - 3 - - - - - - - 3 - - - - - - - S.Port Power - - - - - - - - - - - - - - - AutoComboBox - QComboBox -
autocombobox.h
-
- - AutoLineEdit - QLineEdit -
autolineedit.h
-
-
- - -
diff --git a/companion/src/generaledit/trainer.cpp b/companion/src/generaledit/trainer.cpp index 721e2d60a..997f5f456 100644 --- a/companion/src/generaledit/trainer.cpp +++ b/companion/src/generaledit/trainer.cpp @@ -19,103 +19,106 @@ */ #include "trainer.h" -#include "ui_trainer.h" +#include "compounditemmodels.h" +#include "autocombobox.h" +#include "autolineedit.h" +#include "autospinbox.h" +#include "autodoublespinbox.h" -TrainerPanel::TrainerPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware): - GeneralPanel(parent, generalSettings, firmware), - ui(new Ui::Trainer) +#include +#include +#include + +TrainerPanel::TrainerPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * editorItemModels): + GeneralPanel(parent, generalSettings, firmware) { - ui->setupUi(this); + int modeid = editorItemModels->registerItemModel(TrainerMix::modeItemModel()); + int srcid = editorItemModels->registerItemModel(TrainerMix::srcItemModel()); + Board::Type board = getCurrentBoard(); + const int stickcnt = Boards::getCapability(board, Board::Sticks); - ui->trnMode_1->setCurrentIndex(generalSettings.trainer.mix[0].mode); - ui->trnChn_1->setCurrentIndex(generalSettings.trainer.mix[0].src); - ui->trnWeight_1->setValue(generalSettings.trainer.mix[0].weight); - ui->trnMode_2->setCurrentIndex(generalSettings.trainer.mix[1].mode); - ui->trnChn_2->setCurrentIndex(generalSettings.trainer.mix[1].src); - ui->trnWeight_2->setValue(generalSettings.trainer.mix[1].weight); - ui->trnMode_3->setCurrentIndex(generalSettings.trainer.mix[2].mode); - ui->trnChn_3->setCurrentIndex(generalSettings.trainer.mix[2].src); - ui->trnWeight_3->setValue(generalSettings.trainer.mix[2].weight); - ui->trnMode_4->setCurrentIndex(generalSettings.trainer.mix[3].mode); - ui->trnChn_4->setCurrentIndex(generalSettings.trainer.mix[3].src); - ui->trnWeight_4->setValue(generalSettings.trainer.mix[3].weight); + const FieldRange weightrng = TrainerMix::getWeightRange(); + grid = new QGridLayout(this); + + int col = 0; + int row = 0; + + if (stickcnt) { + addLabel(tr("Mode"), row, 1); + addLabel(tr("Weight"), row, 2); + addLabel(tr("Source"), row++, 3); + + for (int i = 0; i < 4; i++, row++) { // TODO constant + col = 0; + addLabel(Boards::getAnalogInputName(board, i), row, col++); + + AutoComboBox *mode = new AutoComboBox(this); + mode->setModel(editorItemModels->getItemModel(modeid)); + mode->setField(generalSettings.trainer.mix[i].mode, this); + grid->addWidget(mode, row, col++); + + AutoSpinBox *weight = new AutoSpinBox(this); + weight->setRange(weightrng.min, weightrng.max); + weight->setField(generalSettings.trainer.mix[i].weight, this); + grid->addWidget(weight, row, col++); + + AutoComboBox *src = new AutoComboBox(this); + src->setModel(editorItemModels->getItemModel(srcid)); + src->setField(generalSettings.trainer.mix[i].src, this); + grid->addWidget(src, row, col++); + } + + addLine(row++, 0, grid->columnCount()); + } + + col = 0; + addLabel(tr("Multiplier"), row, col++); + + const FieldRange ppmmrng = GeneralSettings::getPPM_MultiplierRange(); + + AutoDoubleSpinBox *multi = new AutoDoubleSpinBox(this); + multi->setDecimals(ppmmrng.decimals); + multi->setOffset(ppmmrng.offset); + multi->setSingleStep(ppmmrng.step); + multi->setRange(ppmmrng.min, ppmmrng.max); + multi->setField(generalSettings.PPM_Multiplier, this); + grid->addWidget(multi, row++, col); + + col = 0; + + addLabel(tr("Calibration"), row, col++); + + for (int i = 0; i < stickcnt; i++, col++) { + AutoLineEdit *calib = new AutoLineEdit(this); + calib->setText(QString("%1").arg(generalSettings.trainer.calib[i])); + calib->setReadOnly(true); + grid->addWidget(calib, row, col); + } + + addHSpring(grid, grid->columnCount(), 0); + addVSpring(grid, 0, grid->rowCount()); + disableMouseScrolling(); } TrainerPanel::~TrainerPanel() { - delete ui; } -void TrainerPanel::on_trnMode_1_currentIndexChanged(int index) +void TrainerPanel::addLabel(QString text, int row, int col) { - generalSettings.trainer.mix[0].mode = index; - emit modified(); + QLabel *label = new QLabel(this); + label->setText(text); + grid->addWidget(label, row, col); } -void TrainerPanel::on_trnChn_1_currentIndexChanged(int index) +void TrainerPanel::addLine(int row, int col, int colspan) { - generalSettings.trainer.mix[0].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_1_editingFinished() -{ - generalSettings.trainer.mix[0].weight = ui->trnWeight_1->value(); - emit modified(); -} - -void TrainerPanel::on_trnMode_2_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[1].mode = index; - emit modified(); -} - -void TrainerPanel::on_trnChn_2_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[1].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_2_editingFinished() -{ - generalSettings.trainer.mix[1].weight = ui->trnWeight_2->value(); - emit modified(); -} - -void TrainerPanel::on_trnMode_3_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[2].mode = index; - emit modified(); -} - -void TrainerPanel::on_trnChn_3_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[2].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_3_editingFinished() -{ - generalSettings.trainer.mix[2].weight = ui->trnWeight_3->value(); - emit modified(); -} - -void TrainerPanel::on_trnMode_4_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[3].mode = index; - emit modified(); -} - -void TrainerPanel::on_trnChn_4_currentIndexChanged(int index) -{ - generalSettings.trainer.mix[3].src = index; - emit modified(); -} - -void TrainerPanel::on_trnWeight_4_editingFinished() -{ - generalSettings.trainer.mix[3].weight = ui->trnWeight_4->value(); - emit modified(); + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + line->setLineWidth(1); + line->setMidLineWidth(0); + grid->addWidget(line, row, col, 1, colspan); } diff --git a/companion/src/generaledit/trainer.h b/companion/src/generaledit/trainer.h index 56442dc8b..e0c777af4 100644 --- a/companion/src/generaledit/trainer.h +++ b/companion/src/generaledit/trainer.h @@ -18,42 +18,23 @@ * GNU General Public License for more details. */ -#ifndef _TRAINER_H_ -#define _TRAINER_H_ +#pragma once #include "generaledit.h" #include "eeprominterface.h" -namespace Ui { - class Trainer; -} +class CompoundItemModelFactory; class TrainerPanel : public GeneralPanel { Q_OBJECT public: - TrainerPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware); + TrainerPanel(QWidget *parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * editorItemModels); virtual ~TrainerPanel(); - private slots: - void on_trnMode_1_currentIndexChanged(int index); - void on_trnChn_1_currentIndexChanged(int index); - void on_trnWeight_1_editingFinished(); - void on_trnMode_2_currentIndexChanged(int index); - void on_trnChn_2_currentIndexChanged(int index); - void on_trnWeight_2_editingFinished(); - void on_trnMode_3_currentIndexChanged(int index); - void on_trnChn_3_currentIndexChanged(int index); - void on_trnWeight_3_editingFinished(); - void on_trnMode_4_currentIndexChanged(int index); - void on_trnChn_4_currentIndexChanged(int index); - void on_trnWeight_4_editingFinished(); - private: - Ui::Trainer *ui; - - void setValues(); + QGridLayout * grid; + void addLabel(QString text, int row, int col); + void addLine(int row, int col, int colspan); }; - -#endif // _TRAINER_H_ diff --git a/companion/src/generaledit/trainer.ui b/companion/src/generaledit/trainer.ui deleted file mode 100644 index 312999a0d..000000000 --- a/companion/src/generaledit/trainer.ui +++ /dev/null @@ -1,475 +0,0 @@ - - - Trainer - - - - 0 - 0 - 400 - 300 - - - - Form - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - Mode - - - - - - - Input - - - - - - - Weight - - - - - - - - 0 - 0 - - - - Rud - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - - 0 - 0 - - - - Ele - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - - 0 - 0 - - - - Thr - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - 0 - 0 - - - - Ail - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - Off - - - - - += (Sum) - - - - - := (Replace) - - - - - - - - - - - - - - Beeper volume - -0 - Quiet. No beeps at all. -1 - No Keys. Normal beeps but menu keys do not beep. -2 - Normal. -3 - Loud. -4 - Extra loud. - - - - chn1 - - - - - chn2 - - - - - chn3 - - - - - chn4 - - - - - - - - - - - - - - - - - -125 - - - 125 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index c1cd7de79..5f749fa33 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -620,6 +620,17 @@ void TableLayout::setColumnWidth(int col, int width) #if defined(TABLE_LAYOUT) tableWidget->setColumnWidth(col, width); #else + gridWidget->setColumnMinimumWidth(col, width); +#endif +} + +void TableLayout::setColumnWidth(int col, QString str) +{ +#if defined(TABLE_LAYOUT) +#else + QFontMetrics *f = new QFontMetrics(QFont()); + QSize sz = f->size(Qt::TextSingleLine, str); + setColumnWidth(col, sz.width()); #endif } @@ -628,12 +639,19 @@ void TableLayout::pushRowsUp(int row) #if defined(TABLE_LAYOUT) #else // Push the rows up - QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ); + QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); gridWidget->addItem(spacer, row, 0); #endif - // Push rows upward - // addDoubleSpring(gridLayout, 5, num_fsw+1); +} +void TableLayout::pushColumnsLeft(int col) +{ +#if defined(TABLE_LAYOUT) +#else + // Push the columns to the left + QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + gridWidget->addItem(spacer, 0, col); +#endif } QString Helpers::getChecklistsPath() diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 8bb1cc6b8..98783439b 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -191,7 +191,9 @@ public: void resizeColumnsToContents(); void setColumnWidth(int col, int width); + void setColumnWidth(int col, QString str); void pushRowsUp(int row); + void pushColumnsLeft(int col); private: #if defined(TABLE_LAYOUT) diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 7f8b80219..600f58e97 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -33,20 +33,20 @@ float curveLinear(float x, float coeff, float yMin, float yMid, float yMax) { - float a = (yMax-yMin) / 200.0; - return yMin + a * (x+100.0); + float a = (yMax - yMin) / 200.0; + return yMin + a * (x + 100.0); } float c9xexpou(float point, float coeff) { - float x = point*1024.0/100.0; - float k = coeff*256.0/100.0; - return ((k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256) / 1024.0 * 100; + float x = point * 1024.0 / 100.0; + float k = coeff * 256.0 / 100.0; + return ((k * x * x * x / (1024 * 1024) + x * (256 - k) + 128) / 256) / 1024.0 * 100; } float curveExpo(float x, float coeff, float yMin, float yMid, float yMax) { - float a = (yMax-yMin) / 100.0; + float a = (yMax - yMin) / 100.0; x += 100.0; x /= 2.0; @@ -57,14 +57,14 @@ float curveExpo(float x, float coeff, float yMin, float yMid, float yMax) else { coeff = -coeff; x = 100 - x; - return round((100.0 - c9xexpou(x, coeff))*a + yMin); + return round((100.0 - c9xexpou(x, coeff)) * a + yMin); } } float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax) { bool invert; - if (x<0) { + if (x < 0) { x = -x; invert = 1; } @@ -74,12 +74,12 @@ float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax float y; if (coeff >= 0) { - y = round(c9xexpou(x, coeff) * (yMax/100.0)); + y = round(c9xexpou(x, coeff) * (yMax / 100.0)); } else { coeff = -coeff; x = 100.0 - x; - y = round((100.0-c9xexpou(x, coeff)) * (yMax/100.0)); + y = round((100.0 - c9xexpou(x, coeff)) * (yMax / 100.0)); } if (invert) { @@ -91,9 +91,9 @@ float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax) { - float a = (yMax-yMid) / 100.0; + float a = (yMax - yMid) / 100.0; - if (x<0) + if (x < 0) x = -x; float y; @@ -102,8 +102,8 @@ float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax } else { coeff = -coeff; - x = 100-x; - y = round((100.0-c9xexpou(x, coeff)) * a + yMid); + x = 100 - x; + y = round((100.0 - c9xexpou(x, coeff)) * a + yMid); } return y; @@ -148,17 +148,14 @@ CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & // The edit curve button QPushButton * edit = new QPushButton(this); edit->setProperty("index", i); - QPalette palette; - palette.setBrush(QPalette::Active, QPalette::Button, QBrush(colors[i])); - palette.setBrush(QPalette::Active, QPalette::ButtonText, QBrush(Qt::white)); -#ifdef __APPLE__ - edit->setStyleSheet(QString("color: %1;").arg(colors[i].name())); -#elif defined WIN32 || !defined __GNUC__ - edit->setStyleSheet(QString("background-color: %1; color: white;").arg(colors[i].name())); -#else + +#ifdef __GNUC__ + // creates more compact and likely consistent buttons across OS flavors edit->setStyleSheet(QString("background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;").arg(colors[i].name())); +#else + edit->setStyleSheet(QString("background-color: %1; color: white;").arg(colors[i].name())); #endif - edit->setPalette(palette); + edit->setText(tr("Curve %1").arg(i + 1)); edit->setContextMenuPolicy(Qt::CustomContextMenu); edit->setToolTip(tr("Popup menu available")); @@ -173,7 +170,6 @@ CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & // The curve plot checkbox QCheckBox * plot = new QCheckBox(this); plot->setProperty("index", i); - plot->setPalette(palette); connect(plot, SIGNAL(toggled(bool)), this, SLOT(plotCurve(bool))); if (i < limit) { ui->curvesLayout->addWidget(plot, i, 2, 1, 1); @@ -332,9 +328,15 @@ void CurvesPanel::updateCurve() int numpoints = model->curves[k].count; for (int i = 0; i < numpoints - 1; i++) { if (model->curves[k].type == CurveData::CURVE_TYPE_CUSTOM) - scene->addLine(centerX + (qreal)model->curves[k].points[i].x*width/200, centerY - (qreal)model->curves[k].points[i].y*height/200,centerX + (qreal)model->curves[k].points[i+1].x*width/200,centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen); + scene->addLine(centerX + (qreal)model->curves[k].points[i].x * width / 200, + centerY - (qreal)model->curves[k].points[i].y * height / 200, + centerX + (qreal)model->curves[k].points[i + 1].x * width / 200, + centerY - (qreal)model->curves[k].points[i + 1].y * height / 200, pen); else - scene->addLine(GFX_MARGIN + i*width/(numpoints-1), centerY - (qreal)model->curves[k].points[i].y*height/200,GFX_MARGIN + (i+1)*width/(numpoints-1),centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen); + scene->addLine(GFX_MARGIN + i * width / (numpoints - 1), + centerY - (qreal)model->curves[k].points[i].y * height / 200, + GFX_MARGIN + (i + 1) * width / (numpoints - 1), + centerY - (qreal)model->curves[k].points[i + 1].y * height / 200, pen); } } } @@ -356,11 +358,13 @@ void CurvesPanel::updateCurve() else { nodex->setFixedX(true); } - nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x * width / 200, centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); + nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x * width / 200, + centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); } else { nodex->setFixedX(true); - nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); + nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), + centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); } connect(nodex, SIGNAL(moved(int, int)), this, SLOT(onNodeMoved(int, int))); connect(nodex, SIGNAL(focus()), this, SLOT(onNodeFocus())); @@ -592,7 +596,8 @@ void CurvesPanel::on_curveApply_clicked() } if (apply) { - model->curves[currentCurve].points[i].y = templates[index].function(x, ui->curveCoeff->value(), ui->yMin->value(), ui->yMid->value(), ui->yMax->value()); + model->curves[currentCurve].points[i].y = templates[index].function(x, ui->curveCoeff->value(), ui->yMin->value(), ui->yMid->value(), + ui->yMax->value()); } } diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 3a3a7e910..de6609591 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -182,6 +182,7 @@ void TimerPanel::onCountdownBeepChanged(int index) #define MASK_ACCESS (1<<13) #define MASK_RX_FREQ (1<<14) #define MASK_RF_POWER (1<<15) +#define MASK_RF_RACING_MODE (1<<16) quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; @@ -371,9 +372,9 @@ void ModulePanel::setupFailsafes() void ModulePanel::update() { - const PulsesProtocol protocol = (PulsesProtocol)module.protocol; - const Board::Type board = firmware->getBoard(); - const Multiprotocols::MultiProtocolDefinition & pdef = multiProtocols.getProtocol(module.multi.rfProtocol); + 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; @@ -406,6 +407,8 @@ void ModulePanel::update() mask |= MASK_RX_NUMBER | MASK_ACCESS; if (moduleIdx == 0 && HAS_EXTERNAL_ANTENNA(board) && generalSettings.antennaMode == 0 /* 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: @@ -447,8 +450,6 @@ void ModulePanel::update() mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES; mask |= MASK_SUBTYPES | MASK_RX_FREQ | MASK_RF_POWER; break; - case PULSES_OFF: - break; default: break; } @@ -512,6 +513,14 @@ void ModulePanel::update() 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); @@ -803,6 +812,11 @@ void ModulePanel::on_disableChMap_stateChanged(int state) module.multi.disableMapping = (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); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 82e69ec75..955f2189b 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -95,6 +95,7 @@ class ModulePanel : public ModelPanel void onSubTypeChanged(); void on_autoBind_stateChanged(int state); void on_disableChMap_stateChanged(int state); + void on_racingMode_stateChanged(int state); void on_disableTelem_stateChanged(int state); void on_lowPower_stateChanged(int state); void on_r9mPower_currentIndexChanged(int index); diff --git a/companion/src/modeledit/setup_module.ui b/companion/src/modeledit/setup_module.ui index 654af2a54..652975acd 100644 --- a/companion/src/modeledit/setup_module.ui +++ b/companion/src/modeledit/setup_module.ui @@ -1077,7 +1077,21 @@ - + + + + Disable Telemetry + + + + + + + Racing Mode + + + + Qt::Horizontal @@ -1090,13 +1104,6 @@ - - - - Disable Telemetry - - - diff --git a/companion/src/shared/CMakeLists.txt b/companion/src/shared/CMakeLists.txt index ffd5942bf..7bedeebf6 100644 --- a/companion/src/shared/CMakeLists.txt +++ b/companion/src/shared/CMakeLists.txt @@ -1,25 +1,33 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -set(shared_SRCS - genericpanel.cpp - hexspinbox.cpp - verticalscrollarea.cpp +set(shared_NAMES + genericpanel + verticalscrollarea + autobitsetcheckbox + autocheckbox + autocombobox + autodoublespinbox + autohexspinbox + autolabel + autolineedit + autoprecisioncombobox + autoslider + autospinbox + autotimeedit ) -set(shared_HDRS - autobitsetcheckbox.h - autocheckbox.h - autocombobox.h - autodoublespinbox.h - autohexspinbox.h - autolineedit.h - genericpanel.h - hexspinbox.h - autoprecisioncombobox.h - autotimeedit.h -) +foreach(name ${shared_NAMES}) + set(shared_SRCS ${shared_SRCS} ${name}.cpp) + set(shared_HDRS ${shared_HDRS} ${name}.h) +endforeach() qt5_wrap_cpp(shared_SRCS ${shared_HDRS}) +# add after moc generation to stop warnings +set(shared_SRCS ${shared_SRCS} + autowidget.h + autowidget.cpp +) + add_library(shared ${shared_SRCS}) target_link_libraries(shared PRIVATE Qt5::Widgets ${CPN_COMMON_LIB}) diff --git a/companion/src/shared/autobitsetcheckbox.cpp b/companion/src/shared/autobitsetcheckbox.cpp new file mode 100644 index 000000000..93faf35e5 --- /dev/null +++ b/companion/src/shared/autobitsetcheckbox.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autobitsetcheckbox.h" + +AutoBitsetCheckBox::AutoBitsetCheckBox(QWidget * parent) : + AutoBitsetCheckBox(QString(), parent) +{ +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + init(); +} + +// int field constructors +AutoBitsetCheckBox::AutoBitsetCheckBox(int & field, int bitmask, const QString & text, QWidget * parent) : + AutoBitsetCheckBox(field, bitmask, false, text, parent) +{ +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, invert); + init(); +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, false, toggleMask); + init(); +} + +// unsigned field constructors +AutoBitsetCheckBox::AutoBitsetCheckBox(unsigned & field, int bitmask, const QString & text, QWidget * parent) : + AutoBitsetCheckBox(field, bitmask, false, text, parent) +{ +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, invert); + init(); +} + +AutoBitsetCheckBox::AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString & text, QWidget * parent) : + QCheckBox(text, parent), + AutoWidget() +{ + setField(field, bitmask, false, toggleMask); + init(); +} + +AutoBitsetCheckBox::~AutoBitsetCheckBox() +{ +} + +void AutoBitsetCheckBox::setField(int & field, int bitmask, bool invert, int toggleMask) +{ + m_field = &field; + initField(bitmask, invert, toggleMask); +} + +void AutoBitsetCheckBox::setField(unsigned & field, int bitmask, bool invert, int toggleMask) +{ + m_field = (int *)&field; + initField(bitmask, invert, toggleMask); +} + +void AutoBitsetCheckBox::setBitmask(int bitmask) +{ + m_bitmask = bitmask; + updateValue(); +} + +void AutoBitsetCheckBox::setToggleMask(int toggleMask) +{ + m_toggleMask = toggleMask; + updateValue(); +} + +void AutoBitsetCheckBox::setInverted(bool invert) +{ + m_invert = invert; + updateValue(); +} + +void AutoBitsetCheckBox::updateValue() +{ + if (m_field) + setLock(true); + setChecked((m_invert && !m_toggleMask) != bool(*m_field & m_bitmask)); + setLock(false); +} + +void AutoBitsetCheckBox::onToggled(bool checked) +{ + if (!m_field || lock()) + return; + + if (m_toggleMask) { + if (checked) { + *m_field &= ~m_toggleMask; + *m_field |= m_bitmask; + } + else { + *m_field &= ~m_bitmask; + *m_field |= m_toggleMask; + } + } + else { + if (m_invert) + checked = !checked; + if (checked) + *m_field |= m_bitmask; + else + *m_field &= ~m_bitmask; + } + + emit currentDataChanged(checked); + dataChanged(); +} + +void AutoBitsetCheckBox::init() +{ + connect(this, &QCheckBox::toggled, this, &AutoBitsetCheckBox::onToggled); +} + +void AutoBitsetCheckBox::initField(int bitmask, bool invert, int toggleMask) +{ + m_bitmask = bitmask; + m_toggleMask = toggleMask; + m_invert = toggleMask ? false : invert; + updateValue(); +} diff --git a/companion/src/shared/autobitsetcheckbox.h b/companion/src/shared/autobitsetcheckbox.h index af831948e..cf54bae85 100644 --- a/companion/src/shared/autobitsetcheckbox.h +++ b/companion/src/shared/autobitsetcheckbox.h @@ -20,165 +20,50 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" -class AutoBitsetCheckBox: public QCheckBox +#include + +class AutoBitsetCheckBox: public QCheckBox, public AutoWidget { Q_OBJECT - //! \property bitmask The value (shifted bit(s)) to set/clear based on checked state. - Q_PROPERTY(int bitmask READ bitmask WRITE setBitmask) - //! \property inverted If \e true, then the \p bitmask is cleared when the checkbox is checked, and vice versa. Default behavior is to set the bitmask when checked. - Q_PROPERTY(bool inverted READ inverted WRITE setInverted) - //! \property toggleMask If > 0, then set this value when clearing the \p bitmask value (toggles between the two masks being set). \p inverted is ignored if \p toggleMask is set. - Q_PROPERTY(int toggleMask READ toggleMask WRITE setToggleMask) public: - explicit AutoBitsetCheckBox(QWidget *parent = nullptr) : - AutoBitsetCheckBox(QString(), parent) {} - explicit AutoBitsetCheckBox(const QString &text, QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - init(); - } + explicit AutoBitsetCheckBox(QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(const QString &text, QWidget * parent = nullptr); // int field constructors - explicit AutoBitsetCheckBox(int & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : - AutoBitsetCheckBox(field, bitmask, false, text, parent) {} - explicit AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, invert); - init(); - } - explicit AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, false, toggleMask); - init(); - } + explicit AutoBitsetCheckBox(int & field, int bitmask, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString & text = QString(), QWidget * parent = nullptr); // unsigned field constructors - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : - AutoBitsetCheckBox(field, bitmask, false, text, parent) {} - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, invert); - init(); - } - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : - QCheckBox(text, parent) - { - setField(field, bitmask, false, toggleMask); - init(); - } + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString & text = QString(), QWidget * parent = nullptr); + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString & text = QString(), QWidget * parent = nullptr); - inline int bitmask() const { return m_bitmask; } - inline int toggleMask() const { return m_toggleMask; } - inline bool inverted() const { return m_toggleMask ? false : m_invert; } + virtual ~AutoBitsetCheckBox(); - public slots: - void setField(int & field, int bitmask = 0, bool invert = false, int toggleMask = 0) - { - m_field = &field; - initField(bitmask, invert, toggleMask); - } + virtual void updateValue() override; - void setField(unsigned & field, int bitmask = 0, bool invert = false, int toggleMask = 0) - { - m_field = reinterpret_cast(&field); - initField(bitmask, invert, toggleMask); - } + void setField(int & field, int bitmask = 0, bool invert = false, int toggleMask = 0); + void setField(unsigned & field, int bitmask = 0, bool invert = false, int toggleMask = 0); + void setBitmask(int bitmask); + void setToggleMask(int toggleMask); + void setInverted(bool invert); - void setBitmask(int bitmask) - { - m_bitmask = bitmask; - updateValue(); - } - - void setToggleMask(int toggleMask) - { - m_toggleMask = toggleMask; - updateValue(); - } - - void setInverted(bool invert) - { - m_invert = invert; - updateValue(); - } - - void updateValue() - { - if (m_panel && m_panel->lock) - return; - if (!m_field) - return; - const bool oldLock = setLocked(true); - setChecked((m_invert && !m_toggleMask) != bool(*m_field & m_bitmask)); - setLocked(oldLock); - } - - void setPanel(GenericPanel * panel) - { - m_panel = panel; - } + signals: + void currentDataChanged(bool checked); protected slots: - void init() - { - connect(this, &AutoBitsetCheckBox::toggled, this, &AutoBitsetCheckBox::onToggled); - } + void onToggled(bool checked); - void initField(int bitmask, bool invert, int toggleMask) - { - m_bitmask = bitmask; - m_toggleMask = toggleMask; - m_invert = toggleMask ? false : invert; - updateValue(); - } - - void onToggled(bool checked) - { - if (!m_field) - return; - - if (m_toggleMask) { - if (checked) { - *m_field &= ~m_toggleMask; - *m_field |= m_bitmask; - } - else { - *m_field &= ~m_bitmask; - *m_field |= m_toggleMask; - } - } - else { - if (m_invert) - checked = !checked; - if (checked) - *m_field |= m_bitmask; - else - *m_field &= ~m_bitmask; - } - - if (m_panel && !m_lock) - emit m_panel->modified(); - } - - bool setLocked(bool lock) - { - const bool ret = m_lock; - m_lock = lock; - return ret; - } - - protected: - int * m_field = nullptr; + private: + int *m_field = nullptr; int m_bitmask = 0; int m_toggleMask = 0; bool m_invert = false; - bool m_lock = false; - GenericPanel * m_panel = nullptr; + + void init(); + void initField(int bitmask, bool invert, int toggleMask); }; diff --git a/companion/src/shared/autocheckbox.cpp b/companion/src/shared/autocheckbox.cpp new file mode 100644 index 000000000..a2af1395c --- /dev/null +++ b/companion/src/shared/autocheckbox.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autocheckbox.h" + +AutoCheckBox::AutoCheckBox(QWidget * parent): + QCheckBox(parent), + AutoWidget(), + m_field(nullptr), + m_invert(false) +{ + connect(this, &QCheckBox::toggled, this, &AutoCheckBox::onToggled); +} + +AutoCheckBox::~AutoCheckBox() +{ +} + +void AutoCheckBox::setField(bool & field, GenericPanel * panel, bool invert) +{ + m_field = &field; + m_invert = invert; + setPanel(panel); + updateValue(); +} + +void AutoCheckBox::setInvert(bool invert) +{ + m_invert = invert; + updateValue(); +} + +void AutoCheckBox::onToggled(bool checked) +{ + if (m_field && !lock()) { + const bool val = m_invert ? !checked : checked; + *m_field = val; + emit currentDataChanged(val); + dataChanged(); + } +} + +void AutoCheckBox::updateValue() +{ + if (m_field) { + setLock(true); + setChecked(m_invert ? !(*m_field) : *m_field); + setLock(false); + } +} diff --git a/companion/src/shared/autocheckbox.h b/companion/src/shared/autocheckbox.h index 1c1a7207c..5eb0a3dfb 100644 --- a/companion/src/shared/autocheckbox.h +++ b/companion/src/shared/autocheckbox.h @@ -20,62 +20,30 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" -class AutoCheckBox: public QCheckBox +#include + +class AutoCheckBox : public QCheckBox, public AutoWidget { Q_OBJECT public: - explicit AutoCheckBox(QWidget * parent = nullptr): - QCheckBox(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(toggled(bool)), this, SLOT(onToggled(bool))); - } + explicit AutoCheckBox(QWidget * parent = nullptr); + virtual ~AutoCheckBox(); - void setField(bool & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + virtual void updateValue() override; - void setEnabled(bool enabled) - { - QCheckBox::setEnabled(enabled); - } - - void updateValue() - { - if (field) { - lock = true; - setChecked(*field); - lock = false; - } - } + void setField(bool & field, GenericPanel * panel = nullptr, bool invert = false); + void setInvert(bool invert); signals: void currentDataChanged(bool value); protected slots: - void onToggled(bool checked) - { - if (panel && panel->lock) - return; - if (field && !lock) { - *field = checked; - emit currentDataChanged(checked); - if (panel) - emit panel->modified(); - } - } + void onToggled(bool checked); - protected: - bool *field = nullptr; - GenericPanel *panel = nullptr; - bool lock = false; + private: + bool *m_field; + bool m_invert; }; diff --git a/companion/src/shared/autocombobox.cpp b/companion/src/shared/autocombobox.cpp new file mode 100644 index 000000000..a14e08f2e --- /dev/null +++ b/companion/src/shared/autocombobox.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autocombobox.h" + +AutoComboBox::AutoComboBox(QWidget * parent): + QComboBox(parent), + AutoWidget(), + m_field(nullptr), + m_next(0), + m_hasModel(false), + m_rawSource(nullptr), + m_rawSwitch(nullptr) +{ + connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); +} + +AutoComboBox::~AutoComboBox() +{ +} + +void AutoComboBox::clear() +{ + if (m_hasModel) + return; + + setLock(true); + QComboBox::clear(); + m_next = 0; + setLock(false); +} + +void AutoComboBox::insertItems(int index, const QStringList & items) +{ + if (m_hasModel) + return; + + foreach(QString item, items) { + addItem(item); + } +} + +void AutoComboBox::addItem(const QString & item) +{ + if (m_hasModel) + return; + + addItem(item, m_next++); +} + +void AutoComboBox::addItem(const QString & item, int value) +{ + if (m_hasModel) + return; + + setLock(true); + QComboBox::addItem(item, value); + setLock(false); + updateValue(); +} + +void AutoComboBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = (int *)&field; + m_rawSource = nullptr; + m_rawSwitch = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setField(int & field, GenericPanel * panel) +{ + m_field = &field; + m_rawSource = nullptr; + m_rawSwitch = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setField(RawSource & field, GenericPanel * panel) +{ + m_rawSource = &field; + m_rawSwitch = nullptr; + m_field = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setField(RawSwitch & field, GenericPanel * panel) +{ + m_rawSwitch = &field; + m_rawSource = nullptr; + m_field = nullptr; + setFieldInit(panel); +} + +void AutoComboBox::setFieldInit(GenericPanel * panel) +{ + setPanel(panel); + updateValue(); +} + +void AutoComboBox::setModel(QAbstractItemModel * model) +{ + setLock(true); + QComboBox::setModel(model); + setLock(false); + m_hasModel = true; + updateValue(); +} + +void AutoComboBox::setAutoIndexes() +{ + if (m_hasModel) + return; + + for (int i = 0; i < count(); ++i) + setItemData(i, i); + updateValue(); +} + +void AutoComboBox::updateValue() +{ + if (!m_field && !m_rawSource && !m_rawSwitch) + return; + + setLock(true); + + if (m_field) + setCurrentIndex(findData(*m_field)); + else if (m_rawSource) + setCurrentIndex(findData(m_rawSource->toValue())); + else if (m_rawSwitch) + setCurrentIndex(findData(m_rawSwitch->toValue())); + + setLock(false); +} + +void AutoComboBox::onCurrentIndexChanged(int index) +{ + if (lock() || index < 0) + return; + + bool ok; + const int val = itemData(index).toInt(&ok); + if (!ok) + return; + + if (m_field && *m_field != val) { + *m_field = val; + } + else if (m_rawSource && m_rawSource->toValue() != val) { + *m_rawSource = RawSource(val); + } + else if (m_rawSwitch && m_rawSwitch->toValue() != val) { + *m_rawSwitch = RawSwitch(val); + } + else + return; + + emit currentDataChanged(val); + dataChanged(); +} diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index ca6054555..1502a6176 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -20,174 +20,49 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" #include "rawsource.h" #include "rawswitch.h" -class AutoComboBox: public QComboBox +#include + +class AutoComboBox : public QComboBox, public AutoWidget { Q_OBJECT public: - explicit AutoComboBox(QWidget * parent = nullptr): - QComboBox(parent), - field(nullptr), - panel(nullptr), - next(0), - lock(false), - hasModel(false), - rawSource(nullptr), - rawSwitch(nullptr) - { - connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); - } + explicit AutoComboBox(QWidget * parent = nullptr); + virtual ~AutoComboBox(); - void clear() - { - if (!hasModel) { - lock = true; - QComboBox::clear(); - next = 0; - lock = false; - } - } + // QComboBox + virtual void addItem(const QString & item); + virtual void addItem(const QString & item, int value); + virtual void insertItems(int index, const QStringList & items); + // AutoWidget + virtual void updateValue() override; - virtual void insertItems(int index, const QStringList & items) - { - if (!hasModel) { - foreach(QString item, items) { - addItem(item); - } - } - } + void clear(); - virtual void addItem(const QString & item) - { - if (!hasModel) - addItem(item, next++); - } + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setField(int & field, GenericPanel * panel = nullptr); + void setField(RawSource & field, GenericPanel * panel = nullptr); + void setField(RawSwitch & field, GenericPanel * panel = nullptr); - virtual void addItem(const QString & item, int value) - { - if (!hasModel) { - lock = true; - QComboBox::addItem(item, value); - lock = false; - updateValue(); - } - } - - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = (int *)&field; - this->rawSource = nullptr; - this->rawSwitch = nullptr; - this->panel = panel; - updateValue(); - } - - void setField(int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->rawSource = nullptr; - this->rawSwitch = nullptr; - this->panel = panel; - updateValue(); - } - - void setField(RawSource & field, GenericPanel * panel = nullptr) - { - this->rawSource = &field; - this->rawSwitch = nullptr; - this->field = nullptr; - this->panel = panel; - updateValue(); - } - - void setField(RawSwitch & field, GenericPanel * panel = nullptr) - { - this->rawSwitch = &field; - this->rawSource = nullptr; - this->field = nullptr; - this->panel = panel; - updateValue(); - } - - void setModel(QAbstractItemModel * model) - { - lock = true; - QComboBox::setModel(model); - lock = false; - hasModel = true; - updateValue(); - } - - void setAutoIndexes() - { - if (!hasModel) { - for (int i = 0; i < count(); ++i) - setItemData(i, i); - updateValue(); - } - } - - void updateValue() - { - if (!field && !rawSource && !rawSwitch) - return; - - lock = true; - - if (field) - setCurrentIndex(findData(*field)); - else if (rawSource) - setCurrentIndex(findData(rawSource->toValue())); - else if (rawSwitch) - setCurrentIndex(findData(rawSwitch->toValue())); - - lock = false; - } + void setAutoIndexes(); + void setModel(QAbstractItemModel * model); signals: void currentDataChanged(int value); protected slots: - void onCurrentIndexChanged(int index) - { - if (panel && panel->lock) - return; - if (lock || index < 0) - return; + void onCurrentIndexChanged(int index); - bool ok; - const int val = itemData(index).toInt(&ok); - if (!ok) - return; + private: + int *m_field; + int m_next; + bool m_hasModel; + RawSource *m_rawSource; + RawSwitch *m_rawSwitch; - if (field && *field != val) { - *field = val; - } - else if (rawSource && rawSource->toValue() != val) { - *rawSource = RawSource(val); - } - else if (rawSwitch && rawSwitch->toValue() != val) { - *rawSwitch = RawSwitch(val); - } - else - return; - - emit currentDataChanged(val); - if (panel) - emit panel->modified(); - } - - protected: - int *field = nullptr; - GenericPanel *panel = nullptr; - int next = 0; - bool lock = false; - bool hasModel = false; - RawSource *rawSource = nullptr; - RawSwitch *rawSwitch = nullptr; + void setFieldInit(GenericPanel * panel); }; diff --git a/companion/src/shared/autodoublespinbox.cpp b/companion/src/shared/autodoublespinbox.cpp new file mode 100644 index 000000000..a5590035f --- /dev/null +++ b/companion/src/shared/autodoublespinbox.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autodoublespinbox.h" + +AutoDoubleSpinBox::AutoDoubleSpinBox(QWidget * parent): + QDoubleSpinBox(parent), + AutoWidget(), + m_field(nullptr), + m_offset(0) +{ + connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double))); +} + +AutoDoubleSpinBox::~AutoDoubleSpinBox() +{ +} + +void AutoDoubleSpinBox::setField(int & field, GenericPanel * panel) +{ + m_field = &field; + setPanel(panel); + updateValue(); +} + +void AutoDoubleSpinBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = (int *)&field; + setPanel(panel); + updateValue(); +} + +void AutoDoubleSpinBox::setDecimals(int prec) +{ + QDoubleSpinBox::setDecimals(prec); + updateValue(); +} + +void AutoDoubleSpinBox::setOffset(int offset) +{ + m_offset = offset; + updateValue(); +} + +void AutoDoubleSpinBox::updateValue() +{ + if (m_field) { + setLock(true); + setValue(float(*m_field + m_offset) / multiplier()); + setLock(false); + } +} + +int AutoDoubleSpinBox::multiplier() +{ + switch (decimals()) { + case 1: + return 10; + case 2: + return 100; + case 3: + return 1000; + default: + return 1; + } + } + +void AutoDoubleSpinBox::onValueChanged(double value) +{ + if (m_field && !lock()) { + *m_field = round(value * multiplier() - m_offset); + emit currentDataChanged(*m_field); + dataChanged(); + } +} diff --git a/companion/src/shared/autodoublespinbox.h b/companion/src/shared/autodoublespinbox.h index a6466269e..31eef93c4 100644 --- a/companion/src/shared/autodoublespinbox.h +++ b/companion/src/shared/autodoublespinbox.h @@ -20,87 +20,38 @@ #pragma once +#include "autowidget.h" + #include -#include "genericpanel.h" + #if __GNUC__ #include #endif -class AutoDoubleSpinBox: public QDoubleSpinBox +class AutoDoubleSpinBox : public QDoubleSpinBox, public AutoWidget { Q_OBJECT public: - explicit AutoDoubleSpinBox(QWidget * parent = nullptr): - QDoubleSpinBox(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double))); - } + explicit AutoDoubleSpinBox(QWidget * parent = nullptr); + virtual ~AutoDoubleSpinBox(); - void setField(int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + virtual void updateValue() override; - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = (int *)&field; - this->panel = panel; - updateValue(); - } - - void updateValue() - { - if (field) { - lock = true; - setValue(float(*field) / multiplier()); - lock = false; - } - } - - void setDecimals(int prec) - { - QDoubleSpinBox::setDecimals(prec); - updateValue(); - } - - protected: - int multiplier() - { - switch (decimals()) { - case 1: - return 10; - case 2: - return 100; - default: - return 1; - } - } + void setField(int & field, GenericPanel * panel = nullptr); + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setDecimals(int prec); + void setOffset(int offset); signals: void currentDataChanged(double value); protected slots: - void onValueChanged(double value) - { - if (panel && panel->lock) - return; - if (field && !lock) { - *field = round(value * multiplier()); - emit currentDataChanged(value); - if (panel) { - emit panel->modified(); - } - } - } + void onValueChanged(double value); - protected: - int * field = nullptr; - GenericPanel * panel = nullptr; - bool lock = false; + private: + int *m_field; + int m_offset; + + int multiplier(); }; diff --git a/companion/src/shared/autohexspinbox.cpp b/companion/src/shared/autohexspinbox.cpp new file mode 100644 index 000000000..3096a1c2f --- /dev/null +++ b/companion/src/shared/autohexspinbox.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autohexspinbox.h" + +AutoHexSpinBox::AutoHexSpinBox(QWidget * parent): + QSpinBox(parent), + AutoWidget(), + m_field(nullptr) +{ + setRange(0, AUTOHEXSPINBOX_MAX_VALUE); + connect(this, QOverload::of(&QSpinBox::valueChanged), this, &AutoHexSpinBox::onValueChanged); +} + +AutoHexSpinBox::~AutoHexSpinBox() +{ +} + +void AutoHexSpinBox::setField(unsigned int & field, const unsigned int min, const unsigned int max, GenericPanel * panel) +{ + m_field = &field; + setRange(min, max); + setPanel(panel); + updateValue(); +} + +void AutoHexSpinBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = &field; + setRange(0, AUTOHEXSPINBOX_MAX_VALUE); + setPanel(panel); + updateValue(); +} + +void AutoHexSpinBox::setRange(const unsigned int min, const unsigned int max) +{ + QSpinBox::setRange(min, max); + + m_length = QString("%1").arg(max, 0, 16).size(); + + m_validator = new QRegExpValidator(QRegExp(QString("[0-9A-Fa-f]{1,%1}").arg(m_length)), this); +} + +void AutoHexSpinBox::updateValue() +{ + if (m_field) { + setLock(true); + setValue(*m_field); + setLock(false); + } +} + +void AutoHexSpinBox::onValueChanged(int value) +{ + if (m_field && !lock()) { + if (*m_field != (unsigned)value) { + *m_field = (unsigned)value; + emit currentDataChanged(value); + dataChanged(); + } + } +} + +// reimplemented +QValidator::State AutoHexSpinBox::validate(QString &text, int &pos) const +{ + return m_validator->validate(text, pos); +} + +int AutoHexSpinBox::valueFromText(const QString &text) const +{ + return text.toInt(0, 16); +} + +QString AutoHexSpinBox::textFromValue(int value) const +{ + return QString("%1").arg(value, m_length, 16, QChar('0')).toUpper(); +} diff --git a/companion/src/shared/autohexspinbox.h b/companion/src/shared/autohexspinbox.h index 40cc24da4..6d7711c32 100644 --- a/companion/src/shared/autohexspinbox.h +++ b/companion/src/shared/autohexspinbox.h @@ -20,58 +20,42 @@ #pragma once -#include "hexspinbox.h" -#include "genericpanel.h" +#include "autowidget.h" -class AutoHexSpinBox: public HexSpinBox +#include +#include + +class QRegExpValidator; + +class AutoHexSpinBox : public QSpinBox, public AutoWidget { Q_OBJECT public: - explicit AutoHexSpinBox(QWidget * parent = nullptr): - HexSpinBox(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); - } + constexpr static unsigned int AUTOHEXSPINBOX_MAX_VALUE {65535}; - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + explicit AutoHexSpinBox(QWidget * parent = nullptr); + virtual ~AutoHexSpinBox(); - void updateValue() - { - if (field) { - lock = true; - setValue(*field); - lock = false; - } - } + virtual void updateValue(); + + void setField(unsigned int & field, const unsigned int min = 0, const unsigned int max = AUTOHEXSPINBOX_MAX_VALUE, GenericPanel * panel = nullptr); + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setRange(unsigned int min = 0, unsigned int max = AUTOHEXSPINBOX_MAX_VALUE); + + protected: + QValidator::State validate(QString &text, int &pos) const; + int valueFromText(const QString &text) const; + QString textFromValue(int value) const; signals: void currentDataChanged(int value); protected slots: - void onValueChanged(int value) - { - if (panel && panel->lock) - return; - if (field && !lock) { - *field = value; - emit currentDataChanged(value); - if (panel) { - emit panel->modified(); - } - } - } + void onValueChanged(int value); - protected: - unsigned int * field = nullptr; - GenericPanel * panel = nullptr; - bool lock = false; + private: + unsigned int *m_field; + QRegExpValidator *m_validator; + unsigned int m_length; }; diff --git a/companion/src/shared/autolabel.cpp b/companion/src/shared/autolabel.cpp new file mode 100644 index 000000000..61099a9a7 --- /dev/null +++ b/companion/src/shared/autolabel.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autolabel.h" + +#include + +AutoLabel::AutoLabel(QWidget * parent): + QLabel(parent), + AutoWidget(), + m_charField(NULL), + m_strField(nullptr) +{ +} + +AutoLabel::~AutoLabel() +{ +} + +void AutoLabel::setField(char * field, GenericPanel * panel) +{ + m_charField = field; + setPanel(panel); + updateValue(); +} + +void AutoLabel::setField(QString & field, GenericPanel * panel) +{ + m_strField = &field; + setPanel(panel); + updateValue(); +} + +void AutoLabel::setWidth(int numChars) +{ + QFontMetrics *f = new QFontMetrics(this->font()); + QSize sz = f->size(Qt::TextSingleLine, QString(numChars, QChar('X'))); + setMinimumWidth(sz.width()); + delete f; +} + +void AutoLabel::updateValue() +{ + setLock(true); + if (m_strField) + setText(*m_strField); + else if (m_charField) + setText(m_charField); + setLock(false); +} diff --git a/companion/src/shared/autolabel.h b/companion/src/shared/autolabel.h new file mode 100644 index 000000000..369a1e680 --- /dev/null +++ b/companion/src/shared/autolabel.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#pragma once + +#include "autowidget.h" + +#include + +class AutoLabel: public QLabel, public AutoWidget +{ + Q_OBJECT + + public: + explicit AutoLabel(QWidget * parent = nullptr); + virtual ~AutoLabel(); + + virtual void updateValue() override; + + void setField(char * field, GenericPanel * panel = nullptr); + void setField(QString & field, GenericPanel * panel = nullptr); + void setWidth(int numChars); + + private: + char *m_charField; + QString *m_strField; +}; diff --git a/companion/src/shared/autolineedit.cpp b/companion/src/shared/autolineedit.cpp new file mode 100644 index 000000000..79e92d482 --- /dev/null +++ b/companion/src/shared/autolineedit.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autolineedit.h" + +AutoLineEdit::AutoLineEdit(QWidget * parent, bool updateOnChange): + QLineEdit(parent), + AutoWidget(), + m_charField(NULL), + m_strField(nullptr) +{ + if (updateOnChange) + connect(this, &QLineEdit::textChanged, this, &AutoLineEdit::onEdited); + else + connect(this, &QLineEdit::editingFinished, this, &AutoLineEdit::onEdited); +} + +AutoLineEdit::~AutoLineEdit() +{ +} + +void AutoLineEdit::setField(char * field, int len, GenericPanel * panel) +{ + m_charField = field; + setPanel(panel); + setValidator(new QRegExpValidator(QRegExp("[ A-Za-z0-9_.-,]*"), this)); + if (len) + setMaxLength(len); + updateValue(); +} + +void AutoLineEdit::setField(QString & field, int len, GenericPanel * panel) +{ + m_strField = &field; + if (len) + setMaxLength(len); + setPanel(panel); + updateValue(); +} + +void AutoLineEdit::updateValue() +{ + setLock(true); + if (m_strField) + setText(*m_strField); + else if (m_charField) + setText(m_charField); + setLock(false); +} + +void AutoLineEdit::onEdited() +{ + if (lock()) + return; + + if (m_strField && *m_strField != text()) + *m_strField = text(); + else if (m_charField && strcmp(m_charField, text().toLatin1())) + strcpy(m_charField, text().toLatin1()); + else + return; + + emit currentDataChanged(); + dataChanged(); +} diff --git a/companion/src/shared/autolineedit.h b/companion/src/shared/autolineedit.h index b2815796c..be1cccaa3 100644 --- a/companion/src/shared/autolineedit.h +++ b/companion/src/shared/autolineedit.h @@ -20,83 +20,31 @@ #pragma once +#include "autowidget.h" + #include #include -#include "genericpanel.h" -class AutoLineEdit: public QLineEdit +class AutoLineEdit: public QLineEdit, public AutoWidget { Q_OBJECT public: - explicit AutoLineEdit(QWidget * parent = nullptr, bool updateOnChange = false): - QLineEdit(parent), - field(NULL), - strField(nullptr), - panel(nullptr), - lock(false) - { - if (updateOnChange) - connect(this, &AutoLineEdit::textChanged, this, &AutoLineEdit::onEdited); - else - connect(this, &AutoLineEdit::editingFinished, this, &AutoLineEdit::onEdited); - } + explicit AutoLineEdit(QWidget * parent = nullptr, bool updateOnChange = false); + virtual ~AutoLineEdit(); - void setField(char * field, int len, GenericPanel * panel = nullptr) - { - this->field = field; - this->panel = panel; - setValidator(new QRegExpValidator(QRegExp("[ A-Za-z0-9_.-,]*"), this)); - setMaxLength(len); - updateValue(); - } + virtual void updateValue() override; - void setField(QString & field, int len = 0, GenericPanel * panel = nullptr) - { - strField = &field; - this->panel = panel; - if (len) - setMaxLength(len); - updateValue(); - } - - void updateValue() - { - lock = true; - if (field) - setText(field); - else if (strField) - setText(*strField); - lock = false; - } + void setField(char * field, int len, GenericPanel * panel = nullptr); + void setField(QString & field, int len = 0, GenericPanel * panel = nullptr); signals: void currentDataChanged(); protected slots: - void onEdited() - { - if (panel && panel->lock) - return; - if (lock) - return; + void onEdited(); - if (field) - strcpy(field, text().toLatin1()); - else if (strField) - *strField = text(); - else - return; - - emit currentDataChanged(); - - if (panel) - emit panel->modified(); - } - - protected: - char * field; - QString * strField = nullptr; - GenericPanel * panel = nullptr; - bool lock = false; + private: + char *m_charField; + QString *m_strField; }; diff --git a/companion/src/shared/autoprecisioncombobox.cpp b/companion/src/shared/autoprecisioncombobox.cpp new file mode 100644 index 000000000..6add0ff92 --- /dev/null +++ b/companion/src/shared/autoprecisioncombobox.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autoprecisioncombobox.h" + +constexpr unsigned int APCB_DECIMALS_MIN {0}; +constexpr unsigned int APCB_DECIMALS_MAX {6}; + +AutoPrecisionComboBox::AutoPrecisionComboBox(QWidget * parent): + QComboBox(parent), + AutoWidget() +{ + setFieldInit(); + init(); +} + +AutoPrecisionComboBox::AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, QWidget * parent, GenericPanel * panel): + QComboBox(parent), + AutoWidget() +{ + m_field = &field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + init(); +} + +AutoPrecisionComboBox::AutoPrecisionComboBox(int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, QWidget * parent, GenericPanel * panel): + QComboBox(parent), + AutoWidget() +{ + m_field = (unsigned int *)&field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + init(); +} + +AutoPrecisionComboBox::~AutoPrecisionComboBox() +{ +} + +void AutoPrecisionComboBox::setField(int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, GenericPanel * panel) +{ + m_field = (unsigned int *)&field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + setPanel(panel); +} + +void AutoPrecisionComboBox::setField(unsigned int & field, unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, GenericPanel * panel) +{ + m_field = &field; + setFieldInit(minDecimals, maxDecimals, padding, suffix, panel); + setPanel(panel); +} + +void AutoPrecisionComboBox::setMinDecimals(unsigned int minDecimals) +{ + m_minDecimals = rangecheckDecimals(minDecimals); + if (m_minDecimals > m_maxDecimals) + m_minDecimals = m_maxDecimals; + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setMaxDecimals(unsigned int maxDecimals) +{ + m_maxDecimals = rangecheckDecimals(maxDecimals); + if (m_maxDecimals < m_minDecimals) + m_maxDecimals = m_minDecimals; + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setDecimalsRange(unsigned int min, unsigned int max) +{ + setMinDecimals(min); + setMaxDecimals(max); +} + +void AutoPrecisionComboBox::setPadding(bool padding) +{ + m_padding = padding; + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setSuffix(QString suffix) +{ + m_suffix = suffix.trimmed(); + if (!lock()) + updateList(); +} + +void AutoPrecisionComboBox::setValue(int value) +{ + if (!m_field) + return; + + const unsigned int val = (unsigned int)value; + if (*m_field != val) { + *m_field = rangecheckDecimals(val); + updateValue(); + emit currentDataChanged(value); + dataChanged(); + } +} + +void AutoPrecisionComboBox::setValue(unsigned int value) +{ + if (!m_field) + return; + + if (*m_field != value) { + *m_field = rangecheckDecimals(value); + updateValue(); + emit currentDataChanged((int)value); + dataChanged(); + } +} + +void AutoPrecisionComboBox::updateValue() +{ + if (!m_field) + return; + + if (!isValidDecimals(*m_field)) + return; + + if (*m_field > m_maxDecimals) + setMaxDecimals(*m_field); + + setLock(true); + setCurrentIndex(findData(*m_field)); + setLock(false); +} + +void AutoPrecisionComboBox::setAutoIndexes() +{ + for (int i = 0; i < count(); ++i) + setItemData(i, i); + updateValue(); +} + +void AutoPrecisionComboBox::init() +{ + connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &AutoPrecisionComboBox::onCurrentIndexChanged); +} + +void AutoPrecisionComboBox::setFieldInit(unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix, GenericPanel * panel) +{ + setLock(true); + setMinDecimals(minDecimals); + setMaxDecimals(maxDecimals); + setPadding(padding); + setSuffix(suffix); + setLock(false); + setPanel(panel); + updateList(); +} + +void AutoPrecisionComboBox::updateList() +{ + setLock(true); + unsigned int i; + int j; + clear(); + + for (i = APCB_DECIMALS_MIN, j = (int)m_maxDecimals; j >= 0; i++, j--) { + if (i >= m_minDecimals) { + QString str = QString("0.%1").arg(QString(i, '0')); + if (m_padding) + str.append(QString(j, '_')); + if (m_suffix != "") + str.append(QString(" %1").arg(m_suffix)); + addItem(str, i); + } + } + + setSizeAdjustPolicy(QComboBox::AdjustToContents); + setLock(false); +} + +void AutoPrecisionComboBox::onCurrentIndexChanged(int index) +{ + if (!m_field || index < 0 || lock()) + return; + + if (*m_field != itemData(index).toUInt()) { + *m_field = itemData(index).toUInt(); + emit currentDataChanged(index); + dataChanged(); + } +} + +unsigned int AutoPrecisionComboBox::rangecheckDecimals(unsigned int decimals) +{ + unsigned int ret; + if (decimals < APCB_DECIMALS_MIN) + ret = APCB_DECIMALS_MIN; + else if (decimals > APCB_DECIMALS_MAX) + ret = APCB_DECIMALS_MAX; + else + ret = decimals; + return ret; +} + +bool AutoPrecisionComboBox::isValidDecimals(unsigned int value) +{ + if (value >= APCB_DECIMALS_MIN && value <= APCB_DECIMALS_MAX) + return true; + else + return false; +} diff --git a/companion/src/shared/autoprecisioncombobox.h b/companion/src/shared/autoprecisioncombobox.h index 72dd89406..b973c09fd 100644 --- a/companion/src/shared/autoprecisioncombobox.h +++ b/companion/src/shared/autoprecisioncombobox.h @@ -20,235 +20,50 @@ #pragma once -#include "genericpanel.h" +#include "autowidget.h" #include #include -class AutoPrecisionComboBox: public QComboBox +class AutoPrecisionComboBox : public QComboBox, public AutoWidget { - Q_OBJECT - //! \property minDecimals Determines the first list item - Q_PROPERTY(unsigned int minDecimals READ minDecimals WRITE setMinDecimals) - //! \property maxDecimals Determines the last list item - Q_PROPERTY(unsigned int maxDecimals READ maxDecimals WRITE setMaxDecimals) - //! \property padding If true, add trailing underscore placeholders to each list item - Q_PROPERTY(bool padding READ padding WRITE setPadding) - //! \property suffix Optional text suffix to each list value - Q_PROPERTY(QString suffix READ suffix WRITE setSuffix) + Q_OBJECT public: - explicit AutoPrecisionComboBox(QWidget * parent = nullptr): - QComboBox(parent) - { - init(); - } + explicit AutoPrecisionComboBox(QWidget * parent = nullptr); + explicit AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr); + explicit AutoPrecisionComboBox(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr); + virtual ~AutoPrecisionComboBox(); - explicit AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr): - QComboBox(parent) - { - setField(field, minDecimals, maxDecimals, padding, suffix, panel); - init(); - } + virtual void updateValue() override; - explicit AutoPrecisionComboBox(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr): - QComboBox(parent) - { - setField(field, minDecimals, maxDecimals, padding, suffix, panel); - init(); - } - - inline int minDecimals() const { return m_minDecimals; } - inline int maxDecimals() const { return m_maxDecimals; } - inline bool padding() const { return m_padding; } - inline QString suffix() const { return m_suffix; } - - public slots: - void setField(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr) - { - m_field = reinterpret_cast(&field); - initField(minDecimals, maxDecimals, padding, suffix); - setPanel(panel); - } - - void setField(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr) - { - m_field = &field; - initField(minDecimals, maxDecimals, padding, suffix); - setPanel(panel); - } - - void setMinDecimals(unsigned int minDecimals) - { - m_minDecimals = rangecheckDecimals(minDecimals); - if (m_minDecimals > m_maxDecimals) - m_minDecimals = m_maxDecimals; - if (!m_lock) - updateList(); - } - - void setMaxDecimals(unsigned int maxDecimals) - { - m_maxDecimals = rangecheckDecimals(maxDecimals); - if (m_maxDecimals < m_minDecimals) - m_maxDecimals = m_minDecimals; - if (!m_lock) - updateList(); - } - - void setPadding(bool padding) - { - m_padding = padding; - if (!m_lock) - updateList(); - } - - void setSuffix(QString suffix) - { - m_suffix = suffix.trimmed(); - if (!m_lock) - updateList(); - } - - void setPanel(GenericPanel * panel) - { - m_panel = panel; - } - - void setValue(int value) - { - if (!m_field) - return; - const unsigned int val = (unsigned int)value; - if (*m_field != val) { - *m_field = rangecheckDecimals(val); - updateValue(); - emit currentDataChanged(value); - } - } - - void setValue(unsigned int value) - { - if (!m_field) - return; - if (*m_field != value) { - *m_field = rangecheckDecimals(value); - updateValue(); - emit currentDataChanged((int)value); - } - } - - void updateValue() - { - if (!m_field) - return; - if (!isValidDecimals(*m_field)) - return; - if (*m_field > m_maxDecimals) { - setMaxDecimals(*m_field); - } - const bool savedlock = setLocked(true); - setCurrentIndex(findData(*m_field)); - setLocked(savedlock); - } - - void setAutoIndexes() - { - for (int i = 0; i < count(); ++i) - setItemData(i, i); - updateValue(); - } + void setField(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr); + void setField(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr); + void setMinDecimals(unsigned int minDecimals); + void setMaxDecimals(unsigned int maxDecimals); + void setDecimalsRange(unsigned int min, unsigned int max); + void setPadding(bool padding); + void setSuffix(QString suffix); + void setValue(int value); + void setValue(unsigned int value); + void setAutoIndexes(); signals: void currentDataChanged(int index); protected slots: - void init() - { - connect(this, QOverload::of(&AutoPrecisionComboBox::currentIndexChanged), this, &AutoPrecisionComboBox::onCurrentIndexChanged); - } + void onCurrentIndexChanged(int index); - void initField(unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix) - { - const bool savedlock = setLocked(true); - setMinDecimals(minDecimals); - setMaxDecimals(maxDecimals); - setPadding(padding); - setSuffix(suffix); - setLocked(savedlock); - updateList(); - } - - void updateList() - { - const bool savedlock = setLocked(true); - unsigned int i; - int j; - clear(); - - for (i = APCB_DECIMALS_MIN, j = (int)m_maxDecimals; j >= 0; i++, j--) { - if (i >= m_minDecimals) { - QString str = QString("0.%1").arg(QString(i, '0')); - if (m_padding) - str.append(QString(j, '_')); - if (m_suffix != "") - str.append(QString(" %1").arg(m_suffix)); - addItem(str, i); - } - } - - setSizeAdjustPolicy(QComboBox::AdjustToContents); - setLocked(savedlock); - } - - void onCurrentIndexChanged(int index) - { - if (index < 0 || (m_panel && m_panel->lock) || m_lock) - return; - if (m_field) { - *m_field = itemData(index).toUInt(); - emit currentDataChanged(index); - if (m_panel) - emit m_panel->modified(); - } - } - - unsigned int rangecheckDecimals(unsigned int decimals) - { - unsigned int ret; - if (decimals < APCB_DECIMALS_MIN) - ret = APCB_DECIMALS_MIN; - else if (decimals > APCB_DECIMALS_MAX) - ret = APCB_DECIMALS_MAX; - else - ret = decimals; - return ret; - } - - bool isValidDecimals(unsigned int value) - { - if (value >= APCB_DECIMALS_MIN && value <= APCB_DECIMALS_MAX) - return true; - else - return false; - } - - bool setLocked(bool lock) - { - const bool ret = m_lock; - m_lock = lock; - return ret; - } - - protected: - constexpr static unsigned int APCB_DECIMALS_MIN = {0}; - constexpr static unsigned int APCB_DECIMALS_MAX = {6}; - - unsigned int * m_field = nullptr; - GenericPanel * m_panel = nullptr; + private: + unsigned int *m_field = nullptr; unsigned int m_minDecimals = 0; - unsigned int m_maxDecimals = 0; + unsigned int m_maxDecimals = 1; bool m_padding = false; QString m_suffix = ""; - bool m_lock = false; + + void init(); + void setFieldInit(unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr); + bool isValidDecimals(unsigned int value); + unsigned int rangecheckDecimals(unsigned int decimals); + void updateList(); }; diff --git a/companion/src/shared/autoslider.cpp b/companion/src/shared/autoslider.cpp new file mode 100644 index 000000000..5b02b3603 --- /dev/null +++ b/companion/src/shared/autoslider.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autoslider.h" + +AutoSlider::AutoSlider(QWidget * parent): + QSlider(parent), + AutoWidget() +{ + init(); +} + +AutoSlider::AutoSlider(Qt::Orientation orientation, QWidget * parent): + QSlider(orientation, parent), + AutoWidget() +{ + init(); +} + +AutoSlider::~AutoSlider() +{ +} + +void AutoSlider::setField(int & field, int min, int max, GenericPanel * panel) +{ + m_field = &field; + setFieldInit(min, max, panel); +} + +void AutoSlider::setField(unsigned int & field, int min, int max, GenericPanel * panel) +{ + m_field = (int *)&field; + setFieldInit(min, max, panel); +} + +void AutoSlider::setTick(int interval, QSlider::TickPosition position) +{ + setTickInterval(interval); + setTickPosition(position); +} + +void AutoSlider::updateValue() +{ + if (m_field) { + setLock(true); + setValue(*m_field); + setLock(false); + } +} + +void AutoSlider::init() +{ + connect(this, &QSlider::valueChanged, this, &AutoSlider::onValueChanged); +} + +void AutoSlider::setFieldInit(int min, int max, GenericPanel * panel) +{ + setPanel(panel); + setRange(min, max); + updateValue(); +} + +void AutoSlider::onValueChanged(int value) +{ + if (m_field && !lock()) { + if (*m_field != value) { + *m_field = value; + emit currentDataChanged(value); + dataChanged(); + } + } +} diff --git a/companion/src/shared/autoslider.h b/companion/src/shared/autoslider.h new file mode 100644 index 000000000..cb3a2fd11 --- /dev/null +++ b/companion/src/shared/autoslider.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#pragma once + +#include "autowidget.h" + +#include + +class AutoSlider : public QSlider, public AutoWidget +{ + Q_OBJECT + + public: + explicit AutoSlider(QWidget * parent = nullptr); + explicit AutoSlider(Qt::Orientation orientation, QWidget * parent = nullptr); + virtual ~AutoSlider(); + + virtual void updateValue() override; + + void setField(int & field, int min, int max, GenericPanel * panel = nullptr); + void setField(unsigned int & field, int min, int max, GenericPanel * panel = nullptr); + void setTick(int interval, QSlider::TickPosition position); + + signals: + void currentDataChanged(int value); + + protected slots: + void onValueChanged(int value); + + private: + int *m_field = nullptr; + + void init(); + void setFieldInit(int min, int max, GenericPanel * panel); +}; diff --git a/companion/src/shared/autospinbox.cpp b/companion/src/shared/autospinbox.cpp new file mode 100644 index 000000000..ecee90edd --- /dev/null +++ b/companion/src/shared/autospinbox.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autospinbox.h" + +AutoSpinBox::AutoSpinBox(QWidget * parent): + QSpinBox(parent), + AutoWidget(), + m_field(nullptr) +{ + connect(this, QOverload::of(&QSpinBox::valueChanged), this, &AutoSpinBox::onValueChanged); +} + +AutoSpinBox::~AutoSpinBox() +{ +} + +void AutoSpinBox::setField(int & field, GenericPanel * panel) +{ + m_field = &field; + setFieldInit(panel); +} + +void AutoSpinBox::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = (int *)&field; + setFieldInit(panel); +} + +void AutoSpinBox::setFieldInit(GenericPanel * panel) +{ + setPanel(panel); + updateValue(); +} + +void AutoSpinBox::updateValue() +{ + if (m_field) { + setLock(true); + setValue(*m_field); + setLock(false); + } +} + +void AutoSpinBox::onValueChanged(int value) +{ + if (m_field && !lock()) { + if (*m_field != value) { + *m_field = value; + emit currentDataChanged(value); + dataChanged(); + } + } +} diff --git a/companion/src/shared/autospinbox.h b/companion/src/shared/autospinbox.h new file mode 100644 index 000000000..38842fbe0 --- /dev/null +++ b/companion/src/shared/autospinbox.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#pragma once + +#include "autowidget.h" + +#include + +class AutoSpinBox : public QSpinBox, public AutoWidget +{ + Q_OBJECT + + public: + explicit AutoSpinBox(QWidget * parent = nullptr); + virtual ~AutoSpinBox(); + + virtual void updateValue() override; + + void setField(int & field, GenericPanel * panel = nullptr); + void setField(unsigned int & field, GenericPanel * panel = nullptr); + + signals: + void currentDataChanged(int value); + + protected slots: + void onValueChanged(int value); + + private: + int *m_field; + + void setFieldInit(GenericPanel * panel); +}; diff --git a/companion/src/shared/autotimeedit.cpp b/companion/src/shared/autotimeedit.cpp new file mode 100644 index 000000000..7b0c4422a --- /dev/null +++ b/companion/src/shared/autotimeedit.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "autotimeedit.h" + +AutoTimeEdit::AutoTimeEdit(QWidget * parent): + QTimeEdit(parent), + AutoWidget(), + m_field(nullptr) +{ + connect(this, &QTimeEdit::timeChanged, this, &AutoTimeEdit::onTimeChanged); +} + +AutoTimeEdit::~AutoTimeEdit() +{ +} + +void AutoTimeEdit::setField(unsigned int & field, GenericPanel * panel) +{ + m_field = &field; + setPanel(panel); + updateValue(); +} + +void AutoTimeEdit::setTimeRange(const QTime min, const QTime max) +{ + QTimeEdit::setMinimumTime(min); + QTimeEdit::setMaximumTime(max); +} + +void AutoTimeEdit::updateValue() +{ + if (m_field) { + setLock(true); + int hour = *m_field / 3600; + int min = (*m_field - (hour * 3600)) / 60; + int sec = (*m_field - (hour * 3600)) % 60; + setTime(QTime(hour, min, sec)); + setLock(false); + } +} + +void AutoTimeEdit::onTimeChanged(QTime time) +{ + if (!m_field || lock()) + return; + + unsigned int val = time.hour() * 3600 + time.minute() * 60 + time.second(); + if (*m_field != val) { + *m_field = val; + emit currentDataChanged(val); + dataChanged(); + } +} diff --git a/companion/src/shared/autotimeedit.h b/companion/src/shared/autotimeedit.h index 2ba75d70e..f398fd855 100644 --- a/companion/src/shared/autotimeedit.h +++ b/companion/src/shared/autotimeedit.h @@ -20,80 +20,30 @@ #pragma once -#include -#include "genericpanel.h" +#include "autowidget.h" -class AutoTimeEdit: public QTimeEdit +#include + +class AutoTimeEdit : public QTimeEdit, public AutoWidget { Q_OBJECT public: - explicit AutoTimeEdit(QWidget * parent = nullptr): - QTimeEdit(parent), - field(nullptr), - panel(nullptr), - lock(false) - { - connect(this, SIGNAL(timeChanged(QTime)), this, SLOT(onTimeChanged(QTime))); - } + explicit AutoTimeEdit(QWidget * parent = nullptr); + virtual ~AutoTimeEdit(); - void setField(unsigned int & field, GenericPanel * panel = nullptr) - { - this->field = &field; - this->panel = panel; - updateValue(); - } + virtual void updateValue() override; - void setMinimumTime(const QTime time) - { - QTimeEdit::setMinimumTime(time); - } - - void setMaximumTime(const QTime time) - { - QTimeEdit::setMaximumTime(time); - } - - void setEnabled(bool enabled) - { - QTimeEdit::setEnabled(enabled); - } - - void updateValue() - { - if (field) { - lock = true; - int hour = *field / 3600; - int min = (*field - (hour * 3600)) / 60; - int sec = (*field - (hour * 3600)) % 60; - setTime(QTime(hour, min, sec)); - lock = false; - } - } + void setField(unsigned int & field, GenericPanel * panel = nullptr); + void setTimeRange(const QTime min, const QTime max); signals: - void currentDataChanged(unsigned int value); + void currentDataChanged(unsigned int val); protected slots: - void onTimeChanged(QTime time) - { - if (panel && panel->lock) - return; - if (!field || lock) - return; + void onTimeChanged(QTime time); - unsigned int val = time.hour() * 3600 + time.minute() * 60 + time.second(); - if (*field != val) { - *field = val; - emit currentDataChanged(val); - if (panel) - emit panel->modified(); - } - } - - protected: - unsigned int *field = nullptr; - GenericPanel *panel = nullptr; - bool lock = false; + private: + unsigned int *m_field; }; diff --git a/companion/src/shared/hexspinbox.cpp b/companion/src/shared/autowidget.cpp similarity index 60% rename from companion/src/shared/hexspinbox.cpp rename to companion/src/shared/autowidget.cpp index 63889ef11..75fd1fb26 100644 --- a/companion/src/shared/hexspinbox.cpp +++ b/companion/src/shared/autowidget.cpp @@ -18,30 +18,44 @@ * GNU General Public License for more details. */ -#include "hexspinbox.h" +#include "autowidget.h" +#include "genericpanel.h" -HexSpinBox::HexSpinBox(QWidget *parent): - QSpinBox(parent) +AutoWidget::AutoWidget(): + m_panel(nullptr), + m_lock(false) { - validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this); } -QValidator::State HexSpinBox::validate(QString &text, int &pos) const +AutoWidget::~AutoWidget() { - return validator->validate(text, pos); } -int HexSpinBox::valueFromText(const QString &text) const +bool AutoWidget::lock() { - return text.toInt(0, 16); + return panelLock() | m_lock; } -QString HexSpinBox::textFromValue(int value) const +void AutoWidget::setLock(bool lock) { - QString text; - text.sprintf("%04X", value); - return text; + m_lock = lock; } +void AutoWidget::setPanel(GenericPanel * panel) +{ + m_panel = panel; +} +void AutoWidget::dataChanged() +{ + if (m_panel) + emit m_panel->modified(); +} +bool AutoWidget::panelLock() +{ + if (m_panel) + return m_panel->lock; + else + return false; +} diff --git a/companion/src/shared/autowidget.h b/companion/src/shared/autowidget.h new file mode 100644 index 000000000..820befbdc --- /dev/null +++ b/companion/src/shared/autowidget.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#pragma once + +class GenericPanel; + +// Note: cannot be a Qt object otherwise it will create a compiler ambiguity in the inheriting class +class AutoWidget +{ + friend class GenericPanel; + + public: + explicit AutoWidget(); + ~AutoWidget(); + + protected: + virtual void updateValue() = 0; + + bool lock(); + void setLock(bool lock); + void setPanel(GenericPanel * panel); + + void dataChanged(); + bool panelLock(); + + private: + GenericPanel *m_panel; + bool m_lock; +}; diff --git a/companion/src/shared/hexspinbox.h b/companion/src/shared/autowidgets.h similarity index 66% rename from companion/src/shared/hexspinbox.h rename to companion/src/shared/autowidgets.h index 204cc8a55..b2619acdb 100644 --- a/companion/src/shared/hexspinbox.h +++ b/companion/src/shared/autowidgets.h @@ -20,22 +20,13 @@ #pragma once -#include - -class QRegExpValidator; - -class HexSpinBox : public QSpinBox -{ - Q_OBJECT - - public: - HexSpinBox(QWidget *parent = 0); - - protected: - QValidator::State validate(QString &text, int &pos) const; - int valueFromText(const QString &text) const; - QString textFromValue(int value) const; - - private: - QRegExpValidator *validator; -}; +#include "autobitsetcheckbox.h" +#include "autocheckbox.h" +#include "autocombobox.h" +#include "autodoublespinbox.h" +#include "autohexspinbox.h" +#include "autolineedit.h" +#include "autoprecisioncombobox.h" +#include "autoslider.h" +#include "autospinbox.h" +#include "autotimeedit.h" diff --git a/companion/src/shared/genericpanel.cpp b/companion/src/shared/genericpanel.cpp index 5dfad648e..a60ed0cf0 100644 --- a/companion/src/shared/genericpanel.cpp +++ b/companion/src/shared/genericpanel.cpp @@ -19,6 +19,7 @@ */ #include "genericpanel.h" +#include "autowidget.h" #include #include @@ -127,3 +128,13 @@ void GenericPanel::disableMouseScrolling() foreach(QWidget * te, findChildren()) setFocusFilter(te); } + +void GenericPanel::updateAutoWidgets() +{ + foreach(QWidget *wdgt, findChildren()) { + AutoWidget *autowdgt = dynamic_cast(wdgt); + if (autowdgt) { + autowdgt->updateValue(); + } + } +} diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index 88c7f841b..32dfdadc8 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -32,15 +32,8 @@ class GenericPanel : public QWidget { Q_OBJECT - friend class AutoComboBox; - friend class AutoDoubleSpinBox; - friend class AutoCheckBox; - friend class AutoHexSpinBox; - friend class AutoLineEdit; friend class GVarGroup; - friend class AutoPrecisionComboBox; - friend class AutoBitsetCheckBox; - friend class AutoTimeEdit; + friend class AutoWidget; public: GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware); @@ -65,4 +58,5 @@ class GenericPanel : public QWidget virtual bool eventFilter(QObject *obj, QEvent *event); void setFocusFilter(QWidget * w); void disableMouseScrolling(); + void updateAutoWidgets(); }; diff --git a/companion/src/shared/verticalscrollarea.h b/companion/src/shared/verticalscrollarea.h index d409a18ca..4f6d2af18 100644 --- a/companion/src/shared/verticalscrollarea.h +++ b/companion/src/shared/verticalscrollarea.h @@ -26,6 +26,8 @@ class GenericPanel; class VerticalScrollArea : public QScrollArea { + Q_OBJECT + public: VerticalScrollArea(QWidget * parent, GenericPanel * panel); diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 2a6eea996..0ece144ea 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -1631,7 +1631,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Trainer Sticks - Manches Écolage + Écolage Manches @@ -1656,7 +1656,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Trainer Channels - Écolage Channels + Écolage Canaux @@ -2233,7 +2233,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import OpenTX ne supporte pas cette fonction sur cette carte - + OpenTX doesn't accept this radio protocol OpenTX ne supporte pas ce protocole radio @@ -8975,28 +8975,33 @@ Mixage actif par défaut si non-renseigné. Maître/Multi - + Disable Telemetry Désactiver la télémétrie - + + Racing Mode + Mode Course/Racing + + + Disable Ch. Map Désactiver "Mapping" des VOIES - + Show values in: Afficher les valeurs en: - + % abbreviation for percent - + μs abbreviation for microseconds @@ -9084,7 +9089,7 @@ Mixage actif par défaut si non-renseigné. ID d'enregistrement - + Failsafe Positions Positions Failsafe @@ -9137,100 +9142,100 @@ Mixage actif par défaut si non-renseigné. Négative - + Trainer Port Port écolage - + Internal Radio System Module HF interne - + External Radio Module Module HF externe - + Extra Radio System Module HF supplémentaire - + Radio System Module HF - + 10mW - 16CH 10mW - 16 VOIES - - + + 100mW - 16CH 100mW - 16 VOIES - + 500mW - 16CH 500mW - 16 VOIES - + Auto <= 1W - 16CH Auto <= 1W - 16 VOIES - - + + 25mW - 8CH 25mW - 8 VOIES - - + + 25mW - 16CH 25mW - 16 VOIES - + 200mW - 16CH (no telemetry) 200mW - 16 VOIES (pas de télémétrie) - + 500mW - 16CH (no telemetry) 500mW - 16 VOIES (pas de télémétrie) - + 100mW - 16CH (no telemetry) 100mW - 16 VOIES (pas de télémétrie) - + 25 mW - + 100 mW - + 500 mW - + 1 W - + 2 W @@ -9238,42 +9243,42 @@ Mixage actif par défaut si non-renseigné. ModulePanel - + Value Valeur - + Hold Maintien - + No Pulse Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - + Bind on channel Bind avec VOIE @@ -11031,87 +11036,87 @@ Si cette option est cochée, la voie des gaz est inversée: le ralenti est &apo SetupPanel - + Popup menu available Menu contextuel disponible - + Timer %1 Chrono %1 - + Profile Settings Paramètres du profil - + SD structure path not specified or invalid Chemin de la structure SD non spécifié ou invalide - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Timer. Are you sure? Effacer le Chrono. Êtes-vous sûr? - + Clear all Timers. Are you sure? Effacer tous les Chronos. Êtes-vous sûr? - + Cut Timer. Are you sure? Couper le Chrono. Êtes-vous sûr? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? diff --git a/companion/src/translations/companion_ja.ts b/companion/src/translations/companion_ja.ts index d423959d4..e276a2539 100644 --- a/companion/src/translations/companion_ja.ts +++ b/companion/src/translations/companion_ja.ts @@ -830,127 +830,127 @@ Mode 4: ChannelsPanel - + Name 名称 - + Subtrim サブトリム - + Min 最小 - + Max 最大 - + Direction 方向 - + Curve カーブ - + PPM Center PPM センター - + Linear Subtrim 線形サブトリム - + CH%1 CH%1 - + Popup menu available 利用可能なポップアップメニュー - + --- --- - + INV リバース - + Delete Channel. Are you sure? チャンネルを削除します。よろしいですか? - + Cut Channel. Are you sure? チャンネルを切り取ります。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Channel. Are you sure? チャンネルを消去します。よろしいですか? - + Clear all Channels. Are you sure? チャンネルをすべて消去します。よろしいですか? @@ -1090,37 +1090,37 @@ Mode 4: 送信機と機体モデルの設定 - + Select or create a file for exported Settings: エクスポートした設定ファイルを選択または作成します: - + Press the 'Retry' button to choose another file. 別のファイルを選択するには『再試行』ボタンを押してください。 - + Simulator for this firmware is not yet available このファームウェアのシミュレータはまだ利用できません - + Uknown error during Simulator startup. シミュレータ起動中に不明なエラーが発生しました。 - + Simulator Error シミュレータ エラー - + Data Load Error データロード エラー - + Error occurred while starting simulator. シミュレータ起動中にエラーが発生しました。 @@ -1370,7 +1370,7 @@ If you have a settings backup file, you may import that instead. CurveData - + CV CV @@ -1603,219 +1603,297 @@ If you have a settings backup file, you may import that instead. CustomFunctionData - + GF GF - + SF SF - + Override %1 上書き %1 - + Trainer Sticks トレーナー Sticks - + Trainer RUD トレーナー RUD - + Trainer ELE トレーナー ELE - + Trainer THR トレーナー THR - + Trainer AIL トレーナー AIL - + Trainer Channels トレーナー チャンネル - + Instant Trim トリム - + Play Sound サウンド 再生 - + Haptic - タッチパネル + バイブレーション - + Reset リセット - + Set %1 設定 %1 - + + Lua Script + Luaスクリプト + + + Set Failsafe フェイルセーフ設定 - + + Range Check Int. Module + 内部モジュール レンジチェック + + + + Range Check Ext. Module + 外部モジュール レンジチェック + + + + Beep 1 + ビープ 1 + + + + Beep 2 + ビープ 2 + + + + Beep 3 + ビープ 3 + + + + Warn 1 + 警告 1 + + + + Warn 2 + 警告 2 + + + + Cheep + チープ + + + + Ratata + ラッタッタ + + + + Tick + ティック + + + + Siren + サイレン + + + + Ring + リング + + + + Sci Fi + Sci Fi + + + + Robot + ロボット + + + + Chirp + + + + + Tada + Tada + + + + Cricket + クリケット + + + + Alarm Clock + アラームクロック + + + + Value + 値 + + + + Source + 選択元 + + + + Global Variable + グローバル変数 + + + + Inc/Decrement + 増/減 + + + Vario バリオ - + Play Track トラック 再生 - + Play Both 両方 実行 - + Play Value 値 実行 - - Play Script - スクリプト 実行 - - - + SD Logs SDログ - + Volume 音量 - + Backlight バックライト - + Screenshot 画面キャプチャ - + Background Music BGM - + Background Music Pause BGM一時停止 - + Adjust %1 調整 %1 - - RangeCheck Int. Module - レンジチェック 内部モジュール - - - - RangeCheck Ext. Module - レンジチェック 外部モジュール - - - + Bind Int. Module バインド 内部モジュール - + Bind Ext. Module バインド 外部モジュール - + + Played once, not during startup + 起動時以外 1回実行 + + + + No repeat + リピートなし + + + + Repeat %1s + リピート %1秒 + + + Flight フライト - + Telemetry テレメトリー - - Rotary Encoder - エンコーダ再生 - - - - REa - エンコーダ再生a - - - - REb - エンコーダ再生b - - - + s s - - - - <font color=red><b>Inconsistent parameter</b></font> - <font color=red><b>矛盾しているパラメータ</b></font> - - - - Value - 値 - - - - played once, not during startup - 起動時以外 1回実行 - - - - repeat(%1s) - リピート(%1s) - - - + DISABLED 無効 - + CFN CFN @@ -1823,142 +1901,122 @@ If you have a settings backup file, you may import that instead. CustomFunctionsPanel - + Switch スイッチ - + Action 実行 - + Parameters パラメータ - - Enable - 有効 - - - + Popup menu available 利用可能なポップアップメニュー - + SF%1 SF%1 - + GF%1 GF%1 - + + GV + GV + + + ON ON - + Error occurred while trying to play sound, possibly the file is already opened. (Err: %1 [%2]) サウンド再生中にエラーが発生しました。おそらくファイルは既に開かれています (Err: %1 [%2]) - + Unable to find or open sound file: %1 音源ファイルが見つからない、または開けません。 %1 - + Delete Function. Are you sure? ファンクションを削除します。よろしいですか? - + Cut Special Function. Are you sure? ファンクションを切り取ります。よろしいですか? - + Clear Function. Are you sure? ファンクションを消去します。よろしいですか? - + Clear all Functions. Are you sure? ファンクションをすべて消去します。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - - - Value - 値 - - - - Source - 選択元 - - - - GVAR - GVAR - - - - Increment - 増加 - CustomizeSplashDialog @@ -2164,11 +2222,64 @@ If you have a settings backup file, you may import that instead. このボード上のOpenTXはこの機能を受け付けません - + OpenTX doesn't accept this radio protocol OpenTXはこの送信機プロトコルを受け付けません + + DataHelpers + + + Disabled + 無効 + + + + Enabled + 有効 + + + + OFF + OFF + + + + ON + ON + + + + False + False + + + + True + True + + + + N + N + + + + Y + Y + + + + No + いいえ + + + + Yes + はい + + DebugOutput @@ -2374,12 +2485,12 @@ To <b>remove a remembered entry</b> from the filter list, first cho ExpoData - + INP INP - + (@%1) (@%1) @@ -2501,22 +2612,22 @@ If blank then the input is considered to be "ON" all the time.編集 %1 - + Popup menu available 利用可能なポップアップメニュー - + Clear All すべて消去 - + Set All すべて設定 - + Invert All すべてリバース @@ -2605,129 +2716,129 @@ If blank then the input is considered to be "ON" all the time. FileSyncDialog - + Synchronize Files ファイルを同期する - + Are you sure you wish to abort the sync? 同期を中止してよろしいですか? - + Source Folder: 移動元フォルダ: - + Destination Folder: 移動先フォルダ: - + %1%2 Both directions, to destination folder first %1%2の両方を最初の移動先フォルダへ - + %1%2 Both directions, to source folder first %1%2の両方を最初の移動元フォルダへ - + %1 Only from source folder to destination folder %1 移動元フォルダから移動先フォルダのみ - + %1 Only from destination folder to source folder %1 移動先フォルダから移動元フォルダのみ - + How to handle overwriting files which already exist in the destination folder. コピー先フォルダに既に存在するファイルを上書きする方法となります。 - + Copy only if newer and different (compare contents) 新しい場合、または異なる場合のみコピー (内容を比較して) - + Copy only if newer (do not compare contents) 新しい場合のみコピー (内容を比較しない) - + Copy only if different (ignore file time stamps) 異なる場合のみコピー (ファイルのタイムスタンプを無視) - + Always copy (force overwite existing files) 常にコピー (既存のファイルを強制上書き) - + Any size 任意のサイズ - + Skip files larger than this size. Enter zero for unlimited. このサイズより大きいファイルをスキップします。無制限の場合はゼロを入力してください。 - + Minimum reporting level. Events of this type and of higher importance are shown. WARNING: High log rates may make the user interface temporarily unresponsive. 最小レポートレベル このような種類の重要度の高いイベントが表示されます。 警告: ログレベルが高いと、ユーザーインターフェースが一時的に応答しなくなる場合があります。 - + Skipped スキップ - + Created 作成 - + Updated 更新 - + Errors Only エラーのみ - + Test-run only テスト稼働のみ - + Run as normal but do not actually copy anything. Useful for verifying results before real sync. 通常通りに実行しますが、実際には何もコピーしません。実際の同期前に結果を検証するのに役立ちます。 - + Log Level: ログレベル: - + Filters: フィルター: - + The "Include" filter will only copy files which match the pattern(s). The "Exclude" filter will skip files matching the filter pattern(s). The Include filter is evaluated first. @@ -2736,148 +2847,148 @@ The Include filter is evaluated first. Includeフィルターが最初に設定されます。 - + One or more file pattern(s) to exclude, separated by commas. Blank means exclude none. ?, *, and [...] wildcards accepted. 除外する1つ以上のファイルパターンでカンマで区切ります。 空白は何も除外しないことを意味します。 ?, *, および [...] ワイルドカードを使用できます。 - + One or more file pattern(s) to include, separated by commas. Blank means include all. ?, *, and [...] wildcards accepted. 含める1つまたは複数のファイルパターン。カンマで区切ります。 空白は何も除外しないことを意味します。 ?, *, および [...] ワイルドカードを使用できます。 - + Include: - + Exclude: - + Case sensitive 敏感なケース - - + + Follow links フォローリンク - + Include hidden 非表示にする - + Recursive 回帰的 - + Skip empty 空をスキップ - + Apply filters フィルター実行 - + Filter Options: フィルターオプション: - + Folder Options: フォルダーオプション: - - + + Options オプション - + Show extra options 追加オプションを表示 - + Reset to defaults 初期値へリセット - + Close 閉じる - + Sync. Direction: 方向の同期: - + Existing Files: 既存のファイル: - + Max. File Size: 最大ファイルサイズ: - + MB MB - + KB KB - + Abort バージョン情報 - + Start スタート - + Total: <b>%1</b>; Created: <b>%2</b>; Updated: <b>%3</b>; Skipped: <b>%4</b>; Errors: <font color=%6><b>%5</b></font>; Total: <b>%1</b>; 作成: <b>%2</b>; アップデート: <b>%3</b>; スキップ: <b>%4</b>; エラー: <font color=%6><b>%5</b></font>; - + Current: <b>%1</b> of 現在: <b>%1</b> - + Source folder not found. 移動元フォルダが見つかりません。 - + Destination folder not found. 移動先フォルダが見つかりません。 - + Source and destination folders are the same. 移動元と移動先のフォルダは同じです。 @@ -2975,7 +3086,7 @@ Blank means include all. ?, *, and [...] wildcards accepted. Haptic module installed - タッチパネル対応モジュールをインストール + バイブレーション対応モジュールをインストール @@ -3660,7 +3771,7 @@ You are currently using: FlightModeData - + FM FM @@ -4131,12 +4242,12 @@ p, li { white-space: pre-wrap; } - + Own value 独自トリム - + Flight mode %1 value フライトモード %1 値 @@ -4625,7 +4736,7 @@ p, li { white-space: pre-wrap; } Haptic Mode - タッチパネルモード + バイブレーションモード @@ -4758,16 +4869,6 @@ p, li { white-space: pre-wrap; } Optrex オプトレックス(京セラ) - - - Battery warning voltage. -This is the threashhold where the battery warning sounds. - -Acceptable values are 5v..10v - バッテリー警告電圧 -これはバッテリー警告音のしきい値です。 -許容値は5~10vです - Only Alarms @@ -4781,7 +4882,7 @@ Acceptable values are 5v..10v Haptic Length - タッチパネル長押し + バイブレーション時間 @@ -4801,7 +4902,7 @@ Acceptable values are 5v..10v Haptic Strength - タッチパネル感度 + バイブレーション強度 @@ -5004,6 +5105,16 @@ Acceptable values are 5v..10v A T E R ↔AIL ↕THR ↕ELE ↔RUD [A T E R] + + + Battery warning voltage. +This is the threashhold where the battery warning sounds. + +Acceptable values are 3v..12v + バッテリー警告電圧 +これはバッテリー警告音がなる3つの電圧しきい値です。 +許容値は3~12vです + Power Off Delay @@ -5727,27 +5838,27 @@ Are you sure ? InputsPanel - - + + Move Up 上へ移動 - + Ctrl+Up - - + + Move Down 下へ移動 - + Ctrl+Down @@ -5772,131 +5883,131 @@ Are you sure ? 選択したInput行を切り取ります。よろしいですか? - + Lines 直線 - + &Add &追加 - + Ctrl+A - + &Edit &編集 - + Enter Enter - + &Delete &削除 - - + + Delete 削除 - + &Copy &コピー - + Ctrl+C - + &Cut &切り取り - + Ctrl+X - + &Paste &貼り付け - + Ctrl+V - + Du&plicate &複製 - + Ctrl+U - + Input 入力 - + Insert 挿入 - + Clear 消去 - + Clear All すべて消去 - + Clear all Input lines. Are you sure? すべてのInput行を消去します。よろしいですか? - - Clear all lines for the selected Inputs. Are you sure? + + Clear all lines for the selected Input. Are you sure? 選択したすべてのInput行を消去します。よろしいですか? - - Delete all lines for the selected Inputs. Are you sure? + + Delete all lines for the selected Input. Are you sure? 選択したすべてのInput行を削除します。よろしいですか? LimitData - + INV リバース - + NOR ノーマル - + CH CH @@ -5904,7 +6015,7 @@ Are you sure ? LimitsGroup - + GV @@ -7658,12 +7769,12 @@ Do you want to save your changes? MixData - + MIX ミックス - + (@%1) @@ -7880,22 +7991,22 @@ If blank then the mix is considered to be "ON" all the time. - + Click to access popup menu クリックしてポップアップメニューにアクセス - + Clear All すべて消去 - + Set All すべて設定 - + Invert All すべてリバース @@ -7927,25 +8038,25 @@ If blank then the mix is considered to be "ON" all the time. MixesPanel - + Move Up 上へ移動 - + Ctrl+Up - + Move Down 下へ移動 - + Ctrl+Down @@ -7970,92 +8081,92 @@ If blank then the mix is considered to be "ON" all the time. 選択したMix行を切り取ります。よろしいですか? - + &Add &追加 - + Ctrl+A - + &Edit &編集 - + Enter Enter - + &Toggle highlight &ハイライト切替 - + Ctrl+T - + &Delete &削除 - + Delete 削除 - + &Copy &コピー - + Ctrl+C - + C&ut &切り取り - + Ctrl+X - + &Paste &貼り付け - + Ctrl+V - + Du&plicate &複製 - + Ctrl+U - + Clear Mixes? ミキサーを消去しますか? - + Really clear all the mixes? すべてのミキサーを本当に消去しますか? @@ -8063,17 +8174,17 @@ If blank then the mix is considered to be "ON" all the time. ModelData - + Model: 機体モデル: - + Throttle Source スロットル値 - + THR THR @@ -8091,52 +8202,52 @@ If blank then the mix is considered to be "ON" all the time. シミュレート - + Setup セットアップ - + Heli ヘリ - + Flight Modes フライトモード - + Inputs 入力 - + Mixes ミキサー - + Outputs 出力 - + Curves カーブ - + Logical Switches 論理スイッチ - + Special Functions スペシャルファンクション - + Telemetry テレメトリー @@ -8190,9 +8301,8 @@ If blank then the mix is considered to be "ON" all the time. - - - + + OFF OFF @@ -8233,7 +8343,7 @@ If blank then the mix is considered to be "ON" all the time. - + Mode モード @@ -8267,13 +8377,13 @@ If blank then the mix is considered to be "ON" all the time. - + Delay 遅延 - + Receiver 受信機 @@ -8365,397 +8475,342 @@ If blank then the mix is considered to be "ON" all the time. - - - - + + + + None なし - - - Name - 名称 - - Countdown - カウントダウン - - - - Minute call - 音声時報 - - - - Persistent - 持続 - - - - - + + FM%1 - + FM%1%2 - + FM%1+%2 - - + + NoTrim トリムなし - - + + Offset(%1) オフセット(%1) - + MULT! 複数! - + No DR/Expo DR/Expoなし - - + + Weight(%1) ウェイト(%1) - - + + Switch(%1) スイッチ(%1) - + Delay(u%1:d%2) 遅延(u%1:d%2) - + Slow(u%1:d%2) 速度低下(u%1:d%2) - + Warn(%1) 警告(%1) - + Disabled in all flight modes すべてのフライトモードを無効 - + Flight modes フライトモード - + Flight mode フライトモード - + All すべて - + Edge Edge:端 - + instant 簡易 - + Sticky Sticky:追尾 - + Timer タイマー - + missing 間違い - + Duration 期間 - - + + Custom カスタム - + Standard 標準 - + Extended Limits 拡張制限 - + Display Checklist ディスプレイ チェックリスト - + Global Functions グローバルファンクション - + Manual 手動 - + Auto 自動 - + Failsafe Mode フェイルセーフモード - - + + Hold ホールド - + No Pulse パルスなし - + Not set セットなし - + No pulses パルスなし - - Silent - 消音 - - - - Beeps - ビープ - - - - Voice - 音声 - - - - Haptic - タッチパネル - - - - Flight - フライト - - - - Manual reset - 手動リセット - - - + Step ステップ - + Display ディスプレイ - + Extended 拡張 - + Never なし - + On Change 変更 - + Always 常に - - - + + + Source 選択元 - + Trim idle only トリムアイドルのみ - + Warning 警告 - + Reversed リバース - - Tmr - Tmr - - - + FrSky S.PORT - + FrSky D - + FrSky D (cable) - + Alti - + Alti+ - + VSpeed - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells セル - + Min 最小 - + Max 最大 - + Numbers ナンバー - + Bars バー - + Script スクリプト - + Filename ファイル名 - + Error: Unable to open or read file! エラー: ファイルを開けないか、読み取れません! @@ -9018,33 +9073,38 @@ If blank then the mix is considered to be "ON" all the time. マスター/Multi - + Disable Telemetry テレメトリー無効 - + + Racing Mode + レーシングモード + + + Disable Ch. Map チャンネルマップ無効 - + Failsafe Positions フェイルセーフポジション - + Show values in: 値の表示: - + % abbreviation for percent - + μs abbreviation for microseconds @@ -9053,100 +9113,100 @@ If blank then the mix is considered to be "ON" all the time. ModuleData - + Trainer Port トレーナーポート - + Internal Radio System 内部送信システム - + External Radio Module 外部送信モジュール - + Extra Radio System 追加送信システム - + Radio System 送信システム - + 10mW - 16CH - - + + 100mW - 16CH - + 500mW - 16CH - + Auto <= 1W - 16CH - - + + 25mW - 8CH - - + + 25mW - 16CH - + 200mW - 16CH (no telemetry) 200mW - 16CH (テレメトリーなし) - + 500mW - 16CH (no telemetry) 500mW - 16CH (テレメトリーなし) - + 100mW - 16CH (no telemetry) 100mW - 16CH (テレメトリーなし) - + 25 mW 25 mW - + 100 mW 100 mW - + 500 mW 500 mW - + 1 W 1 W - + 2 W 2 W @@ -9164,42 +9224,42 @@ If blank then the mix is considered to be "ON" all the time. ModulePanel - + Value 値 - + Hold ホールド - + No Pulse パルスなし - + Ask 確認 - + Internal 内部 - + Internal + External 内部 + 外部 - + External 外部 - + Bind on channel チャンネルへバインド @@ -9213,8 +9273,8 @@ If blank then the mix is considered to be "ON" all the time. - - + + Name 名称 @@ -9270,7 +9330,7 @@ If blank then the mix is considered to be "ON" all the time. - + Switch スイッチ @@ -9290,313 +9350,318 @@ If blank then the mix is considered to be "ON" all the time. 持続 - + + Start + スタート + + + Modules モジュール - + Trainer port トレーナーポート - + Helicopter ヘリコプター - + Swash Swash - - + + Type タイプ - + Ring リング - + Input 入力 - + Weight ウェイト - + Long. cyc 長周期 - + Lateral cyc 側面周期 - + Collective コレクティブ - + Flight modes フライトモード - - + + Flight mode フライトモード - + F.In F.In - + F.Out F.Out - + Global vars グローバル変数 - - + + GV%1 GV%1 - + RE%1 RE%1 - + Unit ユニット - + Prec 精度 - - + + Min 最小 - - + + Max 最大 - + Popup ポップアップ - + Outputs 出力 - + Channel チャンネル - + Subtrim サブトリム - + Direct ダイレクト - + Curve カーブ - + PPM PPM - + Linear 線形 - + Global Variables グローバル変数 - + Inputs 入力 - + Mixers ミキサー - + Curves カーブ - + L%1 L%1 - + Logical Switches 論理スイッチ - + SF%1 SF%1 - + Special Functions スペシャルファンクション - + Telemetry テレメトリー - + Protocol プロトコル - + RSSI Alarms RSSIアラーム - + Low 低 - + Critical クリティカル - + Telemetry audio テレメトリー音源 - + Altimetry 高度計 - - + + Vario source バリオ 元値 - + Vario limits > バリオ リミット値 > - + Sink max Sink 最大 - + Sink min Sink 最小 - + Climb min クライム 最小 - + Climb max クライム 最大 - + Center silent センター サイレント - + Top Bar トップバー - + Volts source ボルト 元値 - + Altitude source 高度 元値 - + Parameters パラメータ - + Telemetry Sensors テレメトリーセンサー - + Telemetry Screens テレメトリースクリーン - + GF%1 GF%1 - + Global Functions グローバルファンクション - + Checklist チェックリスト @@ -9838,12 +9903,12 @@ If blank then the mix is considered to be "ON" all the time. QObject - + WARNING 警告 - + <p>Importing JumperTX data into OpenTX 2.3 is <b>not supported and dangerous.</b></p> <p>It is unfortunately not possible for us to differentiate JumperTX data from legitimate FrSky X10 data, but <b>You should only continue here if the file you opened comes from a real FrSky X10.</b></p> <p>Do you really want to continue?</p> <p>JumperTXデータをOpenTX 2.3にインポートすることは<b>サポート対象外となり危険な行為です。</b></p> <p>残念ながら、JumperTXデータを正当なFrSky X10データと区別することができませんが、<b>開いたファイルが実際のFrSky X10で生成されたものである場合にのみ、続行する事ができます。</b></p> <p>本当に続行しますか?</p> @@ -9851,7 +9916,7 @@ If blank then the mix is considered to be "ON" all the time. RadioData - + Models 機体モデル @@ -10418,24 +10483,6 @@ X OpenTX リリースノート - - RepeatComboBox - - - Played once, not during startup - 起動時以外 1回実行 - - - - No repeat - リピートなし - - - - %1s - %1秒 - - RudderPage @@ -10474,327 +10521,322 @@ X SensorData - + Formula 定式 - + Id ID - + Instance インスタンス - - + + Sensor センサー - + Sources 選択元 - + GPS GPS - + Alt 高度 - + Unit ユニット - + Precision 精度 - + Ratio レシオ - + Offset オフセット - + Auto Offset 自動オフセット - + Blades ブレード - + Multiplier 乗算 - + Filter フィルター - + Persist 持続 - + Positive ノーマル - + Log ログ - + Custom カスタム - + Calculated 計算値 - + Add 追加 - + Average 平均 - + Minimum 最低値 - + Maximum 最大値 - + Multiply 乗算 - + Totalize 合計 - + Cell セル - + Consumption 消費 - + Distance 距離 - + Lowest より低く - + Cell %1 セル %1 - + Highest より高く - + Delta 差分 - + Raw (-) Raw (-) - - + + V V - + A - + mA - + kts - + m/s - + km/h - + mph - + m - + f - + °C - + °F - + % - + mAh - + W - + mW - + dB - + rpms - + g - + ° - + Rad - + hours - + minutes - + seconds - + ml/minute - + Hertz - + mS - + uS - + + (default) + (初期値) + + + TELE - - - Y - Y - - - - N - N - Setup @@ -10976,92 +11018,87 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi SetupPanel - + Popup menu available 利用可能なポップアップメニュー - + Timer %1 タイマー %1 - - THR - - - - + Profile Settings プロファイル設定 - + SD structure path not specified or invalid SDカード保存先パスが指定されていないか無効です - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Timer. Are you sure? タイマーを消去します。よろしいですか? - + Clear all Timers. Are you sure? タイマーをすべて消去します。よろしいですか? - + Cut Timer. Are you sure? タイマーを切り取ります。よろしいですか? - + Delete Timer. Are you sure? タイマーを削除します。よろしいですか? @@ -12515,22 +12552,22 @@ Too many errors, giving up. 最大 - + None なし - + Numbers ナンバー - + Bars バー - + Script スクリプト @@ -12538,52 +12575,52 @@ Too many errors, giving up. TelemetryPanel - + Telemetry screen %1 テレメトリースクリーン %1 - + FrSky S.PORT - + FrSky D - + FrSky D (cable) - + Source 信号元 - + Low Alarm 低アラーム - + Critical Alarm クリティカルアラーム - + Winged Shadow How High Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (サポートなし) - + Delete Sensor. Are you sure? テレメトリーセンサーを削除します。よろしいですか? @@ -12701,72 +12738,72 @@ Too many errors, giving up. TelemetrySensorPanel - + TELE%1 - + Popup menu available 利用可能なポップアップメニュー - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Cut Telemetry Sensor. Are you sure? テレメトリーセンサーを切り取ります。よろしいですか? - + Clear Telemetry Sensor. Are you sure? テレメトリーセンサーを消去します。よろしいですか? - + Clear all Telemetry Sensors. Are you sure? テレメトリーセンサーのすべてを消去します。よろしいですか? @@ -13140,7 +13177,12 @@ hh:mm:ss カウントダウン - + + Start + スタート + + + Minute Call 音声時報 @@ -13148,21 +13190,91 @@ hh:mm:ss TimerData - + TMR TMR - + Timer %1 タイマー %1 - + TMR as in Timer TMR + + + Silent + 消音 + + + + Beeps + ビープ + + + + Voice + 音声 + + + + Haptic + バイブレーション + + + + 5s + 5秒 + + + + 10s + 10秒 + + + + 20s + 20秒 + + + + 30s + 30秒 + + + + Not persistent + 持続なし + + + + NOT + なし + + + + Persistent (flight) + 持続 (フライト) + + + + Flight + フライト + + + + Persistent (manual reset) + 持続 (手動リセット) + + + + Manual reset + 手動リセット + TimerEdit @@ -13189,44 +13301,6 @@ CTRL+スクロールまたはPAGE UP/DOWNキーを押すと、大きなステ 秒 - - TimerPanel - - - Silent - 消音 - - - - Beeps - ビープ - - - - Voice - 音声 - - - - Haptic - タッチパネル - - - - Not persistent - 持続なし - - - - Persistent (flight) - 持続 (フライト) - - - - Persistent (manual reset) - 持続 (手動リセット) - - Trainer diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt index b03b2c860..305451422 100644 --- a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt @@ -6,6 +6,7 @@ 14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video 41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED 42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED 42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol @@ -34,9 +35,9 @@ 6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH 45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH -45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH 16,0,ESKY,Std,0,Gyro,Pitch 16,1,ESKY,ET4,0,Gyro,Pitch 35,0,ESKY150,4CH,0 @@ -54,6 +55,7 @@ 28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 53,0,Height,5ch,0,Gear 53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 @@ -79,6 +81,8 @@ 65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 58,0,FX816,P38,1 20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib 20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib @@ -99,13 +103,14 @@ 26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib -57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess 2,1,Hubsan,H301,0,RTH,Light,Stab,Video 2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes 22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -71,0,JJRC345,Std,1,Flip,HLess,RTH +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 49,0,KF606,Std,1,Trim 9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim 9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim @@ -122,6 +127,8 @@ 17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH 44,0,NCC1701,Std,1,Warp 77,0,OMP,M2,0,THold,IdleUp,6G_3D 60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 @@ -138,10 +145,11 @@ 72,0,Q90C,Std,0,FMode,VTX+ 74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK 50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 19,0,Shenqi,Cycle,1 68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch @@ -174,3 +182,11 @@ 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess 52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiLOLI.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 000000000..4d309e37e --- /dev/null +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|TNE" + +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/radio/sdcard/horus/THEMES/Flexi/ghost.png b/radio/sdcard/horus/THEMES/Flexi/ghost.png new file mode 100644 index 000000000..cfec1cb35 Binary files /dev/null and b/radio/sdcard/horus/THEMES/Flexi/ghost.png differ diff --git a/radio/sdcard/horus/UTILITIES/MultiConfigExternal.lua b/radio/sdcard/horus/UTILITIES/MultiConfigExternal.lua new file mode 100755 index 000000000..a8baf3d13 --- /dev/null +++ b/radio/sdcard/horus/UTILITIES/MultiConfigExternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(1, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(1) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(1, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/horus/UTILITIES/MultiConfigInternal.lua b/radio/sdcard/horus/UTILITIES/MultiConfigInternal.lua new file mode 100755 index 000000000..29ced006c --- /dev/null +++ b/radio/sdcard/horus/UTILITIES/MultiConfigInternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(0, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(0) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(0, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/horus/SxR Calibrate/SxR_Calibrate.lua b/radio/sdcard/horus/UTILITIES/SxR Calibrate/SxR_Calibrate.lua similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/SxR_Calibrate.lua rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/SxR_Calibrate.lua diff --git a/radio/sdcard/horus/SxR Calibrate/img/back.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/back.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/back.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/back.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/delta_b.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/delta_b.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/delta_b.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/delta_b.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/done.bmp b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/done.bmp similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/done.bmp rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/done.bmp diff --git a/radio/sdcard/horus/SxR Calibrate/img/down.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/down.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/down.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/down.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/forward.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/forward.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/forward.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/forward.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/left.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/left.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/left.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/left.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/plane_b.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/plane_b.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/plane_b.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/plane_b.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/planev_b.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_b.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/planev_b.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_b.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/planev_s.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_s.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/planev_s.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/planev_s.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/right.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/right.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/right.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/right.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/up.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/up.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/up.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/up.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/vert-r.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert-r.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/vert-r.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert-r.png diff --git a/radio/sdcard/horus/SxR Calibrate/img/vert.png b/radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert.png similarity index 100% rename from radio/sdcard/horus/SxR Calibrate/img/vert.png rename to radio/sdcard/horus/UTILITIES/SxR Calibrate/img/vert.png diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt index b03b2c860..305451422 100644 --- a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt @@ -6,6 +6,7 @@ 14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video 41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED 42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED 42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol @@ -34,9 +35,9 @@ 6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH 45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH -45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH 16,0,ESKY,Std,0,Gyro,Pitch 16,1,ESKY,ET4,0,Gyro,Pitch 35,0,ESKY150,4CH,0 @@ -54,6 +55,7 @@ 28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 53,0,Height,5ch,0,Gear 53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 @@ -79,6 +81,8 @@ 65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 58,0,FX816,P38,1 20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib 20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib @@ -99,13 +103,14 @@ 26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib -57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess 2,1,Hubsan,H301,0,RTH,Light,Stab,Video 2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes 22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -71,0,JJRC345,Std,1,Flip,HLess,RTH +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 49,0,KF606,Std,1,Trim 9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim 9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim @@ -122,6 +127,8 @@ 17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH 44,0,NCC1701,Std,1,Warp 77,0,OMP,M2,0,THold,IdleUp,6G_3D 60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 @@ -138,10 +145,11 @@ 72,0,Q90C,Std,0,FMode,VTX+ 74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK 50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 19,0,Shenqi,Cycle,1 68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch @@ -174,3 +182,11 @@ 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess 52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiLOLI.lua b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 000000000..4d309e37e --- /dev/null +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|TNE" + +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/radio/sdcard/taranis-x7/UTILITIES/MultiConfigExternal.lua b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigExternal.lua new file mode 100644 index 000000000..a8baf3d13 --- /dev/null +++ b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigExternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(1, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(1) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(1, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x7/UTILITIES/MultiConfigInternal.lua b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigInternal.lua new file mode 100644 index 000000000..29ced006c --- /dev/null +++ b/radio/sdcard/taranis-x7/UTILITIES/MultiConfigInternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(0, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(0) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(0, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x7/SxR Calibrate/SxR_Calibrate.lua b/radio/sdcard/taranis-x7/UTILITIES/SxR Calibrate/SxR_Calibrate.lua similarity index 100% rename from radio/sdcard/taranis-x7/SxR Calibrate/SxR_Calibrate.lua rename to radio/sdcard/taranis-x7/UTILITIES/SxR Calibrate/SxR_Calibrate.lua diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt index b03b2c860..305451422 100644 --- a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt @@ -6,6 +6,7 @@ 14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop 59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video +59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video 41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED 42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED 42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol @@ -34,9 +35,9 @@ 6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill 70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH 45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH -45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH 16,0,ESKY,Std,0,Gyro,Pitch 16,1,ESKY,ET4,0,Gyro,Pitch 35,0,ESKY150,4CH,0 @@ -54,6 +55,7 @@ 28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 +56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 53,0,Height,5ch,0,Gear 53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 @@ -79,6 +81,8 @@ 65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 55,1,FrSkyRX,CloneTX,0 +55,2,FrSkyRX,EraseTX,0 +55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 58,0,FX816,P38,1 20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib 20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib @@ -99,13 +103,14 @@ 26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib 26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib -57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess 2,1,Hubsan,H301,0,RTH,Light,Stab,Video 2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes 22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -71,0,JJRC345,Std,1,Flip,HLess,RTH +71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 +71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3 49,0,KF606,Std,1,Trim 9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim 9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim @@ -122,6 +127,8 @@ 17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess +17,5,MT99XX,A180,0,3D_6G +17,6,MT99XX,Dragon,0,Mode,RTH 44,0,NCC1701,Std,1,Warp 77,0,OMP,M2,0,THold,IdleUp,6G_3D 60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 @@ -138,10 +145,11 @@ 72,0,Q90C,Std,0,FMode,VTX+ 74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 +74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8 76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK 50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 +21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8 19,0,Shenqi,Cycle,1 68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch @@ -174,3 +182,11 @@ 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess 52,0,ZSX,280,1,Light +78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10 +80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH +81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE +82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe +83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR +84,0,JOYSWAY,Std,0 +85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiLOLI.lua b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiLOLI.lua new file mode 100644 index 000000000..4d309e37e --- /dev/null +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiLOLI.lua @@ -0,0 +1,221 @@ + +local toolName = "TNS|Multi LOLI RX config|TNE" + +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local loli_nok = false +local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4 + { 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5 + { -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6 + { 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7 + { -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8 + +local sel = 1 +local edit = false + +local blink = 0 +local BLINK_SPEED = 15 + +local function drawScreenTitle(title) + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, 0, 0) + end +end + +local function LOLI_Draw_LCD(event) + local line = 0 + + lcd.clear() + + --Display settings + local lcd_opt = 0 + if LCD_W == 480 then + drawScreenTitle("Multi - LOLI RX configuration tool") + x_pos = 152 + x_inc = 90 + y_pos = 40 + y_inc = 20 + else + x_pos = 5 + x_inc = 30 + y_pos = 1 + y_inc = 8 + lcd_opt = SMLSIZE + end + + --Multi Module detection + if loli_nok then + if LCD_W == 480 then + lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt) + else + --Draw on LCD_W=128 + lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE) + end + return + end + + --Display current config + if LCD_W == 480 then + line = line + 1 + lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt) + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt) + lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188) + lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0) + lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0) + line = line + 1 + end + + local out + for i = 1, 8 do + out = getValue("ch"..(i+8)) + lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt) + for j = 1, #channels[i], 3 do + if out > channels[i][j] then + if sel == i then + invert = INVERS + if edit == true then + blink = blink + 1 + if blink > BLINK_SPEED then + invert = 0 + if blink > BLINK_SPEED * 2 then + blink = 0 + end + end + end + else + invert = 0 + end + lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert) + break + end + end + line = line + 1 + end +end + +local function LOLI_Change_Value(dir) + local pos = 0 + local out + --look for the current position + out = getValue("ch"..(sel+8)) + for j = 1, #channels[sel], 3 do + if out > channels[sel][j] then + pos = j + break + end + end + + --decrement or increment + if dir < 0 and pos > 1 then + pos = pos - 3 + elseif dir > 0 and pos + 3 < #channels[sel] then + pos = pos + 3 + else + return + end + + --delete all mixers for the selected channel + local num_mix = model.getMixesCount(sel-1 +8) + for i = 1, num_mix do + model.deleteMix(sel-1 +8, 0); + end + + --create new mixer + local source_max = getFieldInfo("cyc1") + + local val = { name = channels[sel][pos+1], + source = source_max.id - 1, -- MAX=100 on TX16S + weight = channels[sel][pos+2], + offset = 0, + switch = 0, + multiplex = 0, + curveType = 0, + curveValue = 0, + flightModes = 0, + carryTrim = false, + mixWarn = 0, + delayUp = 0, + delayDown = 0, + speedUp = 0, + speedDown = 0 } + model.insertMix(sel-1 +8, 0, val) +end + +local function LOLI_Menu(event) + if event == EVT_VIRTUAL_NEXT then + if edit == false then + -- not changing a value + if sel < 8 then + sel = sel + 1 + end + else + -- need to inc the value + LOLI_Change_Value(1) + end + elseif event == EVT_VIRTUAL_PREV then + if edit == false then + -- not changing a value + if sel > 1 then + sel = sel - 1 + end + else + -- need to dec the value + LOLI_Change_Value(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if edit == false then + edit = true + blink = BLINK_SPEED + else + edit = false + end + end +end + +-- Init +local function LOLI_Init() + local module_conf = model.getModule(0) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + module_conf = model.getModule(1) + if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then + loli_nok = true + end + end +end + +-- Main +local function LOLI_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + return 2 + else + LOLI_Menu(event) + LOLI_Draw_LCD(event) + return 0 + end +end + +return { init=LOLI_Init, run=LOLI_Run } diff --git a/radio/sdcard/taranis-x9/UTILITIES/MultiConfigExternal.lua b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigExternal.lua new file mode 100644 index 000000000..a8baf3d13 --- /dev/null +++ b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigExternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(1, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(1) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(1, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x9/UTILITIES/MultiConfigInternal.lua b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigInternal.lua new file mode 100644 index 000000000..29ced006c --- /dev/null +++ b/radio/sdcard/taranis-x9/UTILITIES/MultiConfigInternal.lua @@ -0,0 +1,499 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for Config description +-- To start operation: +-- Write 0xFF at address 4 will request the buffer to be cleared +-- Write "Conf" at address 0..3 +-- Read +-- Read at address 12 gives the current config page +-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters +-- Write +-- Write at address 5..11 the command +-- Write 0x01 at address 4 will send the command to the module +-- !! Before exiting the script must write 0 at address 0 for normal operation !! +--############################################################################### + +local Version = "v0.1" +local Focus = -1 +local Page = 0 +local Edit = -1 +local Edit_pos = 1 +local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""}, + {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} } +local Menu_value = {} +local Blink = 0 +local Module = {} +local InitialProtocol = 0 + +function bitand(a, b) + local result = 0 + local bitval = 1 + while a > 0 and b > 0 do + if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits + result = result + bitval -- set the current bit + end + bitval = bitval * 2 -- shift left + a = math.floor(a/2) -- shift right + b = math.floor(b/2) + end + return result +end + +local function Config_Send(page, line, value) + local i + i = (page*16) + line + multiBuffer( 5, i ) + for i = 1 , 6 , 1 do + multiBuffer( 5+i, value[i] ) + end + multiBuffer( 4, 1 ) +end + +local function Config_Release() + Module.protocol = InitialProtocol + model.setModule(0, Module) + + local i + for i = 3 , 0 , -1 do + multiBuffer( i, 0 ) + end +end + +local function Config_Page( ) + Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 }) +end + +local function Config_Draw_Edit( event ) + local i + local text + + if Menu[Focus].field_type == 0xD0 then + -- Editable Hex value + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 1 + Blink = 0 + for i = 1, Menu[Focus].field_len, 1 do + Menu_value[i] = Menu[Focus].field_value[i] + end + end + if Edit == 0 then + -- Not editing value + if event == EVT_VIRTUAL_ENTER then + if Edit_pos > Menu[Focus].field_len then + -- Save or Cancel + Edit = -1 + if Edit_pos == Menu[Focus].field_len + 1 then + -- Save + Config_Send(Page, Focus, Menu_value) + end + return + else + -- Switch to edit mode + Edit = 1 + end + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Move cursor + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then + -- Move cursor + Edit_pos = Edit_pos + 1 + end + else + -- Editing value + if event == EVT_VIRTUAL_ENTER then + -- End edit + Edit = 0 + elseif event == EVT_VIRTUAL_PREV then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1 + elseif event == EVT_VIRTUAL_NEXT then + -- Change value + Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1 + end + --Blink + Blink = Blink + 1 + if Blink > 30 then + Blink = 0 + end + end + --Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + for i = 1, Menu[Focus].field_len, 1 do + if i==Edit_pos and (Edit ~= 1 or Blink > 15) then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib) + else + lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE) + end + end + if Edit_pos == Menu[Focus].field_len + 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Save", attrib) + else + lcd.drawText(17, 30, "Save", attrib + SMLSIZE) + end + if Edit_pos == Menu[Focus].field_len + 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "Cancel", attrib) + else + lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE) + end + + elseif Menu[Focus].field_type == 0x90 then + -- Action text + if Edit == -1 then + -- Init + Edit = 0 + Edit_pos = 2 + end + if event == EVT_VIRTUAL_ENTER then + -- Exit + Edit = -1 + if Edit_pos == 1 then + -- Yes + Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } ) + end + return + elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then + -- Switch to Yes + Edit_pos = Edit_pos - 1 + elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then + -- Switch to No + Edit_pos = Edit_pos + 1 + end + -- Display + if LCD_W == 480 then + lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR) + lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR) + else + lcd.clear() + end + if LCD_W == 480 then + lcd.drawText(170, 110, Menu[Focus].field_text .. "?") + else + lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE) + end + if Edit_pos == 1 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(170, 130, "Yes", attrib) + else + lcd.drawText(17, 30, "Yes", attrib + SMLSIZE) + end + if Edit_pos == 2 then + attrib = INVERS + else + attrib = 0 + end + if LCD_W == 480 then + lcd.drawText(260, 130, "No", attrib) + else + lcd.drawText(77, 30, "No", attrib) + end + end +end + +local function Config_Next_Prev( event ) +-- Next Prev on main menu + local line + if event == EVT_VIRTUAL_PREV then + for line = Focus - 1, 1, -1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + elseif event == EVT_VIRTUAL_NEXT then + for line = Focus + 1, 7, 1 do + if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then + Focus = line + break + end + end + end +end + +local function Config_Draw_Menu() +-- Main menu + local i + local value + local line + local length + local text + + lcd.clear() + + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR) + if multiBuffer(13) == 0x00 then + lcd.drawText(10,50,"No Config telemetry...", BLINK) + end + else + --Draw on LCD_W=128 + lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE) + if multiBuffer(13) == 0x00 then + lcd.drawText(2,17,"No Config telemetry...",SMLSIZE) + end + end + + if multiBuffer(13) ~= 0x00 then + if LCD_W == 480 then + --Draw firmware version and channels order + local ch_order = multiBuffer(17) + local channel_names = {} + channel_names[bitand(ch_order,3)+1] = "A" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "E" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "T" + ch_order = math.floor(ch_order/4) + channel_names[bitand(ch_order,3)+1] = "R" + lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16), MENU_TITLE_COLOR) + --lcd.drawText(150, 5, "Firmware v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR) + else + lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4]) + end + + --Draw Menu + for line = 1, 7, 1 do + --Clear line info + Menu[line].text = "" + Menu[line].field_type = 0 + Menu[line].field_len = 0 + for i = 1, 7, 1 do + Menu[line].field_value[i] = 0 + end + Menu[line].field_text = "" + length = 0 + --Read line from buffer + for i = 0, 20-1, 1 do + value=multiBuffer( line*20+13+i ) + if value == 0 then + break -- end of line + end + if value > 0x80 and Menu[line].field_type == 0 then + -- Read field type + Menu[line].field_type = bitand(value, 0xF0) + Menu[line].field_len = bitand(value, 0x0F) + length = Menu[line].field_len + if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then + -- First actionnable field if nothing was selected + Focus = line; + end + else + if Menu[line].field_type == 0 then + -- Text + Menu[line].text = Menu[line].text .. string.char(value) + else + -- Menu specific fields + length = length - 1 + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + Menu[line].field_text = Menu[line].field_text .. string.char(value) + else + Menu[line].field_value[Menu[line].field_len-length] = value + end + if length == 0 then + -- End of fields + break + end + end + end + end + -- Display menu text + if Menu[line].text ~= "" then + if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + Menu[line].text = Menu[line].text .. ":" + end + if LCD_W == 480 then + lcd.drawText(10,32+20*line,Menu[line].text ) + else + lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE) + end + end + -- Display specific fields + if line == Focus then + attrib = INVERS + else + attrib = 0 + end + if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then + -- Text + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then + -- Decimal value + value = 0 + for i = 1, Menu[line].field_len, 1 do + value = value*256 + value + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib) + end + elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then + -- Hex value + text="" + for i = 1, Menu[line].field_len, 1 do + text = text .. string.format('%02X ', Menu[line].field_value[i]) + end + if LCD_W == 480 then + lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib) + else + lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib) + end + end + end + end +end + +-- Init +local function Config_Init() + --get Module settings and set to config + Module = model.getModule(0) + InitialProtocol = Module.protocol + Module.protocol = 86 + model.setModule(0, Module) + for i = 0, 10, 1 do end + --Set protocol to talk to + multiBuffer( 0, string.byte('C') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('C') then + error("Not enough memory!") + return 2 + end + --Request init of the buffer + multiBuffer( 4, 0xFF ) + --Continue buffer init + multiBuffer( 1, string.byte('o') ) + multiBuffer( 2, string.byte('n') ) + multiBuffer( 3, string.byte('f') ) + + -- Test set + -- multiBuffer( 12, 0 ) + -- multiBuffer( 13, 1 ) + -- multiBuffer( 14, 3 ) + -- multiBuffer( 15, 2 ) + -- multiBuffer( 16, 62 ) + -- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64) + + -- multiBuffer( 33, string.byte('G') ) + -- multiBuffer( 34, string.byte('l') ) + -- multiBuffer( 35, string.byte('o') ) + -- multiBuffer( 36, string.byte('b') ) + -- multiBuffer( 37, string.byte('a') ) + -- multiBuffer( 38, string.byte('l') ) + -- multiBuffer( 39, string.byte(' ') ) + -- multiBuffer( 40, string.byte('I') ) + -- multiBuffer( 41, string.byte('D') ) + -- multiBuffer( 42, 0xD0 + 4 ) + -- multiBuffer( 43, 0x12 ) + -- multiBuffer( 44, 0x34 ) + -- multiBuffer( 45, 0x56 ) + -- multiBuffer( 46, 0x78 ) + -- multiBuffer( 47, 0x9A ) + -- multiBuffer( 48, 0xBC ) + + -- multiBuffer( 53, 0x90 + 9 ) + -- multiBuffer( 54, string.byte('R') ) + -- multiBuffer( 55, string.byte('e') ) + -- multiBuffer( 56, string.byte('s') ) + -- multiBuffer( 57, string.byte('e') ) + -- multiBuffer( 58, string.byte('t') ) + -- multiBuffer( 59, string.byte(' ') ) + -- multiBuffer( 60, string.byte('G') ) + -- multiBuffer( 61, string.byte('I') ) + -- multiBuffer( 62, string.byte('D') ) + -- multiBuffer( 63, 0x00 ) +end + +-- Main +local function Config_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + Config_Release() + return 2 + else + Config_Draw_Menu() + if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then + -- Not editing, ok to change page + if event == EVT_VIRTUAL_PREV_PAGE then + killEvents(event) + if Page > 0 then + --Page = Page - 1 + --Config_Page() + end + else + --Page = Page + 1 + --Config_Page() + end + end + if Focus > 0 then + -- At least one line has an action + if Edit >= 0 then + -- Currently editing + Config_Draw_Edit( event ) + elseif event == EVT_VIRTUAL_ENTER then + -- Switch to edit + Config_Draw_Edit( 0 ) + elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then + -- Main menu selection + Config_Next_Prev( event ) + end + end + return 0 + end +end + +return { init=Config_Init, run=Config_Run } diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/SxR_Calibrate.lua b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/SxR_Calibrate.lua similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/SxR_Calibrate.lua rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/SxR_Calibrate.lua diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/back.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/back.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/back.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/back.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/delta.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/delta.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/delta.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/delta.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/done.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/done.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/done.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/done.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/down.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/down.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/down.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/down.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/forward.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/forward.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/forward.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/forward.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz-r.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz-r.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz-r.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz-r.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/horz.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/horz.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/left.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/left.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/left.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/left.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/plane.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/plane.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/plane.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/plane.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/right.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/right.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/right.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/right.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/up.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/up.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/up.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/up.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert-r.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert-r.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert-r.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert-r.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/vert.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vert.bmp diff --git a/radio/sdcard/taranis-x9/SxR Calibrate/bmp/vtail.bmp b/radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vtail.bmp similarity index 100% rename from radio/sdcard/taranis-x9/SxR Calibrate/bmp/vtail.bmp rename to radio/sdcard/taranis-x9/UTILITIES/SxR Calibrate/bmp/vtail.bmp diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 02fd5acb4..da1998e4b 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -528,6 +528,11 @@ void audioTask(void * pdata) setSampleRate(AUDIO_SAMPLE_RATE); +#if defined(PCBX12S) || defined(RADIO_TX16S) + // The audio amp needs ~2s to start + RTOS_WAIT_MS(1000); // 1s +#endif + if (!globalData.unexpectedShutdown) { AUDIO_HELLO(); } diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index b11fb6bcc..f7bee9f85 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -863,6 +863,7 @@ enum Functions { FUNC_LOGS, FUNC_BACKLIGHT, FUNC_SCREENSHOT, + FUNC_RACING_MODE, #if defined(DEBUG) FUNC_TEST, // should remain the last before MAX as not added in Companion #endif diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 69bb23166..8b8615b4c 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -455,8 +455,9 @@ PACK(struct ModuleData { uint8_t spare2:1 SKIP; int8_t refreshRate; // definition as framelength for ppm (* 5 + 225 = time in 1/10 ms) } sbus); - NOBACKUP(struct { - uint8_t receivers; // 5 bits spare + NOBACKUP(PACK(struct { + uint8_t receivers:7; // 4 bits spare + uint8_t racingMode:1; char receiverName[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME]; } pxx2); #if defined(AFHDS2) @@ -489,14 +490,21 @@ PACK(struct ModuleData { } NAME(mod) FUNC(select_mod_type); // Helper functions to set both of the rfProto protocol at the same time - NOBACKUP(inline uint8_t getMultiProtocol() { + NOBACKUP(inline uint8_t getMultiProtocol() const + { return ((uint8_t) (rfProtocol & 0x0F)) + (multi.rfProtocolExtra << 4); }) - NOBACKUP(inline void setMultiProtocol(uint8_t proto) { + NOBACKUP(inline void setMultiProtocol(uint8_t proto) + { rfProtocol = (uint8_t) (proto & 0x0F); multi.rfProtocolExtra = (proto & 0x70) >> 4; }) + + NOBACKUP(inline uint8_t getChannelsCount() const + { + return channelsCount + 8; + }) }); /* diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index 76e903d11..a3dc0882c 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -379,10 +379,18 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext case FUNC_SCREENSHOT: if (!(functionsContext.activeSwitches & switch_mask)) { - mainRequestFlags |= (1 << REQUEST_SCREENSHOT); + mainRequestFlags |= (1u << REQUEST_SCREENSHOT); } break; +#if defined(PXX2) + case FUNC_RACING_MODE: + if (isRacingModeEnabled()) { + newActiveFunctions |= (1u << FUNCTION_RACING_MODE); + } + break; +#endif + #if defined(DEBUG) case FUNC_TEST: testFunc(); diff --git a/radio/src/gui/128x64/model_select.cpp b/radio/src/gui/128x64/model_select.cpp index b202b4079..a42b22fb7 100644 --- a/radio/src/gui/128x64/model_select.cpp +++ b/radio/src/gui/128x64/model_select.cpp @@ -244,7 +244,7 @@ void menuModelSelect(event_t event) #endif #endif -#if defined(ROTARY_ENCODER) && defined(NAVIGATION_X7) +#if defined(ROTARY_ENCODER_NAVIGATION) && defined(NAVIGATION_X7) case EVT_ROTARY_LEFT: case EVT_ROTARY_RIGHT: #endif diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 0b013085e..a3a2656f4 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -95,6 +95,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS, #endif ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS, + ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE, ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM, ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS, @@ -347,6 +348,7 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) MULTIMODULE_SUBTYPE_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_SUBTYPE */ \ MULTIMODULE_STATUS_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS */ \ MODULE_CHANNELS_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS */ \ + IF_ALLOW_RACING_MODE(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE */ \ IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, MODULE_BIND_ROWS(INTERNAL_MODULE)), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\ IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM */ \ MODULE_OPTION_ROW(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS */ \ @@ -1217,6 +1219,16 @@ void menuModelSetup(event_t event) break; } +#if defined(HARDWARE_INTERNAL_MODULE) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE: + { + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcdDrawText(INDENT_WIDTH, y, STR_RACING_MODE); + moduleData.pxx2.racingMode = editCheckBox(moduleData.pxx2.racingMode, MODEL_SETUP_2ND_COLUMN, y, "", attr, event); + break; + } +#endif + #if defined(PCBX7) || defined(PCBX9LITE) || defined(PCBXLITE) case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS: lcdDrawTextAlignedLeft(y, STR_PPMFRAME); @@ -1497,7 +1509,6 @@ void menuModelSetup(event_t event) if (event == EVT_KEY_LONG(KEY_ENTER)) { killEvents(event); setCustomFailsafe(moduleIdx); - storageDirty(EE_MODEL); AUDIO_WARNING1(); SEND_FAILSAFE_NOW(moduleIdx); } diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 0dd9d410d..a1ad6cd7a 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -87,6 +87,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL, ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE, ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS, + ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE, ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM, ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE, @@ -362,6 +363,7 @@ void menuModelSetup(event_t event) LABEL(InternalModule), INTERNAL_MODULE_TYPE_ROWS, MODULE_CHANNELS_ROWS(INTERNAL_MODULE), + IF_ALLOW_RACING_MODE(INTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, MODULE_BIND_ROWS(INTERNAL_MODULE)), // RxNum, [Bind] and [Range] buttons IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), // RxNum for ACCESS IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Failsafe @@ -1098,6 +1100,15 @@ void menuModelSetup(event_t event) break; } + case ITEM_MODEL_SETUP_INTERNAL_MODULE_RACING_MODE: + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + ModuleData & moduleData = g_model.moduleData[moduleIdx]; + lcdDrawText(INDENT_WIDTH, y, STR_RACING_MODE); + moduleData.pxx2.racingMode = editCheckBox(moduleData.pxx2.racingMode, MODEL_SETUP_2ND_COLUMN, y, "", attr, event); + break; + } + case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS: lcdDrawTextAlignedLeft(y, STR_PPMFRAME); lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.trainerData.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT); @@ -1270,7 +1281,6 @@ void menuModelSetup(event_t event) if (event == EVT_KEY_LONG(KEY_ENTER)) { killEvents(event); setCustomFailsafe(moduleIdx); - storageDirty(EE_MODEL); AUDIO_WARNING1(); SEND_FAILSAFE_NOW(moduleIdx); } diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 9e2f26935..9b005dbb8 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -191,6 +191,34 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx) } } +#if defined(PXX2) +inline bool isRacingModeAllowed() +{ + return isModulePXX2(INTERNAL_MODULE) && g_model.moduleData[INTERNAL_MODULE].getChannelsCount() == 8; +} + +inline bool isRacingModeEnabled() +{ + return isRacingModeAllowed() && g_model.moduleData[INTERNAL_MODULE].pxx2.racingMode; +} + +inline uint8_t IF_ALLOW_RACING_MODE(int moduleIdx) +{ + if (!IS_MODULE_ENABLED(moduleIdx)) { + return HIDDEN_ROW; + } + else if (isRacingModeAllowed()) { + return 0; + } + return HIDDEN_ROW; +} +#else +inline uint8_t IF_ALLOW_RACING_MODE(int) +{ + return HIDDEN_ROW; +} +#endif + #if defined(MULTIMODULE) inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) { diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index 0d8fda028..d81c94ace 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -124,7 +124,7 @@ class MultiExternalUpdateDriver: public MultiFirmwareUpdateDriver if (inverted) telemetryPortInvertedInit(57600); else - telemetryPortInit(57600, TELEMETRY_SERIAL_DEFAULT); + telemetryPortInit(57600, TELEMETRY_SERIAL_WITHOUT_DMA); } bool getByte(uint8_t & byte) const override @@ -170,7 +170,7 @@ class MultiExtSportUpdateDriver: public MultiFirmwareUpdateDriver void init(bool inverted) const override { - telemetryPortInit(57600, TELEMETRY_SERIAL_DEFAULT); + telemetryPortInit(57600, TELEMETRY_SERIAL_WITHOUT_DMA); } bool getByte(uint8_t & byte) const override diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 7167fc7e8..7a29f9075 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1537,6 +1537,19 @@ static int luaGetUsage(lua_State * L) return 1; } +/*luadoc +@function getAvailableMemory() + +Get available memory remaining in the Heap for Lua. + +@retval usage (number) a value returned in b +*/ +static int luaGetAvailableMemory(lua_State * L) +{ + lua_pushunsigned(L, availableMemory()); + return 1; +} + /*luadoc @function resetGlobalTimer([type]) @@ -1764,6 +1777,7 @@ const luaL_Reg opentxLib[] = { { "chdir", luaChdir }, { "loadScript", luaLoadScript }, { "getUsage", luaGetUsage }, + { "getAvailableMemory", luaGetAvailableMemory }, { "resetGlobalTimer", luaResetGlobalTimer }, #if LCD_DEPTH > 1 && !defined(COLORLCD) { "GREY", luaGrey }, diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index 76800d842..5e8a4bb74 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -121,11 +121,11 @@ Get RF module parameters * `channelsCount` (number) number of channels sent to module * `Type` (number) module type * if the module type is Multi additional information are available - * `protocol` (number) protocol number - * `subProtocol` (number) sub-protocol number - * `channelsOrder` (number) first 4 channels expected order + * `protocol` (number) protocol number (Multi only) + * `subProtocol` (number) sub-protocol number (Multi only) + * `channelsOrder` (number) first 4 channels expected order (Multi only) -@status current Introduced in TODO +@status current Introduced in 2.2.0 */ static int luaModelGetModule(lua_State *L) { @@ -136,7 +136,7 @@ static int luaModelGetModule(lua_State *L) lua_pushtableinteger(L, "subType", module.subType); lua_pushtableinteger(L, "modelId", g_model.header.modelId[idx]); lua_pushtableinteger(L, "firstChannel", module.channelsStart); - lua_pushtableinteger(L, "channelsCount", module.channelsCount + 8); + lua_pushtableinteger(L, "channelsCount", module.getChannelsCount()); lua_pushtableinteger(L, "Type", module.type); #if defined(MULTIMODULE) if (module.type == MODULE_TYPE_MULTIMODULE) { @@ -175,13 +175,16 @@ Set RF module parameters @notice If a parameter is missing from the value, then that parameter remains unchanged. -@status current Introduced in TODO +@status current Introduced in 2.2.0, modified in 2.3.12 (proto/subproto) */ static int luaModelSetModule(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_MODULES) { + int protocol = -1; + int subprotocol = -1; + ModuleData & module = g_model.moduleData[idx]; luaL_checktype(L, -1, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { @@ -202,7 +205,22 @@ static int luaModelSetModule(lua_State *L) else if (!strcmp(key, "channelsCount")) { module.channelsCount = luaL_checkinteger(L, -1) - 8; } +#if defined(MULTIMODULE) + if (!strcmp(key, "protocol")) { + protocol = luaL_checkinteger(L, -1); + } + if (!strcmp(key, "subProtocol")) { + subprotocol = luaL_checkinteger(L, -1); + } +#endif } +#if defined(MULTIMODULE) + if (protocol > 0 && subprotocol >= 0) { // Both are needed to compute otx protocol + convertMultiProtocolToOtx(&protocol, &subprotocol); + g_model.moduleData[idx].setMultiProtocol(protocol - 1); + g_model.moduleData[idx].subType = subprotocol; + } +#endif storageDirty(EE_MODEL); } return 0; diff --git a/radio/src/mixer_scheduler.cpp b/radio/src/mixer_scheduler.cpp index dcf95383b..fef9034c0 100644 --- a/radio/src/mixer_scheduler.cpp +++ b/radio/src/mixer_scheduler.cpp @@ -68,9 +68,13 @@ void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs) mixerSchedules[moduleIdx].period = periodUs; } -bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs) +void mixerSchedulerClearTrigger() { RTOS_CLEAR_FLAG(mixerFlag); +} + +bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs) +{ return RTOS_WAIT_FLAG(mixerFlag, timeoutMs); } diff --git a/radio/src/mixer_scheduler.h b/radio/src/mixer_scheduler.h index 601f3fcd7..75a6ad1aa 100644 --- a/radio/src/mixer_scheduler.h +++ b/radio/src/mixer_scheduler.h @@ -43,6 +43,9 @@ void mixerSchedulerResetTimer(); // Set the scheduling period for a given module void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs); +// Clear the flag before waiting +void mixerSchedulerClearTrigger(); + // Wait for the scheduler timer to trigger // returns true if timeout, false otherwise bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs); @@ -66,6 +69,7 @@ void mixerSchedulerISRTrigger(); #define mixerSchedulerStop() #define mixerSchedulerResetTimer() #define mixerSchedulerSetPeriod(m,p) +#define mixerSchedulerClearTrigger() static inline bool mixerSchedulerWaitForTrigger(uint8_t timeout) { diff --git a/radio/src/opentx.h b/radio/src/opentx.h index f60c4ddeb..b2b3558d0 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -833,6 +833,7 @@ enum FunctionsActive { FUNCTION_BACKGND_MUSIC, FUNCTION_BACKGND_MUSIC_PAUSE, FUNCTION_BACKLIGHT, + FUNCTION_RACING_MODE, }; #define VARIO_FREQUENCY_ZERO 700/*Hz*/ diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 6fbec157e..b6b7a6abc 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -177,12 +177,17 @@ enum ModuleSubtypeMulti { MODULE_SUBTYPE_MULTI_ELRS, MODULE_SUBTYPE_MULTI_REALACC, MODULE_SUBTYPE_MULTI_OMP, - MODULE_SUBTYPE_MULTI_MLINK, //75 + MODULE_SUBTYPE_MULTI_MLINK, //75 MODULE_SUBTYPE_MULTI_WFLY2, MODULE_SUBTYPE_MULTI_E016HV2, - MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_E016HV2 + MODULE_SUBTYPE_MULTI_E010R5, + MODULE_SUBTYPE_MULTI_LOLI, + MODULE_SUBTYPE_MULTI_E129, //80 + MODULE_SUBTYPE_MULTI_JOYSWAY, + MODULE_SUBTYPE_MULTI_E016H, + MODULE_SUBTYPE_MULTI_CONFIG, + MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_CONFIG }; -#define MODULE_SUBTYPE_MULTI_XN297DP 63-3 enum MMDSM2Subtypes { MM_RF_DSM2_SUBTYPE_DSM2_22, diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index f483d9b6b..227aa974d 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -706,7 +706,7 @@ inline void getMultiOptionValues(int8_t multi_proto, int8_t & min, int8_t & max) min = 0; max = 70; break; - case MODULE_SUBTYPE_MULTI_XN297DP: + case MODULE_SUBTYPE_MULTI_XN297DUMP: min = -1; max = 84; break; diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 37364199a..c6e56126c 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -42,6 +42,7 @@ static void sendD16BindOption(uint8_t moduleIdx); #if defined(LUA) static void sendSport(uint8_t moduleIdx); static void sendHott(uint8_t moduleIdx); +static void sendConfig(uint8_t moduleIdx); static void sendDSM(uint8_t moduleIdx); #endif @@ -190,11 +191,14 @@ void setupPulsesMulti(uint8_t moduleIdx) #if defined(LUA) // SPort send if (IS_D16_MULTI(moduleIdx) && outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT && outputTelemetryBuffer.size) { - sendSport(moduleIdx); //8 bytes of additional data + sendSport(moduleIdx); //8 bytes of additional data } else if (IS_HOTT_MULTI(moduleIdx)) { sendHott(moduleIdx); //1 byte of additional data } + else if (IS_CONFIG_MULTI(moduleIdx)) { + sendConfig(moduleIdx); //7 bytes of additional data + } else if (IS_DSM_MULTI(moduleIdx)) { sendDSM(moduleIdx); //7 bytes of additional data } @@ -251,10 +255,54 @@ void sendChannels(uint8_t moduleIdx) } } +void convertMultiProtocolToOtx(int *protocol, int *subprotocol) +{ + if (*protocol == 3 && *subprotocol == 0) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + *subprotocol = MM_RF_FRSKY_SUBTYPE_D8; + return; + } + + if (*protocol == 3 && *subprotocol == 1) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + *subprotocol = MM_RF_FRSKY_SUBTYPE_D8_CLONED; + return; + } + + if (*protocol == 25) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + *subprotocol = MM_RF_FRSKY_SUBTYPE_V8; + return; + } + + if (*protocol == 15) { + *protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1; + + if (*subprotocol == 0) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16; + else if (*subprotocol == 1) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_8CH; + else if (*subprotocol == 2) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_LBT; + else if (*subprotocol == 3) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH; + else if (*subprotocol == 4) + *subprotocol = MM_RF_FRSKY_SUBTYPE_D16_CLONED; + + return; + } + + if (*protocol >= 25) + *protocol -= 1; + + if (*protocol >= 16) + *protocol -= 1; +} + void convertOtxProtocolToMulti(int *protocol, int *subprotocol) { // Special treatment for the FrSky entry... - if (*protocol == MODULE_SUBTYPE_MULTI_FRSKY +1) { + if (*protocol == MODULE_SUBTYPE_MULTI_FRSKY + 1) { if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D8) { //D8 *protocol = 3; @@ -288,10 +336,10 @@ void convertOtxProtocolToMulti(int *protocol, int *subprotocol) // 15 for Multimodule is FrskyX or D16 which we map as a protocol of 3 (FrSky) // all protos > frskyx are therefore also off by one if (*protocol >= 15) - *protocol = *protocol + 1; + *protocol += 1; // 25 is again a FrSky *protocol (FrskyV) so shift again if (*protocol >= 25) - *protocol = *protocol + 1; + *protocol += 1; } } @@ -404,6 +452,23 @@ void sendHott(uint8_t moduleIdx) } } +void sendConfig(uint8_t moduleIdx) +{ + // Multi_Buffer[0..3]=="Conf" -> Lua script is running + // Multi_Buffer[4]==0x01 -> TX to Module data ready to be sent + // Multi_Buffer[4]==0xFF -> Clear buffer data + // Multi_Buffer[5..11]=7 bytes of TX to Module data + // Multi_Buffer[12] -> Current page + // Multi_Buffer[13..172]=8*20=160 bytes of Module to TX data + if (Multi_Buffer && memcmp(Multi_Buffer, "Conf", 4) == 0 && Multi_Buffer[4] == 0x01) { + // Config Lua script is running and sending + for (uint8_t i = 0; i < 7; i++) { + sendMulti(moduleIdx, Multi_Buffer[5 + i]); + } + Multi_Buffer[4] = 0x00; // Send data only once + } +} + void sendDSM(uint8_t moduleIdx) { // Multi_Buffer[0..2]=="DSM" -> Lua script is running diff --git a/radio/src/pulses/multi.h b/radio/src/pulses/multi.h index 29e335d3a..e3771d7eb 100644 --- a/radio/src/pulses/multi.h +++ b/radio/src/pulses/multi.h @@ -25,6 +25,7 @@ void multiPatchCustom(uint8_t moduleIdx); void convertOtxProtocolToMulti(int *protocol, int *subprotocol); +void convertMultiProtocolToOtx(int *protocol, int *subprotocol); class UartMultiPulses: public DataBuffer { diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 7c034a06f..8ba672aa0 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -547,7 +547,7 @@ bool setupPulsesInternalModule(uint8_t protocol) else { #if defined(INTMODULE_HEARTBEAT) mixerSchedulerResetTimer(); - mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000 /* backup */); + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 2000 /* backup */); #else mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD); #endif @@ -641,10 +641,11 @@ void setCustomFailsafe(uint8_t moduleIndex) g_model.failsafeChannels[ch] = channelOutputs[ch]; } } + storageDirty(EE_MODEL); } } -int32_t getChannelValue(uint8_t channel) { +int32_t getChannelValue(uint8_t channel) +{ return channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; } - diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index 5f53a446e..e72caaa66 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -48,14 +48,20 @@ void Pxx2Pulses::addFlag1(uint8_t module) else { subType = g_model.moduleData[module].subType; } - Pxx2Transport::addByte(subType << 4); + + uint8_t flag1 = subType << 4u; + if (isRacingModeEnabled() && isFunctionActive(FUNCTION_RACING_MODE)) { + flag1 |= PXX2_CHANNELS_FLAG1_RACING_MODE; + } + + Pxx2Transport::addByte(flag1); } void Pxx2Pulses::addPulsesValues(uint16_t low, uint16_t high) { Pxx2Transport::addByte(low); // Low byte of channel - Pxx2Transport::addByte(((low >> 8) & 0x0F) | (high << 4)); // 4 bits each from 2 channels - Pxx2Transport::addByte(high >> 4); // High byte of channel + Pxx2Transport::addByte(((low >> 8u) & 0x0Fu) | (high << 4u)); // 4 bits each from 2 channels + Pxx2Transport::addByte(high >> 4u); // High byte of channel } void Pxx2Pulses::addChannels(uint8_t module) diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h index 9fe910c5c..e92a8a350 100644 --- a/radio/src/pulses/pxx2.h +++ b/radio/src/pulses/pxx2.h @@ -47,6 +47,7 @@ #define PXX2_CHANNELS_FLAG0_FAILSAFE (1 << 6) #define PXX2_CHANNELS_FLAG0_RANGECHECK (1 << 7) +#define PXX2_CHANNELS_FLAG1_RACING_MODE (1 << 3) #define PXX2_RX_SETTINGS_FLAG0_WRITE (1 << 6) @@ -269,6 +270,7 @@ enum ReceiverCapabilities { RECEIVER_CAPABILITY_TELEMETRY_25MW, RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6, RECEIVER_CAPABILITY_FPORT2, + RECEIVER_CAPABILITY_RACING_MODE, RECEIVER_CAPABILITY_COUNT }; diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index e73d5c3d3..878e3ffea 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -131,9 +131,8 @@ void extmodulePxx1PulsesStart() #endif EXTMODULE_TIMER->ARR = 45000; - EXTMODULE_TIMER->CCR2 = 40000; // The first frame will be sent in 20ms EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; // Enable DMA on update + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); @@ -183,8 +182,6 @@ void extmoduleSerialStart() NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); NVIC_SetPriority(EXTMODULE_TIMER_DMA_STREAM_IRQn, 7); - NVIC_EnableIRQ(EXTMODULE_TIMER_CC_IRQn); - NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7); } #if defined(EXTMODULE_USART) @@ -292,13 +289,20 @@ void extmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: - EXTMODULE_TIMER->CCR2 = extmodulePulsesData.pxx.getLast() - 4000; // 2mS in advance - EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + + // disable timer + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | EXTMODULE_TIMER_DMA_SIZE | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.pxx.getData()); EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.pxx.getSize(); EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = 1; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; #endif @@ -432,7 +436,6 @@ extern "C" void EXTMODULE_TIMER_DMA_IRQHandler() DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); switch (moduleState[EXTERNAL_MODULE].protocol) { - case PROTOCOL_CHANNELS_PXX1_PULSES: case PROTOCOL_CHANNELS_PPM: EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 512f16c5d..46dd891bf 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -230,8 +230,6 @@ void extmodulePxx1PulsesStart() NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); NVIC_SetPriority(EXTMODULE_TIMER_DMA_STREAM_IRQn, 7); - NVIC_EnableIRQ(EXTMODULE_TIMER_CC_IRQn); - NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7); } #endif @@ -259,14 +257,12 @@ void extmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: - if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) return; // disable timer EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; - EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.pxx.getData()); diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index ec046ddf5..27d68d9c0 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2041,7 +2041,7 @@ #define AUDIO_SPEAKER_ENABLE_GPIO_PIN GPIO_Pin_14 // PD.14 #define HEADPHONE_TRAINER_SWITCH_GPIO GPIOD #define HEADPHONE_TRAINER_SWITCH_GPIO_PIN GPIO_Pin_9 // PD.09 -#elif defined(AUDIO_MUTE_GPIO) +#elif defined(RADIO_TX12) || defined(RADIO_T8) || defined(RADIO_TLITE) #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #define AUDIO_MUTE_GPIO GPIOE #define AUDIO_MUTE_GPIO_PIN GPIO_Pin_12 // PE.12 diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 970e152c5..c635c2ae1 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -110,6 +110,25 @@ void sendSynchronousPulses(uint8_t runMask) #endif } +constexpr uint8_t MIXER_FREQUENT_ACTIONS_PERIOD = 5 /*ms*/; +constexpr uint8_t MIXER_MAX_PERIOD = 30 /*ms*/; + +void execMixerFrequentActions() +{ +#if defined(SBUS_TRAINER) + // SBUS trainer + processSbusInput(); +#endif + +#if defined(GYRO) + gyro.wakeup(); +#endif + +#if defined(BLUETOOTH) + bluetooth.wakeup(); +#endif +} + uint32_t nextMixerTime[NUM_MODULES]; TASK_FUNCTION(mixerTask) @@ -117,26 +136,27 @@ TASK_FUNCTION(mixerTask) s_pulses_paused = true; mixerSchedulerInit(); + #if !defined(PCBSKY9X) mixerSchedulerStart(); #endif + // clear the flag before first loop + mixerSchedulerClearTrigger(); + while (true) { -#if defined(SBUS_TRAINER) - // SBUS trainer - processSbusInput(); -#endif + int timeout = 0; + for (; timeout < MIXER_MAX_PERIOD; timeout += MIXER_FREQUENT_ACTIONS_PERIOD) { -#if defined(GYRO) - gyro.wakeup(); -#endif + // run periodicals before waiting for the trigger + // to keep the delay short + execMixerFrequentActions(); -#if defined(BLUETOOTH) - bluetooth.wakeup(); -#endif - - // run mixer at least every 30ms - bool timeout = mixerSchedulerWaitForTrigger(30); + // mixer flag triggered? + if (!mixerSchedulerWaitForTrigger(MIXER_FREQUENT_ACTIONS_PERIOD)) { + break; + } + } #if defined(DEBUG_MIXER_SCHEDULER) GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); @@ -144,6 +164,9 @@ TASK_FUNCTION(mixerTask) #endif #if !defined(PCBSKY9X) + // clear the flag ASAP to avoid missing a tick + mixerSchedulerClearTrigger(); + // re-enable trigger mixerSchedulerEnableTrigger(); #endif @@ -205,10 +228,6 @@ TASK_FUNCTION(mixerTask) // TODO: // - check the cause of timeouts when switching // between protocols with multi-proto RF -#if defined(DEBUG) - if (timeout) - serialPrint("mix sched timeout!"); -#endif } } } diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index b9972e240..7e7a3d639 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -42,7 +42,8 @@ enum MultiPacketTypes : uint8_t FlyskyIBusTelemetryAC, MultiRxChannels, HottTelemetry, - MLinkTelemetry + MLinkTelemetry, + ConfigTelemetry }; enum MultiBufferState : uint8_t @@ -291,6 +292,31 @@ static void processMultiRxChannels(const uint8_t * data, uint8_t len) } #endif +#if defined(LUA) + +static void processConfigPacket(const uint8_t * packet, uint8_t len) +{ + // Multi_Buffer[0..3]=="Conf" -> Lua script is running + // Multi_Buffer[4]==0x01 -> TX to Module data ready to be sent + // Multi_Buffer[4]==0xFF -> Clear buffer data + // Multi_Buffer[5..11]=7 bytes of TX to Module data + // Multi_Buffer[12] -> Current page + // Multi_Buffer[13..172]=8*20=160 bytes of Module to TX data + if (Multi_Buffer && memcmp(Multi_Buffer, "Conf", 4) == 0) { + // HoTT Lua script is running + if (Multi_Buffer[4] == 0xFF) { + // Init + memset(&Multi_Buffer[4], 0x00, 1 + 7 + 1 + 160); // Clear the buffer + } + if ((packet[0] >> 4) != Multi_Buffer[12]) {// page change + memset(&Multi_Buffer[13], 0x00, 160); // Clear the buffer + Multi_Buffer[12] = (packet[0] >> 4); //Save the page number + } + memcpy(&Multi_Buffer[13 + (packet[0] & 0x0F) * 20], &packet[1], 20); // Store the received page in the buffer + } +} +#endif + static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) { uint8_t type = packet[0]; @@ -363,9 +389,18 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) if (len > 6) processMLinkPacket(data); else - TRACE("[MP] Received M-Link telemetry len %d < 6", len); + TRACE("[MP] Received M-Link telemetry len %d <= 6", len); break; +#if defined(LUA) + case ConfigTelemetry: + if (len >= 21) + processConfigPacket(data, len); + else + TRACE("[MP] Received Config telemetry len %d < 20", len); + break; +#endif + case FrSkyHubTelemetry: if (len >= 4) frskyDProcessPacket(data); diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 9fdc6c749..34c06aeeb 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -400,7 +400,7 @@ ModuleSyncStatus::ModuleSyncStatus() memset(this, 0, sizeof(ModuleSyncStatus)); } -void ModuleSyncStatus::update(uint16_t newRefreshRate, uint16_t newInputLag) +void ModuleSyncStatus::update(uint16_t newRefreshRate, int16_t newInputLag) { if (!newRefreshRate) return; @@ -451,13 +451,12 @@ void ModuleSyncStatus::getRefreshString(char * statusText) char * tmp = statusText; #if defined(DEBUG) *tmp++ = 'L'; - tmp = strAppendUnsigned(tmp, inputLag, 5); - tmp = strAppend(tmp, "us R "); - tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000), 5); - tmp = strAppend(tmp, "us"); + tmp = strAppendSigned(tmp, inputLag, 5); + tmp = strAppend(tmp, "R"); + tmp = strAppendUnsigned(tmp, refreshRate, 5); #else - tmp = strAppend(tmp, "Sync at "); - tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000000)); - tmp = strAppend(tmp, " ms"); + tmp = strAppend(tmp, "Sync "); + tmp = strAppendUnsigned(tmp, refreshRate); #endif + tmp = strAppend(tmp, "us"); } diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index f2a3fc98a..e7063a0e7 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -120,6 +120,7 @@ extern uint8_t telemetryProtocol; || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKYX2)) #define IS_R9_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY_R9) #define IS_HOTT_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_HOTT) + #define IS_CONFIG_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_CONFIG) #define IS_DSM_MULTI(module) (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) #define IS_RX_MULTI(module) ((g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_AFHDS2A_RX) || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKYX_RX) \ || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_BAYANG_RX) || (g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM_RX)) @@ -132,6 +133,7 @@ extern uint8_t telemetryProtocol; #define IS_D16_MULTI(module) false #define IS_R9_MULTI(module) false #define IS_HOTT_MULTI(module) false + #define IS_CONFIG_MULTI(module) false #define IS_DSM_MULTI(module) false #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) #define IS_RX_MULTI(module) false @@ -308,7 +310,7 @@ struct ModuleSyncStatus } // Set feedback from RF module - void update(uint16_t newRefreshRate, uint16_t newInputLag); + void update(uint16_t newRefreshRate, int16_t newInputLag); // Get computed settings for scheduler uint16_t getAdjustedRefreshRate(); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 663e1c5bb..bfb415b13 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -965,3 +965,4 @@ const char STR_ABOUT_PARENTS_4[] = TR_ABOUT_PARENTS_4; const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; const char STR_PROTOCOL[] = TR_PROTOCOL; +const char STR_RACING_MODE[] = TR_RACING_MODE; diff --git a/radio/src/translations.h b/radio/src/translations.h index fa6f69e5c..2c698c3d2 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -1102,6 +1102,7 @@ extern const char STR_ABOUT_PARENTS_4[]; extern const char STR_AUTH_FAILURE[]; extern const char STR_PROTOCOL[]; +extern const char STR_RACING_MODE[]; #define CHR_HOUR TR_CHR_HOUR #define CHR_INPUT TR_CHR_INPUT diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index c235a77b5..f15a00813 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -234,9 +234,10 @@ #endif #define TR_SF_SCREENSHOT "Snímek LCD\0" +#define TR_SF_RACING_MODE "RacingMode\0" #define TR_SF_RESERVE "[rezerva]\0 " -#define TR_VFSWFUNC TR_SF_SAFETY "Trenér \0 ""Insta-Trim\0""Reset\0 ""Změna \0 " TR_ADJUST_GVAR "Hlasitost\0 " "SetFailsafe" "RangeCheck\0" "ModuleBind\0" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podsvětlení" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trenér \0 ""Insta-Trim\0""Reset\0 ""Změna \0 " TR_ADJUST_GVAR "Hlasitost\0 " "SetFailsafe" "RangeCheck\0" "ModuleBind\0" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podsvětlení" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") @@ -1258,6 +1259,7 @@ #define TR_JITTER_FILTER "ADC Filtr" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 901f1a110..8bfc59c6f 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -237,9 +237,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[Reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Lehrer \0 ""Inst. Trim""Rücksetz.\0""Setze \0 " TR_ADJUST_GVAR "Lautstr.\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "LCD Licht\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Lehrer \0 ""Inst. Trim""Rücksetz.\0""Setze \0 " TR_ADJUST_GVAR "Lautstr.\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "LCD Licht\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -1267,6 +1268,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" // ---------------------------------------------------------------- #define ZSTR_VFR "VFR" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 084dba46c..249e07bd3 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -236,10 +236,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Screenshot" -#define TR_SF_RESERVE "[reserve]\0" +#define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" +#define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -1272,6 +1273,7 @@ #define TR_JITTER_FILTER "ADC filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 34065eeb4..17cffb04c 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -234,10 +234,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Captura" +#define TR_SF_SCREENSHOT "Captura\0 " +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserv.]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Entrenador""Inst. Trim""Reset\0 ""Ajuste \0 " TR_ADJUST_GVAR "Volumen\0 " "Failsafe\0 " "CheckRango" "Enl.módulo" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Luz Fondo\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Entrenador""Inst. Trim""Reset\0 ""Ajuste \0 " TR_ADJUST_GVAR "Volumen\0 " "Failsafe\0 " "CheckRango" "Enl.módulo" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Luz Fondo\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") #define TR_FSW_RESET_TELEM TR("Telm", "Telemetría") @@ -1266,6 +1267,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltaje") #define TR_AUTH_FAILURE "Fallo " LCDW_128_480_LINEBREAK "autentificación" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index ab2d5bafe..452971fee 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -238,9 +238,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -1273,6 +1274,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 2d4626477..8e9912197 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -240,9 +240,10 @@ #endif #define TR_SF_SCREENSHOT "Photo Ecran\0 " +#define TR_SF_RACING_MODE "Racing Mode\0 " #define TR_SF_RESERVE "[reserve]\0 " - #define TR_VFSWFUNC TR_SF_SAFETY "Ecolage\0 ""Trim instant.""Remise à 0\0 ""Déf.\0 " TR_ADJUST_GVAR "Volume\0 " "DéfFailsafe\0 " "Test Port.\0 " "Bind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Rétroécl.\0 " TR_SF_SCREENSHOT TR_SF_TEST + #define TR_VFSWFUNC TR_SF_SAFETY "Ecolage\0 ""Trim instant.""Remise à 0\0 ""Déf.\0 " TR_ADJUST_GVAR "Volume\0 " "DéfFailsafe\0 " "Test Port.\0 " "Bind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Rétroécl.\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") @@ -1297,6 +1298,7 @@ #define TR_JITTER_FILTER "Filtre ADC" #define TR_RTC_CHECK TR("Vérif. RTC", "Vérif. pile RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c13fd5dba..0668d3c6b 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -244,9 +244,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot\0 " +#define TR_SF_RACING_MODE "Racing Mode\0 " #define TR_SF_RESERVE "[riserva] \0" -#define TR_VFSWFUNC TR_SF_SAFETY "Maestro \0 ""Trim Instant.""Azzera\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Retroillum.\0 " TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Maestro \0 ""Trim Instant.""Azzera\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Retroillum.\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -1286,6 +1287,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index aa03b5f81..e8e626ee3 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -237,10 +237,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Schermafdruk" +#define TR_SF_SCREENSHOT "Schermafdr" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Inst. Trim""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\012") @@ -1273,6 +1274,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8e86bca94..4eff1c65e 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -238,9 +238,10 @@ #endif #define TR_SF_SCREENSHOT "Zrzut Ekra" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[rezerwa]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trener \0 ""Inst-Trim ""Resetuj\0 ""Ustaw\0 " TR_ADJUST_GVAR "Głośność\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podświetl\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trener \0 ""Inst-Trim ""Resetuj\0 ""Ustaw\0 " TR_ADJUST_GVAR "Głośność\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Podświetl\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") /*9 decimal*/ @@ -1282,6 +1283,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 0909fbd6a..4ec39d8a0 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -234,9 +234,10 @@ #endif #define TR_SF_SCREENSHOT "Screenshot" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0 " -#define TR_VFSWFUNC TR_SF_SAFETY "Aprendiz\0 ""Ajuste Rapido""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0 " TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Aprendiz\0 ""Ajuste Rapido""Reset\0 ""Set \0 " TR_ADJUST_GVAR "Volume\0 " "SetFailsafe\0 " "RangeCheck\0 " "ModuleBind\0 " TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Backlight\0 " TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -1282,7 +1283,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" - +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" #define ZSTR_R9PW "R9PW" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 417d4f7df..ba8e0892a 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -237,10 +237,11 @@ #define TR_SF_SAFETY "---\0 " #endif -#define TR_SF_SCREENSHOT "Skärmbild" +#define TR_SF_SCREENSHOT "Skärmbild\0" +#define TR_SF_RACING_MODE "RacingMode" #define TR_SF_RESERVE "[reserve]\0" -#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Sätt Trim\0""Nollställ\0""Sätt\0 " TR_ADJUST_GVAR "Volym\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Belysning\0" TR_SF_SCREENSHOT TR_SF_TEST +#define TR_VFSWFUNC TR_SF_SAFETY "Trainer\0 ""Sätt Trim\0""Nollställ\0""Sätt\0 " TR_ADJUST_GVAR "Volym\0 " "SetFailsfe" "RangeCheck" "ModuleBind" TR_SOUND TR_PLAY_TRACK TR_PLAY_VALUE TR_SF_RESERVE TR_SF_PLAY_SCRIPT TR_SF_RESERVE TR_SF_BG_MUSIC TR_VVARIO TR_HAPTIC TR_SDCLOGS "Belysning\0" TR_SF_SCREENSHOT TR_SF_RACING_MODE TR_SF_TEST #define LEN_VFSWRESET TR("\004", "\011") @@ -1285,6 +1286,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_RACING_MODE "Racing mode" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index b98d5b70e..124b1a8a1 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -155,7 +155,7 @@ #define TR_DSM_PROTOCOLS "LP45""DSM2""DSMX" #define LEN_MULTI_PROTOCOLS "\007" -#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly 2\0""E016Hv2" +#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly 2\0""E016Hv2""E010r5 ""LOLI\0 ""E129\0 ""JOYSWAY""E016H\0 ""Config\0" #define LEN_MULTI_POWER "\005" #define TR_MULTI_POWER "1.6mW""2.0mW""2.5mW""3.2mW""4.0mW""5.0mW""6.3mW""7.9mW""10mW\0""13mW\0""16mW\0""20mW\0""25mW\0""32mW\0""40mW\0""50mW\0" diff --git a/tools/build-companion-nightly.sh b/tools/build-companion-nightly.sh index a0406f691..8608ac6fd 100755 --- a/tools/build-companion-nightly.sh +++ b/tools/build-companion-nightly.sh @@ -4,7 +4,12 @@ set -e set -x -JOBS=3 +if [ "$(uname)" = "Darwin" ]; then + num_cpus=$(sysctl -n hw.ncpu) + : "${JOBS:=$num_cpus}" +else + JOBS=3 +fi while [ $# -gt 0 ] do @@ -32,6 +37,13 @@ fi if [ "$3" != "" ]; then COMMON_OPTIONS="${COMMON_OPTIONS} -DVERSION_SUFFIX=$3" +else + wget https://downloads.open-tx.org/2.3/nightlies/companion/companion-linux.stamp + version=$(cut -d'N' -f 3 < companion-linux.stamp) + version=$(echo $version | tr -cd '[[:digit:]]') + version=$((version+1)) + nightly=N"$version" + COMMON_OPTIONS="${COMMON_OPTIONS} -DVERSION_SUFFIX=$nightly" fi rm -rf build @@ -74,6 +86,14 @@ cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TX12 ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=T8 ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TLITE ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X9D ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt diff --git a/tools/build-companion-release.sh b/tools/build-companion-release.sh index 463645dc2..e8214977f 100755 --- a/tools/build-companion-release.sh +++ b/tools/build-companion-release.sh @@ -4,7 +4,12 @@ set -e set -x -JOBS=2 +if [ "$(uname)" = "Darwin" ]; then + num_cpus=$(sysctl -n hw.ncpu) + : "${JOBS:=$num_cpus}" +else + JOBS=3 +fi while [ $# -gt 0 ] do @@ -74,6 +79,14 @@ cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TX12 ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=T8 ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=TLITE ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X9D ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt