From 9b64561246f1f44274bd2b87e7218fc95f005a70 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Fri, 19 Jun 2020 07:25:24 +1000 Subject: [PATCH 001/267] Automate Companion About copyright years (#7754) --- companion/src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 4753e0b71..af1aa72f0 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -1065,7 +1065,7 @@ void MainWindow::about() aboutStr.append("

"); aboutStr.append(QString("Version %1, %2").arg(VERSION).arg(__DATE__)); aboutStr.append("

"); - aboutStr.append(tr("Copyright OpenTX Team") + "
© 2011-2019
"); + aboutStr.append(tr("Copyright OpenTX Team") + QString("
© 2011-%1
").arg(QString(__DATE__).right(4))); QMessageBox msgBox(this); msgBox.setWindowIcon(CompanionIcon("information.png")); msgBox.setWindowTitle(tr("About Companion")); From d419c19d1902a690e62d61012224f82ebdddaa84 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 12:54:39 +0200 Subject: [PATCH 002/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4fcf98a29..db6aeaa1c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1761,3 +1761,5 @@ Paul Pearson Stuart Olson Pierre Connan Henk Eeltink +Robert Miller +George Walker From 32f5526bd6e2707c74e2a8dd5d0894c8a5a7e2aa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 22:52:21 +0200 Subject: [PATCH 003/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index db6aeaa1c..f81e514b8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1763,3 +1763,5 @@ Pierre Connan Henk Eeltink Robert Miller George Walker +Александр Щитов +Heiko Polig From 36301696c01a29c8a4fe0e8ed6d34db0ef400b4b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 21 Jun 2020 18:56:20 +0200 Subject: [PATCH 004/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index f81e514b8..357729882 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1765,3 +1765,4 @@ Robert Miller George Walker Александр Щитов Heiko Polig +Gary Wild From 3b287eb5125e96c1756004590b9748a9a9a6c3e3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 21 Jun 2020 18:57:02 +0200 Subject: [PATCH 005/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 357729882..6a5983a80 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1766,3 +1766,4 @@ George Walker Александр Щитов Heiko Polig Gary Wild +Peter Sarcher From 52f1e02247cf0438b54a3b1ba42b53cf1fe2f9fe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Jun 2020 07:44:58 +0200 Subject: [PATCH 006/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 6a5983a80..c895be582 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1767,3 +1767,4 @@ George Walker Heiko Polig Gary Wild Peter Sarcher +Steve Schoonover From 1fccb73e88987cdaed03be8383f63257531917b8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Jun 2020 10:46:28 +0200 Subject: [PATCH 007/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c895be582..5bb418852 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1768,3 +1768,4 @@ Heiko Polig Gary Wild Peter Sarcher Steve Schoonover +Francisco Orcera Exposito From 6d29faa48b269cb6150b4974707e27f655d4d462 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 22 Jun 2020 13:11:54 +0200 Subject: [PATCH 008/267] Version increment (we probably forgot it a t 2.3.9 release) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23596aff6..18d2bc919 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ project(OpenTX) set(VERSION_MAJOR "2") set(VERSION_MINOR "3") -set(VERSION_REVISION "9") +set(VERSION_REVISION "10") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) From 5bf197439a95338263007c501e9f382d68e278a8 Mon Sep 17 00:00:00 2001 From: Jakub Date: Mon, 22 Jun 2020 16:44:35 +0200 Subject: [PATCH 009/267] Support of FRM302 module. (#7533) AFHDS3 support added --- companion/src/firmwares/CMakeLists.txt | 1 + companion/src/firmwares/afhds3.cpp | 12 + companion/src/firmwares/afhds3.h | 12 + companion/src/firmwares/moduledata.cpp | 41 +- companion/src/firmwares/moduledata.h | 8 +- .../src/firmwares/opentx/opentxeeprom.cpp | 38 +- companion/src/firmwares/opentx/opentxeeprom.h | 3 + .../src/firmwares/opentx/opentxinterface.cpp | 47 +- companion/src/modeledit/setup.cpp | 51 +- companion/src/modeledit/setup.h | 1 + companion/src/modeledit/setup_module.ui | 174 ++++- companion/src/modelprinter.cpp | 5 + radio/src/dataconstants.h | 3 +- radio/src/datastructs.h | 18 + radio/src/gui/128x64/model_setup.cpp | 93 ++- radio/src/gui/212x64/model_setup.cpp | 97 ++- radio/src/gui/480x272/model_setup.cpp | 90 ++- .../gui/common/stdlcd/model_setup_afhds3.cpp | 48 ++ radio/src/gui/gui_common.cpp | 12 + radio/src/gui/gui_common.h | 26 +- radio/src/pulses/afhds3.cpp | 706 ++++++++++++++++++ radio/src/pulses/afhds3.h | 595 +++++++++++++++ radio/src/pulses/modules_constants.h | 10 + radio/src/pulses/modules_helpers.h | 64 +- radio/src/pulses/pulses.cpp | 77 ++ radio/src/pulses/pulses.h | 38 +- radio/src/pulses/pulses_common.h | 21 + radio/src/targets/common/arm/CMakeLists.txt | 14 +- radio/src/targets/horus/CMakeLists.txt | 1 + radio/src/targets/horus/extmodule_driver.cpp | 24 + radio/src/targets/horus/telemetry_driver.cpp | 25 +- radio/src/targets/sky9x/CMakeLists.txt | 1 + radio/src/targets/taranis/CMakeLists.txt | 1 + .../src/targets/taranis/extmodule_driver.cpp | 20 +- .../src/targets/taranis/telemetry_driver.cpp | 28 +- radio/src/telemetry/flysky_ibus.cpp | 25 +- radio/src/telemetry/flysky_ibus.h | 2 + radio/src/telemetry/telemetry.cpp | 17 +- radio/src/telemetry/telemetry.h | 11 +- radio/src/telemetry/telemetry_sensors.cpp | 8 +- radio/src/translations.cpp | 15 +- radio/src/translations.h | 11 + radio/src/translations/cz.h.txt | 6 + radio/src/translations/de.h.txt | 6 + radio/src/translations/en.h.txt | 6 + radio/src/translations/es.h.txt | 6 + radio/src/translations/fi.h.txt | 6 + radio/src/translations/fr.h.txt | 6 + radio/src/translations/it.h.txt | 6 + radio/src/translations/nl.h.txt | 6 + radio/src/translations/pl.h.txt | 6 + radio/src/translations/pt.h.txt | 6 + radio/src/translations/se.h.txt | 6 + radio/src/translations/untranslated.h | 9 +- radio/util/fwoptions.py | 30 +- 55 files changed, 2435 insertions(+), 164 deletions(-) create mode 100644 companion/src/firmwares/afhds3.cpp create mode 100644 companion/src/firmwares/afhds3.h create mode 100644 radio/src/gui/common/stdlcd/model_setup_afhds3.cpp create mode 100644 radio/src/pulses/afhds3.cpp create mode 100644 radio/src/pulses/afhds3.h diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index 502a04961..48e6394bc 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -18,6 +18,7 @@ set(firmwares_SRCS rawswitch.cpp sensordata.cpp telem_data.cpp + afhds3.cpp er9x/er9xeeprom.cpp er9x/er9xinterface.cpp ersky9x/ersky9xeeprom.cpp diff --git a/companion/src/firmwares/afhds3.cpp b/companion/src/firmwares/afhds3.cpp new file mode 100644 index 000000000..000fb252c --- /dev/null +++ b/companion/src/firmwares/afhds3.cpp @@ -0,0 +1,12 @@ +#include "afhds3.h" +#include "macros.h" + +QString Afhds3Data::protocolToString(unsigned int protocol) { + const char * const afhds3Protocols[] = { + "PWM/IBUS", + "PWM/SBUS", + "PPM/IBUS", + "PPM/SBUS", + }; + return CHECK_IN_ARRAY(afhds3Protocols, protocol); +} diff --git a/companion/src/firmwares/afhds3.h b/companion/src/firmwares/afhds3.h new file mode 100644 index 000000000..ea4257fba --- /dev/null +++ b/companion/src/firmwares/afhds3.h @@ -0,0 +1,12 @@ +#ifndef AFHDS3_H +#define AFHDS3_H + +#include + +class Afhds3Data +{ + Q_DECLARE_TR_FUNCTIONS(Afhds3) +public: + static QString protocolToString(unsigned int protocol); +}; +#endif // AFHDS3_H diff --git a/companion/src/firmwares/moduledata.cpp b/companion/src/firmwares/moduledata.cpp index 370a6979b..c899632af 100644 --- a/companion/src/firmwares/moduledata.cpp +++ b/companion/src/firmwares/moduledata.cpp @@ -21,6 +21,7 @@ #include "moduledata.h" #include "eeprominterface.h" #include "multiprotocols.h" +#include "afhds3.h" #include "radiodataconversionstate.h" void ModuleData::convert(RadioDataConversionState & cstate) @@ -87,7 +88,6 @@ QString ModuleData::rfProtocolToString() const switch (protocol) { case PULSES_MULTIMODULE: return Multiprotocols::protocolToString((int)multi.rfProtocol); - default: return CPN_STR_UNKNOWN_ITEM; } @@ -108,10 +108,10 @@ QString ModuleData::subTypeToString(int type) const switch (protocol) { case PULSES_MULTIMODULE: return Multiprotocols::subTypeToString((int)multi.rfProtocol, (unsigned)type); - case PULSES_PXX_R9M: return CHECK_IN_ARRAY(strings, type); - + case PULSES_AFHDS3: + return Afhds3Data::protocolToString(type); default: return CPN_STR_UNKNOWN_ITEM; } @@ -119,8 +119,8 @@ QString ModuleData::subTypeToString(int type) const QString ModuleData::powerValueToString(Firmware * fw) const { - const QStringList & strRef = powerValueStrings(subType, fw); - return strRef.value(pxx.power, CPN_STR_UNKNOWN_ITEM); + const QStringList & strRef = powerValueStrings((enum PulsesProtocol)protocol, subType, fw); + return strRef.value(protocol == PULSES_AFHDS3 ? afhds3.rfPower : pxx.power, CPN_STR_UNKNOWN_ITEM); } // static @@ -159,14 +159,15 @@ QString ModuleData::protocolToString(unsigned protocol) "FrSky ACCESS R9M 2019", "FrSky ACCESS R9M Lite", "FrSky ACCESS R9M Lite Pro", - "FrSky XJT lite (D16)", "FrSky XJT lite (D8)", "FrSky XJT lite (LR12)" + "FrSky XJT lite (D16)", "FrSky XJT lite (D8)", "FrSky XJT lite (LR12)", + "AFHDS3" }; return CHECK_IN_ARRAY(strings, protocol); } // static -QStringList ModuleData::powerValueStrings(int subType, Firmware * fw) +QStringList ModuleData::powerValueStrings(enum PulsesProtocol protocol, int subType, Firmware * fw) { static const QStringList strings[] = { { tr("10mW - 16CH"), tr("100mW - 16CH"), tr("500mW - 16CH"), tr("Auto <= 1W - 16CH") }, // full-size FCC @@ -174,12 +175,21 @@ QStringList ModuleData::powerValueStrings(int subType, Firmware * fw) { tr("100mW - 16CH") }, // mini FCC { tr("25mW - 8CH"), tr("25mW - 16CH"), tr("100mW - 16CH (no telemetry)") } // mini EU }; - int strIdx = 0; - if (subType == MODULE_SUBTYPE_R9M_EU) - strIdx += 1; - if (fw->getCapability(HasModuleR9MMini)) - strIdx += 2; - return strings[strIdx]; + static const QStringList afhds3Strings = { + tr("25 mW"), tr("100 mW"), tr("500 mW"), tr("1 W"), tr("2 W") + }; + + switch(protocol) { + case PULSES_AFHDS3: + return afhds3Strings; + default: + int strIdx = 0; + if (subType == MODULE_SUBTYPE_R9M_EU) + strIdx += 1; + if (fw->getCapability(HasModuleR9MMini)) + strIdx += 2; + return strings[strIdx]; + } } bool ModuleData::hasFailsafes(Firmware * fw) const @@ -193,7 +203,8 @@ bool ModuleData::hasFailsafes(Firmware * fw) const protocol == PULSES_ACCESS_R9M_LITE || protocol == PULSES_ACCESS_R9M_LITE_PRO || protocol == PULSES_XJT_LITE_X16 || - protocol == PULSES_MULTIMODULE + protocol == PULSES_MULTIMODULE || + protocol == PULSES_AFHDS3 ); } @@ -230,6 +241,8 @@ int ModuleData::getMaxChannelCount() else return 16; break; + case PULSES_AFHDS3: + return 18; case PULSES_OFF: break; default: diff --git a/companion/src/firmwares/moduledata.h b/companion/src/firmwares/moduledata.h index 86614b392..d3e851879 100644 --- a/companion/src/firmwares/moduledata.h +++ b/companion/src/firmwares/moduledata.h @@ -58,6 +58,7 @@ enum PulsesProtocol { PULSES_XJT_LITE_X16, PULSES_XJT_LITE_D8, PULSES_XJT_LITE_LR12, + PULSES_AFHDS3, PULSES_PROTOCOL_LAST }; @@ -190,6 +191,11 @@ class ModuleData { int optionValue; } multi; + struct Afhds3 { + unsigned int rxFreq; + unsigned int rfPower; + } afhds3; + struct PXX { unsigned int power; // 0 10 mW, 1 100 mW, 2 500 mW, 3 1W bool receiverTelemetryOff; // false = receiver telem enabled @@ -213,7 +219,7 @@ class ModuleData { QString powerValueToString(Firmware * fw) const; static QString indexToString(int index, Firmware * fw); static QString protocolToString(unsigned protocol); - static QStringList powerValueStrings(int subType, Firmware * fw); + static QStringList powerValueStrings(enum PulsesProtocol protocol, int subType, Firmware * fw); bool hasFailsafes(Firmware * fw) const; int getMaxChannelCount(); }; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 17c5b03a5..119bbe396 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -2113,6 +2113,40 @@ class ModuleUnionField: public UnionField { unsigned int version; }; + class Afhds3Field: public UnionField::TransformedMember { + public: + Afhds3Field(DataField * parent, ModuleData& module): + UnionField::TransformedMember(parent, internalField), + internalField(this, "AFHDS3") + { + ModuleData::Afhds3& afhds3 = module.afhds3; + internalField.Append(new UnsignedField<3>(this, minBindPower)); + internalField.Append(new UnsignedField<3>(this, afhds3.rfPower)); + internalField.Append(new UnsignedField<1>(this, emissionFCC)); + internalField.Append(new BoolField<1>(this, operationModeUnicast)); + internalField.Append(new BoolField<1>(this, operationModeUnicast)); + internalField.Append(new UnsignedField<16>(this, defaultFailSafeTimout)); + internalField.Append(new UnsignedField<16>(this, afhds3.rxFreq)); + } + + bool select(const unsigned int& attr) const override { + return attr == PULSES_AFHDS3; + } + + void beforeExport() override {} + + void afterImport() override {} + + private: + StructField internalField; + + unsigned int minBindPower = 0; + unsigned int emissionFCC = 0; + unsigned int defaultFailSafeTimout = 1000; + bool operationModeUnicast = true; + }; + + class AccessField: public UnionField::TransformedMember { public: AccessField(DataField * parent, ModuleData& module): @@ -2173,8 +2207,10 @@ class ModuleUnionField: public UnionField { ModuleUnionField(DataField * parent, ModuleData & module, Board::Type board, unsigned int version): UnionField(parent, module.protocol) { - if (version >= 219) + if (version >= 219) { Append(new AccessField(parent, module)); + Append(new Afhds3Field(parent, module)); + } Append(new PxxField(parent, module, version)); Append(new MultiField(parent, module)); Append(new PPMField(parent, module.ppm)); diff --git a/companion/src/firmwares/opentx/opentxeeprom.h b/companion/src/firmwares/opentx/opentxeeprom.h index ac1cf09dc..ced4dccb6 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.h +++ b/companion/src/firmwares/opentx/opentxeeprom.h @@ -107,6 +107,9 @@ class ProtocolsConversionTable: public ConversionTable addConversion(PULSES_XJT_LITE_X16, val); addConversion(PULSES_XJT_LITE_D8, val); addConversion(PULSES_XJT_LITE_LR12, val++); + if (version >= 219) { + addConversion(PULSES_AFHDS3, val++); + } } }; diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 2fed78ab4..c405f9699 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -823,6 +823,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_SBUS: case PULSES_MULTIMODULE: case PULSES_CROSSFIRE: + case PULSES_AFHDS3: return true; case PULSES_ACCESS_R9M: return IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || board == BOARD_TARANIS_X9DP_2019 || board == BOARD_X10_EXPRESS || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess")); @@ -832,7 +833,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_XJT_LITE_X16: case PULSES_XJT_LITE_D8: case PULSES_XJT_LITE_LR12: - return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board)); + return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board)); default: return false; } @@ -1184,19 +1185,25 @@ void addOpenTxCommonOptions(OpenTxFirmware * firmware) enum RfOptions { NONE = 0, EU = 1 << 0, - FLEX = 1 << 1 + FLEX = 1 << 1, + AFHDS3 = 1 << 2 }; void addOpenTxRfOptions(OpenTxFirmware * firmware, uint8_t options) { static const Firmware::Option opt_eu("eu", Firmware::tr("Removes D8 FrSky protocol support which is not legal for use in the EU on radios sold after Jan 1st, 2015")); static const Firmware::Option opt_fl("flexr9m", Firmware::tr("Enable non certified firmwares")); - if (options == EU + FLEX) + static const Firmware::Option opt_afhds3("afhds3", Firmware::tr("Enable AFHDS3 support")); + + + if ((options & (EU + FLEX)) == EU + FLEX) firmware->addOptionsGroup({opt_eu, opt_fl}); - else if (options == EU) + else if ((options & EU) != 0) firmware->addOption(opt_eu); - else if (options == FLEX) + else if ((options & FLEX) != 0) firmware->addOption(opt_fl); + if ((options & AFHDS3) != 0) + firmware->addOption(opt_afhds3); } void addOpenTxFontOptions(OpenTxFirmware * firmware) @@ -1245,13 +1252,13 @@ void registerOpenTxFirmwares() firmware->addOption("noras", Firmware::tr("Disable RAS (SWR)")); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX); + addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); /* FrSky Taranis X9D+ 2019 board */ firmware = new OpenTxFirmware("opentx-x9d+2019", Firmware::tr("FrSky Taranis X9D+ 2019"), BOARD_TARANIS_X9DP_2019); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* FrSky Taranis X9D board */ firmware = new OpenTxFirmware("opentx-x9d", Firmware::tr("FrSky Taranis X9D"), BOARD_TARANIS_X9D); @@ -1259,7 +1266,7 @@ void registerOpenTxFirmwares() firmware->addOption("haptic", Firmware::tr("Haptic module installed")); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX); + addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); /* FrSky Taranis X9E board */ firmware = new OpenTxFirmware("opentx-x9e", Firmware::tr("FrSky Taranis X9E"), BOARD_TARANIS_X9E); @@ -1267,37 +1274,37 @@ void registerOpenTxFirmwares() firmware->addOption("horussticks", Firmware::tr("Horus gimbals installed (Hall sensors)")); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX); + addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); /* FrSky X9-Lite board */ firmware = new OpenTxFirmware("opentx-x9lite", Firmware::tr("FrSky Taranis X9-Lite"), BOARD_TARANIS_X9LITE); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* FrSky X9-LiteS board */ firmware = new OpenTxFirmware("opentx-x9lites", Firmware::tr("FrSky Taranis X9-Lite S"), BOARD_TARANIS_X9LITES); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* FrSky X7 board */ firmware = new OpenTxFirmware("opentx-x7", Firmware::tr("FrSky Taranis X7 / X7S"), BOARD_TARANIS_X7); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX); + addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); /* FrSky X7 Access board */ firmware = new OpenTxFirmware("opentx-x7access", Firmware::tr("FrSky Taranis X7 / X7S Access"), BOARD_TARANIS_X7_ACCESS); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* FrSky X-Lite S/PRO board */ firmware = new OpenTxFirmware("opentx-xlites", Firmware::tr("FrSky Taranis X-Lite S/PRO"), BOARD_TARANIS_XLITES); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* FrSky X-Lite board */ firmware = new OpenTxFirmware("opentx-xlite", Firmware::tr("FrSky Taranis X-Lite"), BOARD_TARANIS_XLITE); @@ -1311,13 +1318,13 @@ void registerOpenTxFirmwares() addOpenTxFrskyOptions(firmware); firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement")); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX); + addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); /* FrSky X10 Express board */ firmware = new OpenTxFirmware("opentx-x10express", Firmware::tr("FrSky Horus X10 Express / X10S Express"), BOARD_X10_EXPRESS); addOpenTxFrskyOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* FrSky X12 (Horus) board */ firmware = new OpenTxFirmware("opentx-x12s", Firmware::tr("FrSky Horus X12S"), BOARD_HORUS_X12S); @@ -1325,7 +1332,7 @@ void registerOpenTxFirmwares() firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement")); firmware->addOption("pcbdev", Firmware::tr("Use ONLY with first DEV pcb version")); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX); + addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); /* Jumper T12 board */ firmware = new OpenTxFirmware("opentx-t12", QCoreApplication::translate("Firmware", "Jumper T12 / T12 Pro"), BOARD_JUMPER_T12); @@ -1336,20 +1343,20 @@ void registerOpenTxFirmwares() firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module")); addOpenTxFontOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* Jumper T16 board */ firmware = new OpenTxFirmware("opentx-t16", Firmware::tr("Jumper T16 / T16+ / T16 Pro"), BOARD_JUMPER_T16); addOpenTxFrskyOptions(firmware); firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module")); firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); registerOpenTxFirmware(firmware); /* Radiomaster TX16S board */ firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16s / TX16s Hall / TX16s Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module")); static const Firmware::Option opt_internal_gps("internalgps", Firmware::tr("Support internal GPS")); firmware->addOptionsGroup({opt_bt, opt_internal_gps}); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 43eaf11fe..b1fb9af72 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -192,6 +192,8 @@ void TimerPanel::on_name_editingFinished() #define MASK_SBUSPPM_FIELDS (1<<11) #define MASK_SUBTYPES (1<<12) #define MASK_ACCESS (1<<13) +#define MASK_RX_FREQ (1<<14) +#define MASK_RF_POWER (1<<15) quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; @@ -271,7 +273,7 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul connect(ui->multiProtocol, static_cast(&QComboBox::currentIndexChanged), this, &ModulePanel::onMultiProtocolChanged); connect(this, &ModulePanel::channelsRangeChanged, this, &ModulePanel::setupFailsafes); connect(ui->btnGrpValueType, static_cast(&QButtonGroup::buttonClicked), this, &ModulePanel::onFailsafesDisplayValueTypeChanged); - + connect(ui->rxFreq, static_cast(&QSpinBox::valueChanged), this, &ModulePanel::onRfFreqChanged); connect(ui->clearRx1, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); connect(ui->clearRx2, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); connect(ui->clearRx3, SIGNAL(clicked()), this, SLOT(onClearAccessRxClicked())); @@ -391,7 +393,7 @@ void ModulePanel::update() mask |= MASK_PROTOCOL; switch (protocol) { case PULSES_PXX_R9M: - mask |= MASK_R9M | MASK_SUBTYPES; + mask |= MASK_R9M | MASK_RF_POWER | MASK_SUBTYPES; case PULSES_ACCESS_R9M: case PULSES_ACCESS_R9M_LITE: case PULSES_ACCESS_R9M_LITE_PRO: @@ -450,6 +452,11 @@ void ModulePanel::update() if (pdef.hasFailsafe || (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_FRSKY && (module.subType == 0 || module.subType == 2 || module.subType > 3 ))) mask |= MASK_FAILSAFES; break; + case PULSES_AFHDS3: + module.channelsCount = 18; + mask |= MASK_CHANNELS_RANGE| MASK_CHANNELS_COUNT | MASK_FAILSAFES; + mask |= MASK_SUBTYPES | MASK_RX_FREQ | MASK_RF_POWER; + break; case PULSES_OFF: break; default: @@ -516,16 +523,16 @@ void ModulePanel::update() } // R9M options - ui->r9mPower->setVisible(mask & MASK_R9M); - ui->label_r9mPower->setVisible(mask & MASK_R9M); + ui->r9mPower->setVisible(mask & MASK_RF_POWER); + ui->label_r9mPower->setVisible(mask & MASK_RF_POWER); ui->warning_r9mPower->setVisible((mask & MASK_R9M) && module.subType == MODULE_SUBTYPE_R9M_EU); ui->warning_r9mFlex->setVisible((mask & MASK_R9M) && module.subType > MODULE_SUBTYPE_R9M_EU); - if (mask & MASK_R9M) { + if (mask & MASK_RF_POWER) { const QSignalBlocker blocker(ui->r9mPower); ui->r9mPower->clear(); - ui->r9mPower->addItems(ModuleData::powerValueStrings(module.subType, firmware)); - ui->r9mPower->setCurrentIndex(module.pxx.power); + ui->r9mPower->addItems(ModuleData::powerValueStrings(protocol, module.subType, firmware)); + ui->r9mPower->setCurrentIndex(mask & MASK_R9M ? module.pxx.power : module.afhds3.rfPower); } // module subtype @@ -542,6 +549,9 @@ void ModulePanel::update() if (firmware->getCapability(HasModuleR9MFlex)) i = 2; break; + case PULSES_AFHDS3: + numEntries = 4; + break; default: break; } @@ -606,6 +616,8 @@ void ModulePanel::update() // Failsafes ui->label_failsafeMode->setVisible(mask & MASK_FAILSAFES); ui->failsafeMode->setVisible(mask & MASK_FAILSAFES); + //hide reciever mode for afhds3 + qobject_cast(ui->failsafeMode->view())->setRowHidden(FAILSAFE_RECEIVER, protocol == PULSES_AFHDS3); if ((mask & MASK_FAILSAFES) && module.failsafeMode == FAILSAFE_CUSTOM) { if (ui->failsafesGroupBox->isHidden()) { @@ -635,6 +647,9 @@ void ModulePanel::update() } } } + + ui->label_rxFreq->setVisible((mask & MASK_RX_FREQ)); + ui->rxFreq->setVisible((mask & MASK_RX_FREQ)); } void ModulePanel::on_trainerMode_currentIndexChanged(int index) @@ -666,9 +681,14 @@ void ModulePanel::on_ppmPolarity_currentIndexChanged(int index) void ModulePanel::on_r9mPower_currentIndexChanged(int index) { - if (!lock && module.pxx.power != (unsigned int)index) { - module.pxx.power = index; - emit modified(); + if (!lock) { + + if (module.protocol == PULSES_AFHDS3 && module.afhds3.rfPower != (unsigned int)index) { + module.afhds3.rfPower = index; + emit modified(); + } + else if(module.pxx.power != (unsigned int)index) + module.pxx.power = index; } } @@ -766,12 +786,21 @@ void ModulePanel::onSubTypeChanged() if (!lock && module.subType != type) { lock=true; module.subType = type; - update(); + if (module.protocol != PULSES_AFHDS3) { + update(); + } emit modified(); lock = false; } } +void ModulePanel::onRfFreqChanged(int freq) { + if (module.afhds3.rxFreq != (unsigned int)freq) { + module.afhds3.rxFreq = (unsigned int)freq; + emit modified(); + } +} + void ModulePanel::on_disableTelem_stateChanged(int state) { module.multi.disableTelemetry = (state == Qt::Checked); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 1470e4e2f..fbd56ed86 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -97,6 +97,7 @@ class ModulePanel : public ModelPanel void onFailsafeUsecChanged(int value); void onFailsafePercentChanged(double value); void onFailsafesDisplayValueTypeChanged(int type); + void onRfFreqChanged(int freq); void updateFailsafe(unsigned channel); void on_optionValue_editingFinished(); void onClearAccessRxClicked(); diff --git a/companion/src/modeledit/setup_module.ui b/companion/src/modeledit/setup_module.ui index c0a54d024..654af2a54 100644 --- a/companion/src/modeledit/setup_module.ui +++ b/companion/src/modeledit/setup_module.ui @@ -126,6 +126,12 @@ 0 + + + 100 + 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -173,6 +179,15 @@ 0 + + + 100 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + @@ -214,6 +229,12 @@ 0 + + + 100 + 0 + + QComboBox::AdjustToContents @@ -417,6 +438,7 @@ + @@ -485,6 +507,12 @@ 0 + + + 80 + 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -529,6 +557,12 @@ 0 + + + 80 + 0 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -573,6 +607,15 @@ 0 + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + ms @@ -617,19 +660,58 @@ 0 + + + 80 + 0 + + QComboBox::AdjustToContents + + + + RX Frequency + + + + + + Option value + + + + + + + + 0 + 0 + + + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + Output type - + @@ -652,21 +734,7 @@ - - - - Option value - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + Qt::Horizontal @@ -679,6 +747,34 @@ + + + + + 0 + 0 + + + + + 80 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Hz + + + 50 + + + 400 + + + @@ -711,11 +807,17 @@ - + 0 0 + + + 180 + 0 + + QComboBox::AdjustToContents @@ -754,11 +856,17 @@ - + 0 0 + + + 150 + 0 + + QComboBox::AdjustToContents @@ -774,11 +882,17 @@ - + 0 0 + + + 150 + 0 + + QComboBox::AdjustToContents @@ -847,11 +961,17 @@ - + 0 0 + + + 150 + 0 + + QComboBox::AdjustToContents @@ -901,11 +1021,17 @@ - + 0 0 + + + 150 + 0 + + QComboBox::AdjustToContents @@ -934,17 +1060,17 @@ Master/SBUS in battery compartment - + Master/Bluetooth - + Slave/Bluetooth - + Master/Multi diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index dde8517e0..1d1881a75 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -224,6 +224,11 @@ QString ModelPrinter::printModule(int idx) str << printLabelValue(tr("Sub Type"), module.subTypeToString()); str << printLabelValue(tr("RF Output Power"), module.powerValueToString(firmware)); } + if (module.protocol == PULSES_AFHDS3) { + str << printLabelValue(tr("Output Type"), module.subTypeToString()); + str << printLabelValue(tr("RF Output Power"), module.powerValueToString(firmware)); + str << printLabelValue(tr("RX Output Frequency"), QString("%1Hz").arg(module.afhds3.rxFreq)); + } } } result = str.join(" "); diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index f67d0cb07..d47b0eeae 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -269,7 +269,8 @@ enum TelemetryProtocol PROTOCOL_TELEMETRY_HITEC, PROTOCOL_TELEMETRY_HOTT, PROTOCOL_TELEMETRY_MULTIMODULE, - PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_MULTIMODULE, + PROTOCOL_TELEMETRY_AFHDS3, + PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_AFHDS3, PROTOCOL_TELEMETRY_LUA }; diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 9ff05975a..f164b1ce2 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -454,6 +454,24 @@ PACK(struct ModuleData { uint8_t receivers; // 5 bits spare char receiverName[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME]; }) pxx2); + NOBACKUP(PACK(struct { + uint8_t bindPower:3; + uint8_t runPower:3; + uint8_t emi:1; + uint8_t telemetry:1; + uint16_t failsafeTimeout; + uint8_t rx_freq[2]; + uint16_t rxFreq() + { + return (uint16_t)rx_freq[0] | (((uint16_t)rx_freq[1]) << 8); + } + + void setRxFreq(uint16_t value) + { + rx_freq[0] = value & 0xFF; + rx_freq[1] = value >> 8; + } + } afhds3)); }; // Helper functions to set both of the rfProto protocol at the same time diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 47b8e4958..658a90a4e 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -118,12 +118,22 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_SUBTYPE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS, +#endif +#if defined(AFHDS3) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS_PROTOCOL, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM, #if defined(PCBSKY9X) && defined(REVX) ITEM_MODEL_SETUP_EXTERNAL_MODULE_OUTPUT_TYPE, +#endif +#if defined(AFHDS3) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER, ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS, @@ -132,6 +142,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM, ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING, #endif + ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS, @@ -204,11 +215,6 @@ inline uint8_t MODULE_TYPE_ROWS(int moduleIdx) { if (isModuleXJT(moduleIdx) || isModuleR9MNonAccess(moduleIdx) || isModuleDSM2(moduleIdx)) return 1; -#if defined(MULTIMODULE) - else if (isModuleMultimodule(moduleIdx)) { - return 0; - } -#endif else return 0; } @@ -300,6 +306,9 @@ void onBluetoothConnectMenu(const char * result) #if defined(PXX2) #include "common/stdlcd/model_setup_pxx2.cpp" #endif +#if defined(AFHDS3) +#include "common/stdlcd/model_setup_afhds3.cpp" +#endif #if defined(HARDWARE_INTERNAL_MODULE) #define INTERNAL_MODULE_ROWS \ @@ -364,9 +373,12 @@ void menuModelSetup(event_t event) MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) // PROTOCOL MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) // SUBTYPE MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) + AFHDS3_PROTOCOL_ROW(EXTERNAL_MODULE) + AFHDS3_MODE_ROWS(EXTERNAL_MODULE) MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum + AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) MODULE_POWER_ROW(EXTERNAL_MODULE), IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) @@ -406,6 +418,7 @@ void menuModelSetup(event_t event) MODULE_TYPE_ROWS(EXTERNAL_MODULE), MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) + MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum @@ -1047,6 +1060,21 @@ void menuModelSetup(event_t event) break; #endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS_PROTOCOL: + lcdDrawTextAlignedLeft(y, TR_PROTO); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].subType, attr); + if (attr && s_editMode > 0 && menuHorizontalPosition == 0) { + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, AFHDS_SUBTYPE_FIRST, AFHDS_SUBTYPE_LAST); + } + break; + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE: + lcdDrawText(INDENT_WIDTH, y, TR_TELEMETRY_TYPE); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, + g_model.moduleData[EXTERNAL_MODULE].afhds3.telemetry ? STR_AFHDS3_ONE_TO_ONE_TELEMETRY : TR_AFHDS3_ONE_TO_MANY); + break; +#endif + #if defined(PCBTARANIS) case ITEM_MODEL_SETUP_TRAINER_LABEL: lcdDrawTextAlignedLeft(y, STR_TRAINER); @@ -1352,7 +1380,7 @@ void menuModelSetup(event_t event) if (attr && l_posHorz > 0) { if (s_editMode > 0) { if (l_posHorz == 1) { - if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx)) { + if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx)) { #if defined(PCBXLITE) if (EVT_KEY_MASK(event) == KEY_ENTER) { #elif defined(NAVIGATION_9X) @@ -1361,6 +1389,12 @@ void menuModelSetup(event_t event) if (event == EVT_KEY_BREAK(KEY_ENTER)) { #endif killEvents(event); +#if defined(AFHDS3) + if (isModuleAFHDS3(moduleIdx)) { + startBindMenuAfhds3(moduleIdx); + continue; + } +#endif startBindMenu(moduleIdx); continue; } @@ -1604,6 +1638,14 @@ void menuModelSetup(event_t event) module.multi.lowPowerMode = editCheckBox(module.multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, IS_RX_MULTI(moduleIdx) ? STR_MULTI_LNA_DISABLE : STR_MULTI_LOWPOWER, attr, event); } #endif +#if defined(AFHDS3) + else if (isModuleAFHDS3(EXTERNAL_MODULE)) { + lcdDrawText(INDENT_WIDTH, y, STR_RFPOWER); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_POWERS, g_model.moduleData[EXTERNAL_MODULE].afhds3.runPower, LEFT | attr); + if (attr) + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].afhds3.runPower, afhds3::RUN_POWER::RUN_POWER_FIRST, afhds3::RUN_POWER::RUN_POWER_LAST); + } +#endif } break; @@ -1630,7 +1672,25 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING: g_model.moduleData[moduleIdx].multi.disableMapping = editCheckBox(g_model.moduleData[moduleIdx].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_CH_MAP, attr, event); break; +#endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ: + lcdDrawText(INDENT_WIDTH, y, STR_AFHDS3_RX_FREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.moduleData[EXTERNAL_MODULE].afhds3.rxFreq(), attr | LEFT); + if (attr) { + uint16_t rxFreq = g_model.moduleData[EXTERNAL_MODULE].afhds3.rxFreq(); + CHECK_INCDEC_MODELVAR(event, rxFreq, MIN_FREQ, MAX_FREQ); + g_model.moduleData[EXTERNAL_MODULE].afhds3.setRxFreq(rxFreq); + } + break; + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER: + lcdDrawText(INDENT_WIDTH, y, STR_AFHDS3_ACTUAL_POWER); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_POWERS, actualAfhdsRunPower(EXTERNAL_MODULE), LEFT); + break; +#endif + +#if defined(MULTIMODULE) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS: #endif @@ -1638,7 +1698,7 @@ void menuModelSetup(event_t event) lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); char statusText[64]; - getMultiModuleStatus(moduleIdx).getStatusString(statusText); + getModuleStatusString(moduleIdx, statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } @@ -1647,9 +1707,26 @@ void menuModelSetup(event_t event) #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC); + char statusText[64]; + getModuleSyncStatusString(moduleIdx, statusText); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); + break; + } +#endif + +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: { + lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); char statusText[64]; - getMultiSyncStatus(moduleIdx).getRefreshString(statusText); + getModuleStatusString(moduleIdx, statusText); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); + break; + } + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS: { + lcdDrawText(INDENT_WIDTH, y, STR_AFHDS3_POWER_SOURCE); + char statusText[64]; + getModuleSyncStatusString(moduleIdx, statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index f2933d5b7..e6d8758e2 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -101,6 +101,11 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_PROTOCOL, ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS, +#endif +#if defined(AFHDS3) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, @@ -110,6 +115,10 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND, ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM, ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING, +#endif +#if defined(AFHDS3) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER, ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE, @@ -247,7 +256,7 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) inline uint8_t EXTERNAL_MODULE_TYPE_ROW() { - if (isModuleXJT(EXTERNAL_MODULE) || isModuleR9MNonAccess(EXTERNAL_MODULE) || isModuleDSM2(EXTERNAL_MODULE)) + if (isModuleXJT(EXTERNAL_MODULE) || isModuleR9MNonAccess(EXTERNAL_MODULE) || isModuleDSM2(EXTERNAL_MODULE) || isModuleAFHDS3(EXTERNAL_MODULE)) return 1; else return 0; @@ -304,6 +313,10 @@ void onBluetoothConnectMenu(const char * result) #if defined(PXX2) #include "common/stdlcd/model_setup_pxx2.cpp" #endif +#if defined(AFHDS3) +#include "common/stdlcd/model_setup_afhds3.cpp" +#endif + void menuModelSetup(event_t event) { @@ -360,11 +373,13 @@ void menuModelSetup(event_t event) EXTERNAL_MODULE_TYPE_ROW(), MULTIMODULE_TYPE_ROW(EXTERNAL_MODULE) MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) + AFHDS3_MODE_ROWS(EXTERNAL_MODULE) MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum for ACCESS IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) + AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) MODULE_POWER_ROW(EXTERNAL_MODULE), FAILSAFE_ROWS(EXTERNAL_MODULE), IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons @@ -810,6 +825,11 @@ void menuModelSetup(event_t event) lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, menuHorizontalPosition==1 ? attr : 0); else if (isModuleR9MNonAccess(EXTERNAL_MODULE)) lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_R9M_REGION, g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0)); +#if defined(AFHDS3) + else if (isModuleAFHDS3(EXTERNAL_MODULE)) { + lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_AFHDS3_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0)); + } +#endif if (attr && menuHorizontalPosition == 0) { if (s_editMode > 0) { g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_NONE; @@ -850,6 +870,11 @@ void menuModelSetup(event_t event) MODULE_SUBTYPE_R9M_FCC, MODULE_SUBTYPE_R9M_LAST, EE_MODEL, isR9MModeAvailable); } +#if defined(AFHDS3) + else if (isModuleAFHDS3(EXTERNAL_MODULE)) { + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, AFHDS_SUBTYPE_FIRST, AFHDS_SUBTYPE_LAST); + } +#endif else { g_model.moduleData[EXTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_PXX1_ACCST_D16, MODULE_SUBTYPE_PXX1_LAST, EE_MODEL, @@ -898,6 +923,7 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PROTOCOL: { lcdDrawTextAlignedLeft(y, TR_TYPE); + uint8_t multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(); lcdDrawMultiProtocolString(MODEL_SETUP_2ND_COLUMN, y, EXTERNAL_MODULE, multi_rfProto, menuHorizontalPosition == 0 ? attr : 0); if (MULTIMODULE_HAS_SUBTYPE(EXTERNAL_MODULE)) @@ -946,7 +972,6 @@ void menuModelSetup(event_t event) } break; #endif - case ITEM_MODEL_SETUP_TRAINER_LABEL: lcdDrawTextAlignedLeft(y, STR_TRAINER); break; @@ -1170,8 +1195,14 @@ void menuModelSetup(event_t event) if (attr && l_posHorz > 0) { if (s_editMode > 0) { if (l_posHorz == 1) { - if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx)) { + if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx)) { if (event == EVT_KEY_BREAK(KEY_ENTER)) { +#if defined(AFHDS3) + if (isModuleAFHDS3(moduleIdx)) { + startBindMenuAfhds3(moduleIdx); + continue; + } +#endif startBindMenu(moduleIdx); continue; } @@ -1327,6 +1358,14 @@ void menuModelSetup(event_t event) else if (isModuleMultimodule(moduleIdx)) { g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, IS_RX_MULTI(moduleIdx) ? STR_MULTI_LNA_DISABLE : STR_MULTI_LOWPOWER, attr, event); } +#endif +#if defined(AFHDS3) + else if (isModuleAFHDS3(EXTERNAL_MODULE)) { + lcdDrawText(INDENT_WIDTH, y, STR_RFPOWER); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_POWERS, g_model.moduleData[EXTERNAL_MODULE].afhds3.runPower, LEFT | attr); + if (attr) + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].afhds3.runPower, afhds3::RUN_POWER::RUN_POWER_FIRST, afhds3::RUN_POWER::RUN_POWER_LAST); + } #endif break; } @@ -1346,26 +1385,68 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING: g_model.moduleData[EXTERNAL_MODULE].multi.disableMapping = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_CH_MAP, attr, event); break; +#endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ: + lcdDrawText(INDENT_WIDTH, y, STR_AFHDS3_RX_FREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.moduleData[EXTERNAL_MODULE].afhds3.rxFreq(), attr | LEFT); + if (attr) { + uint16_t rxFreq = g_model.moduleData[EXTERNAL_MODULE].afhds3.rxFreq(); + CHECK_INCDEC_MODELVAR(event, rxFreq, MIN_FREQ, MAX_FREQ); + g_model.moduleData[EXTERNAL_MODULE].afhds3.setRxFreq(rxFreq); + } + break; + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER: + lcdDrawText(INDENT_WIDTH, y, STR_AFHDS3_ACTUAL_POWER); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_POWERS, actualAfhdsRunPower(EXTERNAL_MODULE), LEFT); + break; +#endif + +#if defined (MULTIMODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: { - lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); + lcdDrawText(INDENT_WIDTH, y, STR_MODULE_STATUS); char statusText[64]; - getMultiModuleStatus(EXTERNAL_MODULE).getStatusString(statusText); + getModuleStatusString(EXTERNAL_MODULE, statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { - lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC); + lcdDrawText(INDENT_WIDTH, y, STR_MODULE_SYNC); char statusText[64]; - getMultiSyncStatus(EXTERNAL_MODULE).getRefreshString(statusText); + getModuleSyncStatusString(EXTERNAL_MODULE, statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } #endif +#if defined (AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE: + lcdDrawTextAlignedLeft(y, STR_TYPE); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, + g_model.moduleData[EXTERNAL_MODULE].afhds3.telemetry ? STR_AFHDS3_ONE_TO_ONE_TELEMETRY : TR_AFHDS3_ONE_TO_MANY); + break; + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: + { + lcdDrawTextAlignedLeft(y, TR_MODULE_STATUS); + char statusText[64]; + getModuleStatusString(EXTERNAL_MODULE, statusText); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); + break; + } + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS: + { + lcdDrawText(INDENT_WIDTH, y, STR_AFHDS3_POWER_SOURCE); + char statusText[64]; + getModuleSyncStatusString(EXTERNAL_MODULE, statusText); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); + break; + } +#endif + + #if defined(PXX2) case ITEM_MODEL_SETUP_REGISTRATION_ID: lcdDrawTextAlignedLeft(y, STR_REG_ID); diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 191813f38..ebda41874 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -98,6 +98,11 @@ enum MenuModelSetupItems { #if defined(MULTIMODULE) ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS, +#endif +#if defined(AFHDS3) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, @@ -107,6 +112,10 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND, ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM, ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING, +#endif +#if defined(AFHDS3) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ, + ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER, #endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER, ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE, @@ -115,7 +124,6 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3, - ITEM_MODEL_SETUP_TRAINER_LABEL, ITEM_MODEL_SETUP_TRAINER_MODE, #if defined(BLUETOOTH) @@ -156,6 +164,10 @@ void onBluetoothConnectMenu(const char * result) #include "common/stdlcd/model_setup_pxx1.cpp" +#if defined(AFHDS3) +#include "common/stdlcd/model_setup_afhds3.cpp" +#endif + void onPXX2R9MBindModeMenu(const char * result) { if (result == STR_16CH_WITH_TELEMETRY) { @@ -502,7 +514,7 @@ int getSwitchWarningsCount() inline uint8_t MODULE_TYPE_ROWS(int moduleIdx) { - if (isModuleXJT(moduleIdx) || isModuleR9MNonAccess(moduleIdx) || isModuleDSM2(moduleIdx) || isModulePXX2(moduleIdx)) + if (isModuleXJT(moduleIdx) || isModuleR9MNonAccess(moduleIdx) || isModuleDSM2(moduleIdx) || isModulePXX2(moduleIdx) || isModuleAFHDS3(moduleIdx)) return 1; #if defined(MULTIMODULE) else if (isModuleMultimodule(moduleIdx)) { @@ -622,11 +634,13 @@ bool menuModelSetup(event_t event) LABEL(ExternalModule), MODULE_TYPE_ROWS(EXTERNAL_MODULE), MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) + AFHDS3_MODE_ROWS(EXTERNAL_MODULE) MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum for ACCESS IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) + AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) MODULE_POWER_ROW(EXTERNAL_MODULE), FAILSAFE_ROWS(EXTERNAL_MODULE), IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons @@ -1074,6 +1088,11 @@ bool menuModelSetup(event_t event) if (MULTIMODULE_HAS_SUBTYPE(moduleIdx)) lcdDrawMultiSubProtocolString(MODEL_SETUP_4TH_COLUMN, y, moduleIdx, g_model.moduleData[moduleIdx].subType, menuHorizontalPosition==2 ? attr : 0); } +#endif +#if defined(AFHDS3) + else if (isModuleAFHDS3(moduleIdx)) { + lcdDrawTextAtIndex(lcdNextPos + 15, y, STR_AFHDS3_PROTOCOLS, g_model.moduleData[moduleIdx].subType, (menuHorizontalPosition==1 ? attr : 0)); + } #endif if (attr && menuHorizontalPosition == 0 && moduleIdx == EXTERNAL_MODULE) { if (s_editMode > 0) { @@ -1143,6 +1162,11 @@ bool menuModelSetup(event_t event) g_model.moduleData[moduleIdx].subType = checkIncDec(event, g_model.moduleData[moduleIdx].subType, MODULE_SUBTYPE_R9M_FCC, MODULE_SUBTYPE_R9M_LAST, EE_MODEL, isR9MModeAvailable); } +#if defined(AFHDS3) + else if (isModuleAFHDS3(moduleIdx)) { + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, AFHDS_SUBTYPE_FIRST, AFHDS_SUBTYPE_LAST); + } +#endif else { CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, MODULE_SUBTYPE_PXX1_ACCST_D16, MODULE_SUBTYPE_PXX1_LAST); } @@ -1552,8 +1576,14 @@ bool menuModelSetup(event_t event) if (attr && l_posHorz>0) { if (s_editMode>0) { if (l_posHorz == 1) { - if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx)) { + if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx)) { if (event == EVT_KEY_BREAK(KEY_ENTER)) { +#if defined(AFHDS3) + if (isModuleAFHDS3(moduleIdx)) { + startBindMenuAfhds3(moduleIdx); + continue; + } +#endif startBindMenu(moduleIdx); continue; } @@ -1707,6 +1737,14 @@ bool menuModelSetup(event_t event) lcdDrawText(MENUS_MARGIN_LEFT, y, IS_RX_MULTI(moduleIdx) ? STR_MULTI_LNA_DISABLE : STR_MULTI_LOWPOWER); g_model.moduleData[moduleIdx].multi.lowPowerMode = editCheckBox(g_model.moduleData[moduleIdx].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); } +#endif +#if defined(AFHDS3) + else if (isModuleAFHDS3(moduleIdx)) { + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RFPOWER); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_POWERS, g_model.moduleData[moduleIdx].afhds3.runPower, LEFT | attr); + if (attr) + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].afhds3.runPower, afhds3::RUN_POWER::RUN_POWER_FIRST, afhds3::RUN_POWER::RUN_POWER_LAST); + } #endif } break; @@ -1722,7 +1760,6 @@ bool menuModelSetup(event_t event) lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_AUTOBIND); g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); break; - #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: #endif @@ -1730,7 +1767,6 @@ bool menuModelSetup(event_t event) lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_DISABLE_TELEM); g_model.moduleData[moduleIdx].multi.disableTelemetry = editCheckBox(g_model.moduleData[moduleIdx].multi.disableTelemetry, MODEL_SETUP_2ND_COLUMN, y, attr, event); break; - #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING: #endif @@ -1738,29 +1774,65 @@ bool menuModelSetup(event_t event) lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_DISABLE_CH_MAP); g_model.moduleData[moduleIdx].multi.disableMapping = editCheckBox(g_model.moduleData[moduleIdx].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, attr, event); break; +#endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_MODE: + lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TYPE); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, + g_model.moduleData[EXTERNAL_MODULE].afhds3.telemetry ? STR_AFHDS3_ONE_TO_ONE_TELEMETRY : TR_AFHDS3_ONE_TO_MANY); + break; + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ: + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_AFHDS3_RX_FREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.moduleData[moduleIdx].afhds3.rxFreq(), attr | LEFT, 0, NULL, "Hz"); + if (attr) { + uint16_t rxFreq = g_model.moduleData[moduleIdx].afhds3.rxFreq(); + CHECK_INCDEC_MODELVAR(event, rxFreq, MIN_FREQ, MAX_FREQ); + g_model.moduleData[moduleIdx].afhds3.setRxFreq(rxFreq); + } + break; + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER: + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_AFHDS3_ACTUAL_POWER); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_POWERS, actualAfhdsRunPower(moduleIdx), LEFT); + break; +#endif + +#if defined(MULTIMODULE) || defined(AFHDS3) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS: +#endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_STATUS); char statusText[64]; - getMultiModuleStatus(moduleIdx).getStatusString(statusText); + getModuleStatusString(moduleIdx, statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS: +#endif +#if defined(AFHDS3) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS: #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_SYNC); - +#if defined(AFHDS3) + if (isModuleAFHDS3(moduleIdx)) { + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_AFHDS3_POWER_SOURCE); + } + else +#endif + { + lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_SYNC); + } char statusText[64]; - getMultiSyncStatus(moduleIdx).getRefreshString(statusText); + getModuleSyncStatusString(moduleIdx, statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } diff --git a/radio/src/gui/common/stdlcd/model_setup_afhds3.cpp b/radio/src/gui/common/stdlcd/model_setup_afhds3.cpp new file mode 100644 index 000000000..a094ef83a --- /dev/null +++ b/radio/src/gui/common/stdlcd/model_setup_afhds3.cpp @@ -0,0 +1,48 @@ +/* + * 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. + */ + + +void onBindMenuAfhds3(const char * result) +{ + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE); + + if (result == STR_AFHDS3_ONE_TO_ONE_TELEMETRY) { + g_model.moduleData[EXTERNAL_MODULE].afhds3.telemetry = 1; + } + else if (result == STR_AFHDS3_ONE_TO_MANY) { + g_model.moduleData[EXTERNAL_MODULE].afhds3.telemetry = 0; + } + else { + return; + } + + moduleState[moduleIdx].mode = MODULE_MODE_BIND; +} + +void startBindMenuAfhds3(uint8_t moduleIdx) +{ + uint8_t selection = 0; +#if defined(AFHDS3) + POPUP_MENU_ADD_ITEM(STR_AFHDS3_ONE_TO_ONE_TELEMETRY); + POPUP_MENU_ADD_ITEM(STR_AFHDS3_ONE_TO_MANY); +#endif + POPUP_MENU_SELECT_ITEM(selection); + POPUP_MENU_START(onBindMenuAfhds3); +} diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 29666fd26..16929cc7e 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -551,6 +551,7 @@ bool isModuleUsingSport(uint8_t moduleBay, uint8_t moduleType) case MODULE_TYPE_ISRM_PXX2: case MODULE_TYPE_R9M_LITE_PXX2: case MODULE_TYPE_R9M_LITE_PRO_PXX2: + case MODULE_TYPE_AFHDS3: return false; case MODULE_TYPE_XJT_PXX1: @@ -663,6 +664,11 @@ bool isExternalModuleAvailable(int moduleType) return false; #endif +#if !defined(AFHDS3) + if (moduleType == MODULE_TYPE_AFHDS3) + return false; +#endif + return true; } @@ -708,6 +714,12 @@ bool isTelemetryProtocolAvailable(int protocol) } #endif +#if !defined(AFHDS3) + if (protocol == PROTOCOL_TELEMETRY_AFHDS3) { + return false; + } +#endif + #if defined(PCBHORUS) if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { return false; diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 8aa034226..c7229f5c7 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -147,7 +147,7 @@ inline uint8_t MODULE_BIND_ROWS(int moduleIdx) else return 2; } - else if (isModuleXJTD8(moduleIdx) || isModuleSBUS(moduleIdx)) { + else if (isModuleXJTD8(moduleIdx) || isModuleSBUS(moduleIdx) || isModuleAFHDS3(moduleIdx)) { return 1; } else if (isModulePPM(moduleIdx) || isModulePXX1(moduleIdx) || isModulePXX2(moduleIdx) || isModuleDSM2(moduleIdx)) { @@ -276,9 +276,10 @@ inline uint8_t MULTIMODULE_HASOPTIONS(uint8_t moduleIdx) #define MULTIMODULE_TYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW, #define MULTIMODULE_SUBTYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW, #define MULTIMODULE_OPTIONS_ROW(moduleIdx) (isModuleMultimodule(moduleIdx) && MULTIMODULE_HASOPTIONS(moduleIdx)) ? (uint8_t) 0: HIDDEN_ROW -#define MODULE_POWER_ROW(moduleIdx) (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) || isModuleR9MNonAccess(moduleIdx)) ? (isModuleR9MLiteNonPro(moduleIdx) ? (isModuleR9M_FCC_VARIANT(moduleIdx) ? READONLY_ROW : (uint8_t)0) : (uint8_t)0) : HIDDEN_ROW +#define MODULE_POWER_ROW(moduleIdx) (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) || isModuleR9MNonAccess(moduleIdx) || isModuleAFHDS3(moduleIdx)) ? (isModuleR9MLiteNonPro(moduleIdx) ? (isModuleR9M_FCC_VARIANT(moduleIdx) ? READONLY_ROW : (uint8_t)0) : (uint8_t)0) : HIDDEN_ROW #else +#define MULTIMODULE_TYPE_ROWS(moduleIdx) #define MULTIMODULE_STATUS_ROWS(moduleIdx) #define MULTIMODULE_MODULE_ROWS(moduleIdx) #define MULTIMODULE_TYPE_ROW(moduleIdx) @@ -286,11 +287,28 @@ inline uint8_t MULTIMODULE_HASOPTIONS(uint8_t moduleIdx) #define MULTIMODULE_TYPE_ROWS(moduleIdx) #define MULTIMODULE_MODE_ROWS(moduleIdx) (uint8_t)0 #define MULTIMODULE_OPTIONS_ROW(moduleIdx) HIDDEN_ROW -#define MODULE_POWER_ROW(moduleIdx) isModuleR9MNonAccess(moduleIdx) ? (isModuleR9MLiteNonPro(moduleIdx) ? (isModuleR9M_FCC_VARIANT(moduleIdx) ? READONLY_ROW : (uint8_t)0) : (uint8_t)0) : HIDDEN_ROW +#define MODULE_POWER_ROW(moduleIdx) isModuleR9MNonAccess(moduleIdx) || isModuleAFHDS3(moduleIdx) ? (isModuleR9MLiteNonPro(moduleIdx) ? (isModuleR9M_FCC_VARIANT(moduleIdx) ? READONLY_ROW : (uint8_t)0) : (uint8_t)0) : HIDDEN_ROW +#endif + +#if defined(AFHDS3) +#define AFHDS3_PROTOCOL_ROW(moduleIdx) isModuleAFHDS3(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, +#define AFHDS3_MODE_ROWS(moduleIdx) isModuleAFHDS3(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, isModuleAFHDS3(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, isModuleAFHDS3(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, +#define AFHDS3_MODULE_ROWS(moduleIdx) isModuleAFHDS3(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW, isModuleAFHDS3(moduleIdx) ? (uint8_t) TITLE_ROW : HIDDEN_ROW, +#else +#define AFHDS3_PROTOCOL_ROW(moduleIdx) +#define AFHDS3_MODE_ROWS(moduleIdx) +#define AFHDS3_MODULE_ROWS(moduleIdx) #endif #define FAILSAFE_ROWS(moduleIdx) isModuleFailsafeAvailable(moduleIdx) ? (g_model.moduleData[moduleIdx].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW -#define MODULE_OPTION_ROW(moduleIdx) (isModuleR9MNonAccess(moduleIdx) || isModuleSBUS(moduleIdx) ? TITLE_ROW : MULTIMODULE_OPTIONS_ROW(moduleIdx)) + +inline uint8_t MODULE_OPTION_ROW(uint8_t moduleIdx) { + if(isModuleR9MNonAccess(moduleIdx) || isModuleSBUS(moduleIdx)) + return TITLE_ROW; + if(isModuleAFHDS3(moduleIdx)) + return HIDDEN_ROW; + return MULTIMODULE_OPTIONS_ROW(moduleIdx); +} void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags); diff --git a/radio/src/pulses/afhds3.cpp b/radio/src/pulses/afhds3.cpp new file mode 100644 index 000000000..bd3f577a3 --- /dev/null +++ b/radio/src/pulses/afhds3.cpp @@ -0,0 +1,706 @@ +/* + * 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 "afhds3.h" +#include "../debug.h" +#include "../definitions.h" +#include + +#define FAILSAFE_HOLD 1 +#define FAILSAFE_CUSTOM 2 +#define MAX_RETRIES_AFHDS3 5 + +extern void processFlySkySensor(const uint8_t * packet, uint8_t type); + +extern void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); + +namespace afhds3 +{ + +static const char* const moduleStateText[] = +{ + "Not ready", + "HW Error", + "Binding", + "Connecting", + "Connected", + "Standby", + "Waiting for update", + "Updating", + "Updating RX", + "Updating RX failed", + "Testing", + "Ready", + "HW test" +}; + +static const char* const powerSourceText[] = +{ + "Unknown", + "Internal", + "External" +}; + +static const COMMAND periodicRequestCommands[] = +{ + COMMAND::MODULE_STATE, + COMMAND::MODULE_POWER_STATUS, + COMMAND::MODULE_GET_CONFIG, + COMMAND::VIRTUAL_FAILSAFE +}; + +//Address used in transmitted frames - it constrains of target address and source address +const uint8_t FrameAddress = DeviceAddress::TRANSMITTER | (DeviceAddress::MODULE << 4); + +//Static collection of afhds3 object instances by module +PulsesData* AFHDS3PulsesData[EXTERNAL_MODULE + 1] = { nullptr, nullptr }; + +//friends function that can access telemetry parsing method +void processTelemetryData(uint8_t module, uint8_t data, uint8_t* rxBuffer, uint8_t& rxBufferCount, uint8_t maxSize) +{ + if (AFHDS3PulsesData[module]) { + AFHDS3PulsesData[module]->processTelemetryData(data, rxBuffer, rxBufferCount, maxSize); + } +} + +void CommandFifo::clearCommandFifo() +{ + memclear(commandFifo, sizeof(commandFifo)); + setIndex = getIndex = 0; +} + +void CommandFifo::enqueueACK(COMMAND command, uint8_t frameNumber) +{ + uint32_t next = nextIndex(setIndex); + if (next != getIndex) { + commandFifo[setIndex].command = command; + commandFifo[setIndex].frameType = FRAME_TYPE::RESPONSE_ACK; + commandFifo[setIndex].payload = 0; + commandFifo[setIndex].payloadSize = 0; + commandFifo[setIndex].frameNumber = frameNumber; + commandFifo[setIndex].useFrameNumber = true; + setIndex = next; + } +} + +void CommandFifo::enqueue(COMMAND command, FRAME_TYPE frameType, bool useData, uint8_t byteContent) +{ + uint32_t next = nextIndex(setIndex); + if (next != getIndex) { + commandFifo[setIndex].command = command; + commandFifo[setIndex].frameType = frameType; + commandFifo[setIndex].payload = byteContent; + commandFifo[setIndex].payloadSize = useData ? 1 : 0; + commandFifo[setIndex].frameNumber = 0; + commandFifo[setIndex].useFrameNumber = false; + setIndex = next; + } +} + +void PulsesData::getStatusString(char * buffer) const +{ + strcpy(buffer, this->state <= ModuleState::STATE_READY ? moduleStateText[this->state] : "Unknown"); +} + +void PulsesData::getPowerStatus(char* buffer) const +{ + strcpy(buffer, this->powerSource <= MODULE_POWER_SOURCE::EXTERNAL ? powerSourceText[this->powerSource] : "Unknown"); +} + +void PulsesData::processTelemetryData(uint8_t byte, uint8_t* rxBuffer, uint8_t& rxBufferCount, uint8_t maxSize) +{ + if (rxBufferCount == 0 && byte != AfhdsSpecialChars::START) { + TRACE("AFHDS3 [SKIP] %02X", byte); + this->esc_state = 0; + return; + } + + if (byte == AfhdsSpecialChars::ESC) { + this->esc_state = rxBufferCount; + return; + } + + if (rxBufferCount > 1 && byte == AfhdsSpecialChars::END) { + rxBuffer[rxBufferCount++] = byte; + parseData(rxBuffer, rxBufferCount); + rxBufferCount = 0; + return; + } + + if (this->esc_state && byte == AfhdsSpecialChars::ESC_END) { + byte = AfhdsSpecialChars::END; + } + else if (esc_state && byte == AfhdsSpecialChars::ESC_ESC) { + byte = AfhdsSpecialChars::ESC; + } + //reset esc index + this->esc_state = 0; + + if (rxBufferCount >= maxSize) { + TRACE("AFHDS3 [BUFFER OVERFLOW]"); + rxBufferCount = 0; + } + rxBuffer[rxBufferCount++] = byte; +} + +bool PulsesData::isConnectedUnicast() +{ + return cfg.config.telemetry == TELEMETRY::TELEMETRY_ENABLED && this->state == ModuleState::STATE_SYNC_DONE; +} + +bool PulsesData::isConnectedMulticast() +{ + return cfg.config.telemetry == TELEMETRY::TELEMETRY_DISABLED && this->state == ModuleState::STATE_SYNC_RUNNING; +} + +void PulsesData::setupFrame() +{ + if (operationState == State::AWAITING_RESPONSE) { + if (repeatCount++ < MAX_RETRIES_AFHDS3) { + return; //re-send + } + else + { + TRACE("AFHDS3 [NO RESP] module state %d", this->state); + clearFrameData(); + this->state = ModuleState::STATE_NOT_READY; + } + } + else if (operationState == State::UNKNOWN) { + this->state = ModuleState::STATE_NOT_READY; + } + repeatCount = 0; + if (this->state == ModuleState::STATE_NOT_READY) { + TRACE("AFHDS3 [GET MODULE READY]"); + putFrame(COMMAND::MODULE_READY, FRAME_TYPE::REQUEST_GET_DATA); + return; + } + + //check waiting commands + if (!isEmpty()) { + Frame f = commandFifo[getIndex]; + putFrame(f.command, f.frameType, &f.payload, f.payloadSize, f.useFrameNumber ? &f.frameNumber : &frame_index); + getIndex = nextIndex(getIndex); + TRACE("AFHDS3 [CMD QUEUE] cmd: %d frameType %d, useFrameNumber %d frame Number %d size %d", f.command, f.frameType, f.useFrameNumber, f.frameNumber, f.payloadSize); + return; + } + + //config should be loaded already + if (syncSettings()) { + return; + } + + //if module is ready but not started + if (this->state == ModuleState::STATE_READY || this->state == ModuleState::STATE_STANDBY) { + cmdCount = 0; + repeatCount = 0; + requestInfoAndRun(true); + return; + } + + ::ModuleSettingsMode moduleMode = getModuleMode(module_index); + + if (moduleMode == ::ModuleSettingsMode::MODULE_MODE_BIND) { + if (state != STATE_BINDING) { + TRACE("AFHDS3 [BIND]"); + setConfigFromModel(); + putFrame(COMMAND::MODULE_SET_CONFIG, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, cfg.buffer, sizeof(cfg.buffer)); + requestedModuleMode = MODULE_MODE_E::BIND; + enqueue(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, true, requestedModuleMode); + return; + } + } + else if (moduleMode == ::ModuleSettingsMode::MODULE_MODE_RANGECHECK) { + if (cfg.config.runPower != RUN_POWER::RUN_POWER_FIRST) { + TRACE("AFHDS3 [RANGE CHECK]"); + cfg.config.runPower = RUN_POWER::RUN_POWER_FIRST; + uint8_t data[] = { 0x13, 0x20, 0x02, cfg.config.runPower, 0 }; + TRACE("AFHDS3 SET TX POWER %d", moduleData->afhds3.runPower); + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data)); + return; + } + } + else if (moduleMode == ::ModuleSettingsMode::MODULE_MODE_NORMAL) { //exit bind + if (state == STATE_BINDING) { + TRACE("AFHDS3 [EXIT BIND]"); + requestedModuleMode = MODULE_MODE_E::RUN; + putFrame(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, &requestedModuleMode, 1); + return; + } + } + + bool isConnected = isConnectedUnicast() || isConnectedMulticast(); + + if (cmdCount++ >= 150) { + cmdCount = 0; + if (cmdIndex >= sizeof(periodicRequestCommands)) { + cmdIndex = 0; + } + COMMAND cmd = periodicRequestCommands[cmdIndex++]; + + if (cmd == COMMAND::VIRTUAL_FAILSAFE) { + if (isConnected) { + if (isConnectedMulticast()) { + TRACE("AFHDS ONE WAY FAILSAFE"); + uint16_t failSafe[MAX_CHANNELS + 1] = { ((MAX_CHANNELS << 8) | CHANNELS_DATA_MODE::FAIL_SAFE), 0 }; + setFailSafe((int16_t*) (&failSafe[1])); + putFrame(COMMAND::CHANNELS_FAILSAFE_DATA, FRAME_TYPE::REQUEST_SET_NO_RESP, (uint8_t*) failSafe, MAX_CHANNELS * 2 + 2); + } + else { + TRACE("AFHDS TWO WAYS FAILSAFE"); + uint8_t failSafe[3 + MAX_CHANNELS * 2] = { 0x11, 0x60, MAX_CHANNELS * 2 }; + setFailSafe((int16_t*) (failSafe + 3)); + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, failSafe, 3 + MAX_CHANNELS * 2); + } + } + else { + putFrame(COMMAND::MODULE_STATE, FRAME_TYPE::REQUEST_GET_DATA); + } + } + else + { + putFrame(cmd, FRAME_TYPE::REQUEST_GET_DATA); + } + } + else if (isConnected) { + sendChannelsData(); + } + else { + //default frame - request state + putFrame(MODULE_STATE, FRAME_TYPE::REQUEST_GET_DATA); + } +} + +void PulsesData::init(uint8_t moduleIndex, bool resetFrameCount) +{ + module_index = moduleIndex; + AFHDS3PulsesData[module_index] = this; + //clear local vars because it is member of union + moduleData = &g_model.moduleData[module_index]; + operationState = State::UNKNOWN; + state = ModuleState::STATE_NOT_READY; + clearFrameData(); +} + +void PulsesData::clearFrameData() +{ + TRACE("AFHDS3 clearFrameData"); + reset(); + clearCommandFifo(); + repeatCount = 0; + cmdCount = 0; + cmdIndex = 0; + this->frame_index = 1; + this->timeout = 0; + this->esc_state = 0; +} + +void PulsesData::putBytes(uint8_t* data, int length) +{ + for (int i = 0; i < length; i++) { + uint8_t byte = data[i]; + this->crc += byte; + if (END == byte) { + sendByte(ESC); + sendByte(ESC_END); + } + else if (ESC == byte) { + sendByte(ESC); + sendByte(ESC_ESC); + } + else { + sendByte(byte); + } + } +} + +void PulsesData::putFrame(COMMAND command, FRAME_TYPE frame, uint8_t* data, uint8_t dataLength, uint8_t* frameIndex) +{ + //header + operationState = State::SENDING_COMMAND; + reset(); + this->crc = 0; + sendByte(START); + if (frameIndex == nullptr) { + frameIndex = &this->frame_index; + } + uint8_t buffer[] = { FrameAddress, *frameIndex, frame, command }; + putBytes(buffer, 4); + + //payload + if (dataLength > 0) { + putBytes(data, dataLength); + } + //footer + uint8_t crcValue = this->crc ^ 0xff; + putBytes(&crcValue, 1); + sendByte(END); + *frameIndex = *frameIndex + 1; + + switch (frame) { + case FRAME_TYPE::REQUEST_GET_DATA: + case FRAME_TYPE::REQUEST_SET_EXPECT_ACK: + case FRAME_TYPE::REQUEST_SET_EXPECT_DATA: + operationState = State::AWAITING_RESPONSE; + break; + default: + operationState = State::IDLE; + } + + flush(); +} + +bool checkCRC(const uint8_t* data, uint8_t size) +{ + uint8_t crc = 0; + //skip start byte + for (uint8_t i = 1; i < size; i++) { + crc += data[i]; + } + return (crc ^ 0xff) == data[size]; +} + +bool containsData(enum FRAME_TYPE frameType) +{ + return (frameType == FRAME_TYPE::RESPONSE_DATA || + frameType == FRAME_TYPE::REQUEST_SET_EXPECT_DATA || + frameType == FRAME_TYPE::REQUEST_SET_EXPECT_ACK || + frameType == FRAME_TYPE::REQUEST_SET_EXPECT_DATA || + frameType == FRAME_TYPE::REQUEST_SET_NO_RESP); +} + +void PulsesData::setState(uint8_t state) +{ + if (state == this->state) { + return; + } + uint8_t oldState = this->state; + this->state = state; + if (oldState == ModuleState::STATE_BINDING) { + setModuleMode(module_index, ::ModuleSettingsMode::MODULE_MODE_NORMAL); + } + if (state == ModuleState::STATE_NOT_READY) { + operationState = State::UNKNOWN; + } +} + +void PulsesData::requestInfoAndRun(bool send) +{ + if (!send) { + enqueue(COMMAND::MODULE_VERSION, FRAME_TYPE::REQUEST_GET_DATA); + } + enqueue(COMMAND::MODULE_POWER_STATUS, FRAME_TYPE::REQUEST_GET_DATA); + requestedModuleMode = MODULE_MODE_E::RUN; + enqueue(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, true, (uint8_t) MODULE_MODE_E::RUN); + if (send) { + putFrame(COMMAND::MODULE_VERSION, FRAME_TYPE::REQUEST_GET_DATA); + } +} + +void PulsesData::parseData(uint8_t* rxBuffer, uint8_t rxBufferCount) +{ + + if (!checkCRC(rxBuffer, rxBufferCount - 2)) { + TRACE("AFHDS3 [INVALID CRC]"); + return; + } + AfhdsFrame* responseFrame = reinterpret_cast(rxBuffer); + if (containsData((enum FRAME_TYPE) responseFrame->frameType)) { + switch (responseFrame->command) { + case COMMAND::MODULE_READY: + TRACE("AFHDS3 [MODULE_READY] %02X", responseFrame->value); + if (responseFrame->value == MODULE_STATUS_READY) { + setState(ModuleState::STATE_READY); + requestInfoAndRun(); + } + else { + setState(ModuleState::STATE_NOT_READY); + } + break; + case COMMAND::MODULE_GET_CONFIG: + std::memcpy((void*) cfg.buffer, &responseFrame->value, sizeof(cfg.buffer)); + TRACE("AFHDS3 [MODULE_GET_CONFIG] bind power %d run power %d mode %d pwm/ppm %d ibus/sbus %d", cfg.config.bindPower, cfg.config.runPower, cfg.config.telemetry, cfg.config.pulseMode, cfg.config.serialMode); + break; + case COMMAND::MODULE_VERSION: + std::memcpy((void*) &version, &responseFrame->value, sizeof(version)); + TRACE("AFHDS3 [MODULE_VERSION] Product %d, HW %d, BOOT %d, FW %d", version.productNumber, version.hardwereVersion, version.bootloaderVersion, version.firmwareVersion); + break; + case COMMAND::MODULE_POWER_STATUS: + powerSource = (enum MODULE_POWER_SOURCE) responseFrame->value; + TRACE("AFHDS3 [MODULE_POWER_STATUS], %d", powerSource); + break; + case COMMAND::MODULE_STATE: + TRACE("AFHDS3 [MODULE_STATE] %02X", responseFrame->value); + setState(responseFrame->value); + break; + case COMMAND::MODULE_MODE: + TRACE("AFHDS3 [MODULE_MODE] %02X", responseFrame->value); + if (responseFrame->value != CMD_RESULT::SUCCESS) { + setState(ModuleState::STATE_NOT_READY); + } + else { + if (requestedModuleMode == MODULE_MODE_E::RUN) { + enqueue(COMMAND::MODULE_GET_CONFIG, FRAME_TYPE::REQUEST_GET_DATA); + enqueue(COMMAND::MODULE_STATE, FRAME_TYPE::REQUEST_GET_DATA); + } + requestedModuleMode = MODULE_MODE_UNKNOWN; + } + break; + case COMMAND::MODULE_SET_CONFIG: + if (responseFrame->value != CMD_RESULT::SUCCESS) { + setState(ModuleState::STATE_NOT_READY); + } + TRACE("AFHDS3 [MODULE_SET_CONFIG], %02X", responseFrame->value); + break; + case COMMAND::TELEMETRY_DATA: + { + uint8_t* telemetry = &responseFrame->value; + + if (telemetry[0] == 0x22) { + telemetry++; + while (telemetry < rxBuffer + rxBufferCount) { + uint8_t length = telemetry[0]; + uint8_t id = telemetry[1]; + if (id == 0xFE) { + id = 0xF7; //use new id because format is different + } + if (length == 0 || telemetry + length > rxBuffer + rxBufferCount) { + break; + } + if (length == 4) { + //one byte value fill missing byte + uint8_t data[] = { id, telemetry[2], telemetry[3], 0 }; + ::processFlySkySensor(data, 0xAA); + } + if (length == 5) { + if (id == 0xFA) { + telemetry[1] = 0xF8; //remap to afhds3 snr + } + ::processFlySkySensor(telemetry + 1, 0xAA); + } + else if (length == 6 && id == FRM302_STATUS) { + //convert to ibus + uint16_t t = (uint16_t) (((int16_t) telemetry[3] * 10) + 400); + uint8_t dataTemp[] = { ++id, telemetry[2], (uint8_t) (t & 0xFF), (uint8_t) (t >> 8) }; + ::processFlySkySensor(dataTemp, 0xAA); + uint8_t dataVoltage[] = { ++id, telemetry[2], telemetry[4], telemetry[5] }; + ::processFlySkySensor(dataVoltage, 0xAA); + } + else if (length == 7) { + ::processFlySkySensor(telemetry + 1, 0xAC); + } + telemetry += length; + } + } + } + break; + case COMMAND::COMMAND_RESULT: + { + //AfhdsFrameData* respData = responseFrame->GetData(); + //TRACE("COMMAND RESULT %02X result %d datalen %d", respData->CommandResult.command, respData->CommandResult.result, respData->CommandResult.respLen); + } + break; + } + } + + if (responseFrame->frameType == FRAME_TYPE::REQUEST_GET_DATA || responseFrame->frameType == FRAME_TYPE::REQUEST_SET_EXPECT_DATA) { + TRACE("Command %02X NOT IMPLEMENTED!", responseFrame->command); + } + else if (responseFrame->frameType == FRAME_TYPE::REQUEST_SET_EXPECT_ACK) { + //check if such request is not queued + if (!isEmpty()) { + Frame f = commandFifo[getIndex]; + if (f.frameType == FRAME_TYPE::RESPONSE_ACK && f.frameNumber == responseFrame->frameNumber) { + TRACE("ACK for frame %02X already queued", responseFrame->frameNumber); + return; + } + } + TRACE("AFHDS3 [QUEUE ACK] cmd %02X type %02X number %02X", responseFrame->command, responseFrame->frameType, responseFrame->frameNumber); + enqueueACK((enum COMMAND) responseFrame->command, responseFrame->frameNumber); + } + else if (responseFrame->frameType == FRAME_TYPE::RESPONSE_DATA || responseFrame->frameType == FRAME_TYPE::RESPONSE_ACK) { + if (operationState == State::AWAITING_RESPONSE) { + operationState = State::IDLE; + } + } +} + +inline bool isSbus(uint8_t mode) +{ + return (mode & 1); +} + +inline bool isPWM(uint8_t mode) +{ + return mode < 2; +} + +RUN_POWER PulsesData::getMaxRunPower() +{ + if (powerSource == MODULE_POWER_SOURCE::EXTERNAL) { + return RUN_POWER::PLUS_33dBm; + } + + return RUN_POWER::PLUS_27dbm; +} + +RUN_POWER PulsesData::actualRunPower() +{ + uint8_t actualRfPower = cfg.config.runPower; + if (getMaxRunPower() < actualRfPower) { + actualRfPower = getMaxRunPower(); + } + return (RUN_POWER) actualRfPower; +} + +RUN_POWER PulsesData::getRunPower() +{ + RUN_POWER targetPower = (RUN_POWER) moduleData->afhds3.runPower; + if (getMaxRunPower() < targetPower) { + targetPower = getMaxRunPower(); + } + return targetPower; +} + +bool PulsesData::syncSettings() +{ + RUN_POWER targetPower = getRunPower(); + + /*not sure if we need to prevent them in bind mode*/ + if (getModuleMode(module_index) != ::ModuleSettingsMode::MODULE_MODE_BIND && targetPower != cfg.config.runPower) { + cfg.config.runPower = moduleData->afhds3.runPower; + uint8_t data[] = { 0x13, 0x20, 0x02, moduleData->afhds3.runPower, 0 }; + TRACE("AFHDS3 SET TX POWER %d", moduleData->afhds3.runPower); + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data)); + return true; + } + + //other settings only in 2 way mode (state must be synchronized) + if (this->state != ModuleState::STATE_SYNC_DONE) { + return false; + } + + if (moduleData->afhds3.rxFreq() != cfg.config.pwmFreq) { + cfg.config.pwmFreq = moduleData->afhds3.rxFreq(); + uint8_t data[] = { 0x17, 0x70, 0x02, (uint8_t) (moduleData->afhds3.rxFreq() & 0xFF), (uint8_t) (moduleData->afhds3.rxFreq() >> 8) }; + TRACE("AFHDS3 SET RX FREQ"); + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data)); + return true; + } + PULSE_MODE modelPulseMode = isPWM(moduleData->subType) ? PULSE_MODE::PWM_MODE : PULSE_MODE::PPM_MODE; + if (modelPulseMode != cfg.config.pulseMode) { + cfg.config.pulseMode = modelPulseMode; + TRACE("AFHDS3 PWM/PPM %d", modelPulseMode); + uint8_t data[] = { 0x16, 0x70, 0x01, (uint8_t) (modelPulseMode) }; + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data)); + return true; + } + + SERIAL_MODE modelSerialMode = isSbus(moduleData->subType) ? SERIAL_MODE::SBUS_MODE : SERIAL_MODE::IBUS; + if (modelSerialMode != cfg.config.serialMode) { + cfg.config.serialMode = modelSerialMode; + TRACE("AFHDS3 IBUS/SBUS %d", modelSerialMode); + uint8_t data[] = { 0x18, 0x70, 0x01, (uint8_t) (modelSerialMode) }; + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data)); + return true; + } + + if (moduleData->afhds3.failsafeTimeout != cfg.config.failSafeTimout) { + moduleData->afhds3.failsafeTimeout = cfg.config.failSafeTimout; + uint8_t data[] = { 0x12, 0x60, 0x02, (uint8_t) (moduleData->afhds3.failsafeTimeout & 0xFF), (uint8_t) (moduleData->afhds3.failsafeTimeout >> 8) }; + putFrame(COMMAND::SEND_COMMAND, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, data, sizeof(data)); + TRACE("AFHDS3 FAILSAFE TMEOUT, %d", moduleData->afhds3.failsafeTimeout); + return true; + } + return false; +} + +void PulsesData::sendChannelsData() +{ + uint8_t channels_start = moduleData->channelsStart; + uint8_t channelsCount = 8 + moduleData->channelsCount; + uint8_t channels_last = channels_start + channelsCount; + int16_t buffer[MAX_CHANNELS + 1] = { ((MAX_CHANNELS << 8) | CHANNELS_DATA_MODE::CHANNELS), 0 }; + + for (uint8_t channel = channels_start, index = 1; channel < channels_last; channel++, index++) { + int16_t channelValue = convert(::getChannelValue(channel)); + buffer[index] = channelValue; + } + putFrame(COMMAND::CHANNELS_FAILSAFE_DATA, FRAME_TYPE::REQUEST_SET_NO_RESP, (uint8_t*) buffer, sizeof(buffer)); +} + +void PulsesData::stop() +{ + TRACE("AFHDS3 STOP"); + requestedModuleMode = MODULE_MODE_E::STANDBY; + putFrame(COMMAND::MODULE_MODE, FRAME_TYPE::REQUEST_SET_EXPECT_DATA, &requestedModuleMode, 1); +} + +void PulsesData::setConfigFromModel() +{ + cfg.config.bindPower = moduleData->afhds3.bindPower; + cfg.config.runPower = getRunPower(); + cfg.config.emiStandard = EMI_STANDARD::FCC; + cfg.config.telemetry = moduleData->afhds3.telemetry; //always use bidirectional mode + cfg.config.pwmFreq = moduleData->afhds3.rxFreq(); + cfg.config.serialMode = isSbus(moduleData->subType) ? SERIAL_MODE::SBUS_MODE : SERIAL_MODE::IBUS; + cfg.config.pulseMode = isPWM(moduleData->subType) ? PULSE_MODE::PWM_MODE : PULSE_MODE::PPM_MODE; + //use max channels - because channel count can not be changed after bind + cfg.config.channelCount = MAX_CHANNELS; + cfg.config.failSafeTimout = moduleData->afhds3.failsafeTimeout; + setFailSafe(cfg.config.failSafeMode); +} + +inline int16_t PulsesData::convert(int channelValue) +{ + //pulseValue = limit(0, 988 + ((channelValue + 1024) / 2), 0xfff); + //988 - 750 = 238 + //238 * 20 = 4760 + //2250 - 2012 = 238 + //238 * 20 = 4760 + // 988 ---- 2012 + //-10240 ---- 10240 + //-1024 ---- 1024 + return ::limit(FAILSAFE_MIN, channelValue * 10, FAILSAFE_MAX); +} + +uint8_t PulsesData::setFailSafe(int16_t* target) +{ + int16_t pulseValue = 0; + uint8_t channels_start = moduleData->channelsStart; + uint8_t channels_last = channels_start + 8 + moduleData->channelsCount; + + for (uint8_t channel = channels_start; channel < channels_last; channel++) { + if (moduleData->failsafeMode == FAILSAFE_CUSTOM) { + pulseValue = convert(g_model.failsafeChannels[channel]); + } + else if (moduleData->failsafeMode == FAILSAFE_HOLD) { + pulseValue = FAILSAFE_KEEP_LAST; + } + else { + pulseValue = convert(::getChannelValue(channel)); + } + target[channel - channels_start] = pulseValue; + } + //return max channels because channel count can not be change after bind + return (uint8_t) (MAX_CHANNELS); +} + +} diff --git a/radio/src/pulses/afhds3.h b/radio/src/pulses/afhds3.h new file mode 100644 index 000000000..774293b65 --- /dev/null +++ b/radio/src/pulses/afhds3.h @@ -0,0 +1,595 @@ +/* + * 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. + */ + +#ifndef PULSES_AFHDS3_H_ +#define PULSES_AFHDS3_H_ + +#include "bitfield.h" +#include "definitions.h" +#include "dataconstants.h" +#include "opentx_types.h" +#include "myeeprom.h" +#include "opentx_helpers.h" +#include "pulses_common.h" +#include +#include "fifo.h" + +#define AFHDS_MAX_PULSES 72 +//max number of transitions measured so far 290 + 10% +#define AFHDS_MAX_PULSES_TRANSITIONS 320 +//#define AFHDS3_SLOW + +#if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) +#define AFHDS3_BAUDRATE 1500000 +#define AFHDS3_COMMAND_TIMEOUT 5 +#elif !defined(AFHDS3_SLOW) +// 1s = 1 000 000 us +// 1000000/115200 = 8,68 us +// actual timer is ticking in 0,5 us = 8,68*2 = 17,36 because it must be integer take 17 +// difference 1000000 / x = 8.5 --> x = 117 647 = difference = 1.2 % +// allowed half a bit difference on the last bit -- should be fine +#define AFHDS3_BAUDRATE 115200 +//Because timer is ticking with 0.5us +#define BITLEN_AFHDS (17) + +#define AFHDS3_COMMAND_TIMEOUT 15 +#else +// 1000000/57600 = 17,36 us +#define AFHDS3_BAUDRATE 57600 +// 64* 86 = 11 110 +#define AFHDS3_COMMAND_TIMEOUT 20 +#define BITLEN_AFHDS (35) +#endif + +#define AFHDS3_FRAME_HALF_US AFHDS3_COMMAND_TIMEOUT * 2000 + +//get channel value outside of afhds3 namespace +int32_t getChannelValue(uint8_t channel); + +//use uint16_t instead of pulse_duration_t +namespace afhds3 +{ +enum AfhdsSpecialChars +{ + END = 0xC0, //Frame end + START = END, + ESC_END = 0xDC, //Escaped frame end - in case END occurs in fame then ESC ESC_END must be used + ESC = 0xDB, //Escaping character + ESC_ESC = 0xDD, //Escaping character in case ESC occurs in fame then ESC ESC_ESC must be used +}; + +struct Data +{ +#if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) + uint8_t pulses[AFHDS_MAX_PULSES]; + uint8_t * ptr; +#else + uint32_t pulsesSize; + uint16_t pulses[AFHDS_MAX_PULSES_TRANSITIONS]; + uint32_t total; + #endif + + uint8_t frame_index; + uint8_t crc; + uint8_t state; + uint8_t timeout; + uint8_t esc_state; + + void reset() + { +#if !(defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO)) + total = 0; +#endif + pulsesSize = 0; + } +#if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) + void sendByte(uint8_t b) + { + *ptr++ = b; + } + const uint8_t* getData() + { + return pulses; + } + void flush() + { + } +#else + inline void _send_level(uint16_t v) + { + if (pulsesSize >= AFHDS_MAX_PULSES_TRANSITIONS) { + return; + } + pulses[pulsesSize++] = v; + total += v; + } + void sendByte(uint8_t b) + { + if (pulsesSize >= AFHDS_MAX_PULSES_TRANSITIONS) { + return; + } + //use 8n1 + //parity: If the parity is enabled, then the MSB bit of the data to be transmitted is changed by the parity bit + //start is always 0 + bool level = 0; + uint16_t length = BITLEN_AFHDS; //start bit + for (uint8_t i = 0; i <= 8; i++) + { //8 data bits + Stop=1 + bool next_level = b & 1; + if (level == next_level) { + length += BITLEN_AFHDS; + } + else { + _send_level(length); + length = BITLEN_AFHDS; + level = next_level; + } + b = (b >> 1) | 0x80; // shift left to get next bit, fill msb with stop bit - needed just once + } + _send_level(length); //last bit (stop) + + } + //add remaining time of frame + void flush() + { + uint16_t diff = AFHDS3_FRAME_HALF_US - total; + pulses[pulsesSize - 1] += diff; + //ensure 2 ms break + if (pulses[pulsesSize - 1] < 4000) + { + pulses[pulsesSize - 1] = 4050; + } + total += diff; + } + + const uint16_t* getData() + { + return pulses; + } +#endif + uint32_t getSize() + { + return pulsesSize; + } +}; + +enum DeviceAddress +{ + TRANSMITTER = 0x01, + MODULE = 0x03, +}; + +enum FRAME_TYPE +{ + REQUEST_GET_DATA = 0x01, //Get data response: ACK + DATA + REQUEST_SET_EXPECT_DATA = 0x02, //Set data response: ACK + DATA + REQUEST_SET_EXPECT_ACK = 0x03, //Set data response: ACK + REQUEST_SET_NO_RESP = 0x05, //Set data response: none + RESPONSE_DATA = 0x10, //Response ACK + DATA + RESPONSE_ACK = 0x20, //Response ACK + NOT_USED = 0xff +}; + +enum COMMAND +{ + MODULE_READY = 0x01, + MODULE_STATE = 0x02, + MODULE_MODE = 0x03, + MODULE_SET_CONFIG = 0x04, + MODULE_GET_CONFIG = 0x06, + CHANNELS_FAILSAFE_DATA = 0x07, + TELEMETRY_DATA = 0x09, + SEND_COMMAND = 0x0C, + COMMAND_RESULT = 0x0D, + MODULE_POWER_STATUS = 0x0F, + MODULE_VERSION = 0x1F, + VIRTUAL_FAILSAFE = 0x99, // virtual command used to trigger failsafe + UNDEFINED = 0xFF +}; + +enum COMMAND_DIRECTION +{ + RADIO_TO_MODULE = 0, + MODULE_TO_RADIO = 1 +}; + +enum DATA_TYPE +{ + READY_DT, // 8 bytes 0x01 Not ready 0x02 Ready + STATE_DT, // See MODULE_STATE + MODE_DT, + MOD_CONFIG_DT, + CHANNELS_DT, + TELEMETRY_DT, + MODULE_POWER_DT, + MODULE_VERSION_DT, + EMPTY_DT, +}; +//enum used by command response -> translate to ModuleState +enum MODULE_READY_E +{ + MODULE_STATUS_UNKNOWN = 0x00, + MODULE_STATUS_NOT_READY = 0x01, + MODULE_STATUS_READY = 0x02 +}; + +enum ModuleState +{ + STATE_NOT_READY = 0x00, //virtual + STATE_HW_ERROR = 0x01, + STATE_BINDING = 0x02, + STATE_SYNC_RUNNING = 0x03, + STATE_SYNC_DONE = 0x04, + STATE_STANDBY = 0x05, + STATE_UPDATING_WAIT = 0x06, + STATE_UPDATING_MOD = 0x07, + STATE_UPDATING_RX = 0x08, + STATE_UPDATING_RX_FAILED = 0x09, + STATE_RF_TESTING = 0x0a, + STATE_READY = 0x0b, //virtual + STATE_HW_TEST = 0xff, +}; + +//used for set command +enum MODULE_MODE_E +{ + STANDBY = 0x01, + BIND = 0x02, //after bind module will enter run mode + RUN = 0x03, + RX_UPDATE = 0x04, //after successful update module will enter standby mode, otherwise hw error will be raised + MODULE_MODE_UNKNOWN = 0xFF +}; + +enum CMD_RESULT +{ + FAILURE = 0x01, + SUCCESS = 0x02, +}; + +#define MIN_FREQ 50 +#define MAX_FREQ 400 +#define MAX_CHANNELS 18 +#define FAILSAFE_KEEP_LAST 0x8000 +#define FAILSAFE_MIN -15000 +#define FAILSAFE_MAX 15000 + +enum BIND_POWER +{ + MIN_16dBm = 0x00, + BIND_POWER_FIRST = MIN_16dBm, + MIN_5dBm = 0x01, + MIN_0dbm = 0x02, + PLUS_5dBm = 0x03, + PLUS_14dBm = 0x04, + BIND_POWER_LAST = PLUS_14dBm, +}; + +enum RUN_POWER +{ + PLUS_15bBm = 0x00, + RUN_POWER_FIRST = PLUS_15bBm, + PLUS_20bBm = 0x01, + PLUS_27dbm = 0x02, + PLUS_30dBm = 0x03, + PLUS_33dBm = 0x04, + RUN_POWER_LAST = PLUS_33dBm, +}; + +enum EMI_STANDARD +{ + FCC = 0x00, + CE = 0x01 +}; + +enum TELEMETRY +{ + TELEMETRY_DISABLED = 0x00, + TELEMETRY_ENABLED = 0x01 +}; + +enum PULSE_MODE +{ + PWM_MODE = 0x00, + PPM_MODE = 0x01, +}; + +enum SERIAL_MODE +{ + IBUS = 0x00, + SBUS_MODE = 0x02 +}; + +PACK(struct Config_s { + uint8_t bindPower; + uint8_t runPower; + uint8_t emiStandard; + uint8_t telemetry; + uint16_t pwmFreq; + uint8_t pulseMode; + uint8_t serialMode; + uint8_t channelCount; + uint16_t failSafeTimout; + int16_t failSafeMode[MAX_CHANNELS]; +}); + +union Config_u +{ + Config_s config; + uint8_t buffer[sizeof(Config_s)]; +}; + +enum CHANNELS_DATA_MODE +{ + CHANNELS = 0x01, + FAIL_SAFE = 0x02, +}; + +PACK(struct ChannelsData { + uint8_t mode; + uint8_t channelsNumber; + int16_t data[MAX_CHANNELS]; +}); + +union ChannelsData_u +{ + ChannelsData data; + uint8_t buffer[sizeof(ChannelsData)]; +}; + +PACK(struct TelemetryData +{ + uint8_t sensorType; + uint8_t length; + uint8_t type; + uint8_t semsorID; + uint8_t data[8]; +}); + +enum MODULE_POWER_SOURCE +{ + INTERNAL = 0x01, + EXTERNAL = 0x02, +}; + +PACK(struct ModuleVersion +{ + uint32_t productNumber; + uint32_t hardwereVersion; + uint32_t bootloaderVersion; + uint32_t firmwareVersion; + uint32_t rfVersion; +}); + +PACK(struct CommandResult_s +{ + uint16_t command; + uint8_t result; + uint8_t respLen; +}); + +union AfhdsFrameData +{ + uint8_t value; + Config_s Config; + ChannelsData Channels; + TelemetryData Telemetry; + ModuleVersion Version; + CommandResult_s CommandResult; +}; + +PACK(struct AfhdsFrame +{ + uint8_t startByte; + uint8_t address; + uint8_t frameNumber; + uint8_t frameType; + uint8_t command; + uint8_t value; + + AfhdsFrameData* GetData() + { + return reinterpret_cast(&value); + } +}); + +#define FRM302_STATUS 0x56 + +enum State +{ + UNKNOWN = 0, + SENDING_COMMAND, + AWAITING_RESPONSE, + IDLE +}; + +//one byte frames for request queue +struct Frame +{ + enum COMMAND command; + enum FRAME_TYPE frameType; + uint8_t payload; + uint8_t frameNumber; + bool useFrameNumber; + uint8_t payloadSize; +}; + +//simple fifo implementation because Pulses is used as member of union and can not be non trivial type +struct CommandFifo +{ + Frame commandFifo[8]; + volatile uint32_t setIndex; + volatile uint32_t getIndex; + + void clearCommandFifo(); + + inline uint32_t nextIndex(uint32_t idx) + { + return (idx + 1) & (sizeof(commandFifo) / sizeof(commandFifo[0]) - 1); + } + + inline uint32_t prevIndex(uint32_t idx) + { + if (idx == 0) + { + return (sizeof(commandFifo) / sizeof(commandFifo[0]) - 1); + } + return (idx - 1); + } + + inline bool isEmpty() const + { + return (getIndex == setIndex); + } + + inline void skip() + { + getIndex = nextIndex(getIndex); + } + + void enqueueACK(COMMAND command, uint8_t frameNumber); + + void enqueue(COMMAND command, FRAME_TYPE frameType, bool useData = false, uint8_t byteContent = 0); + +}; + +void processTelemetryData(uint8_t module, uint8_t byte, uint8_t* rxBuffer, uint8_t& rxBufferCount, uint8_t maxSize); + +class PulsesData: public Data, CommandFifo +{ + public: + /** + * Initialize class for operation + * @param moduleIndex index of module one of INTERNAL_MODULE, EXTERNAL_MODULE + * @param resetFrameCount flag if current frame count should be reseted + */ + + void init(uint8_t moduleIndex, bool resetFrameCount = true); + + /** + * Fills DMA buffers with frame to be send depending on actual state + */ + void setupFrame(); + + /** + * Gets actual module status into provided buffer + * @param statusText target buffer for status + */ + void getStatusString(char * statusText) const; + + /** + * Gets actual power source and voltage + */ + void getPowerStatus(char* buffer) const; + + RUN_POWER actualRunPower(); + + /** + * Sends stop command to prevent any further module operations + */ + void stop(); + + protected: + + void setConfigFromModel(); + + private: + + inline void putBytes(uint8_t* data, int length); + + inline void putFrame(COMMAND command, FRAME_TYPE frameType, uint8_t* data = nullptr, uint8_t dataLength = 0, uint8_t* frame_index = nullptr); + + void parseData(uint8_t* rxBuffer, uint8_t rxBufferCount); + + void setState(uint8_t state); + + bool syncSettings(); + + void requestInfoAndRun(bool send = false); + + uint8_t setFailSafe(int16_t* target); + + inline int16_t convert(int channelValue); + + void sendChannelsData(); + + void clearFrameData(); + + void processTelemetryData(uint8_t byte, uint8_t* rxBuffer, uint8_t& rxBufferCount, uint8_t maxSize); + + //friendship declaration - use for passing telemetry + friend void processTelemetryData(uint8_t module, uint8_t byte, uint8_t* rxBuffer, uint8_t& rxBufferCount, uint8_t maxSize); + + /** + * Returns max power that currently can be set - use it to validate before synchronization of settings + */ + RUN_POWER getMaxRunPower(); + + RUN_POWER getRunPower(); + + bool isConnectedUnicast(); + + bool isConnectedMulticast(); + + /** + * Index of the module + */ + uint8_t module_index; + /** + * Target mode to be set to the module one of MODULE_MODE_E + */ + uint8_t requestedModuleMode; + /** + * Internal operation state one of UNKNOWN, SENDING_COMMAND, AWAITING_RESPONSE, IDLE + * Used to avoid sending commands when not allowed to + */ + State operationState; + /** + * Actual repeat count for requested command/operation - incremented by every attempt sending anything + */ + uint16_t repeatCount; + /** + * Command count used for counting actual number of commands sent in run mode + */ + uint32_t cmdCount; + /** + * Command index of command to be send when cmdCount reached necessary value + */ + uint32_t cmdIndex; + /** + * Actual power source of the module - should be requested time to time + * Currently requested once + */ + enum MODULE_POWER_SOURCE powerSource; + /** + * Pointer to module config - it is making operations easier and faster + */ + ModuleData* moduleData; + /** + * Actual module configuration - must be requested from module + */ + Config_u cfg; + + /** + * Actual module version - must be requested from module + */ + ModuleVersion version; +}; +} /* Namespace ahfds3 */ +#endif /* PULSES_AFHDS3_H_ */ diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 1e5879d92..4f391243a 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -37,6 +37,7 @@ enum ModuleType { MODULE_TYPE_R9M_LITE_PRO_PXX2, MODULE_TYPE_SBUS, MODULE_TYPE_XJT_LITE_PXX2, + MODULE_TYPE_AFHDS3, //no more protocols possible because of 4 bits value MODULE_TYPE_COUNT, MODULE_TYPE_MAX = MODULE_TYPE_COUNT - 1 }; @@ -218,4 +219,13 @@ enum AntennaModes { ANTENNA_MODE_LAST = ANTENNA_MODE_EXTERNAL }; +enum AfhdsModes { + AFHDS_SUBTYPE_FIRST, + AFHDS_SUBTYPE_PWM_IBUS = AFHDS_SUBTYPE_FIRST, + AFHDS_SUBTYPE_PWM_SBUS, + AFHDS_SUBTYPE_PPM_IBUS, + AFHDS_SUBTYPE_PPM_SBUS, + AFHDS_SUBTYPE_LAST = AFHDS_SUBTYPE_PPM_SBUS +}; + #endif // _MODULES_CONSTANTS_H_ diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index cdaf58336..5f2cca40f 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -299,8 +299,32 @@ inline bool isModuleSBUS(uint8_t moduleIdx) return g_model.moduleData[moduleIdx].type == MODULE_TYPE_SBUS; } +inline bool isModuleAFHDS3(uint8_t idx) +{ + return g_model.moduleData[idx].type == MODULE_TYPE_AFHDS3; +} + +inline bool isModulePxx2(uint8_t idx) +{ + if (idx != EXTERNAL_MODULE) + return false; +#if defined(PXX2) + #if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) + if (g_model.moduleData[idx].type == MODULE_TYPE_XJT_LITE_PXX2 || + g_model.moduleData[idx].type == MODULE_TYPE_R9M_LITE_PRO_PXX2) + return true; + #endif + #if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) + if (g_model.moduleData[idx].type == MODULE_TYPE_R9M_LITE_PXX2) + return true; + #endif +#endif + return false; +} + // order is the same as in enum Protocols in myeeprom.h (none, ppm, pxx, pxx2, dsm, crossfire, multi, r9m, r9m2, sbus) -static const int8_t maxChannelsModules[] = { 0, 8, 8, 16, -2, 8, 4, 8, 16, 8}; // relative to 8! +//qba667 count is not matching! +static const int8_t maxChannelsModules[] = { 0, 8, 8, 16, -2, 8, 4, 8, 16, 8, 10}; // relative to 8! static const int8_t maxChannelsXJT[] = { 0, 8, 0, 4 }; // relative to 8! constexpr int8_t MAX_TRAINER_CHANNELS_M8 = MAX_TRAINER_CHANNELS - 8; @@ -325,6 +349,9 @@ inline int8_t maxModuleChannels_M8(uint8_t moduleIdx) return 8; // always 16 channels in FCC / FLEX } } + else if (isModuleAFHDS3(moduleIdx)) { + return 10; + } else { return maxChannelsModules[g_model.moduleData[moduleIdx].type]; } @@ -401,7 +428,7 @@ inline bool isModuleFailsafeAvailable(uint8_t moduleIdx) return g_model.moduleData[moduleIdx].subType == MODULE_SUBTYPE_PXX1_ACCST_D16; #if defined(MULTIMODULE) - if (isModuleMultimodule(moduleIdx)){ + if (isModuleMultimodule(moduleIdx)) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); if (status.isValid()) { return status.supportsFailsafe(); @@ -413,6 +440,11 @@ inline bool isModuleFailsafeAvailable(uint8_t moduleIdx) } #endif +#if defined(AFHDS3) + if (isModuleAFHDS3(moduleIdx)) + return true; +#endif + if (isModuleR9M(moduleIdx)) return true; @@ -421,7 +453,7 @@ inline bool isModuleFailsafeAvailable(uint8_t moduleIdx) inline bool isModuleBindRangeAvailable(uint8_t moduleIdx) { - return isModulePXX2(moduleIdx) || isModulePXX1(moduleIdx) || isModuleDSM2(moduleIdx) || isModuleMultimodule(moduleIdx); + return isModulePXX2(moduleIdx) || isModulePXX1(moduleIdx) || isModuleDSM2(moduleIdx) || isModuleMultimodule(moduleIdx) || isModuleAFHDS3(moduleIdx); } inline bool isModuleRangeAvailable(uint8_t moduleIdx) @@ -548,6 +580,30 @@ inline void resetAccessAuthenticationCount() #endif } +inline void resetAfhds3Options(uint8_t moduleIdx) +{ + auto & data = g_model.moduleData[moduleIdx]; + data.rfProtocol = 0; + data.subType = 0; +#if defined(AFHDS3) + data.afhds3.bindPower = 0; + data.afhds3.runPower = 0; + data.afhds3.emi = 0; + data.afhds3.telemetry = 1; + data.afhds3.rx_freq[0] = 50; + data.afhds3.rx_freq[1] = 0; + data.afhds3.failsafeTimeout = 1000; + data.channelsCount = 14 - 8; + data.failsafeMode = 1; + //" PWM+i"" PWM+s"" PPM+i"" PPM+s" + data.subType = 0; + for (uint8_t channel = 0; channel < MAX_OUTPUT_CHANNELS; channel++) { + g_model.failsafeChannels[channel] = 0; + } +#endif +} + + inline void setModuleType(uint8_t moduleIdx, uint8_t moduleType) { ModuleData & moduleData = g_model.moduleData[moduleIdx]; @@ -558,6 +614,8 @@ inline void setModuleType(uint8_t moduleIdx, uint8_t moduleType) moduleData.sbus.refreshRate = -31; else if (moduleData.type == MODULE_TYPE_PPM) setDefaultPpmFrameLength(moduleIdx); + else if (moduleData.type == MODULE_TYPE_AFHDS3) + resetAfhds3Options(moduleIdx); else resetAccessAuthenticationCount(); } diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index f0e48f4b6..f669f5fdb 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -28,6 +28,7 @@ InternalModulePulsesData intmodulePulsesData __DMA; ExternalModulePulsesData extmodulePulsesData __DMA; TrainerPulsesData trainerPulsesData __DMA; +//use only for PXX void ModuleState::startBind(BindInformation * destination, ModuleCallback bindCallback) { bindInformation = destination; @@ -40,6 +41,56 @@ void ModuleState::startBind(BindInformation * destination, ModuleCallback bindCa #endif } +void getModuleStatusString(uint8_t moduleIdx, char * statusText) +{ + *statusText = 0; +#if defined(MULTIMODULE) + if (isModuleMultimodule(moduleIdx)) { + //change it + getMultiModuleStatus(moduleIdx).getStatusString(statusText); + } +#endif +#if defined(AFHDS3) + if (moduleIdx == EXTERNAL_MODULE && isModuleAFHDS3(moduleIdx)) { + extmodulePulsesData.afhds3.getStatusString(statusText); + } +#endif +} + +void getModuleSyncStatusString(uint8_t moduleIdx, char * statusText) +{ + *statusText = 0; +#if defined(MULTIMODULE) + if (isModuleMultimodule(moduleIdx)) { + getMultiSyncStatus(moduleIdx).getRefreshString(statusText); + } +#endif +#if defined(AFHDS3) + if (moduleIdx == EXTERNAL_MODULE && isModuleAFHDS3(moduleIdx)) { + extmodulePulsesData.afhds3.getPowerStatus(statusText); + } +#endif +} + +#if defined(AFHDS3) +uint8_t actualAfhdsRunPower(int moduleIndex) +{ + if (moduleIndex == EXTERNAL_MODULE && isModuleAFHDS3(moduleIndex)) { + return (uint8_t)extmodulePulsesData.afhds3.actualRunPower(); + } + return 0; +} +#endif + +ModuleSettingsMode getModuleMode(int moduleIndex) +{ + return (ModuleSettingsMode)moduleState[moduleIndex].mode; +} +void setModuleMode(int moduleIndex, ModuleSettingsMode mode) +{ + moduleState[moduleIndex].mode = mode; +} + uint8_t getModuleType(uint8_t module) { uint8_t type = g_model.moduleData[module].type; @@ -136,6 +187,12 @@ uint8_t getRequiredProtocol(uint8_t module) break; #endif +#if defined(AFHDS3) + case MODULE_TYPE_AFHDS3: + protocol = PROTOCOL_CHANNELS_AFHDS3; + break; +#endif + default: protocol = PROTOCOL_CHANNELS_NONE; break; @@ -214,6 +271,14 @@ void enablePulsesExternalModule(uint8_t protocol) break; #endif +#if defined(AFHDS3) + case PROTOCOL_CHANNELS_AFHDS3: + extmodulePulsesData.afhds3.init(EXTERNAL_MODULE); + extmoduleSerialStart(AFHDS3_BAUDRATE, AFHDS3_COMMAND_TIMEOUT * 2000, false); + break; +#endif + + default: break; } @@ -281,6 +346,13 @@ bool setupPulsesExternalModule(uint8_t protocol) return true; #endif +#if defined(AFHDS3) + case PROTOCOL_CHANNELS_AFHDS3: + extmodulePulsesData.afhds3.setupFrame(); + scheduleNextMixerCalculation(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT); + return true; +#endif + default: scheduleNextMixerCalculation(EXTERNAL_MODULE, 50/*ms*/); return false; @@ -438,3 +510,8 @@ void setCustomFailsafe(uint8_t moduleIndex) } } } + +int32_t getChannelValue(uint8_t channel) { + return channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; +} + diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 3b3fffca5..f92629f6e 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -27,6 +27,7 @@ #include "pxx1.h" #include "pxx2.h" #include "multi.h" +#include "afhds3.h" #include "modules_helpers.h" #include "ff.h" @@ -56,29 +57,14 @@ #define IS_MULTIMODULE_PROTOCOL(protocol) (0) #endif -#define IS_SBUS_PROTOCOL(protocol) (protocol == PROTOCOL_CHANNELS_SBUS) +#if defined(AFHDS3) +#define IS_AFHDS3_PROTOCOL(protocol) (protocol == PROTOCOL_CHANNELS_AFHDS3) +#else +#define IS_AFHDS3_PROTOCOL(protocol) (0) +#endif extern uint8_t s_pulses_paused; -enum ModuleSettingsMode -{ - MODULE_MODE_NORMAL, - MODULE_MODE_SPECTRUM_ANALYSER, - MODULE_MODE_POWER_METER, - MODULE_MODE_GET_HARDWARE_INFO, - MODULE_MODE_MODULE_SETTINGS, - MODULE_MODE_RECEIVER_SETTINGS, - MODULE_MODE_BEEP_FIRST, - MODULE_MODE_REGISTER = MODULE_MODE_BEEP_FIRST, - MODULE_MODE_BIND, - MODULE_MODE_SHARE, - MODULE_MODE_RANGECHECK, - MODULE_MODE_RESET, - MODULE_MODE_AUTHENTICATION, - MODULE_MODE_OTA_UPDATE, -}; - - PACK(struct PXX2Version { uint8_t major; uint8_t revision:4; @@ -293,6 +279,10 @@ union ExternalModulePulsesData { Dsm2PulsesData dsm2; #endif +#if defined(AFHDS3) + afhds3::PulsesData afhds3; +#endif + PpmPulsesData ppm; CrossfirePulsesData crossfire; @@ -338,6 +328,11 @@ void extmodulePxx1SerialStart(); void extmodulePpmStart(); void intmoduleStop(); void extmoduleStop(); +void getModuleStatusString(uint8_t moduleIdx, char * statusText); +void getModuleSyncStatusString(uint8_t moduleIdx, char * statusText); +#if defined(AFHDS3) +uint8_t actualAfhdsRunPower(int moduleIndex); +#endif void extramodulePpmStart(); inline void startPulses() @@ -369,6 +364,7 @@ enum ChannelsProtocols { PROTOCOL_CHANNELS_SBUS, PROTOCOL_CHANNELS_PXX2_LOWSPEED, PROTOCOL_CHANNELS_PXX2_HIGHSPEED, + PROTOCOL_CHANNELS_AFHDS3 }; inline void stopPulses() @@ -434,4 +430,4 @@ inline bool isModuleInBeepMode() return false; } -#endif // _PULSES_H_ \ No newline at end of file +#endif // _PULSES_H_ diff --git a/radio/src/pulses/pulses_common.h b/radio/src/pulses/pulses_common.h index 83058f635..80f5518a1 100644 --- a/radio/src/pulses/pulses_common.h +++ b/radio/src/pulses/pulses_common.h @@ -31,6 +31,27 @@ typedef uint16_t trainer_pulse_duration_t; +enum ModuleSettingsMode +{ + MODULE_MODE_NORMAL, + MODULE_MODE_SPECTRUM_ANALYSER, + MODULE_MODE_POWER_METER, + MODULE_MODE_GET_HARDWARE_INFO, + MODULE_MODE_MODULE_SETTINGS, + MODULE_MODE_RECEIVER_SETTINGS, + MODULE_MODE_BEEP_FIRST, + MODULE_MODE_REGISTER = MODULE_MODE_BEEP_FIRST, + MODULE_MODE_BIND, + MODULE_MODE_SHARE, + MODULE_MODE_RANGECHECK, + MODULE_MODE_RESET, + MODULE_MODE_AUTHENTICATION, + MODULE_MODE_OTA_UPDATE, +}; + +ModuleSettingsMode getModuleMode(int moduleIndex); +void setModuleMode(int moduleIndex, ModuleSettingsMode mode); + template class DataBuffer { public: diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 97ca896c6..3a8846dfc 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -148,7 +148,7 @@ endif() if(MULTIMODULE) add_definitions(-DMULTIMODULE) - set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/flysky_ibus.cpp telemetry/hitec.cpp telemetry/hott.cpp telemetry/multi.cpp io/multi_firmware_update.cpp) + set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/hitec.cpp telemetry/hott.cpp telemetry/multi.cpp io/multi_firmware_update.cpp) endif() if(CROSSFIRE) @@ -163,6 +163,18 @@ if(CROSSFIRE) ) endif() +if(AFHDS3) + add_definitions(-DAFHDS3) + set(PULSES_SRC + ${PULSES_SRC} + afhds3.cpp + ) +endif() + +if(MULTIMODULE OR AFHDS3) + set(SRC ${SRC} telemetry/flysky_ibus.cpp ) +endif() + add_definitions(-DCPUARM) add_definitions(-DGPS) add_definitions(-DBOLD_FONT -DBATTGRAPH -DTHRTRACE) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index dfa235baa..4fa4238de 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -2,6 +2,7 @@ option(DISK_CACHE "Enable SD card disk cache" ON) option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON) option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) +option(AFHDS3 "AFHDS3 TX Module" ON) option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 7e8b90937..9c04aeb21 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -316,6 +316,30 @@ void extmoduleSendNextFrame() break; #endif +#if defined(AFHDS3) + case PROTOCOL_CHANNELS_AFHDS3: + { +#if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) + extmoduleSendBuffer(extmodulePulsesData.afhds3.getData(), extmodulePulsesData.afhds3.getSize()); +#else + const uint16_t* dataPtr = extmodulePulsesData.afhds3.getData(); + uint32_t dataSize = extmodulePulsesData.afhds3.getSize(); + //Peripheral data size + //we know that afdhds3 uses 2 byte values! + uint32_t dmaSize = (EXTMODULE_TIMER_DMA_SIZE & (DMA_SxCR_PSIZE_0 | DMA_SxCR_PSIZE_1)) | DMA_SxCR_MSIZE_0; + + EXTMODULE_TIMER->CCR2 = dataPtr[dataSize -1] - 4000; // 2mS in advance + 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 | dmaSize | 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(dataPtr); + EXTMODULE_TIMER_DMA_STREAM->NDTR = (uint32_t)dataSize; + EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA +#endif + } + break; +#endif + #if defined(DSM2) case PROTOCOL_CHANNELS_SBUS: #if defined(PCBX10) || PCBREV >= 13 diff --git a/radio/src/targets/horus/telemetry_driver.cpp b/radio/src/targets/horus/telemetry_driver.cpp index ffb1cbc2c..23318dd7e 100644 --- a/radio/src/targets/horus/telemetry_driver.cpp +++ b/radio/src/targets/horus/telemetry_driver.cpp @@ -45,7 +45,8 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode) USART_DeInit(TELEMETRY_USART); return; } - + //deinit inverted mode + telemetryPortInvertedInit(0); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TELEMETRY_DMA_TX_Stream_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; @@ -136,6 +137,9 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode) // soft serial vars static uint8_t rxBitCount; static uint8_t rxByte; +// single bit length expresses in half us +static uint16_t bitLength; +static uint16_t probeTimeFromStartBit; void telemetryPortInvertedInit(uint32_t baudrate) { @@ -155,6 +159,20 @@ void telemetryPortInvertedInit(uint32_t baudrate) rxBitCount = 0; + switch(baudrate) { + case 115200: + bitLength = 17; + probeTimeFromStartBit = 23; //because pin is not probed immediately + break; + case 57600: + bitLength = 35; //34 was used before - I prefer to use use 35 because of lower error + probeTimeFromStartBit = 48; // 48 used in original implementation + break; + default: + bitLength = 2000000/baudrate; //because of 0,5 us tick + probeTimeFromStartBit = 3000000/baudrate; + } + // configure bit sample timer TELEMETRY_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 2000000 - 1; // 0.5uS TELEMETRY_TIMER->CCER = 0; @@ -196,7 +214,7 @@ void telemetryPortInvertedRxBit() { if (rxBitCount < 8) { if (rxBitCount == 0) { - TELEMETRY_TIMER->ARR = 34; + TELEMETRY_TIMER->ARR = bitLength; rxByte = 0; } else { @@ -209,7 +227,6 @@ void telemetryPortInvertedRxBit() ++rxBitCount; } else if (rxBitCount == 8) { - telemetryNoDMAFifo.push(rxByte); rxBitCount = 0; @@ -360,7 +377,7 @@ extern "C" void TELEMETRY_EXTI_IRQHandler(void) if (rxBitCount == 0) { - TELEMETRY_TIMER->ARR = 48; // 1,5 cycle from start at 57600bps + TELEMETRY_TIMER->ARR = probeTimeFromStartBit; // 1,5 cycle from start at 57600bps TELEMETRY_TIMER->CR1 |= TIM_CR1_CEN; // disable start bit interrupt diff --git a/radio/src/targets/sky9x/CMakeLists.txt b/radio/src/targets/sky9x/CMakeLists.txt index 0036e3630..4188444fe 100644 --- a/radio/src/targets/sky9x/CMakeLists.txt +++ b/radio/src/targets/sky9x/CMakeLists.txt @@ -10,6 +10,7 @@ set(TARGET_DIR sky9x) set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} 9x_bitmaps) set(PPM_LIMITS_SYMETRICAL YES) set(PXX1 YES) +set(AFHDS3 NO) add_definitions(-DDISABLE_MULTI_UPDATE) if(PCB STREQUAL 9XRPRO) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index fe9a02922..82dbcef91 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -2,6 +2,7 @@ option(SHUTDOWN_CONFIRMATION "Shutdown confirmation" OFF) option(LCD_DUAL_BUFFER "Dual LCD Buffer" OFF) option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) +option(AFHDS3 "AFHDS3 TX Module" OFF) option(INTERNAL_MODULE_PPM "Support for PPM internal module" OFF) option(AUTOUPDATE "Auto update internal chips from SD" OFF) diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 19bd04525..0910f156e 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -278,7 +278,25 @@ void extmoduleSendNextFrame() extmoduleSendBuffer(extmodulePulsesData.pxx2.getData(), extmodulePulsesData.pxx2.getSize()); break; #endif - +#if defined(AFHDS3) + case PROTOCOL_CHANNELS_AFHDS3: + { +#if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) + extmoduleSendBuffer(extmodulePulsesData.afhds3.getData(), extmodulePulsesData.afhds3.getSize()); +#else + const uint16_t* dataPtr = extmodulePulsesData.afhds3.getData(); + uint32_t dataSize = extmodulePulsesData.afhds3.getSize(); + EXTMODULE_TIMER->CCR2 = dataPtr[dataSize -1] - 4000; // 2mS in advance + 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(dataPtr); + EXTMODULE_TIMER_DMA_STREAM->NDTR = dataSize; + EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA +#endif + } + break; +#endif #if defined(SBUS) || defined(DSM2) || defined(MULTIMODULE) case PROTOCOL_CHANNELS_SBUS: EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? 0 : EXTMODULE_TIMER_OUTPUT_POLARITY); // reverse polarity for Sbus if needed diff --git a/radio/src/targets/taranis/telemetry_driver.cpp b/radio/src/targets/taranis/telemetry_driver.cpp index e5b0d8cfe..b46eca541 100644 --- a/radio/src/targets/taranis/telemetry_driver.cpp +++ b/radio/src/targets/taranis/telemetry_driver.cpp @@ -40,7 +40,8 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode) USART_DeInit(TELEMETRY_USART); return; } - + //deinit inverted mode + telemetryPortInvertedInit(0); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TELEMETRY_DMA_TX_Stream_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; @@ -87,11 +88,10 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode) // soft serial vars static uint8_t rxBitCount; static uint8_t rxByte; +// single bit length expresses in half us +static uint16_t bitLength; +static uint16_t probeTimeFromStartBit; -//TODO: -// - only 57600 supported for now -// - handle other bitrates as well? -// void telemetryPortInvertedInit(uint32_t baudrate) { if (baudrate == 0) { @@ -116,6 +116,20 @@ void telemetryPortInvertedInit(uint32_t baudrate) rxBitCount = 0; + switch(baudrate) { + case 115200: + bitLength = 17; + probeTimeFromStartBit = 25; + break; + case 57600: + bitLength = 35; //34 was used before - I prefer to use use 35 because of lower error + probeTimeFromStartBit = 52; //round down - 48 used in original implementation + break; + default: + bitLength = 2000000/baudrate; //because of 0,5 us tick + probeTimeFromStartBit = 3000000/baudrate; + } + // configure bit sample timer TELEMETRY_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 2000000 - 1; // 0.5uS TELEMETRY_TIMER->CCER = 0; @@ -162,7 +176,7 @@ void telemetryPortInvertedRxBit() { if (rxBitCount < 8) { if (rxBitCount == 0) { - TELEMETRY_TIMER->ARR = 34; + TELEMETRY_TIMER->ARR = bitLength; rxByte = 0; } else { @@ -333,7 +347,7 @@ void check_telemetry_exti() if (rxBitCount == 0) { - TELEMETRY_TIMER->ARR = 48; // 1,5 cycle from start at 57600bps + TELEMETRY_TIMER->ARR = probeTimeFromStartBit; // 1,5 cycle from start at 57600bps TELEMETRY_TIMER->CR1 |= TIM_CR1_CEN; // disable start bit interrupt diff --git a/radio/src/telemetry/flysky_ibus.cpp b/radio/src/telemetry/flysky_ibus.cpp index af4d4b82c..4b2d05da9 100644 --- a/radio/src/telemetry/flysky_ibus.cpp +++ b/radio/src/telemetry/flysky_ibus.cpp @@ -61,7 +61,8 @@ enum AFHDS2A_ID_GPS_DIST = 0x14, // 2 bytes distance from home m unsigned AFHDS2A_ID_ARMED = 0x15, // 2 bytes AFHDS2A_ID_FLIGHT_MODE = 0x16, // 2 bytes - + AFHDS3_FRM_TEMP = 0x57, //virtual + AFHDS3_FRM_EXT_V = 0x58, //virtual AFHDS2A_ID_PRES = 0x41, // Pressure AFHDS2A_ID_ODO1 = 0x7C, // Odometer1 AFHDS2A_ID_ODO2 = 0x7D, // Odometer2 @@ -79,8 +80,9 @@ enum AFHDS2A_ID_S88 = 0x88, AFHDS2A_ID_S89 = 0x89, AFHDS2A_ID_S8a = 0x8A, - - AFHDS2A_ID_ALT_FLYSKY = 0xF9, // Altitude 2 bytes signed in m - used in FlySky native TX - SIGNED value + AFHDS2A_ID_RX_SIG_AFHDS3 = 0xF7, // SIG + AFHDS2A_ID_RX_SNR_AFHDS3 = 0xF8, // SNR + AFHDS2A_ID_ALT_FLYSKY = 0xF9, // Altitude 2 bytes signed in m - used in FlySky native TX AFHDS2A_ID_RX_SNR = 0xFA, // SNR AFHDS2A_ID_RX_NOISE = 0xFB, // Noise AFHDS2A_ID_RX_RSSI = 0xFC, // RSSI @@ -120,6 +122,8 @@ const FlySkySensor flySkySensors[] = { {AFHDS2A_ID_FLIGHT_MODE, ZSTR_FLIGHT_MODE, UNIT_RAW, 0}, // 2 bytes index {AFHDS2A_ID_PRES, ZSTR_PRES, UNIT_RAW, 2}, // 4 bytes In fact Temperature + Pressure -> Altitude {AFHDS2A_ID_PRES | 0x100, ZSTR_TEMP2, UNIT_CELSIUS, 1}, // 2 bytes Temperature + {AFHDS3_FRM_TEMP, ZSTR_TEMP2, UNIT_CELSIUS, 1}, // 2 bytes temperature + {AFHDS3_FRM_EXT_V, ZSTR_TXV, UNIT_VOLTS, 2}, // 2 bytes voltage {AFHDS2A_ID_ODO1, ZSTR_ODO1, UNIT_METERS, 2}, // 2 bytes Odometer1 -- some magic with 330 needed {AFHDS2A_ID_ODO2, ZSTR_ODO2, UNIT_METERS, 2}, // 2 bytes Odometer2 -- some magic with 330 needed {AFHDS2A_ID_SPE, ZSTR_ASPD, UNIT_KMH, 2}, // 2 bytes Speed km/h -- some magic with 330 needed @@ -128,7 +132,8 @@ const FlySkySensor flySkySensors[] = { {AFHDS2A_ID_GPS_LON, ZSTR_GPS, UNIT_RAW, 7}, // 4 bytes signed WGS84 in degrees * 1E7 {AFHDS2A_ID_GPS_ALT, ZSTR_GPSALT, UNIT_METERS, 2}, // 4 bytes signed GPS alt m*100 {AFHDS2A_ID_ALT, ZSTR_ALT, UNIT_METERS, 2}, // 4 bytes signed Alt m*100 - + {AFHDS2A_ID_RX_SIG_AFHDS3, ZSTR_RX_QUALITY, UNIT_RAW, 0}, // RX error rate + {AFHDS2A_ID_RX_SNR_AFHDS3, ZSTR_RX_SNR, UNIT_DB, 1}, // RX SNR {AFHDS2A_ID_RX_SNR, ZSTR_RX_SNR, UNIT_DB, 0}, // RX SNR {AFHDS2A_ID_RX_NOISE, ZSTR_RX_NOISE, UNIT_DB, 0}, // RX Noise {AFHDS2A_ID_RX_RSSI, ZSTR_RSSI, UNIT_DB, 0}, // RX RSSI (0xfc) @@ -140,7 +145,7 @@ const FlySkySensor flySkySensors[] = { int32_t getALT(uint32_t value); -static void processFlySkySensor(const uint8_t * packet, uint8_t type) +void processFlySkySensor(const uint8_t * packet, uint8_t type) { uint8_t buffer[8]; uint16_t id = packet[0]; @@ -156,13 +161,17 @@ static void processFlySkySensor(const uint8_t * packet, uint8_t type) if (id == 0) id = 0x100; // Some part of OpenTX does not like sensor with id and instance 0, remap to 0x100 if (id == AFHDS2A_ID_RX_NOISE || id == AFHDS2A_ID_RX_RSSI) { - value = -value; + value = 135 - value; } else if (id == AFHDS2A_ID_RX_ERR_RATE) { value = 100 - value; telemetryData.rssi.set(value); if (value > 0) telemetryStreaming = TELEMETRY_TIMEOUT10ms; } + else if(id == AFHDS2A_ID_RX_SIG_AFHDS3) { + telemetryData.rssi.set(value); + if(value>0) telemetryStreaming = TELEMETRY_TIMEOUT10ms; + } else if (id == AFHDS2A_ID_PRES && value) { // Extract temperature to a new sensor setTelemetryValue(PROTOCOL_TELEMETRY_FLYSKY_IBUS, id | 0x100, 0, instance, ((value >> 19) - 400), UNIT_CELSIUS, 1); @@ -216,7 +225,7 @@ static void processFlySkySensor(const uint8_t * packet, uint8_t type) for (const FlySkySensor * sensor = flySkySensors; sensor->id; sensor++) { if (sensor->id != id) continue; if (sensor->unit == UNIT_CELSIUS) value -= 400; // Temperature sensors have 40 degree offset - else if (sensor->unit == UNIT_VOLTS) value = (uint16_t) value; // Voltage types are unsigned 16bit integers + else if (sensor->unit == UNIT_VOLTS) value = (int16_t) value; // Voltage types are unsigned 16bit integers setTelemetryValue(PROTOCOL_TELEMETRY_FLYSKY_IBUS, id, 0, instance, value, sensor->unit, sensor->precision); return; } @@ -390,4 +399,4 @@ int32_t getALT(uint32_t value) if (neg ^ tempNegative) result = result * -1; return result; -} \ No newline at end of file +} diff --git a/radio/src/telemetry/flysky_ibus.h b/radio/src/telemetry/flysky_ibus.h index bfb0db640..dd30e0058 100644 --- a/radio/src/telemetry/flysky_ibus.h +++ b/radio/src/telemetry/flysky_ibus.h @@ -21,6 +21,8 @@ #ifndef _FLYSKY_IBUS_H #define _FLYSKY_IBUS_H +void processFlySkySensor(const uint8_t *packet, uint8_t type); + void processFlySkyTelemetryData(uint8_t data, uint8_t * rxBuffer, uint8_t &rxBufferCount); void flySkySetDefault(int index, uint16_t id, uint8_t subId, uint8_t instance); diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 4e7bc8550..a5f1af94f 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "multi.h" +#include "pulses/afhds3.h" uint8_t telemetryStreaming = 0; uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1) @@ -59,6 +60,13 @@ void processTelemetryData(uint8_t data) } #endif +#if defined(AFHDS3) + if (telemetryProtocol == PROTOCOL_TELEMETRY_AFHDS3) { + afhds3::processTelemetryData(EXTERNAL_MODULE, data, telemetryRxBuffer, telemetryRxBufferCount, TELEMETRY_RX_PACKET_SIZE); + return; + } +#endif + processFrskyTelemetryData(data); } @@ -103,7 +111,7 @@ void telemetryWakeup() #endif #if defined(EXTMODULE_USART) - while (extmoduleFifo.getFrame(frame)) { + while (isModulePxx2(EXTERNAL_MODULE) && extmoduleFifo.getFrame(frame)) { processPXX2Frame(EXTERNAL_MODULE, frame); } #endif @@ -294,6 +302,13 @@ void telemetryInit(uint8_t protocol) } #endif +#if defined(AFHDS3) && !defined(SIMU) + else if (protocol == PROTOCOL_TELEMETRY_AFHDS3) { + telemetryPortInvertedInit(AFHDS3_BAUDRATE); + telemetryPortSetDirectionInput(); + } +#endif + else { telemetryPortInit(FRSKY_SPORT_BAUDRATE, TELEMETRY_SERIAL_WITHOUT_DMA); #if defined(LUA) diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 506b949a5..74671bbd4 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -28,11 +28,13 @@ #if defined(MULTIMODULE) #include "spektrum.h" - #include "flysky_ibus.h" #include "hitec.h" #include "hott.h" #include "multi.h" #endif +#if defined(MULTIMODULE) || defined(AFHDS3) + #include "flysky_ibus.h" +#endif extern uint8_t telemetryStreaming; // >0 (true) == data is streaming in. 0 = no data detected for some time @@ -54,7 +56,7 @@ constexpr uint8_t TELEMETRY_TIMEOUT10ms = 100; // 1 second #define TELEMETRY_SERIAL_8E2 1 #define TELEMETRY_SERIAL_WITHOUT_DMA 2 -#if defined(CROSSFIRE) || defined(MULTIMODULE) +#if defined(CROSSFIRE) || defined(MULTIMODULE) || defined(AFHDS3) #define TELEMETRY_RX_PACKET_SIZE 128 // multi module Spektrum telemetry is 18 bytes, FlySky is 37 bytes #else @@ -162,6 +164,11 @@ inline uint8_t modelTelemetryProtocol() return PROTOCOL_TELEMETRY_MULTIMODULE; } #endif +#endif +#if defined(AFHDS3) + if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_AFHDS3) { + return PROTOCOL_TELEMETRY_AFHDS3; + } #endif // default choice return PROTOCOL_TELEMETRY_FRSKY_SPORT; diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index 0896e1424..d94a8471a 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -526,13 +526,15 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, ui crossfireSetDefault(index, id, instance); break; #endif +#if defined(MULTIMODULE) || defined(AFHDS3) + case PROTOCOL_TELEMETRY_FLYSKY_IBUS: + flySkySetDefault(index,id, subId, instance); + break; +#endif #if defined(MULTIMODULE) case PROTOCOL_TELEMETRY_SPEKTRUM: spektrumSetDefault(index, id, subId, instance); break; - case PROTOCOL_TELEMETRY_FLYSKY_IBUS: - flySkySetDefault(index,id, subId, instance); - break; case PROTOCOL_TELEMETRY_HITEC: hitecSetDefault(index, id, subId, instance); break; diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 906bf56c7..ab57c5a33 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -68,6 +68,8 @@ ISTR(XJT_ACCST_RF_PROTOCOLS); ISTR(ISRM_RF_PROTOCOLS); ISTR(R9M_PXX2_RF_PROTOCOLS); ISTR(DSM_PROTOCOLS); +ISTR(AFHDS3_PROTOCOLS); +ISTR(AFHDS3_POWERS); ISTR(CURVE_TYPES); ISTR(VSENSORTYPES); ISTR(VFORMULAS); @@ -468,6 +470,7 @@ const char STR_BINDING_1_8_TELEM_OFF[] = TR_BINDING_CH1_8_TELEM_OFF; const char STR_BINDING_9_16_TELEM_ON[] = TR_BINDING_CH9_16_TELEM_ON; const char STR_BINDING_9_16_TELEM_OFF[] = TR_BINDING_CH9_16_TELEM_OFF; const char STR_CHANNELRANGE[] = TR_CHANNELRANGE; +const char STR_RXFREQUENCY[] = TR_RXFREQUENCY; const char STR_ANTENNACONFIRM1[] = TR_ANTENNACONFIRM1; const char STR_ANTENNACONFIRM2[] = TR_ANTENNACONFIRM2; const char STR_USE_INTERNAL_ANTENNA[] = TR_USE_INTERNAL_ANTENNA; @@ -715,8 +718,16 @@ const char STR_MULTI_SERVOFREQ[] = TR_MULTI_SERVOFREQ; const char STR_MULTI_MAX_THROW[] = TR_MULTI_MAX_THROW; const char STR_MULTI_RFCHAN[] = TR_MULTI_RFCHAN; const char STR_NEEDS_FILE[] = TR_NEEDS_FILE; -const char STR_EXT_MULTI_SPEC[] = TR_EXT_MULTI_SPEC; -const char STR_INT_MULTI_SPEC[] = TR_INT_MULTI_SPEC; +const char STR_EXT_MULTI_SPEC[] = TR_EXT_MULTI_SPEC; +const char STR_INT_MULTI_SPEC[] = TR_INT_MULTI_SPEC; + +#if defined(AFHDS3) +const char STR_AFHDS3_RX_FREQ[] = TR_AFHDS3_RX_FREQ; +const char STR_AFHDS3_ACTUAL_POWER[] = TR_AFHDS3_ACTUAL_POWER; +const char STR_AFHDS3_POWER_SOURCE[] = TR_AFHDS3_POWER_SOURCE; +const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[] =TR_AFHDS3_ONE_TO_ONE_TELEMETRY; +const char STR_AFHDS3_ONE_TO_MANY[] = TR_AFHDS3_ONE_TO_MANY; +#endif #if LCD_W < 212 const char STR_SUBTYPE[] = TR_SUBTYPE; diff --git a/radio/src/translations.h b/radio/src/translations.h index c10c7ffde..a4b9fbb32 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -164,6 +164,16 @@ extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; #endif +#if defined(AFHDS3) +extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; +extern const char STR_AFHDS3_ONE_TO_MANY[]; +extern const char STR_AFHDS3_PROTOCOLS[]; +extern const char STR_AFHDS3_POWERS[]; +extern const char STR_AFHDS3_RX_FREQ[]; +extern const char STR_AFHDS3_ACTUAL_POWER[]; +extern const char STR_AFHDS3_POWER_SOURCE[]; +#endif + extern const char STR_CURVE_TYPES[]; extern const char STR_VSENSORTYPES[]; extern const char STR_VFORMULAS[]; @@ -764,6 +774,7 @@ extern const char STR_BINDING_1_8_TELEM_OFF[]; extern const char STR_BINDING_9_16_TELEM_ON[]; extern const char STR_BINDING_9_16_TELEM_OFF[]; extern const char STR_CHANNELRANGE[]; +extern const char STR_RXFREQUENCY[]; extern const char STR_ANTENNASELECTION[]; extern const char STR_ANTENNACONFIRM1[]; extern const char STR_ANTENNACONFIRM2[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 2086ff7a8..7ae453e04 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -935,6 +935,12 @@ #define TR_UNIT "Jednotky" #define TR_TELEMETRY_NEWSENSOR INDENT "Přidat senzor ručně" #define TR_CHANNELRANGE TR(INDENT "Kanály", INDENT "Rozsah kanálů") +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", TR("Power src.", "Power source")) +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Opravdu přepnout?" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index fc900bcdf..e0e4eb437 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -941,6 +941,12 @@ #define TR_UNIT "Einheit" #define TR_TELEMETRY_NEWSENSOR INDENT "Sensor hinzufügen ..." #define TR_CHANNELRANGE TR(INDENT "Kanäle", INDENT "Ausgangs Kanäle") //wg 9XR-Pro +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Ant. umschalten" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index eae2ad25a..a270a1f34 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -663,6 +663,11 @@ #define TR_MULTI_SERVOFREQ TR("Servo rate", "Servo update rate") #define TR_MULTI_MAX_THROW TR("Max. Throw", "Enable max. throw") #define TR_MULTI_RFCHAN TR("RF Channel", "Select RF channel") +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") #define TR_SYNCMENU "[Sync]" #define TR_LIMIT INDENT "Limit" #define TR_MINRSSI "Min Rssi" @@ -940,6 +945,7 @@ #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE TR(INDENT "Ch. Range", INDENT "Channel Range") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 16752d94f..822ace072 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -938,6 +938,12 @@ #define TR_UNIT "Unidad" #define TR_TELEMETRY_NEWSENSOR INDENT "Añadir sensor..." #define TR_CHANNELRANGE INDENT "Canales" +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "ANTENA EXT." #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\020" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 22b2ac966..ec28eabcb 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -954,6 +954,12 @@ #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE INDENT "Channel Range" +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 5ef45e312..466effb38 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -956,6 +956,12 @@ #define TR_UNIT "Unité" #define TR_TELEMETRY_NEWSENSOR TR(INDENT"Nouveau capteur...", INDENT "Ajout d'un nouveau capteur...") #define TR_CHANNELRANGE TR(INDENT "Canaux", INDENT "Plage de canaux") +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Vraiment changer?" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\021" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 22416cdd2..cea9b2716 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -956,6 +956,12 @@ #define TR_UNIT "Unita" #define TR_TELEMETRY_NEWSENSOR INDENT "Aggiungi nuovo sensore" #define TR_CHANNELRANGE INDENT "Numero Canali" +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "ANTENNA EST." #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index a3c642135..5c4310875 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -942,6 +942,12 @@ #define TR_UNIT "Eenheid" #define TR_TELEMETRY_NEWSENSOR INDENT "Sensor toevoegen ..." #define TR_CHANNELRANGE TR(INDENT "Kanalen", INDENT "Uitgangs Kanalen") //wg 9XR-Pro +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Antennes wisselen?" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index e64b4edff..9f3efa84f 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -955,6 +955,12 @@ #define TR_UNIT "Jedn" #define TR_TELEMETRY_NEWSENSOR INDENT "Dodaj nowy czujnik..." #define TR_CHANNELRANGE TR(INDENT "ZakrKn",INDENT "Zakres kanału") +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 75830e538..e11cd03e6 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -945,6 +945,12 @@ #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE INDENT "Channel Range" +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 368ad237b..ab1e44f3b 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -955,6 +955,12 @@ #define TR_UNIT "Enhet" #define TR_TELEMETRY_NEWSENSOR INDENT "Lägg till sensor..." #define TR_CHANNELRANGE INDENT "Kanalområde" +#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") +#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 38631545a..97a431879 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -87,7 +87,7 @@ #endif #define LEN_EXTERNAL_MODULE_PROTOCOLS "\014" -#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""R9MLP\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite" +#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""R9MLP\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3" #define LEN_INTERNAL_MODULE_PROTOCOLS LEN_EXTERNAL_MODULE_PROTOCOLS #define TR_INTERNAL_MODULE_PROTOCOLS TR_EXTERNAL_MODULE_PROTOCOLS @@ -125,4 +125,11 @@ #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 ""SFHSS\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 ""FlyZone""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 " + +#define LEN_AFHDS3_PROTOCOLS "\x008" +#define TR_AFHDS3_PROTOCOLS "PWM/IBUS""PWM/SBUS""PPM/IBUS""PPM/SBUS" + +#define LEN_AFHDS3_POWERS "\006" +#define TR_AFHDS3_POWERS "25 mW\0""100 mW""500 mW""1 W\0 ""2 W\0 " diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index 8ee448f11..cae773561 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -77,7 +77,8 @@ options_taranis_x9d = { "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_taranis_x9dp = { @@ -93,7 +94,8 @@ options_taranis_x9dp = { "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_taranis_x9lite = { @@ -110,7 +112,8 @@ options_taranis_x9lite = { "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "internalpxx1": ("INTERNAL_MODULE_PXX1", "YES", "NO"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_taranis_xlite = { @@ -124,7 +127,8 @@ options_taranis_xlite = { "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_taranis_xlites = { @@ -140,7 +144,8 @@ options_taranis_xlites = { "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "internalpxx1": ("INTERNAL_MODULE_PXX1", "YES", "NO"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_taranis_x9e = { @@ -156,7 +161,8 @@ options_taranis_x9e = { "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "horussticks": ("STICKS", "HORUS", "STANDARD"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_horus_x12s = { @@ -170,7 +176,8 @@ options_horus_x12s = { "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "pcbdev": ("PCBREV", "10", None), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), - "internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)] + "internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)], + "afhds3": ("AFHDS3", "YES", "NO") } options_horus_x10 = { @@ -183,7 +190,8 @@ options_horus_x10 = { "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), - "internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)] + "internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)], + "afhds3": ("AFHDS3", "YES", "NO") } options_horus_x10express = { @@ -196,7 +204,8 @@ options_horus_x10express = { "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), - "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None) + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "afhds3": ("AFHDS3", "YES", "NO") } options_jumper_t12 = { @@ -209,6 +218,7 @@ options_jumper_t12 = { "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "internalmulti": ("INTERNAL_MODULE_MULTI", "YES", "NO"), + "afhds3": ("AFHDS3", "YES", "NO") } options_jumper_t16 = { @@ -222,6 +232,7 @@ options_jumper_t16 = { "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "internalmulti": ("INTERNAL_MODULE_MULTI", "YES", "NO"), "bluetooth": ("BLUETOOTH", "YES", "NO"), + "afhds3": ("AFHDS3", "YES", "NO") } options_radiomaster_tx16s = { @@ -235,4 +246,5 @@ options_radiomaster_tx16s = { "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "bluetooth": ("BLUETOOTH", "YES", "NO"), "internalgps": ("INTERNAL_GPS", "YES", "NO"), + "afhds3": ("AFHDS3", "YES", "NO") } \ No newline at end of file From 5d4f6679e5d4715223f7a8e8982d824de7c12594 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Mon, 22 Jun 2020 15:52:52 +0100 Subject: [PATCH 010/267] Remove Avr boards (#7713) Remove AVR --- companion/src/CMakeLists.txt | 16 +- companion/src/burnconfigdialog.cpp | 197 +----- companion/src/burnconfigdialog.h | 20 - companion/src/burnconfigdialog.ui | 660 +----------------- companion/src/firmwares/CMakeLists.txt | 2 - companion/src/firmwares/boards.cpp | 37 +- companion/src/firmwares/boards.h | 24 - .../src/firmwares/customfunctiondata.cpp | 19 +- companion/src/firmwares/eeprominterface.cpp | 3 - companion/src/firmwares/eeprominterface.h | 3 - companion/src/firmwares/generalsettings.cpp | 4 +- companion/src/firmwares/modeldata.cpp | 29 +- companion/src/firmwares/modeldata.h | 3 - .../src/firmwares/opentx/opentxeeprom.cpp | 14 +- companion/src/firmwares/opentx/opentxeeprom.h | 3 - .../src/firmwares/opentx/opentxinterface.cpp | 206 ++---- companion/src/firmwares/rawsource.cpp | 220 +----- companion/src/firmwares/rawsource.h | 4 - companion/src/firmwares/telem_data.cpp | 3 - companion/src/generaledit/generaledit.cpp | 4 +- companion/src/generaledit/generalsetup.cpp | 15 +- companion/src/mainwindow.cpp | 20 +- companion/src/mainwindow.h | 2 - companion/src/modeledit/CMakeLists.txt | 1 - companion/src/modeledit/channels.cpp | 2 +- companion/src/modeledit/customfunctions.cpp | 36 +- companion/src/modeledit/expodialog.cpp | 14 +- companion/src/modeledit/logicalswitches.cpp | 26 +- companion/src/modeledit/modeledit.cpp | 2 +- companion/src/modeledit/setup.cpp | 15 +- companion/src/modeledit/telemetry.cpp | 467 ++----------- companion/src/modeledit/telemetry.h | 37 - companion/src/modelprinter.cpp | 3 +- companion/src/multimodelprinter.cpp | 59 +- companion/src/radiointerface.cpp | 226 ++---- companion/src/radiointerface.h | 4 - companion/src/storage/rlefile.cpp | 134 +--- companion/src/wizarddata.cpp | 19 +- launchpad/common/rules | 3 - radio/src/io/frsky_firmware_update.cpp | 2 +- radio/src/targets/simu/opentxsimulator.cpp | 4 +- 41 files changed, 276 insertions(+), 2286 deletions(-) diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 016d962a6..f33f430ad 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -24,8 +24,6 @@ set(COMPANION_OSX_APP_BUNDLE_NAME "OpenTX Companion ${VERSION_MAJOR}.${VERSION_M # On OS X we copy dfu-util to the application bundle. This the path from which we copy # the binary (default homebrew path) set(DFU_UTIL_PATH "/usr/local/bin/dfu-util") -set(AVRDUDE_PATH "/usr/local/bin/avrdude") -set(AVRDUDE_CONF "/usr/local/etc/avrdude.conf") option(ALLOW_NIGHTLY_BUILDS "Allow nightly builds download / update") # Disabled by default if(ALLOW_NIGHTLY_BUILDS) @@ -239,7 +237,6 @@ set(companion_SRCS flasheepromdialog.cpp printdialog.cpp modelprinter.cpp - fusesdialog.cpp logsdialog.cpp downloaddialog.cpp splashlibrarydialog.cpp @@ -259,7 +256,6 @@ set(companion_MOC_HDRS burnconfigdialog.h comparedialog.h printdialog.h - fusesdialog.h logsdialog.h creditsdialog.h releasenotesdialog.h @@ -291,7 +287,6 @@ set(companion_MOC_HDRS set(companion_UIS mdichild.ui comparedialog.ui - fusesdialog.ui logsdialog.ui apppreferencesdialog.ui fwpreferencesdialog.ui @@ -407,7 +402,7 @@ elseif(WIN32) install(TARGETS ${SIMULATOR_NAME} DESTINATION ${INSTALL_DESTINATION}) install(FILES ${simulator_plugins} DESTINATION "${INSTALL_DESTINATION}") # supporting utilities - set(INSTALL_TEMP_FILES avrdude.exe avrdude.conf dfu-util.exe libusb0.dll libusb-1.0.dll license.txt) + set(INSTALL_TEMP_FILES dfu-util.exe libusb0.dll libusb-1.0.dll license.txt) foreach(tmpfile ${INSTALL_TEMP_FILES}) install(FILES "${COMPANION_SRC_DIRECTORY}/../targets/windows/${tmpfile}" DESTINATION ${INSTALL_DESTINATION}) endforeach() @@ -554,14 +549,11 @@ IF(APPLE) set(${COMPANION_SOURCES} ${COMPANION_SOURCES} ${PROJECT_SOURCE_DIR}/images/${MACOSX_BUNDLE_ICON_FILE}) install(FILES ${MAC_ICON_FILE} DESTINATION ${companion_res_dir} COMPONENT Runtime) - # Copy dfu-util and avrdude, resolve symlink first + # Copy dfu-util, resolve symlink first get_filename_component(DFU_UTIL_ABSOLUTE_PATH ${DFU_UTIL_PATH} REALPATH) - get_filename_component(AVRDUDE_ABSOLUTE_PATH ${AVRDUDE_PATH} REALPATH) - get_filename_component(AVRDUDECONF_ABSOLUTE_PATH ${AVRDUDE_CONF} REALPATH) - install(PROGRAMS ${DFU_UTIL_ABSOLUTE_PATH} ${AVRDUDE_ABSOLUTE_PATH} DESTINATION ${companion_res_dir} COMPONENT Runtime) - install(FILES ${AVRDUDECONF_ABSOLUTE_PATH} DESTINATION ${companion_res_dir} COMPONENT Runtime) + install(PROGRAMS ${DFU_UTIL_ABSOLUTE_PATH} DESTINATION ${companion_res_dir} COMPONENT Runtime) - set(bundle_tools_path "\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/dfu-util;\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/avrdude") + set(bundle_tools_path "\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/dfu-util;") # Include depencies (adding frameworks, fixing the embbeded libraries) diff --git a/companion/src/burnconfigdialog.cpp b/companion/src/burnconfigdialog.cpp index 0fe22be03..43a60c7ea 100644 --- a/companion/src/burnconfigdialog.cpp +++ b/companion/src/burnconfigdialog.cpp @@ -36,61 +36,23 @@ burnConfigDialog::burnConfigDialog(QWidget *parent) : { ui->setupUi(this); setWindowIcon(CompanionIcon("configure.png")); - ui->avrdude_programmer->model()->sort(0); getSettings(); - populateProgrammers(); Board::Type board = getCurrentBoard(); if (IS_STM32(board)) { setWindowTitle(tr("DFU-UTIL Configuration")); - ui->avrArgs->hide(); - ui->avrdude_location->hide(); - ui->avrdude_port->hide(); - ui->avrdude_programmer->hide(); - ui->label_av1->hide(); - ui->label_av2->hide(); - ui->label_av4->hide(); - ui->label_av5->hide(); - ui->pushButton->hide(); - ui->pushButton_3->hide(); - ui->pushButton_4->hide(); ui->label_sb1->hide(); ui->label_sb3->hide(); ui->samba_location->hide(); ui->samba_port->hide(); ui->sb_browse->hide(); } - else if (IS_SKY9X(board)) { - setWindowTitle(tr("SAM-BA Configuration")); - ui->avrArgs->hide(); - ui->avrdude_location->hide(); - ui->avrdude_port->hide(); - ui->avrdude_programmer->hide(); - ui->label_av1->hide(); - ui->label_av2->hide(); - ui->label_av4->hide(); - ui->label_av5->hide(); - ui->pushButton->hide(); - ui->pushButton_3->hide(); - ui->pushButton_4->hide(); - ui->label_dfu1->hide(); - ui->dfu_location->hide(); - ui->dfu_browse->hide(); - } else { - setWindowTitle(tr("AVRDUDE Configuration")); - ui->label_sb1->hide(); - ui->label_sb3->hide(); - ui->samba_location->hide(); - ui->samba_port->hide(); - ui->sb_browse->hide(); + setWindowTitle(tr("SAM-BA Configuration")); ui->label_dfu1->hide(); - ui->label_dfu2->hide(); ui->dfu_location->hide(); ui->dfu_browse->hide(); } - ui->label_av3->hide(); - ui->avrdude_mcu->hide(); ui->label_sb2->hide(); ui->arm_mcu->hide(); ui->label_dfu2->hide(); @@ -105,46 +67,26 @@ burnConfigDialog::~burnConfigDialog() delete ui; } -QStringList burnConfigDialog::getAvrdudeArgs() -{ - QStringList args = avrArgs; - if (!avrPort.isEmpty()) - args << "-P" << avrPort; - -#if defined(__APPLE__) - args << "-C" << QFileInfo(QApplication::applicationDirPath() + "/../Resources/avrdude.conf").absoluteFilePath(); -#endif - return args; -} void burnConfigDialog::getSettings() { - avrLoc = g.avrdudeLocation(); sambaLoc = g.sambaLocation(); dfuLoc = g.dfuLocation(); #if defined WIN32 || !defined __GNUC__ - if ( avrLoc.isEmpty()) - avrLoc = QFileInfo("avrdude.exe").absoluteFilePath(); if ( sambaLoc.isEmpty()) sambaLoc = QFileInfo("sam-ba.exe").absoluteFilePath(); if ( dfuLoc.isEmpty()) dfuLoc = QFileInfo("dfu-util.exe").absoluteFilePath(); #elif defined __APPLE__ - if ( avrLoc.isEmpty()) - avrLoc = QFileInfo(QApplication::applicationDirPath() + "/../Resources/avrdude").absoluteFilePath(); if ( sambaLoc.isEmpty()) sambaLoc = "/usr/local/bin/sam-ba"; if ( dfuLoc.isEmpty()) dfuLoc = QFileInfo(QApplication::applicationDirPath() + "/../Resources/dfu-util").absoluteFilePath(); #elif defined __FreeBSD__ - if (avrLoc.isEmpty()) - avrLoc = "/usr/local/bin/avrdude"; if (dfuLoc.isEmpty()) dfuLoc = "/usr/local/bin/dfu-util"; #else - if ( avrLoc.isEmpty()) - avrLoc = "/usr/bin/avrdude"; if ( sambaLoc.isEmpty()) sambaLoc = "/usr/bin/sam-ba"; if ( dfuLoc.isEmpty()) @@ -152,45 +94,22 @@ void burnConfigDialog::getSettings() #endif dfuArgs = g.dfuArguments().split(" ", QString::SkipEmptyParts); - avrArgs = g.avrArguments().split(" ", QString::SkipEmptyParts); - avrProgrammer = g.programmer(); - avrPort = g.avrPort(); - avrMCU = g.mcu(); armMCU = g.armMcu(); sambaPort = g.sambaPort(); - ui->avrdude_location->setText(getAVRDUDE()); - ui->avrArgs->setText(avrArgs.join(" ")); - ui->samba_location->setText(getSAMBA()); ui->samba_port->setText(getSambaPort()); ui->dfu_location->setText(getDFU()); ui->dfuArgs->setText(getDFUArgs().join(" ")); - int idx1 = ui->avrdude_programmer->findText(getProgrammer()); - int idx2 = ui->avrdude_port->findText(getPort()); - int idx3 = ui->avrdude_mcu->findText(getMCU()); int idx4 = ui->arm_mcu->findText(getArmMCU()); - if(idx1>=0) ui->avrdude_programmer->setCurrentIndex(idx1); - if(idx2>=0) ui->avrdude_port->setCurrentIndex(idx2); - if(idx3>=0) ui->avrdude_mcu->setCurrentIndex(idx3); if(idx4>=0) ui->arm_mcu->setCurrentIndex(idx4); QFile file; - if (file.exists(avrLoc)) { - ui->pushButton_3->setEnabled(true); - } else { - ui->pushButton_3->setDisabled(true); - } } void burnConfigDialog::putSettings() { - g.avrdudeLocation( avrLoc ); - g.programmer( avrProgrammer); - g.mcu( avrMCU ); - g.avrPort( avrPort ); - g.avrArguments( avrArgs.join(" ") ); g.sambaLocation( sambaLoc ); g.sambaPort( sambaPort ); g.armMcu( armMCU ); @@ -198,73 +117,6 @@ void burnConfigDialog::putSettings() g.dfuArguments( dfuArgs.join(" ") ); } -void burnConfigDialog::populateProgrammers() -{ - QString fileName = QFileInfo(avrLoc).canonicalPath() + "/avrdude.conf"; //for windows - if(!QFileInfo(fileName).exists()) fileName = "/etc/avrdude.conf"; //for linux - if(!QFileInfo(fileName).exists()) fileName = "/etc/avrdude/avrdude.conf"; //for linux - if(!QFileInfo(fileName).exists()) return; // not found avrdude.conf - returning - - QFile file(fileName); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; - - QStringList items; - QString line = ""; - QString prevline = ""; - QTextStream in(&file); - while (!in.atEnd()) - { - prevline = line; - line = in.readLine(); - - if(prevline.left(10).toLower()=="programmer") - items << line.section('"',1,1); - } - file.close(); - - items.sort(); - - QString avrProgrammer_temp = avrProgrammer; - ui->avrdude_programmer->clear(); - ui->avrdude_programmer->addItems(items); - int idx1 = ui->avrdude_programmer->findText(avrProgrammer_temp); - if(idx1>=0) ui->avrdude_programmer->setCurrentIndex(idx1); -} - -void burnConfigDialog::on_avrdude_programmer_currentIndexChanged(QString ) -{ - avrProgrammer = ui->avrdude_programmer->currentText(); -} - -void burnConfigDialog::on_avrdude_mcu_currentIndexChanged(QString ) -{ - avrMCU = ui->avrdude_mcu->currentText(); -} - -void burnConfigDialog::on_avrdude_location_editingFinished() -{ - avrLoc = ui->avrdude_location->text(); - if (avrLoc.isEmpty()) { - ui->pushButton_3->setDisabled(true); - } else { - QFile file; - if (file.exists(avrLoc)) { - ui->pushButton_3->setEnabled(true); - } - } -} - -void burnConfigDialog::on_avrArgs_editingFinished() -{ - avrArgs = ui->avrArgs->text().split(" ", QString::SkipEmptyParts); -} - -void burnConfigDialog::on_avrdude_port_currentIndexChanged(QString ) -{ - avrPort = ui->avrdude_port->currentText(); -} - void burnConfigDialog::on_samba_location_editingFinished() { sambaLoc = ui->samba_location->text(); @@ -280,17 +132,6 @@ void burnConfigDialog::on_arm_mcu_currentIndexChanged(QString ) armMCU = ui->arm_mcu->currentText(); } -void burnConfigDialog::on_pushButton_clicked() -{ - QString fileName = QFileDialog::getOpenFileName(this, tr("Select Location"),ui->avrdude_location->text()); - - if(!fileName.isEmpty()) - { - ui->avrdude_location->setText(fileName); - avrLoc = fileName; - } -} - void burnConfigDialog::on_sb_browse_clicked() { QString fileName = QFileDialog::getOpenFileName(this, tr("Select Location"),ui->samba_location->text()); @@ -322,27 +163,6 @@ void burnConfigDialog::on_dfuArgs_editingFinished() dfuArgs = ui->dfuArgs->text().split(" ", QString::SkipEmptyParts); } -void burnConfigDialog::listAvrdudeProgrammers() -{ - ProgressDialog progressDialog(this, tr("List available programmers"), CompanionIcon("list.png"), true); - FlashProcess flashProcess(ui->avrdude_location->text(), QStringList() << "-c?", progressDialog.progress()); - flashProcess.run(); -} - -// TODO choose better name when no merge in progress.... -void burnConfigDialog::on_pushButton_3_clicked() -{ - listAvrdudeProgrammers(); -} - -// TODO choose better name when no merge in progress.... -void burnConfigDialog::on_pushButton_4_clicked() -{ - ProgressDialog progressDialog(this, tr("Avrdude help"), CompanionIcon("configure.png"), true); - FlashProcess flashProcess(ui->avrdude_location->text(), QStringList() << "-?", progressDialog.progress()); - flashProcess.run(); -} - void burnConfigDialog::on_advCtrChkB_toggled(bool checked) { Board::Type board = getCurrentBoard(); @@ -351,31 +171,20 @@ void burnConfigDialog::on_advCtrChkB_toggled(bool checked) ui->label_dfu2->show(); ui->dfuArgs->show(); } - else if (IS_SKY9X(board)) { + else { ui->label_sb2->show(); ui->arm_mcu->show(); } - else { - ui->label_av3->show(); - ui->avrdude_mcu->show(); - QMessageBox::warning(this, CPN_STR_APP_NAME, - tr("WARNING!
Normally CPU type is automatically selected according to the chosen firmware.
If you change the CPU type the resulting eeprom could be inconsistent."), - QMessageBox::Ok); - } } else { if (IS_STM32(board)) { ui->label_dfu2->hide(); ui->dfuArgs->hide(); } - else if (IS_SKY9X(board)) { + else { ui->label_sb2->hide(); ui->arm_mcu->hide(); } - else { - ui->label_av3->hide(); - ui->avrdude_mcu->hide(); - } } QTimer::singleShot(0, this, SLOT(shrink())); } diff --git a/companion/src/burnconfigdialog.h b/companion/src/burnconfigdialog.h index 5cf7bb375..06810bbd2 100644 --- a/companion/src/burnconfigdialog.h +++ b/companion/src/burnconfigdialog.h @@ -39,45 +39,25 @@ public: burnConfigDialog(QWidget *parent = 0); ~burnConfigDialog(); - QString getAVRDUDE() {return avrLoc;} QString getSAMBA() {return sambaLoc;} QString getDFU() {return dfuLoc;} - QStringList getAvrdudeArgs(); QStringList getDFUArgs() {return dfuArgs;} - QString getProgrammer() {return avrProgrammer;} - QString getMCU() {return avrMCU;} QString getArmMCU() {return armMCU;} - QString getPort() {return avrPort;} QString getSambaPort() {return sambaPort;} - void listAvrdudeProgrammers(); - private: Ui::burnConfigDialog *ui; void populateProgrammers(); - QString avrLoc; QString sambaLoc; QString dfuLoc; - QStringList avrArgs; - QString avrProgrammer; - QString avrMCU; QString armMCU; - QString avrPort; QString sambaPort; QStringList dfuArgs; private slots: void shrink(); - void on_avrArgs_editingFinished(); - void on_pushButton_4_clicked(); - void on_pushButton_3_clicked(); - void on_pushButton_clicked(); - void on_avrdude_location_editingFinished(); - void on_avrdude_programmer_currentIndexChanged(QString ); - void on_avrdude_mcu_currentIndexChanged(QString ); - void on_avrdude_port_currentIndexChanged(QString ); void on_samba_location_editingFinished(); void on_samba_port_editingFinished(); void on_arm_mcu_currentIndexChanged(QString ); diff --git a/companion/src/burnconfigdialog.ui b/companion/src/burnconfigdialog.ui index 36d343884..e52aaf797 100644 --- a/companion/src/burnconfigdialog.ui +++ b/companion/src/burnconfigdialog.ui @@ -45,615 +45,7 @@
- - - - Location of sam-ba executable - - - The location of the AVRDUDE executable. - - - dfu-util - - - - - - - DFU-Util Location - - - - - - - Location of AVRDUDE executable - - - The location of the AVRDUDE executable. - - - - - - - - - - Use this button to browse and look for the AVRDUDE executable file. - - - Browse... - - - - - - - Programmer - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Programmer used for communicating with the controller. -Please consult the programmer's documentation and the AVRDUDE documentation to select the appropriate programmer. - - - QComboBox::InsertAlphabetically - - - - c2n232i - - - - - dasa3 - - - - - dasa - - - - - siprog - - - - - ponyser - - - - - 89isp - - - - - frank-stk200 - - - - - blaster - - - - - ere-isp-avr - - - - - atisp - - - - - dapa - - - - - xil - - - - - futurlec - - - - - abcmini - - - - - picoweb - - - - - sp12 - - - - - alf - - - - - bascom - - - - - dt006 - - - - - pony-stk200 - - - - - stk200 - - - - - bsd - - - - - pavr - - - - - dragon_pdi - - - - - dragon_dw - - - - - dragon_hvsp - - - - - dragon_pp - - - - - dragon_isp - - - - - dragon_jtag - - - - - jtag2pdi - - - - - jtag2avr32 - - - - - jtagmkII_avr32 - - - - - jtag2dw - - - - - jtag2isp - - - - - jtag2 - - - - - jtag2fast - - - - - jtag2slow - - - - - jtagmkII - - - - - jtag1slow - - - - - jtag1 - - - - - jtagmkI - - - - - avr911 - - - - - avr109 - - - - - butterfly - - - - - usbtiny - - - - - usbasp - - - - - avr910 - - - - - stk600hvsp - - - - - stk600pp - - - - - stk600 - - - - - stk500hvsp - - - - - stk500pp - - - - - stk500v2 - - - - - mib510 - - - - - stk500v1 - - - - - stk500 - - - - - buspirate - - - - - avrisp2 - - - - - avrispmkII - - - - - avrispv2 - - - - - avrisp - - - - - arduino - - - - - - - - List all available programmers. - - - List Available - - - - - - - Extra arguments that will be passed to AVRDUDE on every call - - - Extra arguments used in AVRDUDE. -This can be used for providing extra information to AVRDUDE. - -Please only use this if you know what you are doing. There are no error checks and you could cripple your controller. - - - - - - - - - - Extra Arguments - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Show AVRDUDE help - - - Show Help - - - - - - - Communication port to the programmer. - - - - - - - - - - usb - - - - - com1 - - - - - com2 - - - - - com3 - - - - - com4 - - - - - com5 - - - - - com6 - - - - - com7 - - - - - com8 - - - - - com9 - - - - - com10 - - - - - com11 - - - - - com12 - - - - - com13 - - - - - com14 - - - - - com15 - - - - - lpt1 - - - - - lpt2 - - - - - lpt3 - - - - - lpt4 - - - - - avrdoper - - - - - /dev/ttyUSB0 - - - - - /dev/ttyUSB1 - - - - - - - - Port - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - AVRDUDE Location - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - MCU - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - CPU of your TX - - - CPU present on your 9x radio -Should be m64 for stock radios -m2560 for v4.1 boards - - - QComboBox::InsertAlphabetically - - - - m64 - - - - - m128 - - - - - m2560 - - - - - + CPU of your TX @@ -678,7 +70,27 @@ m2560 for v4.1 boards
- + + + + Location of sam-ba executable + + + The location of the AVRDUDE executable. + + + dfu-util + + + + + + + DFU-Util Location + + + + SAM-BA Location @@ -688,7 +100,7 @@ m2560 for v4.1 boards - + Location of sam-ba executable @@ -701,7 +113,7 @@ m2560 for v4.1 boards - + ARM MCU @@ -711,7 +123,7 @@ m2560 for v4.1 boards - + Port @@ -721,7 +133,7 @@ m2560 for v4.1 boards - + sam-ba serial port @@ -734,14 +146,14 @@ m2560 for v4.1 boards - + Alternate device - + Extra arguments that will be passed to AVRDUDE on every call @@ -757,7 +169,7 @@ Please only use this if you know what you are doing. There are no error checks - + Use this button to browse and look for the AVRDUDE executable file. @@ -767,7 +179,7 @@ Please only use this if you know what you are doing. There are no error checks - + Use this button to browse and look for the AVRDUDE executable file. @@ -777,14 +189,14 @@ Please only use this if you know what you are doing. There are no error checks - + - + Use advanced controls @@ -809,14 +221,6 @@ Please only use this if you know what you are doing. There are no error checks - avrdude_location - pushButton - avrdude_programmer - pushButton_3 - avrdude_mcu - avrdude_port - avrArgs - pushButton_4 samba_location sb_browse arm_mcu diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index 48e6394bc..1b99964fa 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -19,8 +19,6 @@ set(firmwares_SRCS sensordata.cpp telem_data.cpp afhds3.cpp - er9x/er9xeeprom.cpp - er9x/er9xinterface.cpp ersky9x/ersky9xeeprom.cpp ersky9x/ersky9xinterface.cpp opentx/opentxeeprom.cpp diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index cfadf5936..a189e16c8 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -25,18 +25,13 @@ // Update: These are now all only used within this class. // External access is only via getEEpromSize() and getFlashSize() -#define EESIZE_STOCK 2048 -#define EESIZE_M128 4096 -#define EESIZE_GRUVIN9X 4096 #define EESIZE_TARANIS (32*1024) #define EESIZE_SKY9X (128*4096) #define EESIZE_9XRPRO (128*4096) #define EESIZE_MAX EESIZE_9XRPRO // getFlashSize() (and these macros) is only used by radiointerface::getDfuArgs (perhaps can find a better way?) -#define FSIZE_STOCK (64*1024) -#define FSIZE_M128 (128*1024) -#define FSIZE_GRUVIN9X (256*1024) + #define FSIZE_TARANIS (512*1024) #define FSIZE_SKY9X (256*1024) #define FSIZE_9XRPRO (512*1024) @@ -82,12 +77,6 @@ uint32_t Boards::getFourCC(Type board) case BOARD_AR9X: case BOARD_9XRPRO: return 0x3278746F; - case BOARD_MEGA2560: - case BOARD_GRUVIN9X: - return 0x3178746F; - case BOARD_9X_M64: - case BOARD_9X_M128: - return 0; case BOARD_JUMPER_T12: return 0x3D78746F; case BOARD_JUMPER_T16: @@ -104,13 +93,6 @@ uint32_t Boards::getFourCC(Type board) int Boards::getEEpromSize(Board::Type board) { switch (board) { - case BOARD_9X_M64: - return EESIZE_STOCK; - case BOARD_9X_M128: - return EESIZE_M128; - case BOARD_MEGA2560: - case BOARD_GRUVIN9X: - return EESIZE_GRUVIN9X; case BOARD_SKY9X: return EESIZE_SKY9X; case BOARD_9XRPRO: @@ -144,13 +126,6 @@ int Boards::getEEpromSize(Board::Type board) int Boards::getFlashSize(Type board) { switch (board) { - case BOARD_9X_M64: - return FSIZE_STOCK; - case BOARD_9X_M128: - return FSIZE_M128; - case BOARD_MEGA2560: - case BOARD_GRUVIN9X: - return FSIZE_GRUVIN9X; case BOARD_SKY9X: return FSIZE_SKY9X; case BOARD_9XRPRO: @@ -412,7 +387,7 @@ QString Boards::getAnalogInputName(Board::Type board, int index) index -= getCapability(board, Board::Sticks); - if (IS_9X(board) || IS_2560(board) || IS_SKY9X(board)) { + if (IS_SKY9X(board)) { const QString pots[] = { "P1", "P2", @@ -496,14 +471,6 @@ bool Boards::isBoardCompatible(Type board1, Type board2) QString Boards::getBoardName(Board::Type board) { switch (board) { - case BOARD_9X_M64: - return "9X"; - case BOARD_9X_M128: - return "9X128"; - case BOARD_GRUVIN9X: - return "Gruvin9x"; - case BOARD_MEGA2560: - return "MEGA2560"; case BOARD_TARANIS_X7: return "Taranis X7/X7S"; case BOARD_TARANIS_X7_ACCESS: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index e4149a298..937703eda 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -32,10 +32,6 @@ namespace Board { enum Type { BOARD_UNKNOWN = -1, - BOARD_9X_M64 = 0, - BOARD_9X_M128, - BOARD_MEGA2560, - BOARD_GRUVIN9X, BOARD_SKY9X, BOARD_9XRPRO, BOARD_AR9X, @@ -191,26 +187,6 @@ class Boards // temporary aliases for transition period, use Boards class instead. #define getBoardCapability(b__, c__) Boards::getCapability(b__, c__) -inline bool IS_9X(Board::Type board) -{ - return board == Board::BOARD_9X_M64 || board == Board::BOARD_9X_M128; -} - -inline bool IS_STOCK(Board::Type board) -{ - return board == Board::BOARD_9X_M64; -} - -inline bool IS_M128(Board::Type board) -{ - return board == Board::BOARD_9X_M128; -} - -inline bool IS_2560(Board::Type board) -{ - return board == Board::BOARD_GRUVIN9X || board == Board::BOARD_MEGA2560; -} - inline bool IS_SKY9X(Board::Type board) { return board == Board::BOARD_SKY9X || board == Board::BOARD_9XRPRO || board == Board::BOARD_AR9X; diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 9313ace5a..3248f1fa4 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -112,13 +112,10 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox { int val = 0; Firmware * firmware = Firmware::getCurrentVariant(); - Board::Type board = firmware->getBoard(); b->addItem(tr("Timer1"), val++); b->addItem(tr("Timer2"), val++); - if (IS_ARM(board)) { - b->addItem( tr("Timer3"), val++); - } + b->addItem( tr("Timer3"), val++); b->addItem(tr("Flight"), val++); b->addItem(tr("Telemetry"), val++); int reCount = firmware->getCapability(RotaryEncoders); @@ -132,7 +129,7 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox if ((int)value < b->count()) { b->setCurrentIndex(value); } - if (model && IS_ARM(board)) { + if (model) { for (unsigned i=0; isensorData[i].isAvailable()) { RawSource item = RawSource(SOURCE_TYPE_TELEMETRY, 3*i); @@ -210,14 +207,8 @@ QString CustomFunctionData::paramToString(const ModelData * model) const case FUNC_ADJUST_GVAR_INCDEC: float val; QString unit; - if (IS_ARM(getCurrentBoard())) { - val = param * model->gvarData[func - FuncAdjustGV1].multiplierGet(); - unit = model->gvarData[func - FuncAdjustGV1].unitToString(); - } - else { - val = param; - unit = ""; - } + val = param * model->gvarData[func - FuncAdjustGV1].multiplierGet(); + unit = model->gvarData[func - FuncAdjustGV1].unitToString(); return QString("Increment: %1%2").arg(val).arg(unit); } } @@ -233,7 +224,7 @@ QString CustomFunctionData::repeatToString() const return ""; } else { - unsigned int step = IS_ARM(getCurrentBoard()) ? 1 : 10; + unsigned int step = 1; return tr("repeat(%1s)").arg(step*repeatParam); } } diff --git a/companion/src/firmwares/eeprominterface.cpp b/companion/src/firmwares/eeprominterface.cpp index d4121a8a4..90272d0ad 100644 --- a/companion/src/firmwares/eeprominterface.cpp +++ b/companion/src/firmwares/eeprominterface.cpp @@ -75,10 +75,7 @@ void EEPROMInterface::showEepromErrors(QWidget *parent, const QString &title, co if (errors.test(UNSUPPORTED_NEWER_VERSION)) { errorsList << tr("- Eeprom is from a newer version of OpenTX"); } if (errors.test(NOT_OPENTX)) { errorsList << tr("- Eeprom is not from OpenTX"); } - if (errors.test(NOT_TH9X)) { errorsList << tr("- Eeprom is not from Th9X"); } - if (errors.test(NOT_GRUVIN9X)) { errorsList << tr("- Eeprom is not from Gruvin9X"); } if (errors.test(NOT_ERSKY9X)) { errorsList << tr("- Eeprom is not from ErSky9X"); } - if (errors.test(NOT_ER9X)) { errorsList << tr("- Eeprom is not from Er9X"); } if (errors.test(WRONG_SIZE)) { errorsList << tr("- Eeprom size is invalid"); } if (errors.test(WRONG_FILE_SYSTEM)) { errorsList << tr("- Eeprom file system is invalid"); } if (errors.test(UNKNOWN_BOARD)) { errorsList << tr("- Eeprom is from a unknown board"); } diff --git a/companion/src/firmwares/eeprominterface.h b/companion/src/firmwares/eeprominterface.h index c2bf0dafa..b3680b4ba 100644 --- a/companion/src/firmwares/eeprominterface.h +++ b/companion/src/firmwares/eeprominterface.h @@ -235,10 +235,7 @@ enum EepromLoadErrors { WRONG_SIZE, WRONG_FILE_SYSTEM, NOT_OPENTX, - NOT_TH9X, - NOT_GRUVIN9X, NOT_ERSKY9X, - NOT_ER9X, UNKNOWN_BOARD, WRONG_BOARD, BACKUP_NOT_SUPPORTED, diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index 9c38391c7..f307dbf26 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -110,9 +110,7 @@ GeneralSettings::GeneralSettings() backlightOffBright = 20; } - if (IS_ARM(board)) { - speakerVolume = 12; - } + speakerVolume = 12; if (IS_JUMPER_T16(board)) strcpy(bluetoothName, "t16"); diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 3386ba41d..3add6de8b 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -207,36 +207,25 @@ bool ModelData::isEmpty() const void ModelData::setDefaultInputs(const GeneralSettings & settings) { - Board::Type board = getCurrentBoard(); - if (IS_ARM(board)) { - for (int i = 0; i < CPN_MAX_STICKS; i++) { - ExpoData * expo = &expoData[i]; - expo->chn = i; - expo->mode = INPUT_MODE_BOTH; - expo->srcRaw = settings.getDefaultSource(i); - expo->weight = 100; - strncpy(inputNames[i], Helpers::removeAccents(expo->srcRaw.toString(this)).toLatin1().constData(), sizeof(inputNames[i]) - 1); - } + for (int i = 0; i < CPN_MAX_STICKS; i++) { + ExpoData * expo = &expoData[i]; + expo->chn = i; + expo->mode = INPUT_MODE_BOTH; + expo->srcRaw = settings.getDefaultSource(i); + expo->weight = 100; + strncpy(inputNames[i], Helpers::removeAccents(expo->srcRaw.toString(this)).toLatin1().constData(), sizeof(inputNames[i]) - 1); } } void ModelData::setDefaultMixes(const GeneralSettings & settings) { - Board::Type board = getCurrentBoard(); - if (IS_ARM(board)) { - setDefaultInputs(settings); - } + setDefaultInputs(settings); for (int i = 0; i < CPN_MAX_STICKS; i++) { MixData * mix = &mixData[i]; mix->destCh = i + 1; mix->weight = 100; - if (IS_ARM(board)) { - mix->srcRaw = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, i); - } - else { - mix->srcRaw = RawSource(SOURCE_TYPE_STICK, i); - } + mix->srcRaw = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, i); } } diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 93e98588e..df5c6d33d 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -41,13 +41,10 @@ class RadioDataConversionState; class RSSIAlarmData { public: RSSIAlarmData() { clear(); } - unsigned int level[2]; // AVR Only int warning; int critical; bool disabled; void clear() { - this->level[0] = 2; - this->level[1] = 3; this->warning = 45; this->critical = 42; this->disabled = false; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 119bbe396..8b0e8643d 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -721,13 +721,7 @@ class FlightModeField: public TransformedField { trim = 501 + phase.trimRef[i] - (phase.trimRef[i] > index ? 1 : 0); else trim = std::max(-500, std::min(500, phase.trim[i])); - if (board == BOARD_9X_M64 || (board == BOARD_9X_M128 && version >= 215)) { - trimBase[i] = trim >> 2; - trimExt[i] = (trim & 0x03); - } - else { - trimBase[i] = trim; - } + trimBase[i] = trim; } } } @@ -751,11 +745,7 @@ class FlightModeField: public TransformedField { phase.trim[i] = 0; } else { - int trim; - if (board == BOARD_9X_M64 || (board == BOARD_9X_M128 && version >= 215)) - trim = ((trimBase[i]) << 2) + (trimExt[i] & 0x03); - else - trim = trimBase[i]; + int trim = trimBase[i]; if (trim > 500) { phase.trimRef[i] = trim - 501; if (phase.trimRef[i] >= index) diff --git a/companion/src/firmwares/opentx/opentxeeprom.h b/companion/src/firmwares/opentx/opentxeeprom.h index ced4dccb6..29d5b0b93 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.h +++ b/companion/src/firmwares/opentx/opentxeeprom.h @@ -37,9 +37,6 @@ #define TARANIS_X9LITES_VARIANT 0x0801 #define JUMPER_T12_VARIANT 0x4001 -#define SIMU_STOCK_VARIANTS (GVARS_VARIANT|FRSKY_VARIANT) -#define SIMU_M128_VARIANTS (M128_VARIANT|SIMU_STOCK_VARIANTS) - class OpenTxGeneralData: public TransformedField { public: OpenTxGeneralData(GeneralSettings & generalData, Board::Type board, unsigned int version, unsigned int variant=0); diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index c405f9699..f329d98bb 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -58,14 +58,6 @@ OpenTxEepromInterface::~OpenTxEepromInterface() const char * OpenTxEepromInterface::getName() { switch (board) { - case BOARD_9X_M64: - return "OpenTX for 9X board"; - case BOARD_9X_M128: - return "OpenTX for M128 / 9X board"; - case BOARD_MEGA2560: - return "OpenTX for MEGA2560 board"; - case BOARD_GRUVIN9X: - return "OpenTX for Gruvin9x board / 9X"; case BOARD_JUMPER_T12: return "OpenTX for Jumper T12"; case BOARD_JUMPER_T16: @@ -280,16 +272,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t * uint8_t OpenTxEepromInterface::getLastDataVersion(Board::Type board) { - switch (board) { - case BOARD_9X_M64: - return 216; - case BOARD_GRUVIN9X: - case BOARD_MEGA2560: - case BOARD_9X_M128: - return 217; - default: - return 219; - } + return 219; } void OpenTxEepromInterface::showErrors(const QString & title, const QStringList & errors) @@ -325,10 +308,7 @@ int OpenTxEepromInterface::save(uint8_t * eeprom, const RadioData & radioData, u efile->EeFsCreate(eeprom, size, board, version); - if (board == BOARD_9X_M128) { - variant |= M128_VARIANT; - } - else if (IS_TARANIS_X9E(board)) { + if (IS_TARANIS_X9E(board)) { variant |= TARANIS_X9E_VARIANT; } else if (IS_TARANIS_X9LITES(board)) { @@ -445,43 +425,26 @@ int OpenTxFirmware::getCapability(::Capability capability) case Models: if (IS_FAMILY_HORUS_OR_T16(board)) return 0; - else if (IS_ARM(board)) + else return 60; - else if (board == BOARD_9X_M128) - return 30; - else if (IS_2560(board)) - return 30; - else - return 16; case Imperial: - if (IS_ARM(board)) - return 0; - else - return id.contains("imperial") ? 1 : 0; + return 0; case ModelImage: return (board == BOARD_TARANIS_X9D || IS_TARANIS_PLUS(board) || board == BOARD_TARANIS_X9DP_2019 || IS_FAMILY_HORUS_OR_T16(board)); case HasBeeper: - return (!IS_ARM(board)); + return false; case HasPxxCountry: return 1; case HasGeneralUnits: - if (IS_ARM(board)) - return 1; - else - return 0; + return true; case HasNegAndSwitches: - return IS_ARM(board); + return true; case PPMExtCtrl: return 1; case PPMFrameLength: return 40; case FlightModes: - if (IS_ARM(board)) - return 9; - else if (IS_2560(board)) - return 6; - else - return 5; + return 9; case FlightModesHaveFades: return 1; case Heli: @@ -493,7 +456,7 @@ int OpenTxFirmware::getCapability(::Capability capability) if (IS_HORUS_OR_TARANIS(board)) return id.contains("nogvars") ? 0 : 9; else if (id.contains("gvars")) - return IS_ARM(board) ? 9 : 5; + return 9; else return 0; case ModelName: @@ -501,66 +464,48 @@ int OpenTxFirmware::getCapability(::Capability capability) case FlightModesName: return (IS_HORUS_OR_TARANIS(board) ? 10 : 6); case GvarsName: - return (IS_9X(board) ? 0 : 3); + return 3; case GvarsInCS: case HasFAIMode: return 1; case GvarsAreNamed: case GvarsFlightModes: - return ((IS_ARM(board) || IS_2560(board)) ? 1 : 0); + return 1; case Mixes: - return (IS_ARM(board) ? 64 : 32); + return 64; case OffsetWeight: - return (IS_ARM(board) ? 500 : 245); + return 500; case Timers: - return (IS_ARM(board) ? 3 : 2); + return 3; case TimersName: if (HAS_LARGE_LCD(board)) return 8; - else if (IS_ARM(board)) + else return 3; - else - return 0; case PermTimers: - return (IS_2560(board) || IS_ARM(board)); + return true; case CustomFunctions: - if (IS_ARM(board)) - return 64; - else if (IS_2560(board) || board == BOARD_9X_M128) - return 24; - else - return 16; + return 64; case SafetyChannelCustomFunction: return id.contains("nooverridech") ? 0 : 1; case LogicalSwitches: - if (IS_ARM(board)) - return 64; - else - return 12; + return 64; case CustomAndSwitches: - if (IS_ARM(board)) - return getCapability(LogicalSwitches); - else - return 15/*4bits*/- 9/*sw positions*/; + return getCapability(LogicalSwitches); case LogicalSwitchesExt: - return (IS_ARM(board) ? true : false); + return true; case RotaryEncoders: - if (board == BOARD_GRUVIN9X) - return 2; - else if (IS_SKY9X(board)) - return 1; - else return 0; case Outputs: - return (IS_ARM(board) ? 32 : 16); + return 32; case NumCurvePoints: - return (IS_ARM(board) ? 512 : 104); + return 512; case VoicesAsNumbers: - return (IS_ARM(board) ? 0 : 1); + return 0; case VoicesMaxLength: - return (IS_ARM(board) ? (IS_TARANIS_X9(board) ? 8 : 6) : 0); + return (IS_TARANIS_X9(board) ? 8 : 6); case MultiLangVoice: - return (IS_ARM(board) ? 1 : 0); + return 1; case SoundPitch: return 1; case Haptic: @@ -571,7 +516,7 @@ int OpenTxFirmware::getCapability(::Capability capability) else return 0; case MaxVolume: - return (IS_ARM(board) ? 23 : 7); + return 23; case MaxContrast: if (IS_TARANIS_SMALL(board) || IS_JUMPER_T12(board)) return 30; @@ -583,7 +528,7 @@ int OpenTxFirmware::getCapability(::Capability capability) else return 10; case HasSoundMixer: - return (IS_ARM(board) ? 1 : 0); + return 1; case ExtraInputs: return 1; case TrimsRange: @@ -593,29 +538,26 @@ int OpenTxFirmware::getCapability(::Capability capability) case Simulation: return 1; case NumCurves: - return (HAS_LARGE_LCD(board) ? 32 : (IS_ARM(board) ? 16 : 8)); + return (HAS_LARGE_LCD(board) ? 32 : 16); case HasMixerNames: - return (IS_ARM(board) ? (IS_TARANIS_X9(board) ? 8 : 6) : false); + return (IS_TARANIS_X9(board) ? 8 : 6); case HasExpoNames: - return (IS_ARM(board) ? (IS_TARANIS_X9(board) ? 8 : 6) : false); + return (IS_TARANIS_X9(board) ? 8 : 6); case HasNoExpo: return (IS_HORUS_OR_TARANIS(board) ? false : true); case ChannelsName: - return (IS_ARM(board) ? (HAS_LARGE_LCD(board) ? 6 : 4) : 0); + return (HAS_LARGE_LCD(board) ? 6 : 4); case HasCvNames: - return (IS_ARM(board) ? 1 : 0); + return 1; case Telemetry: - if (IS_2560(board) || IS_ARM(board) || id.contains("frsky") || id.contains("telemetrez")) - return TM_HASTELEMETRY | TM_HASOFFSET | TM_HASWSHH; - else - return 0; + return 1; case TelemetryBars: return 1; case TelemetryCustomScreens: if (IS_FAMILY_HORUS_OR_T16(board)) return 0; else - return IS_ARM(board) ? 4 : 2; + return 4; case TelemetryCustomScreensBars: return (getCapability(TelemetryCustomScreens) ? 4 : 0); case TelemetryCustomScreensLines: @@ -627,7 +569,7 @@ int OpenTxFirmware::getCapability(::Capability capability) case TelemetryUnits: return 0; case TelemetryMaxMultiplier: - return (IS_ARM(board) ? 32 : 8); + return 32; case PPMCenter: return (IS_HORUS_OR_TARANIS(board) ? 500 : (id.contains("ppmca") ? 125 : 0)); case PPMUnitMicroseconds: @@ -639,11 +581,11 @@ int OpenTxFirmware::getCapability(::Capability capability) case HasVario: return 1; case HasVarioSink: - return ((IS_2560(board) || IS_ARM(board)) ? true : false); + return true; case HasFailsafe: - return (IS_ARM(board) ? 32 : 0); + return 32; case NumModules: - return (IS_ARM(board) ? 2 : 1); + return 2; case NumFirstUsableModule: return (IS_JUMPER_T12(board) ? 1 : 0); case HasModuleR9MFlex: @@ -651,23 +593,23 @@ int OpenTxFirmware::getCapability(::Capability capability) case HasModuleR9MMini: return IS_TARANIS_XLITE(board) && !id.contains("stdr9m"); case HasPPMStart: - return (IS_ARM(board) ? true : false); + return true; case HastxCurrentCalibration: return (IS_SKY9X(board) ? true : false); case HasVolume: - return (IS_ARM(board) ? true : false); + return true; case HasBrightness: - return (IS_ARM(board) ? true : false); + return true; case PerModelTimers: return 1; case SlowScale: - return (IS_ARM(board) ? 10 : 2); + return 10; case SlowRange: - return (IS_ARM(board) ? 250 : 15); + return 250; case CSFunc: return 18; case HasSDLogs: - return ((IS_2560(board) || IS_ARM(board)) ? true : false); + return true; case LcdWidth: if (IS_FAMILY_HORUS_OR_T16(board)) return 480; @@ -694,19 +636,19 @@ int OpenTxFirmware::getCapability(::Capability capability) case GetThrSwitch: return (IS_HORUS_OR_TARANIS(board) ? SWITCH_SF1 : SWITCH_THR); case HasDisplayText: - return IS_ARM(board) ? 1 : 0; + return 1; case HasTopLcd: return IS_TARANIS_X9E(board) ? 1 : 0; case GlobalFunctions: - return IS_ARM(board) ? 64 : 0; + return 64; case VirtualInputs: - return IS_ARM(board) ? 32 : 0; + return 32; case InputsLength: return HAS_LARGE_LCD(board) ? 4 : 3; case TrainerInputs: - return IS_ARM(board) ? 16 : 8; + return 16; case RtcTime: - return IS_ARM(board) || IS_2560(board) ? 1 : 0; + return 1; case LuaScripts: return IS_HORUS_OR_TARANIS(board) && id.contains("lua") ? 7 : 0; case LuaInputsPerScript: @@ -715,17 +657,13 @@ int OpenTxFirmware::getCapability(::Capability capability) return IS_HORUS_OR_TARANIS(board) ? 6 : 0; case LimitsPer1000: case EnhancedCurves: - return IS_ARM(board); + return 1; case HasFasOffset: - return (IS_STOCK(board) ? false : true); + return true; case HasMahPersistent: - return (IS_ARM(board) ? true : false); + return true; case SimulatorVariant: - if (board == BOARD_9X_M64) - return SIMU_STOCK_VARIANTS; - else if (board == BOARD_9X_M128) - return SIMU_M128_VARIANTS; - else if (IS_TARANIS_X9E(board)) + if (IS_TARANIS_X9E(board)) return TARANIS_X9E_VARIANT; else if (IS_TARANIS_X9LITES(board)) return TARANIS_X9LITES_VARIANT; @@ -744,7 +682,7 @@ int OpenTxFirmware::getCapability(::Capability capability) case MavlinkTelemetry: return id.contains("mavlink") ? 1 : 0; case SportTelemetry: - return IS_ARM(board) ? 1 : 0; + return 1; case HasInputDiff: case HasMixerExpo: return (IS_HORUS_OR_TARANIS(board) ? true : false); @@ -777,10 +715,8 @@ QTime OpenTxFirmware::getMaxTimerStart() { if (IS_HORUS_OR_TARANIS(board)) return QTime(23, 59, 59); - else if (IS_ARM(board)) - return QTime(8, 59, 59); else - return QTime(0, 59, 59); + return QTime(8, 59, 59); } bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) @@ -947,14 +883,9 @@ EepromLoadErrors OpenTxEepromInterface::checkVersion(unsigned int version) // M128 revert because too much RAM used! case 216: // A lot of things (first github release) - if (IS_2560(board) || IS_M128(board)) { - return OLD_VERSION; - } case 217: // 3 logical switches removed on M128 / gruvin9x boards - if (IS_ARM(board)) { return OLD_VERSION; - } case 218: // ACCESS // switches add for X7/X10/X12S @@ -974,21 +905,7 @@ EepromLoadErrors OpenTxEepromInterface::checkVersion(unsigned int version) bool OpenTxEepromInterface::checkVariant(unsigned int version, unsigned int variant) { bool variantError = false; - if (board == BOARD_9X_M128 && !(variant & M128_VARIANT)) { - if (version == 212) { - uint8_t tmp[1000]; - for (int i = 1; i < 31; i++) { - efile->openRd(i); - int sz = efile->readRlc2(tmp, sizeof(tmp)); - if (sz == 849) { - qWarning() << " warning: M128 variant not set (but model size seems ok)"; - return true; - } - } - } - variantError = true; - } - else if (IS_TARANIS_X9E(board)) { + if (IS_TARANIS_X9E(board)) { if (variant != TARANIS_X9E_VARIANT) { variantError = true; } @@ -1080,9 +997,6 @@ unsigned long OpenTxEepromInterface::loadBackup(RadioData &radioData, const uint case 0x32: backupBoard = BOARD_SKY9X; break; - case 0x31: - backupBoard = BOARD_GRUVIN9X; - break; default: dbg << " unknown board"; errors.set(UNKNOWN_BOARD); @@ -1379,14 +1293,6 @@ void registerOpenTxFirmwares() addOpenTxArm9xOptions(firmware); registerOpenTxFirmware(firmware); - // These are kept only for import purposes, marked as deprecated to hide from UI. - registerOpenTxFirmware(new OpenTxFirmware("opentx-9xr", Firmware::tr("Turnigy 9XR"), BOARD_9X_M64), true); - registerOpenTxFirmware(new OpenTxFirmware("opentx-9xr128", Firmware::tr("Turnigy 9XR with m128 chip"), BOARD_9X_M128), true); - registerOpenTxFirmware(new OpenTxFirmware("opentx-9x", Firmware::tr("9X with stock board"), BOARD_9X_M64), true); - registerOpenTxFirmware(new OpenTxFirmware("opentx-9x128", Firmware::tr("9X with stock board and m128 chip"), BOARD_9X_M128), true); - registerOpenTxFirmware(new OpenTxFirmware("opentx-gruvin9x", Firmware::tr("9X with Gruvin9x board"), BOARD_GRUVIN9X), true); - registerOpenTxFirmware(new OpenTxFirmware("opentx-mega2560", Firmware::tr("DIY MEGA2560 radio"), BOARD_MEGA2560), true); - Firmware::setDefaultVariant(Firmware::getFirmwareForId("opentx-x9d+")); Firmware::setCurrentVariant(Firmware::getDefaultVariant()); } diff --git a/companion/src/firmwares/rawsource.cpp b/companion/src/firmwares/rawsource.cpp index 401eb9781..1cb5bcfbb 100644 --- a/companion/src/firmwares/rawsource.cpp +++ b/companion/src/firmwares/rawsource.cpp @@ -32,10 +32,7 @@ float RawSourceRange::getValue(int value) { - if (IS_ARM(getCurrentBoard())) - return float(value) * step; - else - return min + float(value) * step; + return float(value) * step; } @@ -48,11 +45,10 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting RawSourceRange result; Firmware * firmware = Firmware::getCurrentVariant(); - Board::Type board = firmware->getBoard(); switch (type) { case SOURCE_TYPE_TELEMETRY: - if (IS_ARM(board)) { + { div_t qr = div(index, 3); const SensorData & sensor = model->sensorData[qr.quot]; if (sensor.prec == 2) @@ -65,192 +61,8 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting result.max = +30000 * result.step; result.decimals = sensor.prec; result.unit = sensor.unitString(); + break; } - else { - result.offset = -DBL_MAX; - - switch (index) { - case TELEMETRY_SOURCE_TX_BATT: - result.step = 0.1; - result.decimals = 1; - result.max = 25.5; - result.unit = tr("V"); - break; - case TELEMETRY_SOURCE_TX_TIME: - result.step = 60; - result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution - result.unit = tr("s"); - break; - case TELEMETRY_SOURCE_TIMER1: - case TELEMETRY_SOURCE_TIMER2: - case TELEMETRY_SOURCE_TIMER3: - result.step = 5; - result.max = 255 * result.step; - result.unit = tr("s"); - break; - case TELEMETRY_SOURCE_RSSI_TX: - case TELEMETRY_SOURCE_RSSI_RX: - result.max = 100; - result.offset = 128; - break; - case TELEMETRY_SOURCE_A1_MIN: - case TELEMETRY_SOURCE_A2_MIN: - case TELEMETRY_SOURCE_A3_MIN: - case TELEMETRY_SOURCE_A4_MIN: - if (model) result = model->frsky.channels[index-TELEMETRY_SOURCE_A1_MIN].getRange(); - break; - case TELEMETRY_SOURCE_A1: - case TELEMETRY_SOURCE_A2: - case TELEMETRY_SOURCE_A3: - case TELEMETRY_SOURCE_A4: - if (model) result = model->frsky.channels[index-TELEMETRY_SOURCE_A1].getRange(); - break; - case TELEMETRY_SOURCE_ALT: - case TELEMETRY_SOURCE_ALT_MIN: - case TELEMETRY_SOURCE_ALT_MAX: - case TELEMETRY_SOURCE_GPS_ALT: - result.step = 8; - result.min = -500; - result.max = 1540; - if (firmware->getCapability(Imperial) || settings.imperial) { - result.step = (result.step * 105) / 32; - result.min = (result.min * 105) / 32; - result.max = (result.max * 105) / 32; - result.unit = tr("ft"); - } - else { - result.unit = tr("m"); - } - break; - case TELEMETRY_SOURCE_T1: - case TELEMETRY_SOURCE_T1_MAX: - case TELEMETRY_SOURCE_T2: - case TELEMETRY_SOURCE_T2_MAX: - result.min = -30; - result.max = 225; - result.unit = tr("°C"); - break; - case TELEMETRY_SOURCE_HDG: - result.step = 2; - result.max = 360; - result.offset = 256; - result.unit = tr("°"); - break; - case TELEMETRY_SOURCE_RPM: - case TELEMETRY_SOURCE_RPM_MAX: - result.step = 50; - result.max = 12750; - break; - case TELEMETRY_SOURCE_FUEL: - result.max = 100; - result.unit = tr("%"); - break; - case TELEMETRY_SOURCE_ASPEED: - case TELEMETRY_SOURCE_ASPEED_MAX: - result.decimals = 1; - result.step = 2.0; - result.max = (2*255); - if (firmware->getCapability(Imperial) || settings.imperial) { - result.step *= 1.150779; - result.max *= 1.150779; - result.unit = tr("mph"); - } - else { - result.step *= 1.852; - result.max *= 1.852; - result.unit = tr("km/h"); - } - break; - case TELEMETRY_SOURCE_SPEED: - case TELEMETRY_SOURCE_SPEED_MAX: - result.step = 2; - result.max = (2*255); - if (firmware->getCapability(Imperial) || settings.imperial) { - result.step *= 1.150779; - result.max *= 1.150779; - result.unit = tr("mph"); - } - else { - result.step *= 1.852; - result.max *= 1.852; - result.unit = tr("km/h"); - } - break; - case TELEMETRY_SOURCE_VERTICAL_SPEED: - result.step = 0.1; - result.min = -12.5; - result.max = 13.0; - result.decimals = 1; - result.unit = tr("m/s"); - break; - case TELEMETRY_SOURCE_DTE: - result.max = 30000; - break; - case TELEMETRY_SOURCE_DIST: - case TELEMETRY_SOURCE_DIST_MAX: - result.step = 8; - result.max = 2040; - result.unit = tr("m"); - break; - case TELEMETRY_SOURCE_CELL: - case TELEMETRY_SOURCE_CELL_MIN: - result.step = 0.02; - result.max = 5.1; - result.decimals = 2; - result.unit = tr("V"); - break; - case TELEMETRY_SOURCE_CELLS_SUM: - case TELEMETRY_SOURCE_CELLS_MIN: - case TELEMETRY_SOURCE_VFAS: - case TELEMETRY_SOURCE_VFAS_MIN: - result.step = 0.1; - result.max = 25.5; - result.decimals = 1; - result.unit = tr("V"); - break; - case TELEMETRY_SOURCE_CURRENT: - case TELEMETRY_SOURCE_CURRENT_MAX: - result.step = 0.5; - result.max = 127.5; - result.decimals = 1; - result.unit = tr("A"); - break; - case TELEMETRY_SOURCE_CONSUMPTION: - result.step = 100; - result.max = 25500; - result.unit = tr("mAh"); - break; - case TELEMETRY_SOURCE_POWER: - case TELEMETRY_SOURCE_POWER_MAX: - result.step = 5; - result.max = 1275; - result.unit = tr("W"); - break; - case TELEMETRY_SOURCE_ACCX: - case TELEMETRY_SOURCE_ACCY: - case TELEMETRY_SOURCE_ACCZ: - result.step = 0.01; - result.decimals = 2; - result.max = 2.55; - result.min = 0; - result.unit = tr("g"); - break; - default: - result.max = 125; - break; - } - - if (result.offset == -DBL_MAX) { - result.offset = result.max - (127*result.step); - } - - if (flags & (RANGE_DELTA_FUNCTION | RANGE_ABS_FUNCTION)) { - result.offset = 0; - result.min = result.step * -127; - result.max = result.step * 127; - } - } - break; case SOURCE_TYPE_LUA_OUTPUT: result.max = 30000; @@ -414,7 +226,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return CHECK_IN_ARRAY(special, index); case SOURCE_TYPE_TELEMETRY: - if (IS_ARM(board)) { + { div_t qr = div(index, 3); if (model) result = model->sensorData[qr.quot].nameToString(qr.quot); @@ -424,9 +236,6 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con result += (qr.rem == 1 ? "-" : "+"); return result; } - else { - return CHECK_IN_ARRAY(telemetry, index); - } case SOURCE_TYPE_GVAR: if (model) @@ -486,13 +295,7 @@ bool RawSource::isSlider(int * sliderIndex, Board::Type board) const bool RawSource::isTimeBased(Board::Type board) const { - if (board == Board::BOARD_UNKNOWN) - board = getCurrentBoard(); - - if (IS_ARM(board)) - return (type == SOURCE_TYPE_SPECIAL && index > 0); - else - return (type==SOURCE_TYPE_TELEMETRY && (index==TELEMETRY_SOURCE_TX_TIME || index==TELEMETRY_SOURCE_TIMER1 || index==TELEMETRY_SOURCE_TIMER2 || index==TELEMETRY_SOURCE_TIMER3)); + return (type == SOURCE_TYPE_SPECIAL && index > 0); } bool RawSource::isAvailable(const ModelData * const model, const GeneralSettings * const gs, Board::Type board) const @@ -516,20 +319,9 @@ bool RawSource::isAvailable(const ModelData * const model, const GeneralSettings return false; if (type == SOURCE_TYPE_TELEMETRY) { - if (IS_ARM(board) && !model->sensorData[div(index, 3).quot].isAvailable()) { + if (!model->sensorData[div(index, 3).quot].isAvailable()) { return false; } - else if (!IS_ARM(board)) { - Firmware * fw = getCurrentFirmware(); - if (type == (int)TELEMETRY_SOURCE_TX_TIME && !fw->getCapability(RtcTime)) - return false; - - if (type == (int)TELEMETRY_SOURCE_RAS && !fw->getCapability(SportTelemetry)) - return false; - - if (type == (int)TELEMETRY_SOURCE_TIMER3 && fw->getCapability(Timers) < 3) - return false; - } } } diff --git a/companion/src/firmwares/rawsource.h b/companion/src/firmwares/rawsource.h index f59ab70f5..b3220dbd3 100644 --- a/companion/src/firmwares/rawsource.h +++ b/companion/src/firmwares/rawsource.h @@ -159,10 +159,6 @@ enum TelemetrySource { TELEMETRY_SOURCE_RESERVE = -1 }; -#define TM_HASTELEMETRY 0x01 -#define TM_HASOFFSET 0x02 -#define TM_HASWSHH 0x04 - enum RawSourceType { SOURCE_TYPE_NONE, SOURCE_TYPE_VIRTUAL_INPUT, diff --git a/companion/src/firmwares/telem_data.cpp b/companion/src/firmwares/telem_data.cpp index af31f3b38..99743c7e2 100644 --- a/companion/src/firmwares/telem_data.cpp +++ b/companion/src/firmwares/telem_data.cpp @@ -48,9 +48,6 @@ RawSourceRange FrSkyChannelData::getRange() const void FrSkyScreenData::clear() { memset(reinterpret_cast(this), 0, sizeof(FrSkyScreenData)); - if (!IS_ARM(getCurrentBoard())) { - type = TELEMETRY_SCREEN_NUMBERS; - } } void FrSkyData::clear() diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index 7b424ec02..34d090e65 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -56,9 +56,7 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); - if (IS_ARM(firmware->getBoard())) { - addTab(new CustomFunctionsPanel(this, NULL, generalSettings, firmware), tr("Global Functions")); - } + addTab(new CustomFunctionsPanel(this, NULL, generalSettings, firmware), tr("Global Functions")); addTab(new TrainerPanel(this, generalSettings, firmware), tr("Trainer")); addTab(new HardwarePanel(this, generalSettings, firmware), tr("Hardware")); addTab(new CalibrationPanel(this, generalSettings, firmware), tr("Calibration")); diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 1f74e2734..773e66734 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -234,12 +234,7 @@ ui(new Ui::GeneralSetup) ui->memwarnChkB->setChecked(!generalSettings.disableMemoryWarning); // Default is zero=checked ui->alarmwarnChkB->setChecked(!generalSettings.disableAlarmWarning); // Default is zero=checked - if (IS_ARM(firmware->getBoard())) { - ui->rssiPowerOffWarnChkB->setChecked(!generalSettings.disableRssiPoweroffAlarm); // Default is zero=checked - } - else { - ui->rssiPowerOffWarnChkB->hide(); - } + ui->rssiPowerOffWarnChkB->setChecked(!generalSettings.disableRssiPoweroffAlarm); // Default is zero=checked if (IS_FAMILY_HORUS_OR_T16(firmware->getBoard())) { ui->splashScreenChkB->hide(); @@ -302,13 +297,7 @@ ui(new Ui::GeneralSetup) ui->backlightColor2_label->hide(); } - if (IS_ARM(firmware->getBoard())) { - ui->switchesDelay->setValue(10*(generalSettings.switchesDelay+15)); - } - else { - ui->switchesDelay->hide(); - ui->switchesDelayLabel->hide(); - } + ui->switchesDelay->setValue(10*(generalSettings.switchesDelay+15)); ui->blAlarm_ChkB->setChecked(generalSettings.flashBeep); if (!firmware->getCapability(HasBatMeterRange)) { diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index af1aa72f0..50574ecd5 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -26,7 +26,6 @@ #include "apppreferencesdialog.h" #include "fwpreferencesdialog.h" #include "firmwareinterface.h" -#include "fusesdialog.h" #include "downloaddialog.h" #include "printdialog.h" #include "version.h" @@ -939,10 +938,8 @@ void MainWindow::readEeprom() QString tempFile; if (IS_FAMILY_HORUS_OR_T16(board)) tempFile = generateProcessUniqueTempFileName("temp.otx"); - else if (IS_ARM(board)) - tempFile = generateProcessUniqueTempFileName("temp.bin"); else - tempFile = generateProcessUniqueTempFileName("temp.hex"); + tempFile = generateProcessUniqueTempFileName("temp.bin"); qDebug() << "MainWindow::readEeprom(): using temp file: " << tempFile; @@ -1029,14 +1026,6 @@ void MainWindow::burnConfig() void MainWindow::burnList() { burnConfigDialog bcd(this); - bcd.listAvrdudeProgrammers(); -} - -void MainWindow::burnFuses() -{ - FusesDialog *fd = new FusesDialog(this); - fd->exec(); - delete fd; } void MainWindow::compare() @@ -1232,8 +1221,6 @@ void MainWindow::retranslateUi(bool showMsg) trAct(changelogAct, tr("Release notes..."), tr("Show release notes")); trAct(compareAct, tr("Compare Models..."), tr("Compare models")); trAct(editSplashAct, tr("Edit Radio Splash Image..."), tr("Edit the splash image of your Radio")); - trAct(burnListAct, tr("List programmers..."), tr("List available programmers")); - trAct(burnFusesAct, tr("Fuses..."), tr("Show fuses dialog")); trAct(readFlashAct, tr("Read Firmware from Radio"), tr("Read firmware from Radio")); trAct(writeFlashAct, tr("Write Firmware to Radio"), tr("Write firmware to Radio")); trAct(sdsyncAct, tr("Synchronize SD"), tr("SD card synchronization")); @@ -1295,7 +1282,6 @@ void MainWindow::createActions() editSplashAct = addAct("paintbrush.png", SLOT(customizeSplash())); burnListAct = addAct("list.png", SLOT(burnList())); - burnFusesAct = addAct("fuses.png", SLOT(burnFuses())); readFlashAct = addAct("read_flash.png", SLOT(readFlash())); writeFlashAct = addAct("write_flash.png", SLOT(writeFlash())); writeEepromAct = addAct("write_eeprom.png", SLOT(writeEeprom())); @@ -1400,10 +1386,6 @@ void MainWindow::createMenus() burnMenu->addAction(readFlashAct); burnMenu->addSeparator(); burnMenu->addSeparator(); - if (!IS_ARM(getCurrentBoard())) { - burnMenu->addAction(burnFusesAct); - burnMenu->addAction(burnListAct); - } windowMenu = menuBar()->addMenu(""); windowMenu->addAction(actTabbedWindows); diff --git a/companion/src/mainwindow.h b/companion/src/mainwindow.h index 9369dacac..0be249f04 100644 --- a/companion/src/mainwindow.h +++ b/companion/src/mainwindow.h @@ -111,7 +111,6 @@ class MainWindow : public QMainWindow void readBackup(); void burnConfig(); void burnList(); - void burnFuses(); void contributors(); void sdsync(); void changelog(); @@ -210,7 +209,6 @@ class MainWindow : public QMainWindow QAction *readEepromAct; QAction *burnConfigAct; QAction *burnListAct; - QAction *burnFusesAct; QAction *writeFlashAct; QAction *readFlashAct; QAction *writeBUToRadioAct; diff --git a/companion/src/modeledit/CMakeLists.txt b/companion/src/modeledit/CMakeLists.txt index 300fb27cc..31e727081 100644 --- a/companion/src/modeledit/CMakeLists.txt +++ b/companion/src/modeledit/CMakeLists.txt @@ -42,7 +42,6 @@ set(modeledit_UIS setup_timer.ui setup_module.ui flightmode.ui - telemetry_analog.ui telemetry_customscreen.ui telemetry_sensor.ui ) diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index 450f785fd..0cde45109 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -45,7 +45,7 @@ LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row spinbox->setSuffix("%"); } - if (IS_ARM(board) || deflt == 0 /*it's the offset*/) { + if (deflt == 0 /*it's the offset*/) { spinbox->setDecimals(1); } else { diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 34f05a707..6b8456eb2 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -29,7 +29,7 @@ RepeatComboBox::RepeatComboBox(QWidget *parent, int & repeatParam): QComboBox(parent), repeatParam(repeatParam) { - unsigned int step = IS_ARM(getCurrentBoard()) ? 1 : 10; + unsigned int step = 1; int value = repeatParam/step; setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); @@ -58,7 +58,7 @@ void RepeatComboBox::onIndexChanged(int index) void RepeatComboBox::update() { - unsigned int step = IS_ARM(getCurrentBoard()) ? 1 : 10; + unsigned int step = 1; int value = repeatParam/step; if (step == 1) { value++; @@ -422,29 +422,20 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) if (cfn.adjustMode == FUNC_ADJUST_GVAR_CONSTANT || cfn.adjustMode == FUNC_ADJUST_GVAR_INCDEC) { if (modified) cfn.param = fswtchParam[i]->value() * model->gvarData[gvidx].multiplierSet(); - if (IS_ARM(getCurrentBoard())) { - fswtchParam[i]->setDecimals(model->gvarData[gvidx].prec); - fswtchParam[i]->setSingleStep(model->gvarData[gvidx].multiplierGet()); - fswtchParam[i]->setSuffix(model->gvarData[gvidx].unitToString()); - if (cfn.adjustMode==FUNC_ADJUST_GVAR_INCDEC) { - double rng = abs(model->gvarData[gvidx].getMax() - model->gvarData[gvidx].getMin()); - rng *= model->gvarData[gvidx].multiplierGet(); - fswtchParam[i]->setMinimum(-rng); - fswtchParam[i]->setMaximum(rng); - } - else { - fswtchParam[i]->setMinimum(model->gvarData[gvidx].getMinPrec()); - fswtchParam[i]->setMaximum(model->gvarData[gvidx].getMaxPrec()); - } - fswtchParam[i]->setValue(cfn.param * model->gvarData[gvidx].multiplierGet()); + fswtchParam[i]->setDecimals(model->gvarData[gvidx].prec); + fswtchParam[i]->setSingleStep(model->gvarData[gvidx].multiplierGet()); + fswtchParam[i]->setSuffix(model->gvarData[gvidx].unitToString()); + if (cfn.adjustMode==FUNC_ADJUST_GVAR_INCDEC) { + double rng = abs(model->gvarData[gvidx].getMax() - model->gvarData[gvidx].getMin()); + rng *= model->gvarData[gvidx].multiplierGet(); + fswtchParam[i]->setMinimum(-rng); + fswtchParam[i]->setMaximum(rng); } else { - fswtchParam[i]->setDecimals(0); - fswtchParam[i]->setSingleStep(1); - fswtchParam[i]->setMinimum(-125); - fswtchParam[i]->setMaximum(125); - fswtchParam[i]->setValue(cfn.param); + fswtchParam[i]->setMinimum(model->gvarData[gvidx].getMinPrec()); + fswtchParam[i]->setMaximum(model->gvarData[gvidx].getMaxPrec()); } + fswtchParam[i]->setValue(cfn.param * model->gvarData[gvidx].multiplierGet()); widgetsMask |= CUSTOM_FUNCTION_NUMERIC_PARAM; } else { @@ -679,7 +670,6 @@ void CustomFunctionsPanel::populateFuncCB(QComboBox *b, unsigned int value) ((i==FuncPlayHaptic) && !firmware->getCapability(Haptic)) || ((i==FuncPlayBoth) && !firmware->getCapability(HasBeeper)) || ((i==FuncLogs) && !firmware->getCapability(HasSDLogs)) || - ((i==FuncSetTimer1 || i==FuncSetTimer2) && !IS_ARM(firmware->getBoard())) || ((i==FuncSetTimer3) && firmware->getCapability(Timers) < 3) || ((i==FuncScreenshot) && !IS_HORUS_OR_TARANIS(firmware->getBoard())) || ((i>=FuncRangeCheckInternalModule && i<=FuncBindExternalModule) && (!model || !firmware->getCapability(DangerousFunctions))) || diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 0745f5c84..95aaeca66 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -46,18 +46,8 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G setWindowTitle(tr("Edit %1").arg(RawSource(srcType, ed->chn).toString(&model, &generalSettings))); QRegExp rx(CHAR_FOR_NAMES_REGEX); - if (IS_ARM(getCurrentBoard())) { - gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); - gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - } - else { - gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, 0, 100); - gvOffsetGroup = NULL; - ui->offsetLabel->hide(); - ui->offsetGV->hide(); - ui->offsetSB->hide(); - ui->offsetCB->hide(); - } + gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); + gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); curveGroup = new CurveGroup(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueCB, ui->curveValueSB, ed->curve, model, firmware->getCapability(HasInputDiff) ? 0 : (HIDE_DIFF | HIDE_NEGATIVE_CURVES)); diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index 13e20b8f6..63a2ae22f 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -479,10 +479,6 @@ void LogicalSwitchesPanel::populateFunctionCB(QComboBox *b) int func = order[i]; if (func == LS_FN_NEQUAL || func == LS_FN_EGREATER || func == LS_FN_ELESS) continue; - if (!IS_ARM(firmware->getBoard())) { - if (func == LS_FN_VEQUAL || func == LS_FN_EDGE) - continue; - } b->addItem(LogicalSwitchData(func).funcToString(), func); } b->setMaxVisibleItems(10); @@ -490,27 +486,7 @@ void LogicalSwitchesPanel::populateFunctionCB(QComboBox *b) void LogicalSwitchesPanel::populateAndSwitchCB(QComboBox *b) { - if (IS_ARM(firmware->getBoard())) { - b->setModel(rawSwitchItemModel); - } - else { - RawSwitch item; - - b->clear(); - - item = RawSwitch(SWITCH_TYPE_NONE); - b->addItem(item.toString(), item.toValue()); - - for (int i=1; i <= Boards::getCapability(firmware->getBoard(), Board::SwitchPositions); i++) { - item = RawSwitch(SWITCH_TYPE_SWITCH, i); - b->addItem(item.toString(), item.toValue()); - } - - for (int i=1; i<=6; i++) { - item = RawSwitch(SWITCH_TYPE_VIRTUAL, i); - b->addItem(item.toString(), item.toValue()); - } - } + b->setModel(rawSwitchItemModel); b->setVisible(true); } diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 59fb408c4..af3166c7f 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -65,7 +65,7 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw s1.report("LS"); addTab(new CustomFunctionsPanel(this, &radioData.models[modelId], radioData.generalSettings, firmware), tr("Special Functions")); s1.report("CF"); - if (firmware->getCapability(Telemetry) & TM_HASTELEMETRY) + if (firmware->getCapability(Telemetry)) addTab(new TelemetryPanel(this, radioData.models[modelId], radioData.generalSettings, firmware), tr("Telemetry")); onTabIndexChanged(ui->tabWidget->currentIndex()); // make sure to trigger update on default tab panel diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index b1fb9af72..3fc74a3e7 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -36,7 +36,6 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge timer(timer), ui(new Ui::Timer) { - Board::Type board = firmware->getBoard(); ui->setupUi(this); @@ -65,10 +64,8 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui->countdownBeep->setField(timer.countdownBeep, this); ui->countdownBeep->addItem(tr("Silent"), TimerData::COUNTDOWN_SILENT); ui->countdownBeep->addItem(tr("Beeps"), TimerData::COUNTDOWN_BEEPS); - if (IS_ARM(board) || IS_2560(board)) { - ui->countdownBeep->addItem(tr("Voice"), TimerData::COUNTDOWN_VOICE); - ui->countdownBeep->addItem(tr("Haptic"), TimerData::COUNTDOWN_HAPTIC); - } + ui->countdownBeep->addItem(tr("Voice"), TimerData::COUNTDOWN_VOICE); + ui->countdownBeep->addItem(tr("Haptic"), TimerData::COUNTDOWN_HAPTIC); ui->value->setMaximumTime(firmware->getMaxTimerStart()); @@ -1205,13 +1202,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge ui->potWarningMode->hide(); } - if (IS_ARM(board)) { - ui->trimsDisplay->setField(model.trimsDisplay, this); - } - else { - ui->labelTrimsDisplay->hide(); - ui->trimsDisplay->hide(); - } + ui->trimsDisplay->setField(model.trimsDisplay, this); for (int i = firmware->getCapability(NumFirstUsableModule); i < firmware->getCapability(NumModules); i++) { modules[i] = new ModulePanel(this, model, model.moduleData[i], generalSettings, firmware, i); diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 83565c03e..99915eae5 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -20,7 +20,6 @@ #include "telemetry.h" #include "ui_telemetry.h" -#include "ui_telemetry_analog.h" #include "ui_telemetry_customscreen.h" #include "ui_telemetry_sensor.h" #include "helpers.h" @@ -29,291 +28,6 @@ #include -TelemetryAnalog::TelemetryAnalog(QWidget *parent, FrSkyChannelData & analog, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): - ModelPanel(parent, model, generalSettings, firmware), - ui(new Ui::TelemetryAnalog), - analog(analog), - lock(false) -{ - ui->setupUi(this); - - float ratio = analog.getRatio(); - - if (analog.type==0 || analog.type==1 || analog.type==2) { - ui->RatioSB->setDecimals(1); - ui->RatioSB->setMaximum(25.5*firmware->getCapability(TelemetryMaxMultiplier)); - } - else { - ui->RatioSB->setDecimals(0); - ui->RatioSB->setMaximum(255*firmware->getCapability(TelemetryMaxMultiplier)); - } - ui->RatioSB->setValue(ratio); - - update(); - - ui->UnitCB->setCurrentIndex(analog.type); - ui->alarm1LevelCB->setCurrentIndex(analog.alarms[0].level); - ui->alarm1GreaterCB->setCurrentIndex(analog.alarms[0].greater); - ui->alarm2LevelCB->setCurrentIndex(analog.alarms[1].level); - ui->alarm2GreaterCB->setCurrentIndex(analog.alarms[1].greater); - - if (!(firmware->getCapability(Telemetry) & TM_HASOFFSET)) { - ui->CalibSB->hide(); - ui->CalibLabel->hide(); - } - else { - ui->label_Max->setText(tr("Range")); - } - - disableMouseScrolling(); -} - -void TelemetryAnalog::update() -{ - float ratio = analog.getRatio(); - float step = ratio / 255; - float mini = (ratio * analog.offset) / 255; - float maxi = mini + ratio; - - ui->alarm1ValueSB->setDecimals(2); - ui->alarm1ValueSB->setSingleStep(step); - ui->alarm1ValueSB->setMinimum(mini); - ui->alarm1ValueSB->setMaximum(maxi); - ui->alarm1ValueSB->setValue(mini + step*analog.alarms[0].value); - - ui->alarm2ValueSB->setDecimals(2); - ui->alarm2ValueSB->setSingleStep(step); - ui->alarm2ValueSB->setMinimum(mini); - ui->alarm2ValueSB->setMaximum(maxi); - ui->alarm2ValueSB->setValue(mini + step*analog.alarms[1].value); - - ui->CalibSB->setDecimals(2); - ui->CalibSB->setMaximum(step*127); - ui->CalibSB->setMinimum(-step*128); - ui->CalibSB->setSingleStep(step); - ui->CalibSB->setValue(mini); -} - -static int findmult(float value, float base) -{ - int vvalue = value*10; - int vbase = base*10; - vvalue--; - - int mult = 0; - for (int i=8; i>=0; i--) { - if (vvalue/vbase >= (1<RatioSB->setDecimals(1); - ui->RatioSB->setMaximum(25.5*firmware->getCapability(TelemetryMaxMultiplier)); - break; - default: - ui->RatioSB->setDecimals(0); - ui->RatioSB->setMaximum(255*firmware->getCapability(TelemetryMaxMultiplier)); - break; - } - ui->RatioSB->setValue(ratio); - update(); - emit modified(); -} - -void TelemetryAnalog::on_RatioSB_valueChanged() -{ - if (!lock) { - if (analog.type==0 || analog.type==1 || analog.type==2) { - analog.multiplier = findmult(ui->RatioSB->value(), 25.5); - float singlestep =(1<RatioSB->setSingleStep(singlestep); - ui->RatioSB->setValue(round(ui->RatioSB->value()/singlestep)*singlestep); - lock=false; - } - else { - analog.multiplier = findmult(ui->RatioSB->value(), 255); - float singlestep = (1<RatioSB->setSingleStep(singlestep); - ui->RatioSB->setValue(round(ui->RatioSB->value()/singlestep)*singlestep); - lock = false; - } - emit modified(); - } -} - -void TelemetryAnalog::on_RatioSB_editingFinished() -{ - if (!lock) { - float ratio, calib, alarm1value,alarm2value; - - if (analog.type==0 || analog.type==1 || analog.type==2) { - analog.multiplier = findmult(ui->RatioSB->value(), 25.5); - ui->CalibSB->setSingleStep((1<alarm1ValueSB->setSingleStep((1<alarm2ValueSB->setSingleStep((1<RatioSB->value()*10))/(1 <RatioSB->value(), 255); - ui->CalibSB->setSingleStep(1<alarm1ValueSB->setSingleStep(1<alarm2ValueSB->setSingleStep(1<RatioSB->value()/(1 << analog.multiplier)); - } - ui->CalibSB->setMaximum((ui->RatioSB->value()*127)/255); - ui->CalibSB->setMinimum((ui->RatioSB->value()*-128)/255); - ui->alarm1ValueSB->setMaximum(ui->RatioSB->value()); - ui->alarm2ValueSB->setMaximum(ui->RatioSB->value()); - repaint(); - ratio=analog.ratio * (1 << analog.multiplier); - calib=ui->CalibSB->value(); - alarm1value=ui->alarm1ValueSB->value(); - alarm2value=ui->alarm2ValueSB->value(); - if (analog.type==0) { - calib*=10; - alarm1value*=10; - alarm2value*=10; - } - if (calib>0) { - if (calib>((ratio*127)/255)) { - analog.offset=127; - } - else { - analog.offset=round(calib*255/ratio); - } - } - if (calib<0) { - if (calib<((ratio*-128)/255)) { - analog.offset=-128; - } - else { - analog.offset=round(calib*255/ratio); - } - } - analog.alarms[0].value=round((alarm1value*255-analog.offset*(analog.ratio<CalibSB->value()/ratio)); - calib=ratio*analog.offset/255.0; - alarm1value=ui->alarm1ValueSB->value(); - alarm2value=ui->alarm2ValueSB->value(); - if (alarm1value(ratio+calib)) { - alarm1value=ratio+calib; - } - if (alarm2value(ratio+calib)) { - alarm2value=ratio+calib; - } - analog.alarms[0].value=round(((alarm1value-calib)*255)/ratio); - analog.alarms[1].value=round(((alarm2value-calib)*255)/ratio); - } - else { - analog.offset=0; - analog.alarms[0].value=0; - analog.alarms[1].value=0; - } - update(); - emit modified(); -} - -void TelemetryAnalog::on_alarm1LevelCB_currentIndexChanged(int index) -{ - analog.alarms[0].level = index; - emit modified(); -} - - -void TelemetryAnalog::on_alarm1GreaterCB_currentIndexChanged(int index) -{ - analog.alarms[0].greater = index; - emit modified(); -} - -void TelemetryAnalog::on_alarm1ValueSB_editingFinished() -{ - float ratio = analog.getRatio(); - float calib, alarm1value; - - calib=analog.offset; - alarm1value=ui->alarm1ValueSB->value(); - - if (alarm1value<((calib*ratio)/255)) { - analog.alarms[0].value=0; - } - else if (alarm1value>(ratio+(calib*ratio)/255)) { - analog.alarms[0].value=255; - } - else { - analog.alarms[0].value = round((alarm1value-((calib*ratio)/255))/ratio*255); - } - update(); - emit modified(); -} - -void TelemetryAnalog::on_alarm2LevelCB_currentIndexChanged(int index) -{ - analog.alarms[1].level = index; - emit modified(); -} - -void TelemetryAnalog::on_alarm2GreaterCB_currentIndexChanged(int index) -{ - analog.alarms[1].greater = index; - emit modified(); -} - -void TelemetryAnalog::on_alarm2ValueSB_editingFinished() -{ - float calib, alarm2value; - float ratio = analog.getRatio(); - calib = analog.offset; - alarm2value = ui->alarm2ValueSB->value(); - if (alarm2value<((calib*ratio)/255)) { - analog.alarms[1].value=0; - } - else if (alarm2value>(ratio+(calib*ratio)/255)) { - analog.alarms[1].value=255; - } - else { - analog.alarms[1].value = round((alarm2value-((calib*ratio)/255))/ratio*255); - } - update(); - emit modified(); -} - -TelemetryAnalog::~TelemetryAnalog() -{ - delete ui; -} /******************************************************/ @@ -377,8 +91,7 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, disableMouseScrolling(); lock = true; - if (IS_ARM(firmware->getBoard())) - ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE); + ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE); ui->screenType->addItem(tr("Numbers"), TELEMETRY_SCREEN_NUMBERS); ui->screenType->addItem(tr("Bars"), TELEMETRY_SCREEN_BARS); if (IS_TARANIS(firmware->getBoard())) @@ -439,14 +152,8 @@ void TelemetryCustomScreen::updateBar(int line) if (source.type != SOURCE_TYPE_NONE) { RawSourceRange range = source.getRange(model, generalSettings); - if (!IS_ARM(getCurrentBoard())) { - int max = round((range.max - range.min) / range.step); - if (int(255-screen.body.bars[line].barMax) > max) { - screen.body.bars[line].barMax = 255 - max; - } - } float minVal = range.getValue(screen.body.bars[line].barMin); - float maxVal = IS_ARM(getCurrentBoard()) ? screen.body.bars[line].barMax : 255 - screen.body.bars[line].barMax; + float maxVal = screen.body.bars[line].barMax; maxVal = range.getValue(maxVal); if (source.isTimeBased()) { @@ -552,10 +259,7 @@ void TelemetryCustomScreen::barMinChanged(double value) { if (!lock) { int line = sender()->property("index").toInt(); - if (IS_ARM(getCurrentBoard())) - screen.body.bars[line].barMin = round(value / minSB[line]->singleStep()); - else - screen.body.bars[line].barMin = round((value-minSB[line]->minimum()) / minSB[line]->singleStep()); + screen.body.bars[line].barMin = round(value / minSB[line]->singleStep()); // TODO set min (maxSB) emit modified(); } @@ -565,10 +269,7 @@ void TelemetryCustomScreen::barMaxChanged(double value) { if (!lock) { int line = sender()->property("index").toInt(); - if (IS_ARM(getCurrentBoard())) - screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep()); - else - screen.body.bars[line].barMax = 255 - round((value-minSB[line]->minimum()) / maxSB[line]->singleStep()); + screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep()); // TODO set max (minSB) emit modified(); } @@ -583,10 +284,7 @@ void TelemetryCustomScreen::barTimeChanged() if (!te) return; - if (IS_ARM(getCurrentBoard())) - valRef = round(te->timeInSeconds() / te->singleStep()); - else - valRef = round((te->timeInSeconds() - te->minimumTime()) / te->singleStep()); + valRef = round(te->timeInSeconds() / te->singleStep()); emit modified(); } @@ -946,29 +644,17 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin model.frsky.usrProto = 1; } - if (IS_ARM(firmware->getBoard())) { - ui->varioSource->setField(model.frsky.varioSource, this); - ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); - ui->A1GB->hide(); - ui->A2GB->hide(); - for (unsigned i= 0; i < CPN_MAX_SENSORS; ++i) { - TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, model, generalSettings, firmware); - ui->sensorsLayout->addWidget(panel); - sensorPanels[i] = panel; - connect(panel, SIGNAL(dataModified()), this, SLOT(update())); - connect(panel, SIGNAL(modified()), this, SLOT(onModified())); - connect(panel, SIGNAL(clearAllSensors()), this, SLOT(on_clearAllSensors())); - } - } - else { - ui->sensorsGB->hide(); - ui->altimetryGB->hide(); - analogs[0] = new TelemetryAnalog(this, model.frsky.channels[0], model, generalSettings, firmware); - ui->A1Layout->addWidget(analogs[0]); - connect(analogs[0], SIGNAL(modified()), this, SLOT(onAnalogModified())); - analogs[1] = new TelemetryAnalog(this, model.frsky.channels[1], model, generalSettings, firmware); - ui->A2Layout->addWidget(analogs[1]); - connect(analogs[1], SIGNAL(modified()), this, SLOT(onModified())); + ui->varioSource->setField(model.frsky.varioSource, this); + ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); + ui->A1GB->hide(); + ui->A2GB->hide(); + for (unsigned i= 0; i < CPN_MAX_SENSORS; ++i) { + TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, model, generalSettings, firmware); + ui->sensorsLayout->addWidget(panel); + sensorPanels[i] = panel; + connect(panel, SIGNAL(dataModified()), this, SLOT(update())); + connect(panel, SIGNAL(modified()), this, SLOT(onModified())); + connect(panel, SIGNAL(clearAllSensors()), this, SLOT(on_clearAllSensors())); } if (IS_TARANIS_X9(firmware->getBoard())) { @@ -1017,10 +703,8 @@ void TelemetryPanel::update() populateTelemetrySourcesComboBox(ui->varioSource, model, false); } - if (IS_ARM(firmware->getBoard())) { - for (unsigned i=0; iupdate(); - } + for (unsigned i=0; iupdate(); } emit updated(); @@ -1032,52 +716,29 @@ void TelemetryPanel::setup() lock = true; - if (IS_ARM(firmware->getBoard())) { - ui->telemetryProtocol->addItem(tr("FrSky S.PORT"), 0); - ui->telemetryProtocol->addItem(tr("FrSky D"), 1); - if (IS_9XRPRO(firmware->getBoard()) || - (IS_TARANIS(firmware->getBoard()) && generalSettings.auxSerialMode == 2)) { - ui->telemetryProtocol->addItem(tr("FrSky D (cable)"), 2); - } - ui->telemetryProtocol->setCurrentIndex(model->telemetryProtocol); - ui->ignoreSensorIds->setField(model->frsky.ignoreSensorIds, this); - ui->disableTelemetryAlarms->setField(model->rssiAlarms.disabled); - } - else { - ui->telemetryProtocolLabel->hide(); - ui->telemetryProtocol->hide(); - ui->ignoreSensorIds->hide(); - ui->disableTelemetryAlarms->hide(); + ui->telemetryProtocol->addItem(tr("FrSky S.PORT"), 0); + ui->telemetryProtocol->addItem(tr("FrSky D"), 1); + if (IS_9XRPRO(firmware->getBoard()) || + (IS_TARANIS(firmware->getBoard()) && generalSettings.auxSerialMode == 2)) { + ui->telemetryProtocol->addItem(tr("FrSky D (cable)"), 2); } + ui->telemetryProtocol->setCurrentIndex(model->telemetryProtocol); + ui->ignoreSensorIds->setField(model->frsky.ignoreSensorIds, this); + ui->disableTelemetryAlarms->setField(model->rssiAlarms.disabled); ui->rssiAlarmWarningSB->setValue(model->rssiAlarms.warning); ui->rssiAlarmCriticalSB->setValue(model->rssiAlarms.critical); - if (!IS_ARM(firmware->getBoard())) { - ui->rssiSourceLabel->hide(); - ui->rssiSourceCB->hide(); - ui->rssiAlarmWarningCB->setCurrentIndex(model->rssiAlarms.level[0]); - ui->rssiAlarmCriticalCB->setCurrentIndex(model->rssiAlarms.level[1]); - } - else { - ui->rssiSourceLabel->show(); - ui->rssiSourceLabel->setText(tr("Source")); - ui->rssiSourceCB->setField(model->rssiSource, this); - ui->rssiSourceCB->show(); - populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); - ui->rssiAlarmWarningCB->hide(); - ui->rssiAlarmCriticalCB->hide(); - ui->rssiAlarmWarningLabel->setText(tr("Low Alarm")); - ui->rssiAlarmCriticalLabel->setText(tr("Critical Alarm")); - } + ui->rssiSourceLabel->show(); + ui->rssiSourceLabel->setText(tr("Source")); + ui->rssiSourceCB->setField(model->rssiSource, this); + ui->rssiSourceCB->show(); + populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); - /*if (IS_ARM(firmware->getBoard())) { - for (int i=0; i, model, generalSettings, firmware); - ui->sensorsLayout->addWidget(panel); - sensorPanels[i] = panel; - } - }*/ + ui->rssiAlarmWarningCB->hide(); + ui->rssiAlarmCriticalCB->hide(); + ui->rssiAlarmWarningLabel->setText(tr("Low Alarm")); + ui->rssiAlarmCriticalLabel->setText(tr("Critical Alarm")); int varioCap = firmware->getCapability(HasVario); if (!varioCap) { @@ -1109,7 +770,7 @@ void TelemetryPanel::setup() ui->altimetryGB->setVisible(firmware->getCapability(HasVario)), ui->frskyProtoCB->setDisabled(firmware->getCapability(NoTelemetryProtocol)); - if (firmware->getCapability(Telemetry) & TM_HASWSHH) { + if (firmware->getCapability(Telemetry)) { ui->frskyProtoCB->addItem(tr("Winged Shadow How High")); } else { @@ -1117,38 +778,6 @@ void TelemetryPanel::setup() } ui->variousGB->hide(); - if (!IS_ARM(firmware->getBoard())) { - if (!(firmware->getCapability(HasFasOffset)) && !(firmware_id.contains("fasoffset"))) { - ui->fasOffset_label->hide(); - ui->fasOffset_DSB->hide(); - } - else { - ui->fasOffset_DSB->setValue(model->frsky.fasOffset/10.0); - ui->variousGB->show(); - } - - if (!(firmware->getCapability(HasMahPersistent))) { - ui->mahCount_label->hide(); - ui->mahCount_SB->hide(); - ui->mahCount_ChkB->hide(); - } - else { - if (model->frsky.mAhPersistent) { - ui->mahCount_ChkB->setChecked(true); - ui->mahCount_SB->setValue(model->frsky.storedMah); - } - else { - ui->mahCount_SB->setDisabled(true); - } - ui->variousGB->show(); - } - - ui->frskyProtoCB->setCurrentIndex(model->frsky.usrProto); - ui->bladesCount->setValue(model->frsky.blades); - populateVarioSource(); - populateVoltsSource(); - populateCurrentSource(); - } lock = false; } @@ -1170,10 +799,8 @@ void TelemetryPanel::populateVoltsSource() cb->setField(model->frsky.voltsSource, this); cb->addItem(tr("A1"), TELEMETRY_VOLTS_SOURCE_A1); cb->addItem(tr("A2"), TELEMETRY_VOLTS_SOURCE_A2); - if (IS_ARM(firmware->getBoard())) { - cb->addItem(tr("A3"), TELEMETRY_VOLTS_SOURCE_A3); - cb->addItem(tr("A4"), TELEMETRY_VOLTS_SOURCE_A4); - } + cb->addItem(tr("A3"), TELEMETRY_VOLTS_SOURCE_A3); + cb->addItem(tr("A4"), TELEMETRY_VOLTS_SOURCE_A4); cb->addItem(tr("FAS"), TELEMETRY_VOLTS_SOURCE_FAS); cb->addItem(tr("Cells"), TELEMETRY_VOLTS_SOURCE_CELLS); } @@ -1185,10 +812,8 @@ void TelemetryPanel::populateCurrentSource() cb->addItem(tr("---"), TELEMETRY_CURRENT_SOURCE_NONE); cb->addItem(tr("A1"), TELEMETRY_CURRENT_SOURCE_A1); cb->addItem(tr("A2"), TELEMETRY_CURRENT_SOURCE_A2); - if (IS_ARM(firmware->getBoard())) { - cb->addItem(tr("A3"), TELEMETRY_CURRENT_SOURCE_A3); - cb->addItem(tr("A4"), TELEMETRY_CURRENT_SOURCE_A4); - } + cb->addItem(tr("A3"), TELEMETRY_CURRENT_SOURCE_A3); + cb->addItem(tr("A4"), TELEMETRY_CURRENT_SOURCE_A4); cb->addItem(tr("FAS"), TELEMETRY_CURRENT_SOURCE_FAS); } @@ -1223,18 +848,6 @@ void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index) } } -void TelemetryPanel::on_rssiAlarmWarningCB_currentIndexChanged(int index) -{ - model->rssiAlarms.level[0] = index; - emit modified(); -} - -void TelemetryPanel::on_rssiAlarmCriticalCB_currentIndexChanged(int index) -{ - model->rssiAlarms.level[1] = index; - emit modified(); -} - void TelemetryPanel::on_rssiAlarmWarningSB_editingFinished() { model->rssiAlarms.warning= ui->rssiAlarmWarningSB->value(); diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 39f3a42ad..28dc7643c 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -31,45 +31,11 @@ class RawSourceFilterItemModel; class TimerEdit; namespace Ui { - class TelemetryAnalog; class TelemetryCustomScreen; class TelemetrySensor; class Telemetry; } -class TelemetryAnalog: public ModelPanel -{ - Q_OBJECT - - friend class TelemetryPanel; - - public: - TelemetryAnalog(QWidget *parent, FrSkyChannelData & analog, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); - virtual ~TelemetryAnalog(); - - signals: - void modified(); - - private slots: - void on_UnitCB_currentIndexChanged(int index); - void on_RatioSB_editingFinished(); - void on_RatioSB_valueChanged(); - void on_CalibSB_editingFinished(); - void on_alarm1LevelCB_currentIndexChanged(int index); - void on_alarm1GreaterCB_currentIndexChanged(int index); - void on_alarm1ValueSB_editingFinished(); - void on_alarm2LevelCB_currentIndexChanged(int index); - void on_alarm2GreaterCB_currentIndexChanged(int index); - void on_alarm2ValueSB_editingFinished(); - - private: - Ui::TelemetryAnalog *ui; - FrSkyChannelData & analog; - bool lock; - - void update(); -}; - class TelemetryCustomScreen: public ModelPanel { Q_OBJECT @@ -155,8 +121,6 @@ class TelemetryPanel : public ModelPanel void onModified(); void on_frskyProtoCB_currentIndexChanged(int index); void on_bladesCount_editingFinished(); - void on_rssiAlarmWarningCB_currentIndexChanged(int index); - void on_rssiAlarmCriticalCB_currentIndexChanged(int index); void on_rssiAlarmWarningSB_editingFinished(); void on_rssiAlarmCriticalSB_editingFinished(); void on_varioLimitMin_DSB_editingFinished(); @@ -170,7 +134,6 @@ class TelemetryPanel : public ModelPanel private: Ui::Telemetry *ui; - TelemetryAnalog * analogs[4]; TelemetryCustomScreen * telemetryCustomScreens[4]; TelemetrySensorPanel * sensorPanels[CPN_MAX_SENSORS]; diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index 1d1881a75..bd56876c6 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -973,8 +973,7 @@ QString ModelPrinter::printSettingsTrim() { QStringList str; str << printLabelValue(tr("Step"), printTrimIncrementMode()); - if (IS_ARM(firmware->getBoard())) - str << printLabelValue(tr("Display"), printTrimsDisplayMode()); + str << printLabelValue(tr("Display"), printTrimsDisplayMode()); str << printLabelValue(tr("Extended"), printBoolean(model.extendedTrims, BOOLEAN_YESNO)); return str.join(" "); } diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index f9d039d69..9d57e1f62 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -292,7 +292,7 @@ QString MultiModelPrinter::print(QTextDocument * document) if (firmware->getCapability(GlobalFunctions)) str.append(printGlobalFunctions()); str.append(printSpecialFunctions()); - if (firmware->getCapability(Telemetry) & TM_HASTELEMETRY) { + if (firmware->getCapability(Telemetry)) { str.append(printTelemetry()); str.append(printSensors()); if (firmware->getCapability(TelemetryCustomScreens)) { @@ -751,32 +751,19 @@ QString MultiModelPrinter::printTelemetry() MultiColumns columns(modelPrinterMap.size()); columns.appendSectionTableStart(); - // Analogs on non ARM boards - if (!IS_ARM(firmware->getBoard())) { - columns.appendRowHeader(QStringList() << tr("Analogs") << tr("Unit") << tr("Scale") << tr("Offset")); - for (int i=0; i<2; i++) { - columns.appendRowStart(QString("A%1").arg(i+1), 20); - COMPARECELLWIDTH(FrSkyChannelData::unitString(model->frsky.channels[i].type), 20); - COMPARECELLWIDTH(QString::number((model->frsky.channels[i].ratio / (model->frsky.channels[i].type==0 ? 10.0 : 1)), 10, (model->frsky.channels[i].type==0 ? 1 : 0)), 20); - COMPARECELLWIDTH(QString::number((model->frsky.channels[i].offset*(model->frsky.channels[i].ratio / (model->frsky.channels[i].type==0 ?10.0 : 1)))/255, 10, (model->frsky.channels[i].type==0 ? 1 : 0)), 40); - columns.appendRowEnd(); - } - } - else { - // Protocol - columns.appendRowStart(tr("Protocol"), 20); - COMPARECELL(modelPrinter->printTelemetryProtocol(model->telemetryProtocol)); - columns.appendRowEnd(); - } + // Protocol + columns.appendRowStart(tr("Protocol"), 20); + COMPARECELL(modelPrinter->printTelemetryProtocol(model->telemetryProtocol)); + columns.appendRowEnd(); // RSSI alarms { columns.appendRowStart(tr("RSSI Alarms"), 20); columns.appendCellStart(80); - COMPARESTRING("", (IS_ARM(getCurrentBoard()) ? tr("Low") : FrSkyAlarmData::alarmLevelName(model->rssiAlarms.level[0])), false); + COMPARESTRING("", tr("Low"), false); columns.append(": "); COMPARESTRING("", QString("< %1").arg(QString::number(model->rssiAlarms.warning, 10)), true); - COMPARESTRING("", (IS_ARM(getCurrentBoard()) ? tr("Critical") : FrSkyAlarmData::alarmLevelName(model->rssiAlarms.level[1])), false); + COMPARESTRING("", tr("Critical"), false); columns.append(": "); COMPARESTRING("", QString("< %1").arg(QString::number(model->rssiAlarms.critical, 10)), true); COMPARESTRING(tr("Telemetry audio"), modelPrinter->printRssiAlarmsDisabled(model->rssiAlarms.disabled), false); @@ -818,39 +805,9 @@ QString MultiModelPrinter::printTelemetry() columns.appendRowEnd(); } - if (IS_ARM(firmware->getBoard())) { - ROWLABELCOMPARECELL("Multi sensors", 0, modelPrinter->printIgnoreSensorIds(!model->frsky.ignoreSensorIds), 0); - } + ROWLABELCOMPARECELL("Multi sensors", 0, modelPrinter->printIgnoreSensorIds(!model->frsky.ignoreSensorIds), 0); // Various - if (!IS_ARM(firmware->getBoard())) { - columns.appendRowHeader(QStringList() << tr("Various")); - if (!firmware->getCapability(NoTelemetryProtocol)) { - columns.appendRowStart("", 20); - LABELCOMPARECELL(tr("Serial protocol"), modelPrinter->printTelemetrySource(model->frsky.voltsSource), 80); - columns.appendRowEnd(); - } - columns.appendRowStart("", 20); - columns.appendCellStart(80); - QString firmware_id = g.profile[g.id()].fwType(); - if (firmware->getCapability(HasFasOffset) && firmware_id.contains("fasoffset")) { - COMPARESTRING(tr("FAS offset"), QString("%1 A").arg(model->frsky.fasOffset/10.0), true); - } - if (firmware->getCapability(HasMahPersistent)) { - COMPARESTRING(tr("mAh count"), QString("%1 mAh").arg(model->frsky.storedMah), true); - COMPARESTRING(tr("Persistent mAh"), modelPrinter->printMahPersistent(model->frsky.mAhPersistent), false); - } - columns.appendRowEnd(); - columns.appendRowStart("", 20); - columns.appendCellStart(80); - COMPARESTRING(tr("Volts source"), modelPrinter->printVoltsSource(model->frsky.voltsSource), true); - COMPARESTRING(tr("Current source"), modelPrinter->printCurrentSource(model->frsky.currentSource), false); - columns.appendCellEnd(); - columns.appendRowEnd(); - columns.appendRowStart("", 20); - LABELCOMPARECELL(tr("Blades"), model->frsky.blades, 80); - columns.appendRowEnd(); - } columns.appendTableEnd(); str.append(columns.print()); return str; diff --git a/companion/src/radiointerface.cpp b/companion/src/radiointerface.cpp index fbc03186f..0ad24fe6c 100644 --- a/companion/src/radiointerface.cpp +++ b/companion/src/radiointerface.cpp @@ -36,43 +36,8 @@ QString getRadioInterfaceCmd() if (IS_STM32(board)) { return bcd.getDFU(); } - else if (IS_SKY9X(board)) { + else return bcd.getSAMBA(); - } - else { - return bcd.getAVRDUDE(); - } -} - -QStringList getAvrdudeArgs(const QString & cmd, const QString & filename) -{ - QStringList args; - burnConfigDialog bcd; - QString programmer = bcd.getProgrammer(); - QString mcu = bcd.getMCU(); - Board::Type board = getCurrentBoard(); - - args << "-c" << programmer << "-p"; - if (IS_2560(board)) - args << "m2560"; - else if (board == Board::BOARD_9X_M128) - args << "m128"; - else - args << mcu; - - args << bcd.getAvrdudeArgs(); - - QString fullcmd = cmd + filename; - if (QFileInfo(filename).suffix().toUpper() == "HEX") - fullcmd += ":i"; - else if (QFileInfo(filename).suffix().toUpper()=="BIN") - fullcmd += ":r"; - else - fullcmd += ":a"; - - args << "-U" << fullcmd; - - return args; } QStringList getDfuArgs(const QString & cmd, const QString & filename) @@ -119,11 +84,8 @@ QStringList getReadEEpromCmd(const QString & filename) if (IS_STM32(eepromInterface->getBoard())) { // impossible } - else if (IS_SKY9X(eepromInterface->getBoard())) { - result = getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename + "\" 0x0 0x80000 0\n"); - } else { - result = getAvrdudeArgs("eeprom:r:", filename); + result = getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename + "\" 0x0 0x80000 0\n"); } return result; } @@ -135,11 +97,8 @@ QStringList getWriteEEpromCmd(const QString & filename) // impossible return QStringList(); } - else if (IS_SKY9X(board)) { - return getSambaArgs(QString("SERIALFLASH::Init 0\n") + "send_file {SerialFlash AT25} \"" + filename + "\" 0x0 0\n"); - } else { - return getAvrdudeArgs("eeprom:w:", filename); + return getSambaArgs(QString("SERIALFLASH::Init 0\n") + "send_file {SerialFlash AT25} \"" + filename + "\" 0x0 0\n"); } } @@ -152,11 +111,8 @@ QStringList getWriteFirmwareArgs(const QString & filename) else if (board == Board::BOARD_SKY9X) { return getSambaArgs(QString("send_file {Flash} \"") + filename + "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n"); } - else if (board == Board::BOARD_9XRPRO || board == Board::BOARD_AR9X) { - return getSambaArgs(QString("send_file {Flash} \"") + filename + "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n"); - } else { - return getAvrdudeArgs("flash:w:", filename); + return getSambaArgs(QString("send_file {Flash} \"") + filename + "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n"); } } @@ -169,87 +125,8 @@ QStringList getReadFirmwareArgs(const QString & filename) else if (board == Board::BOARD_SKY9X) { return getSambaArgs(QString("receive_file {Flash} \"") + filename + "\" 0x400000 0x40000 0\n"); } - else if (board == Board::BOARD_9XRPRO) { - return getSambaArgs(QString("receive_file {Flash} \"") + filename + "\" 0x400000 0x80000 0\n"); - } else { - return getAvrdudeArgs("flash:r:", filename); - } -} - -void readAvrdudeFuses(ProgressWidget * progress) -{ - burnConfigDialog bcd; - QStringList args; - args << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << bcd.getAvrdudeArgs() << "-U" << "lfuse:r:-:i" << "-U" << "hfuse:r:-:i" << "-U" << "efuse:r:-:i"; - FlashProcess flashProcess(bcd.getAVRDUDE(), args, progress); - flashProcess.run(); -} - -void resetAvrdudeFuses(bool eepromProtect, ProgressWidget * progress) -{ - //fuses - //avrdude -c usbasp -p m64 -U lfuse:w:<0x0E>:m - //avrdude -c usbasp -p m64 -U hfuse:w:<0x89>:m 0x81 for eeprom protection - //avrdude -c usbasp -p m64 -U efuse:w:<0xFF>:m - - burnConfigDialog bcd; - QMessageBox::StandardButton ret = QMessageBox::No; - ret = QMessageBox::warning(NULL, CPN_STR_APP_NAME, - QCoreApplication::translate("RadioInterface", - "WARNING!" \ - "
This will reset the fuses of %1 to the factory settings.
" - "Writing fuses can mess up your radio.
Do this only if you are sure they are wrong!
" - "Are you sure you want to continue?").arg(bcd.getMCU()), - QMessageBox::Yes | QMessageBox::No); - if (ret == QMessageBox::Yes) { - QStringList args = bcd.getAvrdudeArgs(); - QStringList str; - if (bcd.getMCU() == "m2560") { - args << "-B8"; - QString erStr = eepromProtect ? "hfuse:w:0x51:m" : "hfuse:w:0x59:m"; - str << "-U" << "lfuse:w:0xD7:m" << "-U" << erStr << "-U" << "efuse:w:0xFC:m"; - //use hfuse = 0x81 to prevent eeprom being erased with every flashing - } - else { - QString lfuses; - QString tempFile = generateProcessUniqueTempFileName("ftemp.bin"); - QStringList argread; - argread << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << args <<"-U" << "lfuse:r:"+tempFile+":r"; - FlashProcess flashProcess(bcd.getAVRDUDE(), argread, progress); - flashProcess.run(); - QFile file(tempFile); - if (file.exists() && file.size()==1) { - file.open(QIODevice::ReadOnly); - char bin_flash[1]; - file.read(bin_flash, 1); - if (bin_flash[0]==0x0E) { - lfuses = "lfuse:w:0x0E:m"; - } - else { - lfuses = "lfuse:w:0x3F:m"; - } - file.close(); - qunlink(tempFile); - } - else { - lfuses = "lfuse:w:0x3F:m"; - } - - QString erStr = eepromProtect ? "hfuse:w:0xC1:m" : "hfuse:w:0xC9:m"; - str << "-U" << lfuses << "-U" << erStr << "-U" << "efuse:w:0xFF:m"; - //use hfuse = 0x81 to prevent eeprom being erased with every flashing - } - - QStringList arguments; - if (bcd.getMCU() == "m2560") { - arguments << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << args << "-u" << str; - } - else { - arguments << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << args << "-B" << "100" << "-u" << str; - } - FlashProcess flashProcess(bcd.getAVRDUDE(), arguments, progress); - flashProcess.run(); + return getSambaArgs(QString("receive_file {Flash} \"") + filename + "\" 0x400000 0x80000 0\n"); } } @@ -264,13 +141,11 @@ bool readFirmware(const QString & filename, ProgressWidget * progress) return false; } - if (IS_ARM(getCurrentBoard())) { - QString path = findMassstoragePath("FIRMWARE.BIN"); - if (!path.isEmpty()) { - qDebug() << "readFirmware: reading" << path << "into" << filename; - CopyProcess copyProcess(path, filename, progress); - result = copyProcess.run(); - } + QString path = findMassstoragePath("FIRMWARE.BIN"); + if (!path.isEmpty()) { + qDebug() << "readFirmware: reading" << path << "into" << filename; + CopyProcess copyProcess(path, filename, progress); + result = copyProcess.run(); } if (!result) { @@ -288,13 +163,11 @@ bool readFirmware(const QString & filename, ProgressWidget * progress) bool writeFirmware(const QString & filename, ProgressWidget * progress) { - if (IS_ARM(getCurrentBoard())) { - QString path = findMassstoragePath("FIRMWARE.BIN"); - if (!path.isEmpty()) { - qDebug() << "writeFirmware: writing" << path << "from" << filename; - CopyProcess copyProcess(filename, path, progress); - return copyProcess.run(); - } + QString path = findMassstoragePath("FIRMWARE.BIN"); + if (!path.isEmpty()) { + qDebug() << "writeFirmware: writing" << path << "from" << filename; + CopyProcess copyProcess(filename, path, progress); + return copyProcess.run(); } qDebug() << "writeFirmware: writing" << filename << "with" << getRadioInterfaceCmd() << getWriteFirmwareArgs(filename); @@ -339,26 +212,25 @@ bool readEeprom(const QString & filename, ProgressWidget * progress) } } else { - if (IS_ARM(board)) { - QString path = findMassstoragePath("EEPROM.BIN"); - if (path.isEmpty()) { - // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :( - path = findMassstoragePath("TARANIS.BIN"); - } - if (path.isEmpty()) { - // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :( - path = findMassstoragePath("ERSKY9X.BIN"); - } - if (path.isEmpty()) { - RadioNotFoundDialog dialog(progress); - dialog.exec(); - return false; - } - CopyProcess copyProcess(path, filename, progress); - if (!copyProcess.run()) { - return false; - } + QString path = findMassstoragePath("EEPROM.BIN"); + if (path.isEmpty()) { + // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :( + path = findMassstoragePath("TARANIS.BIN"); } + if (path.isEmpty()) { + // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :( + path = findMassstoragePath("ERSKY9X.BIN"); + } + if (path.isEmpty()) { + RadioNotFoundDialog dialog(progress); + dialog.exec(); + return false; + } + CopyProcess copyProcess(path, filename, progress); + if (!copyProcess.run()) { + return false; + } + if (!IS_STM32(board)) { FlashProcess flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename), progress); @@ -375,20 +247,18 @@ bool writeEeprom(const QString & filename, ProgressWidget * progress) { Board::Type board = getCurrentBoard(); - if (IS_ARM(board)) { - QString path = findMassstoragePath("EEPROM.BIN"); - if (path.isEmpty()) { - // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :( - path = findMassstoragePath("TARANIS.BIN"); - } - if (path.isEmpty()) { - // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :( - path = findMassstoragePath("ERSKY9X.BIN"); - } - if (!path.isEmpty()) { - CopyProcess copyProcess(filename, path, progress); - return copyProcess.run(); - } + QString path = findMassstoragePath("EEPROM.BIN"); + if (path.isEmpty()) { + // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :( + path = findMassstoragePath("TARANIS.BIN"); + } + if (path.isEmpty()) { + // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :( + path = findMassstoragePath("ERSKY9X.BIN"); + } + if (!path.isEmpty()) { + CopyProcess copyProcess(filename, path, progress); + return copyProcess.run(); } if (!IS_TARANIS(board)) { @@ -396,10 +266,8 @@ bool writeEeprom(const QString & filename, ProgressWidget * progress) return flashProcess.run(); } - if (IS_ARM(board)) { - RadioNotFoundDialog dialog(progress); - dialog.exec(); - } + RadioNotFoundDialog dialog(progress); + dialog.exec(); return false; } diff --git a/companion/src/radiointerface.h b/companion/src/radiointerface.h index f896d96c5..37053fac5 100644 --- a/companion/src/radiointerface.h +++ b/companion/src/radiointerface.h @@ -30,13 +30,9 @@ QString getRadioInterfaceCmd(); QString findMassstoragePath(const QString &filename, bool onlyPath = false); -QStringList getAvrdudeArgs(const QString &cmd, const QString &filename); QStringList getSambaArgs(const QString &tcl); QStringList getDfuArgs(const QString &cmd, const QString &filename); -void readAvrdudeFuses(ProgressWidget *progress); -void resetAvrdudeFuses(bool eepromProtect, ProgressWidget *progress); - QStringList getReadEEpromCmd(const QString &filename); QStringList getWriteEEpromCmd(const QString &filename); QStringList getReadFirmwareArgs(const QString &filename); diff --git a/companion/src/storage/rlefile.cpp b/companion/src/storage/rlefile.cpp index fe297339f..4d38adac9 100644 --- a/companion/src/storage/rlefile.cpp +++ b/companion/src/storage/rlefile.cpp @@ -64,7 +64,7 @@ void RleFile::EeFsCreate(uint8_t *eeprom, int size, Board::Type board, unsigned } } } - else if (IS_TARANIS(board)) { + else { eeFsArm = (EeFsArm *)eeprom; eeFsVersion = 5; eeFsSize = 8+4*62; @@ -84,36 +84,6 @@ void RleFile::EeFsCreate(uint8_t *eeprom, int size, Board::Type board, unsigned eeFsArm->freeList = eeFsFirstBlock; // EeFsFlush(); } - else { - eeFs = (EeFs *)eeprom; - eeFsVersion = (IS_2560(board) || board==Board::BOARD_9X_M128) ? 5 : 4; - eeFsBlockSize = 16; - eeFsLinkSize = 1; - - if (eeFsVersion == 5) { - eeFsSize = 4+3*36; - eeFsFirstBlock = 1; - eeFsBlocksOffset = 112 - 16; - eeFsBlocksMax = 1 + (4096-112)/16; - } - else { - eeFsSize = 4+3*20; - eeFsFirstBlock = 4; - eeFsBlocksOffset = 0; - eeFsBlocksMax = 2048/16; - } - - memset(eeprom, 0, size); - eeFs->version = eeFsVersion; - eeFs->mySize = eeFsSize; - eeFs->freeList = 0; - eeFs->bs = eeFsBlockSize; - for (unsigned int i=eeFsFirstBlock; ifreeList = eeFsFirstBlock; - // EeFsFlush(); - } } PACK(struct EepromFileHeader @@ -146,7 +116,7 @@ bool RleFile::EeFsOpen(uint8_t *eeprom, int size, Board::Type board) searchFat(); return 1; } - else if (IS_TARANIS(board)) { + else { eeFsArm = (EeFsArm *)eeprom; eeFsVersion = eeFsArm->version; eeFsSize = 8+4*62; @@ -157,29 +127,6 @@ bool RleFile::EeFsOpen(uint8_t *eeprom, int size, Board::Type board) eeFsBlocksMax = 1 + (Boards::getEEpromSize(board)-eeFsSize) / eeFsBlockSize; return eeFsArm->mySize == eeFsSize; } - else { - eeFs = (EeFs *)eeprom; - eeFsVersion = eeFs->version; - eeFsBlockSize = 16; - eeFsLinkSize = 1; - if (eeFsVersion == 5) { - eeFsSize = 4+3*36; - eeFsFirstBlock = 1; - eeFsBlocksOffset = 112 - 16; - eeFsBlocksMax = 1 + (4096-112)/16; - } - else if (eeFsVersion == 4) { - eeFsSize = 4+3*20; - eeFsFirstBlock = 4; - eeFsBlocksOffset = 0; - eeFsBlocksMax = 2048/16; - } - else { - return 0; - } - - return eeFs->mySize == eeFsSize; - } } void RleFile::eeprom_read_block (void *pointer_ram, unsigned int pointer_eeprom, size_t size) @@ -206,25 +153,16 @@ void RleFile::EeFsWrite(unsigned int blk, unsigned int ofs, uint8_t val) unsigned int RleFile::EeFsGetLink(unsigned int blk) { - if (IS_ARM(board)) { - int16_t ret; - eeprom_read_block((uint8_t *)&ret, blk*eeFsBlockSize+eeFsBlocksOffset, eeFsLinkSize); - return ret; - } - else { - return EeFsRead(blk, 0); - } + int16_t ret; + eeprom_read_block((uint8_t *)&ret, blk*eeFsBlockSize+eeFsBlocksOffset, eeFsLinkSize); + return ret; } void RleFile::EeFsSetLink(unsigned int blk, unsigned int val) { - if (IS_ARM(board)) { - int16_t s_link = val; - eeprom_write_block((uint8_t *)&s_link, (blk*eeFsBlockSize)+eeFsBlocksOffset, eeFsLinkSize); - } - else { - EeFsWrite(blk, 0, val); - } + int16_t s_link = val; + eeprom_write_block((uint8_t *)&s_link, (blk*eeFsBlockSize)+eeFsBlocksOffset, eeFsLinkSize); + } uint8_t RleFile::EeFsGetDat(unsigned int blk, unsigned int ofs) @@ -244,10 +182,7 @@ unsigned int RleFile::EeFsGetFree() unsigned int ret = 0; unsigned int i; - if (IS_ARM(board)) - i = eeFsArm->freeList; - else - i = eeFs->freeList; + i = eeFsArm->freeList; while (i) { ret += eeFsBlockSize-eeFsLinkSize; @@ -266,14 +201,8 @@ void RleFile::EeFsFree(unsigned int blk) unsigned int i = blk; while (EeFsGetLink(i)) i = EeFsGetLink(i); - if (IS_ARM(board)) { - EeFsSetLink(i, eeFsArm->freeList); - eeFsArm->freeList = blk; //chain in front - } - else { - EeFsSetLink(i, eeFs->freeList); - eeFs->freeList = blk; //chain in front - } + EeFsSetLink(i, eeFsArm->freeList); + eeFsArm->freeList = blk; //chain in front } /* @@ -283,12 +212,9 @@ unsigned int RleFile::EeFsAlloc() { if (IS_FAMILY_HORUS_OR_T16(board)) return 0; - unsigned int ret = (IS_ARM(board) ? eeFsArm->freeList : eeFs->freeList); + unsigned int ret = eeFsArm->freeList; if (ret) { - if (IS_ARM(board)) - eeFsArm->freeList = EeFsGetLink(ret); - else - eeFs->freeList = EeFsGetLink(ret); + eeFsArm->freeList = EeFsGetLink(ret); EeFsSetLink(ret, 0); } return ret; @@ -299,7 +225,7 @@ unsigned int RleFile::size(unsigned int id) if (IS_FAMILY_HORUS_OR_T16(board)) return 0; else - return IS_ARM(board) ? eeFsArm->files[id].size : eeFs->files[id].size; + return eeFsArm->files[id].size; } unsigned int RleFile::openRd(unsigned int i_fileId) @@ -327,15 +253,12 @@ unsigned int RleFile::openRd(unsigned int i_fileId) else { m_fileId = i_fileId; m_pos = 0; - m_currBlk = (IS_ARM(board) ? eeFsArm->files[m_fileId].startBlk : eeFs->files[m_fileId].startBlk); + m_currBlk = eeFsArm->files[m_fileId].startBlk; m_ofs = 0; m_zeroes = 0; m_bRlc = 0; m_err = ERR_NONE; //error reasons - if (IS_ARM(board)) - return eeFsArm->files[m_fileId].typ; - else - return eeFs->files[m_fileId].typ; + return eeFsArm->files[m_fileId].typ; } } @@ -344,14 +267,14 @@ unsigned int RleFile::read(uint8_t *buf, unsigned int i_len) if (IS_FAMILY_HORUS_OR_T16(board)) return 0; - unsigned int len = IS_ARM(board) ? eeFsArm->files[m_fileId].size : eeFs->files[m_fileId].size; + unsigned int len = eeFsArm->files[m_fileId].size; len -= m_pos; if (i_len > len) i_len = len; len = i_len; while(len) { if (!m_currBlk) break; *buf++ = EeFsGetDat(m_currBlk, m_ofs++); - if (m_ofs >= (eeFsBlockSize-(IS_ARM(board)? 2 : 1))) { + if (m_ofs >= (eeFsBlockSize - 2)) { m_ofs = 0; m_currBlk = EeFsGetLink(m_currBlk); } @@ -492,10 +415,7 @@ unsigned int RleFile::write(const uint8_t *buf, unsigned int i_len) unsigned int len = i_len; if (!m_currBlk && m_pos==0) { - if (IS_ARM(board)) - eeFsArm->files[m_fileId].startBlk = m_currBlk = EeFsAlloc(); - else - eeFs->files[m_fileId].startBlk = m_currBlk = EeFsAlloc(); + eeFsArm->files[m_fileId].startBlk = m_currBlk = EeFsAlloc(); } while (len) { @@ -527,23 +447,15 @@ unsigned int RleFile::write(const uint8_t *buf, unsigned int i_len) void RleFile::create(unsigned int i_fileId, unsigned int typ) { openRd(i_fileId); //internal use - if (IS_ARM(board)) { - eeFsArm->files[i_fileId].typ = typ; - eeFsArm->files[i_fileId].size = 0; - } - else { - eeFs->files[i_fileId].typ = typ; - eeFs->files[i_fileId].size = 0; - } + eeFsArm->files[i_fileId].typ = typ; + eeFsArm->files[i_fileId].size = 0; + } void RleFile::closeTrunc() { unsigned int fri=0; - if (IS_ARM(board)) - eeFsArm->files[m_fileId].size = m_pos; - else - eeFs->files[m_fileId].size = m_pos; + eeFsArm->files[m_fileId].size = m_pos; if (m_currBlk && ( fri = EeFsGetLink(m_currBlk))) EeFsSetLink(m_currBlk, 0); if(fri) EeFsFree( fri ); //chain in } diff --git a/companion/src/wizarddata.cpp b/companion/src/wizarddata.cpp index f544ee4b0..25adaee24 100644 --- a/companion/src/wizarddata.cpp +++ b/companion/src/wizarddata.cpp @@ -61,27 +61,28 @@ void WizMix::maxMixSwitch(char *name, MixData &mix, int channel, int sw, int wei void WizMix::addMix(ModelData &model, Input input, int weight, int channel, int & mixIndex) { if (input != NO_INPUT) { - bool isHorusOrTaranis = IS_ARM(getCurrentBoard()); if (input >= RUDDER_INPUT && input <= AILERONS_INPUT) { MixData & mix = model.mixData[mixIndex++]; mix.destCh = channel+1; - if (isHorusOrTaranis){ + if (IS_SKY9X(getCurrentBoard())) { + mix.srcRaw = RawSource(SOURCE_TYPE_STICK, input-1); + } + else { int channel = settings.getDefaultChannel(input-1); mix.srcRaw = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, channel); } - else - mix.srcRaw = RawSource(SOURCE_TYPE_STICK, input-1); + mix.weight = weight; } else if (input==FLAPS_INPUT){ // There ought to be some kind of constants for switches somewhere... - maxMixSwitch((char *)"Flaps Up", model.mixData[mixIndex++], channel+1, isHorusOrTaranis ? SWITCH_SA0 :-SWITCH_ELE , weight); //Taranis-Horus SA-UP, 9X ELE-UP - maxMixSwitch((char *)"Flaps Dn", model.mixData[mixIndex++], channel+1, isHorusOrTaranis ? SWITCH_SA2 : SWITCH_ELE , -weight); //Taranis-Horus SA-DOWN, 9X ELE-DOWN + maxMixSwitch((char *)"Flaps Up", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? -SWITCH_ELE : SWITCH_SA0, weight); //Taranis-Horus SA-UP, 9X ELE-UP + maxMixSwitch((char *)"Flaps Dn", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? SWITCH_ELE : SWITCH_SA2, -weight); //Taranis-Horus SA-DOWN, 9X ELE-DOWN } else if (input==AIRBRAKES_INPUT){ - maxMixSwitch((char *)"AirbkOff", model.mixData[mixIndex++], channel+1, isHorusOrTaranis ? SWITCH_SE0 :-SWITCH_RUD , -weight); //Taranis-Horus SE-UP, 9X RUD-UP - maxMixSwitch((char *)"AirbkOn", model.mixData[mixIndex++], channel+1, isHorusOrTaranis ? SWITCH_SE2 : SWITCH_RUD , weight); //Tatanis-Horus SE-DOWN, 9X RUD-DOWN + maxMixSwitch((char *)"AirbkOff", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? -SWITCH_RUD : SWITCH_SE0, -weight); //Taranis-Horus SE-UP, 9X RUD-UP + maxMixSwitch((char *)"AirbkOn", model.mixData[mixIndex++], channel+1, IS_SKY9X(getCurrentBoard()) ? SWITCH_RUD : SWITCH_SE2, weight); //Tatanis-Horus SE-DOWN, 9X RUD-DOWN } } } @@ -120,7 +121,7 @@ WizMix::operator ModelData() mix.srcRaw = SOURCE_TYPE_MAX; mix.weight = -100; mix.swtch.type = SWITCH_TYPE_SWITCH; - mix.swtch.index = IS_ARM(getCurrentBoard()) ? SWITCH_SF0 : SWITCH_THR; + mix.swtch.index = IS_SKY9X(getCurrentBoard()) ? SWITCH_THR : SWITCH_SF0; mix.mltpx = MLTPX_REP; memset(mix.name, 0, sizeof(mix.name)); strncpy(mix.name, "Cut", MIXDATA_NAME_LEN); diff --git a/launchpad/common/rules b/launchpad/common/rules index cf96b0fda..2ee5a0a6c 100755 --- a/launchpad/common/rules +++ b/launchpad/common/rules @@ -15,9 +15,6 @@ STM32_OPTIONS = -DLUA=YES build: mkdir $(BUILDDIR); - cd $(BUILDDIR); cmake $(COMMON_OPTIONS) -DPCB=9X ../ ; make libsimulator - cd $(BUILDDIR); cmake $(COMMON_OPTIONS) -DPCB=GRUVIN9X ../ ; make libsimulator - cd $(BUILDDIR); cmake $(COMMON_OPTIONS) -DPCB=MEGA2560 ../ ; make libsimulator cd $(BUILDDIR); cmake $(COMMON_OPTIONS) -DPCB=SKY9X ../ ; make libsimulator cd $(BUILDDIR); cmake $(COMMON_OPTIONS) -DPCB=9XRPRO ../ ; make libsimulator diff --git a/radio/src/io/frsky_firmware_update.cpp b/radio/src/io/frsky_firmware_update.cpp index 2d9a62d5e..eb1ab0df4 100644 --- a/radio/src/io/frsky_firmware_update.cpp +++ b/radio/src/io/frsky_firmware_update.cpp @@ -160,7 +160,7 @@ const uint8_t * FrskyDeviceFirmwareUpdate::readFullDuplexFrame(ModuleFifo & fifo const uint8_t * FrskyDeviceFirmwareUpdate::readHalfDuplexFrame(uint32_t timeout) { - for (int i=timeout; i>=0; i--) { + for (int i = timeout; i >= 0; i--) { uint8_t byte ; while (telemetryGetByte(&byte)) { if (pushFrskyTelemetryData(byte)) { diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index edc435c12..f7e4f80e9 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -613,10 +613,8 @@ class OpenTxSimulatorFactory: public SimulatorFactory return Board::BOARD_TARANIS_X9LITES; #elif defined(PCBX9LITE) return Board::BOARD_TARANIS_X9LITE; -#elif defined(PCBTARANIS) - return Board::BOARD_TARANIS_X9D; #else - return Board::BOARD_9X_M64; + return Board::BOARD_TARANIS_X9D; #endif } }; From 02b61fff24a99add17feb4a9fccc96606f7c3717 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Jun 2020 18:37:39 +0200 Subject: [PATCH 011/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5bb418852..92fd0a455 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1769,3 +1769,4 @@ Gary Wild Peter Sarcher Steve Schoonover Francisco Orcera Exposito +Alan Powell From 2d01916055a8654f1ae44e3d574464b3de0c77cb Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 22 Jun 2020 21:15:47 +0200 Subject: [PATCH 012/267] Preliminary T18 support (#7581) --- companion/src/CMakeLists.txt | 2 + companion/src/companion.qrc | 10 + companion/src/firmwares/boards.cpp | 6 + companion/src/firmwares/boards.h | 10 +- companion/src/firmwares/generalsettings.h | 1 + companion/src/firmwares/multiprotocols.cpp | 2 +- .../src/firmwares/opentx/opentxeeprom.cpp | 8 +- .../src/firmwares/opentx/opentxinterface.cpp | 11 +- companion/src/generaledit/generalsetup.cpp | 6 + companion/src/generaledit/generalsetup.ui | 790 +++++++++--------- .../src/images/simulator/JumperT18/bottom.png | Bin 0 -> 250 bytes .../src/images/simulator/JumperT18/left.png | Bin 0 -> 38029 bytes .../images/simulator/JumperT18/left_mdl.png | Bin 0 -> 37985 bytes .../images/simulator/JumperT18/left_page.png | Bin 0 -> 38006 bytes .../images/simulator/JumperT18/left_sys.png | Bin 0 -> 38021 bytes .../images/simulator/JumperT18/left_tele.png | Bin 0 -> 37972 bytes .../src/images/simulator/JumperT18/right.png | Bin 0 -> 36816 bytes .../images/simulator/JumperT18/right_ent.png | Bin 0 -> 36767 bytes .../images/simulator/JumperT18/right_rtn.png | Bin 0 -> 36822 bytes .../src/images/simulator/JumperT18/top.png | Bin 0 -> 439 bytes companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 13 + .../simulation/simulateduiwidgetJumperT18.cpp | 69 ++ .../simulation/simulateduiwidgetJumperT18.ui | 197 +++++ .../src/simulation/simulatorstartupdialog.cpp | 2 +- companion/src/simulation/simulatorwidget.cpp | 3 + radio/sdcard/horus/THEMES/Darkblue/t18.bmp | Bin 0 -> 164938 bytes radio/sdcard/horus/THEMES/Default/t18.bmp | Bin 0 -> 164938 bytes radio/src/bitmaps/480x272/CMakeLists.txt | 3 + radio/src/datastructs.h | 5 + radio/src/gui/128x64/model_setup.cpp | 5 +- radio/src/gui/128x64/radio_diagkeys.cpp | 2 +- radio/src/gui/212x64/model_setup.cpp | 5 +- radio/src/gui/212x64/radio_diagkeys.cpp | 2 +- radio/src/gui/480x272/bitmapbuffer.h | 8 +- radio/src/gui/480x272/lcd.h | 2 +- radio/src/gui/480x272/model_setup.cpp | 5 +- radio/src/gui/480x272/radio_diagkeys.cpp | 2 +- radio/src/gui/480x272/radio_setup.cpp | 20 +- radio/src/gui/480x272/themes/darkblue.cpp | 2 + radio/src/gui/480x272/themes/default.cpp | 2 + radio/src/gui/gui_common.cpp | 3 +- radio/src/opentx.cpp | 4 + radio/src/opentx.h | 2 +- radio/src/pulses/modules_helpers.h | 4 + radio/src/sdcard.h | 2 + .../targets/common/arm/stm32/adc_driver.cpp | 2 + .../common/arm/stm32/bootloader/boot.cpp | 3 +- .../targets/common/arm/stm32/pwr_driver.cpp | 17 + .../arm/stm32/rotary_encoder_driver.cpp | 2 +- radio/src/targets/horus/CMakeLists.txt | 7 + radio/src/targets/horus/backlight_driver.cpp | 18 + radio/src/targets/horus/board.cpp | 1 + radio/src/targets/horus/board.h | 9 + radio/src/targets/horus/hal.h | 23 +- radio/src/targets/horus/lcd_driver.cpp | 33 +- radio/src/targets/sky9x/board.h | 1 + radio/src/targets/taranis/board.h | 1 + radio/src/targets/taranis/hal.h | 1 + radio/src/translations.cpp | 3 + radio/src/translations.h | 6 +- radio/src/translations/cz.h.txt | 2 + radio/src/translations/de.h.txt | 2 + radio/src/translations/en.h.txt | 2 + radio/src/translations/es.h.txt | 2 + radio/src/translations/fi.h.txt | 2 + radio/src/translations/fr.h.txt | 2 + radio/src/translations/it.h.txt | 2 + radio/src/translations/nl.h.txt | 2 + radio/src/translations/pl.h.txt | 2 + radio/src/translations/pt.h.txt | 2 + radio/src/translations/se.h.txt | 2 + radio/src/translations/untranslated.h | 4 +- radio/util/build-firmware.py | 5 + tools/build-companion-nightly.sh | 4 + tools/build-jumper.py | 6 + 76 files changed, 952 insertions(+), 426 deletions(-) create mode 100644 companion/src/images/simulator/JumperT18/bottom.png create mode 100755 companion/src/images/simulator/JumperT18/left.png create mode 100755 companion/src/images/simulator/JumperT18/left_mdl.png create mode 100755 companion/src/images/simulator/JumperT18/left_page.png create mode 100755 companion/src/images/simulator/JumperT18/left_sys.png create mode 100755 companion/src/images/simulator/JumperT18/left_tele.png create mode 100644 companion/src/images/simulator/JumperT18/right.png create mode 100644 companion/src/images/simulator/JumperT18/right_ent.png create mode 100644 companion/src/images/simulator/JumperT18/right_rtn.png create mode 100644 companion/src/images/simulator/JumperT18/top.png create mode 100644 companion/src/simulation/simulateduiwidgetJumperT18.cpp create mode 100644 companion/src/simulation/simulateduiwidgetJumperT18.ui create mode 100755 radio/sdcard/horus/THEMES/Darkblue/t18.bmp create mode 100755 radio/sdcard/horus/THEMES/Default/t18.bmp diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index f33f430ad..3cf15d85f 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -351,6 +351,8 @@ elseif(PCB STREQUAL X10 AND PCBREV STREQUAL T16) set(FLAVOUR t16) elseif(PCB STREQUAL X10 AND PCBREV STREQUAL TX16S) set(FLAVOUR tx16s) +elseif(PCB STREQUAL X10 AND PCBREV STREQUAL T18) + set(FLAVOUR t18) else() string(TOLOWER ${PCB} FLAVOUR) endif() diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index ce172fbb7..cddc2fa3e 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -190,6 +190,16 @@ images/simulator/JumperT16/left_tele.png images/simulator/JumperT16/right_ent.png images/simulator/JumperT16/right_rtn.png + images/simulator/JumperT18/left.png + images/simulator/JumperT18/right.png + images/simulator/JumperT18/top.png + images/simulator/JumperT18/bottom.png + images/simulator/JumperT18/left_mdl.png + images/simulator/JumperT18/left_page.png + images/simulator/JumperT18/left_sys.png + images/simulator/JumperT18/left_tele.png + images/simulator/JumperT18/right_ent.png + images/simulator/JumperT18/right_rtn.png images/simulator/TX16S/left.png images/simulator/TX16S/right.png images/simulator/TX16S/top.png diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index a189e16c8..a39fe3369 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -81,6 +81,8 @@ uint32_t Boards::getFourCC(Type board) return 0x3D78746F; case BOARD_JUMPER_T16: return 0x3F78746F; + case BOARD_JUMPER_T18: + return 0x4078746F; case BOARD_RADIOMASTER_TX16S: return 0x3878746F; case BOARD_UNKNOWN: @@ -116,6 +118,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_X10: case BOARD_X10_EXPRESS: case BOARD_JUMPER_T16: + case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: return 0; } @@ -147,6 +150,7 @@ int Boards::getFlashSize(Type board) case BOARD_X10: case BOARD_X10_EXPRESS: case BOARD_JUMPER_T16: + case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: return FSIZE_HORUS; case BOARD_UNKNOWN: @@ -507,6 +511,8 @@ QString Boards::getBoardName(Board::Type board) return "Horus X10/X10S Express"; case BOARD_JUMPER_T16: return "Jumper T16"; + case BOARD_JUMPER_T18: + return "Jumper T18"; case BOARD_RADIOMASTER_TX16S: return "Radiomaster TX16S"; default: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 937703eda..ed5ab9e25 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -51,9 +51,10 @@ namespace Board { BOARD_JUMPER_T12, BOARD_JUMPER_T16, BOARD_RADIOMASTER_TX16S, + BOARD_JUMPER_T18, }; - constexpr int BOARD_TYPE_MAX = BOARD_RADIOMASTER_TX16S ; + constexpr int BOARD_TYPE_MAX = BOARD_JUMPER_T18; enum PotType { @@ -207,6 +208,11 @@ inline bool IS_JUMPER_T16(Board::Type board) return board == Board::BOARD_JUMPER_T16; } +inline bool IS_JUMPER_T18(Board::Type board) +{ + return board == Board::BOARD_JUMPER_T18; +} + inline bool IS_RADIOMASTER_TX16S(Board::Type board) { return board == Board::BOARD_RADIOMASTER_TX16S; @@ -214,7 +220,7 @@ inline bool IS_RADIOMASTER_TX16S(Board::Type board) inline bool IS_FAMILY_T16(Board::Type board) { - return board == Board::BOARD_JUMPER_T16 || board == Board::BOARD_RADIOMASTER_TX16S; + return board == Board::BOARD_JUMPER_T16 || board == Board::BOARD_RADIOMASTER_TX16S || board == Board::BOARD_JUMPER_T18; } inline bool IS_TARANIS_XLITE(Board::Type board) diff --git a/companion/src/firmwares/generalsettings.h b/companion/src/firmwares/generalsettings.h index a111cac1e..cdb72d9c8 100644 --- a/companion/src/firmwares/generalsettings.h +++ b/companion/src/firmwares/generalsettings.h @@ -142,6 +142,7 @@ class GeneralSettings { unsigned int countryCode; bool jitterFilter; bool rtcCheckDisable; + bool keysBacklight; unsigned int imperial; char ttsLanguage[2+1]; int beepVolume; diff --git a/companion/src/firmwares/multiprotocols.cpp b/companion/src/firmwares/multiprotocols.cpp index 737bd36ed..f6ab2ac12 100644 --- a/companion/src/firmwares/multiprotocols.cpp +++ b/companion/src/firmwares/multiprotocols.cpp @@ -26,8 +26,8 @@ #define STR_MULTI_SUBTYPE QT_TRANSLATE_NOOP("Multiprotocols", "Subtype") #define STR_MULTI_VIDFREQ QT_TRANSLATE_NOOP("Multiprotocols", "Video TX frequency") #define STR_MULTI_RFTUNE QT_TRANSLATE_NOOP("Multiprotocols", "CC2500 frequency fine tune") +#define STR_MULTI_RFPOWER QT_TRANSLATE_NOOP("Multiprotocols", "RF power") #define STR_MULTI_TELEMETRY QT_TRANSLATE_NOOP("Multiprotocols", "Telemetry") -#define STR_MULTI_RFPOWER QT_TRANSLATE_NOOP("Multiprotocols", "Radio output power") #define STR_MULTI_SERVOFREQ QT_TRANSLATE_NOOP("Multiprotocols", "Servo output frequency") #define STR_MULTI_OPTION QT_TRANSLATE_NOOP("Multiprotocols", "Option value") #define STR_MULTI_FIXEDID QT_TRANSLATE_NOOP("Multiprotocols", "Fixed ID value") diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 8b0e8643d..b7e4d8644 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -2607,7 +2607,13 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type else internalField.Append(new SpareBitsField<2>(this)); internalField.Append(new BoolField<1>(this, generalData.rtcCheckDisable)); - internalField.Append(new SpareBitsField<2>(this)); + if (IS_JUMPER_T18(board)) { + internalField.Append(new BoolField<1>(this, generalData.keysBacklight)); + internalField.Append(new SpareBitsField<1>(this)); + } + else { + internalField.Append(new SpareBitsField<2>(this)); + } for (int i=0; i<4; i++) { internalField.Append(new SignedField<16>(this, generalData.trainer.calib[i])); diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index f329d98bb..ea13bdb18 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -62,6 +62,8 @@ const char * OpenTxEepromInterface::getName() return "OpenTX for Jumper T12"; case BOARD_JUMPER_T16: return "OpenTX for Jumper T16"; + case BOARD_JUMPER_T18: + return "OpenTX for Jumper T18"; case BOARD_RADIOMASTER_TX16S: return "OpenTX for Radiomaster TX16S"; case BOARD_TARANIS_X9D: @@ -738,7 +740,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_ACCST_ISRM_D16: return IS_ACCESS_RADIO(board, id); case PULSES_MULTIMODULE: - return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board); + return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board) || IS_JUMPER_T18(board); default: return false; } @@ -1276,6 +1278,13 @@ void registerOpenTxFirmwares() firmware->addOptionsGroup({opt_bt, opt_internal_gps}); registerOpenTxFirmware(firmware); + /* Jumper T18 board */ + firmware = new OpenTxFirmware("opentx-t18", Firmware::tr("Jumper T18"), BOARD_JUMPER_T18); + addOpenTxFrskyOptions(firmware); + firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); + registerOpenTxFirmware(firmware); + addOpenTxRfOptions(firmware, FLEX); + /* 9XR-Pro */ firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO); addOpenTxArm9xOptions(firmware, false); diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 773e66734..16ef5b271 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -197,6 +197,12 @@ ui(new Ui::GeneralSetup) ui->label_OFFBright->hide(); } + if (!IS_JUMPER_T18(firmware->getBoard())) { + ui->keysBl_ChkB->hide(); + ui->keysBl_ChkB->setDisabled(true); + ui->label_KeysBl->hide(); + } + if (!firmware->getCapability(SoundMod)) { ui->soundModeCB->setDisabled(true); ui->label_soundMode->hide(); diff --git a/companion/src/generaledit/generalsetup.ui b/companion/src/generaledit/generalsetup.ui index 98f9e76dd..7353b9c4b 100644 --- a/companion/src/generaledit/generalsetup.ui +++ b/companion/src/generaledit/generalsetup.ui @@ -554,348 +554,13 @@
- - - - - 0 - 0 - - - - 23 - - - - - - - RotEnc Navigation - - - - - - - - - - - - - - - 0 - 0 - - - - Stick reverse - - - - - - - - - - - - - This is the switch selectrion for turning on the backlight (if installed). - - - - - -1 - - - - - - - Backlight flash on alarm - - - - - - - Backlight Auto OFF after - - - - - - - Backlight Switch - - - - - - - ms - - - 200 - - - 1000 - - - 10 - - - 500 - - - - - - Voice Language - - - - - - - Hz - - - 300 - - - 1100 - - - 10 - - - 700 - - - - - - - - - 1 - - - - - - - 2 - - - - - - - 3 - - - - - - - 4 - - - - - - - - - Vario repeat at zero - - - - - - - Vario pitch at zero - - - - - - - FAI Mode - - - - - - - Backlight OFF Brightness - - - - - - - 100 - - - - - - - - 0 - 0 - - - - - - - - - - If this value is not 0, any keypress will turn on the backlight and turn it off after the specified number of seconds. - - - sec - - - 600 - - - 5 - - - - - - - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">LCD Screen Contrast</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Values can be 20-45</span></p></body></html> - - - 1 - - - 20 - - - 1 - - - - - - - Hz - - - 1300 - - - 2900 - - - 10 - - - 1700 - - - - Timeshift from UTC - - - - 5 - - - 100 - - - - - - - - America - - - - - Japan - - - - - Europe - - - - - - - - If you enable FAI, only RSSI and RxBt sensors will keep working. This function cannot be disabled by the radio. - - - - - - - - - - - Beeper - - - - - Speaker - - - - - BeeperVoice - - - - - SpeakerVoice - - - - - - - - Backlight Brightness - - - @@ -992,24 +657,288 @@ p, li { white-space: pre-wrap; } - - + + + + + - Backlight color + Sound Mode - - + + - Vario pitch at max + - - + + + + + + 1 + + + + + + + 2 + + + + + + + 3 + + + + + + + 4 + + + + + + + + + + + + + + + This is the switch selectrion for turning on the backlight (if installed). + + + + + -1 + + + + + + + Hz + + + 300 + + + 1100 + + + 10 + + + 700 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + ms + + + 200 + + + 1000 + + + 10 + + + 500 + + + + + + + + 0 + 0 + + - Speaker Pitch (spkr only) + Stick reverse + + + + + + + nnnnnnNN + + + 8 + + + + + + + + Beeper + + + + + Speaker + + + + + BeeperVoice + + + + + SpeakerVoice + + + + + + + + Backlight OFF Brightness + + + + + + + + 0 + 0 + + + + 23 + + + + + + + RotEnc Navigation + + + + + + + 5 + + + 100 + + + + + + + Backlight Switch + + + + + + + + America + + + + + Japan + + + + + Europe + + + + + + + + Vario pitch at zero + + + + + + + If you enable FAI, only RSSI and RxBt sensors will keep working. This function cannot be disabled by the radio. + + + + + + + + + + Backlight Auto OFF after + + + + + + + Hz + + + 1300 + + + 2900 + + + 10 + + + 1700 + + + + + + + 100 + + + + + + + FAI Mode @@ -1044,31 +973,56 @@ p, li { white-space: pre-wrap; } - + + + + Backlight color + + + + + + + Owner Registration ID + + + + + + + + 0 + 0 + + + + + + + + + + If this value is not 0, any keypress will turn on the backlight and turn it off after the specified number of seconds. + + + sec + + + 600 + + + 5 + + + + Country Code - - - - - - - Sound Mode - - - - - - - Speaker Volume - - - - + @@ -1098,36 +1052,96 @@ p, li { white-space: pre-wrap; } - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - + + - Owner Registration ID + Backlight Brightness - - - - nnnnnnNN + + + + Vario repeat at zero - - 8 + + + + + + Voice Language + + + + + + + Backlight flash on alarm + + + + + + + Vario pitch at max + + + + + + + + + + Speaker Pitch (spkr only) + + + + + + + Speaker Volume + + + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">LCD Screen Contrast</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Values can be 20-45</span></p></body></html> + + + 1 + + + 20 + + + 1 + + + + + + + Keys Backlight + + + + + + + diff --git a/companion/src/images/simulator/JumperT18/bottom.png b/companion/src/images/simulator/JumperT18/bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8f0d29f7e4c93a2b813c766f31b5ce2b2cda5d22 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0y~yV0-{%b8)Z%$()+rqdmmtT}V`<;yxP*J(3 zi(^Q|oVRy2@*Xzeadq@spuqfU&;LbjTuV=IIY#`*Yr1c{A@TLz`3wyIR)zC3G?)t| zFlZdIVLcB{>DqNWabOM8?tDnm{r-UW|2^CXQ literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/left.png b/companion/src/images/simulator/JumperT18/left.png new file mode 100755 index 0000000000000000000000000000000000000000..4683595ece3009762ca2824a00d2ed375fa48f0a GIT binary patch literal 38029 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 C)6ud3 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/left_page.png b/companion/src/images/simulator/JumperT18/left_page.png new file mode 100755 index 0000000000000000000000000000000000000000..5fbeafbdee6af1d7051d63e5ed8790ad30b75320 GIT binary patch literal 38006 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/left_sys.png b/companion/src/images/simulator/JumperT18/left_sys.png new file mode 100755 index 0000000000000000000000000000000000000000..777f996e42306d177142d92f6ec0da0d88c9d5d2 GIT binary patch literal 38021 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≦(b3W9=qR8JD39LY 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 Unul#n>Hq)$07*qoM6N<$f|$hiTmS$7 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/left_tele.png b/companion/src/images/simulator/JumperT18/left_tele.png new file mode 100755 index 0000000000000000000000000000000000000000..3867d3eed5f6c1b8fbfdac2fb999b93b035ad4aa GIT binary patch literal 37972 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
lcdwidget.h
+ 1 + + + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+ + + + diff --git a/companion/src/simulation/simulatorstartupdialog.cpp b/companion/src/simulation/simulatorstartupdialog.cpp index 32d343321..b7faf0261 100644 --- a/companion/src/simulation/simulatorstartupdialog.cpp +++ b/companion/src/simulation/simulatorstartupdialog.cpp @@ -86,7 +86,7 @@ void SimulatorStartupDialog::changeEvent(QEvent *e) // FIXME : need a better way to check for this bool SimulatorStartupDialog::usesCategorizedStorage(const QString & name) { - return name.contains(QRegExp("(x12|x10|horus|16)", Qt::CaseInsensitive)); + return name.contains(QRegExp("(x12|x10|horus|16|18)", Qt::CaseInsensitive)); } bool SimulatorStartupDialog::usesCategorizedStorage() diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index d20e34cc7..f70813442 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -94,6 +94,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato case Board::BOARD_JUMPER_T16: radioUiWidget = new SimulatedUIWidgetJumperT16(simulator, this); break; + case Board::BOARD_JUMPER_T18: + radioUiWidget = new SimulatedUIWidgetJumperT18(simulator, this); + break; case Board::BOARD_RADIOMASTER_TX16S: radioUiWidget = new SimulatedUIWidgetTX16S(simulator, this); break; diff --git a/radio/sdcard/horus/THEMES/Darkblue/t18.bmp b/radio/sdcard/horus/THEMES/Darkblue/t18.bmp new file mode 100755 index 0000000000000000000000000000000000000000..c1220ca1b707e5d86b0335ad7d9abc884dccb0ca GIT binary patch literal 164938 zcmeF)1-PBX)iz-23Z>Mjy9;%9ccDtD0(JKmx8hD)T#A$676OEXAfzZE&(vK1^{;>3JT`Movz)1Y7}N6pJIsIt zrn28tX*)2D|EQ0VOXA5Er_SQSKmYm9LQf@@zA0g%cJfCNZpvJiWF3o>$6)zn^8Ihw z%|inA^sj&YYx(VOe_MX@o8OdQ|N7VE`s=T6LHd`!{AIcBy6ei%fBy6Gv!DH}{NyJ; zDL?-4kIRpK^rP~_AO5ghbImp7>Z`9VS6y{gx$?>@Tlm2beo%zVF1xIS?|=XME&ks3 zzE^}xF1e&!dg-MtzWCyc%Xhx>ofa;-=%RArg%_3!F1X;vaQ^w{7va3~&MW7hd+tbr zzVvCVemvuwPeMOW^X=D@HOZ5<-mIT2$c2mqG84$}+H0?E^8Cdwe$iwbdEaou4MngA z_Tu-y|9$!6AOBeX^rt_SKmYm9E&Sy#e`$MVzxwdEzx}NYhQI&)?$(LS|pk3NdR9}CCVWPUp zPrpkV@3`ZRWmwo@haJjI3EOYKeM#7EyX}hLbJCN=>SZkH!TDrugR;oyZ+zn$Mc8GR zUCM5|?N)Z*efP4*9($C%_S&oLv(G-|o8SCq*>Au7%KrQBe`7fCzyr&nhaOrEKm70( zjyU3oa`e$hm*bB=zRaCF_r`GY$tRZu3l@~qPd~k!dFGkrTi^Ot(?xW~cfb4HrpxFy zdIHb!%in)FvxqO`Ku6ib4}IuEZEyD6bI%+1XYalDZhK^(gzQznUk4m;K=EbP1QzGZPCYZ?~!>z1BsAk*{#_dYLaz?B5t!69MSU3YDq z3ctj3p1th#3+`^a?Y1p!v&}YT>#etLVaqMIEL&``McI7w&C6H6`qi@Orkl2~$tIhW zjW*h-Y`Ecu<;!3Ga{1J!K2;VjT-bcq+ya`DX8b|NEOxvtQZcU{W5= zz-XvKujh;M{&R0PK8t-G3_X7Pw0c?hAeQ^j(t|-i;Z@&f()#Yx>2I>QpHJ4HkEDmi%Jr!%Hp1t~+dA?qb}3op8bl z&HmyE-}SxkeQ&chWC#1f9L&OV@UF~RCFc_77wMd1`gHR8b58QokA9OD3(4z0hf~Uu z7Mou<)TalzqfXW&-Q!Pw{~6{|H~8zZn7mh)#PeK3~(Ge(~tTuXFD>ZIJ~tSM!Q zVMdL-GjLMJmio=%9p)YP;w(_!s^go(yOn& zdMoGWzWwcQFK>F&o60-i`Ofl&H@u;|^{sDhZC?A@*S31{^rhabUiGThhdkxD`pQ?n zvX#q|*8UZ*ctv^r>tEmUrHyHR+N`?js;!<_o8+r6&luv@z3z3b4{3E$U%qtuH}^hI zpDBOyo8R2Vke9SF^AVfBv|#;WG9e!^oh+@4T&2l&jWyON?|%2Y%UWx#Rn}Z{%~mGQ zXF5z89;M}d<};sZX?f1%pZLTlTHY7G_{H+cPkyp|gFDyPk=}Av&b>(UI+~+>Gtg^}~ zEq=;Vp3>^5C;hzVJ+D0NX-{i;&wlo^Tc6^mKmF1yTX zt**BERwg#Kw(4bW^uO`+!G36~u6o*Ar?hh15R_}r2C72_^2AxMHu{e3Q*IpftvO@r z2hNnksqwUdH)CcE@F}f-vLcgUQa#+(ufEhJ7yTK>x^YpNd1)^#RxXdMrRA9udCOCV zU&$j&GSZ*e7y`M7$)6nbZ!P9w&dSN-BOm!lvq8?H-ZjbJvdovv%{vuy=FDk689Pcg zYz_I+5!RPIA`9y$2Xevz^0l{cLq_nfjyC)Ua)MiZswXcPlO{vhRY$+_?71MVPvwI7 zq+GxJXXS9Ce{10iT@g zl!ph(jcfm2@q=|AQAR)?QyPRjWE(&{Q_Pt9MM=i0^;d-nm(_ztk8 z9hpdL!?(aw<<1s-bV1sALVxP0s|}gLgL3^?yYj5lnCc1E14rgyp3>@(5gzCp@55>R zS~FRwAAO;neKAjaWxd9Q@0`&iH*KspZR~@6CO`7g-hQfYTy>Sp^I5*M@yt_RbgjOX zY3J0Of3=hLeuy^Cuj;|QbFFi;&-zhUTleqoft-VtJ1;v!8=vfyX`?TB z%G|4%xAxljEFY)zt)6^o^WI>C4O%~BmAuS_tn^Pd+W8D)#aIr)*7I`Awe zM;s$pm?cAd0_Wjpbcy{{Zf|fwUpT71G4zSwaBolT9q!o+v34*JKEjf^>=EwZFg<4M z@EW$Poh;5e>#Q5^se7!>B!|Kmb8vTt-{eNuL`RT~v^A0eTWXDBdd@zP8(i22dG?Fk ztlR#QyM4zE!Cj6v+6wj_mQo+~^#xl3%(}CIak%x4ly-0k^ZHJ?IxtVa=N&0~pq{xH z!&u>(x#`QgrC}NV;6omKnKL}Vp!LB3T>(4x+*;IwHS2~YeQ6`#`p8b3)YW(NgE^5i zOh>Pfk+GCnlXmz*w({`FXY10AoWdvTx7O$c<&hKpploGU;vIcUI z7rDT?b!dlI)=Ca!n6=8YcI9LPKe&NI{5bfMFHc|EyoBd;LF~6-;aKTXZvLz?2%xP>1AcG3fs}ubhCW#Fu}L_o{`ym zd4e|K2(0LvOoMT{(mtA(IooSzQZnabySI`rmM_n}kaMj&D0cJ7PkwSs+e7zA>bWO+ z%wrzY%H1dBu1FqxtG_2c@rmuMt3UO%b04T5cT(E$Yt>Wc&Pre8sGf59k9yRjTA%L5 zlxv^6FJl>#@8aG{9s0_g=`rQjr(D1AZH+Jpr{qFb_zjQtL%w~s56bY5-cV-$l+j0I zPxs&${3_Rm4#6>c4IkByzENgBV8fW&Wv}F=OuoJ1=dowjEVdSVYL500_q3liYgYUI z!?MO5?*f>cIa&+ZTNhnoZ>&MEH*iZIxsNB)U>gT=r!Nif@nO`(2fVR2+Te(C{!BQc z91rOj_2HU5Cr`M+Vfcd^X*h#D_25??48x>#z^pmLl{-~>z}=E^{b?&tnKfH0Y-+0x zercQWl;;jv9A86U+UQ#yY4gyhxyhHO4K8V?|E$Not-A7*o3k-94{OE~>(y3$a~8Bw z7EUX}MfKFDAK=nGvwp3aTrx-F!Kym!15Crey~GcDL8sw|y}}(q8V;2Ej2HG3*L>Di zTDf|B0(ttUXZ07~4QI>)-^~l|aEA{UuK8Y4Z*ThaOqm_$n&^=DyX4NU(r@k>*=2zY z=qNJ64ejVB0oG%Ga7wwnJPX5@*cbKjNuJnedm(Kcc(B(Gd)UL8-ZUTkZhr7brUKnA z4_;ux9>BgbZS*5gxpm-)Hu_Sgo-)`}2CuLVN8t+&z&;Map?;Oi*It>=u&*z9IA=c6 zcm><)n7evn^GTm#pUn-QjH3^2;5;1Dmw75zpY1Y+v@$j(ZOv0#duWcvjJ<+W!5Y)v z9AQ~M#)WbC&bf`fxA!;_-6n0X;R0Xmcle@=ouWteCDxz1_MOkj&$WNnFZgT?&I-XF z-^X`?JX6fyO!LMan(rXtI-3Hs{84kKJJ}sFqz7;XFW3xx;e(NB_@bO#@r7K(_&~9RcFhl#@IXCygOP9uK42A|lv|fQ zf)99uN%&C**5#|GKXt=DxH1O;*6>rAATK(`e3XYnI4Z4AefzAA{=*^V<`mu1&)8D|9{GWI#1Fz*`h8jXb4N~3L{~e%kSAX`eu_M6 z!vS2t8*(HMJc4_eS0)cH@IyX-j6Tu^cfui`$=$oN;f?*VSH`uMo=e-mM?B&YO<%*C zV2{`2iTUDhll#cG5zV=T(pBv z@{|2Am7|Z;uFJ;=AoAI<&t{%SNta58K zU-h&Vi|+`z0?n2aa$i_6JAs17GYhUg3&*Y>hNMs||fk zCiWgztiid-8OPq)^Ak@z@y73s&NP2~r`;UM60g})`$*30B)Pc<2oK=iT5*7UTn(tzbH3_PNjVxew~4j&y>&KCN$`^#y0?1J2~9yl*3|Zt~R8 zPMh?fa&h|T^U^kX>Le}B`1q|%kj~iVn=)hKP1Yb^TAp^waWdudQm(G?| zMV>xzi@x%XxiWotcbzZDCv!JsKj|HIkAK475VPg-`=G23DNCNu(tMJ^Sebk}SX!Jq zWDsA5jFibsx{u`x^kV8L?}M^F(5*g;6WAMhu#-G#`GP!g^7xJ^W5cAyf-*KEpQV$R ze0$uNODhY<9{SLSwl&yOb_GZ1LiWSn@ws5Z-pA(1V_W3q9Hc(J$a60MYwF2^FFbOO z$6vG$WC(}O+Ri!bkGa$H?tf?UkB?>_aGX7aXZQSY<6ecXF<-U;=5dDnV4wV8A77NI z!{?I55#=}}t)9NMmG-?1Tu~o)*c{(4As_ngKKHp#(~&rWH{yhF$7kt$PFhUveR*G& z^1-~+&p3UouFu2z?em7U(Y9YhpH@G8_w&(b|2gT@&*zEav==9+YhT658;nyf=J&Bp z_yfcCk`9D_dxsbJV(;+9e&ZEiN*)`6BR=DXJa!0QP5}T<%_jqTVaYmC5n~2wVfj@K^`I@)$8tlV3Igy#bmV__(KrZSj3t!}^hcjdz zzTlWN4)M{H_@4K?XSv_~?$>myy|&MMF<577VspX;c{rkO?1?>= z=d%!-B*v5U7aPSVWb@nw&@t-y4jf!MgWFHPFE>+vzQ=j&vBx%i;xHZHdxi9b`O!1( z(LATe1^9-2a=-^RhaBk{Wn_k1((2%la``wCTNU3-9=_n1aoJzzD84vfo2?7($x0e$ z`n-IGtEuo|ux-YZukW6FgM3g2-Ujoh`nl&q(y*>yaq{|jSbp-vOA>HKpL`-`EBBf9 z0$1!SEWo>19^6Z_HTc5DNW+Slu2nBK2R~ptMR@UQt5c|P=A5WG}`*5W1+a3;UyCmF5yVQlr zq$i8jgL8F){hlL9FRncG(qH--#vzaSBhA%fkbC zY?5+ZfhBEm1;>0=p65F9@PzE$OTmM_oPD*Y-*NbqQ%?Cm?|P1tL(W!k?VieftP}p( zL$;EQ#C!5l&wB9&KjM#tFY&?ff!uIIj6>`So1+i>(Z+YtaS@lot%DCfxY@S{Jm3NC zPR-f0=L}f}%SlfZhXYF+>lc2~KI7(dVs%n4={{C3pW${`I(dCOtZtA0KHc*`em?hg zk{;wr>JQ6zU%=Mf>t6S2IEXIfYuQ7%zz2H^+xAl#+X4@8E-yAo9=j6GN#lv2FI-Z` z9RWRV|KU=7zmGtl=Us-QjykIS-tIX0lCk`5Fn!_t#~#ADv$=a{b_Q?g8Fz(#dw~qd zi2V3h_+y=7dCJ3q*cW}WDdgPq1($Hk_{LNpm)L0cXlxEX!|R}ZnTRWk%MaG4Gw!iJ zUFmROs&UViomuBMccfb2xv!mvLH|!1kV2ZAzaZXZjiHjWUmb!f`9s!#xMGXj&nC)UZdoVzNV8i>3h2P()T-6 z&ZK?MoupHSEBC$cecN+qWq80x7o_biEW?exg?IKx9{j^QT+xO2qD^cLzNn`SzTg_4 z&S&l1OBvJMu5!QYrLFTXJOJK+JC)S5MNY6d_)XsmfxHvm9mGWSHy5b(|qfQ6k zQ;oxy!8L%7xZ8Bt22swW6%EFCwr$E?qLR&!ZG}TL3SzU zA!i+Hf=irJm%cSNy^1${70>Kv;?M6MxHET#aZf=m*6-c|Z#;W5ck9IsIt4GR!S80l zzx%i&k36z*McQ`}4m<3ymX9m)_@eUtX2I;)vv1^;{_seDUg)=S1iFw+ZYw-3bd~=N^6N%X1L2Vqb7d8|MJ{z$aXD&X|cm z|K5=2jMnMh8vYdD$vY6%YJT`c56}~A4O!ujwc-NK_`N9n!56j%U)UODd^UVZ8rR4m z_au0uZ~971&yX?yfIXg!H!wMj15?cl)<+#r7iZFM@IKWz{Gm_X3wZB|&4Fk872gb2 z^8SnaFYmO$OwROt7Ww=ccMHnxEkA~?gbDU0oTIaGP=HD6f=_nJSadeOkN-}W&(!;! z-c^8KHrTxddBpd0-)EiP7oZEw8?Wdab-ZV1ooq9IEquWlzZ(}$`S*r!2w(6^J-oq7 z_YHK8zS(WEbpLIi+LzJ~?=M$WwK z4O^rxPLLfgdDqc7McSDa@6^L9=Vtc?u%RDmXMY^>S=v0*^?fxQp;KXxe#d8bSN-p+ zjnnSpX9=PIGi$;2ehUuq{n4-pj!mer{~Hbz7gltQC)ND14%0 z@RLsB1LKtXILT(Q>wE+DhTJCOjk4(uJ>Ex|o-WR$-yZ)lAU62V9R*Qa_)BT z;NN8&=U={G>faWi3#nl-Yc){b-5p5Q)_4d$P+ zz5F|TqKC-EJ&G~)&HnT0^hw6{%RM+=;Mh3MIFoU3l$`1N3eKk^URu6nE^y0+un(T4 z;fei&2Q~+eaRXNP25gQr-q5Sz4IJ4^{l))>3HPmh4LpP`Sn;gKIL=4*11@3Ac(CG* zP9N?Z{5vw^{=*&b#*qJKT(>xQCm!Mzu z1l%AKwuzl_E+Rv=j6dvNz**Ej;7fQTJ(K+D@jl9Qa5^3Fl5%IzPqRMfOYb}R4x8^L zunBmBFZLB)=zU|r3Y)~Xzy&+uj)1*kgYbr(fqDB1C+>`LR)z)qVOx}`XRn=S=wUda zvz;r9=iKdlJ`;cb%~JB9Bdpa~gFM&|YxPbazFQ9&;k&y5a`XNgoq{9o5%8Z4VMqCR zKGQpN4qMJ2^_iTNvDx$r&ghdbhd1_*&8O$cd0FC3Z;wXF8+}a&XY{!wydm>pb@}M_ zf#1yL*e~`07T{f)-psiL&w{1i-pC7Yj3tc=&bqNPY>hPhNt-u}@N0}=JiLJ&m~gM= z-`bstKmTUAxzQ19fID&=abAagXF2mHC$>fm=hEt%yLDQZdjYnuM`sB3#9FnX ze{e~jdq;N#`o$UCaUX#<_rCYN+xWwHBX2PDm>ySp)ZFR&BcD-*CE-tB$JqGr@P|LV zamO8)yJuYC%fo=Z&)t_hB=vjVus8H1eDULy;|;q6WA>hZj*sk&Kqu=H4(Lrh!6SZ} zv#>L@z4uILCV0b7JNMjko6pHNf@QuEU(#I7+dkqzo~@7)mcvc^%xqvTBASI>>f(%Qf+ zpV{3e8$qXfUVtzBCb-~t^G(<#dg_&f0BykwmGhUYkKPQJ6|*;c;8?ObjRI884&SC~Iv4A@XdHGyF4W zInQ5k1sAo&KODg==O%nJoOtvpQsOZj{aizC*uwLDjybxVSBpsM#Y)*)8l=V z>0x})zx>3~d_KuXEJw$=0$&)H~j+Ff;!g9N3#Z4 zXG7@?vS35lEd42Wzl~SUN$~CL#Ga8eS=k46&OJ3BY{#4qV}fw7umUx<7O8%HFs~fE~PHxBS~|yUdYI zCNI2!b2djCYw*lPIqc7!JGWeV>80h8OD<{8Lw)wH06V2U8L=;T<$16>6y?h39&+Iu zc-H1lLY*by#$VQ<14eX&!p#Mg5d!1sd(X8;`G+go?;p^XVY&Qo~f z+=^;wgwzwXJDV*!3TTKS7mpt1*X{zHb+b@xF$_MkRSVDedM5?a|>>| zZ^R4hreoMC{GhMg3FtfZ@g%{C)i^u}cf|ZOWAc;f zRnKp{GiV><8^q_f_prkM;-}?&L$A6sfD!nDv)CK{zrMW3$S-4G=v>_4@4%9N%mq(i z2)6h&#&ZY77A{z@VC=s!;I{?njPS-Y81{xOmbO-RTF&#hf@9_l{|`e)Ja)`EN_Zk%`(2Vt*8$ zbDw5Rw%>QwS6XSM#vQokbKpy2_r`oPIu>5&J!kp;d=o!Sx&EDBopI?*J_f&x?*~hC zIV{4Y^*G-cgO8&w-Z=ZfEuY^xeVl)OlY{LuKL6C**b;MxeQSj!@?#J9S9F1xuE7oG zpgikkL&(qlTOaTve8L-db@ThwfBSlawi z@I@cS7x(vR&L#Y2&!Opkdj;q0j`NN4z5TRT-p3t$e&Yxfft>$ck>T9vq-I z*bnl_GaLNyyo=t!FSbeAc}L*8;wnDkiuL0UeWJ`cl+F21djVY3FPj4+ z`lWx}G4YMmWpC(V=Nt1F_ix(VaKjCaAI@I5ZQgj}%z-y}z@FlQ=aJ@2zc_>77<*w& zerrP+?2GgKif+;mx$*n-2a9+kkN?GP`RxJg&-X&%ioNGov+MY)ZXbf_nTUh?>4>Mo znY0}Rcfuccy)e#KqsL&D-sD%of><7|xFcf&>{re=_F14uaZ0(q>0{XN9Y+1JC9nW{ z&H&QR0M^8A!ICo%d!r7Wj5m1O|GS0busfbdI=j0kfonWxFWEtJj!l49_K>aN)AC8J z4d?lD*6v(n4ekYfr^ojbJd--8UJ?2Bdx%-q})_8-f z)4>^i2K!TuZwhz#SA+G9Z$5OYdriI?oZ^V{P3{HgJ9k3fLBtE(a>gCBH$89kjf?#C zJTs?e1x1ju%4ZiS&%)`9d3+EfYmG#jh_<=9% zjB*@-cRrZ4lcV*=*Ya=v3cgEf4ByE+{q)nDEyEjkAfB_bW4Io)WXHIE%1Ju{V4c zKAJNQ9f>z|p|QM^k@_h3IAddVa$PVvAq}#_-U&+?l|=@Hqc`$CNKd zR?cwbVQ#pME4V~2;CAl1aEIN&d1oTJz`2m_um*P|xRiS{ca(y1a^a)m4td%;e9ZZV z?jb{G8-9VYacf!TOt|@@{Jq%B{fW8g?9d<~}2XepXK6u=}p?2MM*R{JZbHiVA zaxdWC!d&qNr|Akl6x%>n&LiFz(1uR2CVnp)ghSSqd&TGx<#pj*q-iqS?A6mFBbP1zVQau*$400us5(^|DESy+`i(4vp#zg z--<2{Z|GG1OV9V=osXv-o{^{f5%nY{AQx`R=jrClu>MEIA$v2*usE&^mh-*bKYK2KH|~S!H|G)W3(#}k zt#TH@3w(&Z32$%*Z|FmNOE&Il=}$UXKX`*H@WAJRGkxL>yTfNuhBtIFOyZ4qQ9Mr> z$6wwb{n3wp)Ybrl=Eg^2AK46c#QNwC_@-Ojqgf9hlO4f%GVz`P+3`c^ojku`k9^i1 z4?PPN_<`()GY+o0LnA+Wj4pu_=STa*-c7~USlcjMk0WnX+(|#9;?E#A@>%~$i;YWf z!8$v_{@Xjx3-}&z&VRS}o=G^1s2h8eXW{fD9E3Myk2h?E=bkXd)`UBFArA)A7k-62tl{fGa%#SL*soa78VQqSTOq4d~ z>_wgl*gJa$2e3|`I^XES{gN{btlMw<&lj`jbf-Ip@QPi6hxzm8Hyh$?!`IQLdjYnE zUUi0`kJ&kP2o9Cg*{}<@h-62Vk3xWE=QX)<~}I0mu#a zoQDLu#k%MVYsNu(ie1vTHsq*18<%s9-^*qHjp@A(cArkUDL&b-y5q1dlZ`Pd&ZM7V zJXo5%LB8liUSet3biVQ46YlWG-Rsb?IKw}Hd43aK@U6x6fnJ1**dk@@41Rgezz3l# zVas_b_ql8t-q5#rgMV~6&Z-B8@|3}tXC34G`|fwY+wLUdL$bU0V1DN8`9A#sv@GyD|zpo3vR zd+&w{xfh@}-O;i$YzYj(lQf;{jLq)g4SU1ZX#-EjQ#S73?7ZND3z}Ta&HC`(odw&9 zGjs*qkb|=gTSHFLc+dVuHf##+JB>Xx$M|XR&Q3TJnj3DgGwuwnM_?z&DtF5GLO-yd{8oM+A6FnxzMekWF}jKF z!AH8r*%EKuN0SRX?;I-Fx9R2$UQLGgQI_?%A4Pg;crvU_xPvp{k32YcW^pIrPK`dK z-|U++I{l9mun!ZSr+6-bOLQPVj9sEL;YFaE;l;f`+G%U=wP9~?gN-s5c%h5=i0l%) zz>#VaHesLN%@ZFlsk9^|WO5>FEJHL~ex^$28igkI0#s1JK>@uF< z4h~trby_>SieG$Q9I^+3zUR%G*Y?7={_StSKji(Waq#BbTBNy2|n`E@Cx?$ zf%F+)$#1`m>+fIx`d7QlaP~Gf-oP(g$Y(QGHqbf4{5`X=X1*G`D|r5o3q4%bXb1q>^_y(}fN8_)-h5ZX(U|Slma0GX7L7sH%3H*mA`pfx7tPf{k*rb2y za?j5Bb^3)f=Lli4-?|;O-{w2n-c^Q8bAxYd!+WxE_F#wjYSQik=oLDJ{s@1_Kyc^o zUJV!cr}C`5zdMvKkDk$<|4Z-im+{Abn1lT?FLy4ZxQAX^UQLGiQIz$VA4Pg;IWw#s z{>bO2IdAfr?T0f7-wU>J04DH3Y;Wb;Q#epA4;G~DdpN9U z*qr!n{WE{|gB;~)gCk@u#u0o`jwjl)?b-BQ3N z4Ax8iK2DpP%8_tjN!q3Euo!NKfx??{+$O@)PW-{lg7=2NUot z4~JkQ9H0;Fu{O9sFQ-ghX*P$wvG;g}Q)2eUKGEsULwq3|#4+aqzP`C$b=6f9!F=TT zcw;T@=G{Z{p`5qa4IFY_fnULAI)n^hpIpcQ&-5YIR$7@D&&U)v)WxyX#SitB$Brp; z_rfN*=jR`KS74Z**5f%imp90T)C*Vo&uQ1kgMAF^Pn`s9lNQ5S|2gT2^3z_q00T)6 z#`@~Zr8DMW-Pj6sCJO5GV-9}Sj=v`G<@wFHL#M((_yXV3a1SGNDn7vkI|2vipMQQk zyW$nx_k7Ww4a)gOTfESgPw#w#H~8v2&3}O%zQ5-ho&}BbH=_Lph&AC2Tj-2p?YZ~h zKhpDTvoj3*hZE#Rhmei5Af4}PlA|;^;tReh4_~ZXnjFyuoC9Ll?3bx-Xk3JyF<6Oz?%BTI?aTLW@CKi7 z#on_;@?eC$VN+ny{WadW7choz_K73J*TVlbuo+k_Jv@=$0jx;Riv-CtE{OZg5 zyd~+QZ`-GP9!->1f1axt6d{j@vZ`Oa+)aA41SJPhI|cn}9aiGv59Q!jbJNB_Cc zlO7h}ZnBVZQfIJ_lqX+3>BN0n-NAnPyuq@xwchk6P8)G#k-YTL*HZ^Bz3=2*8~3hk zi@mkia34J?-#)@Z@}2K-0j_c%Krgc?>gAqIIUA#n`uM00Zn&F*Mdt|ihA(H#*dp`u zJO1PP!yEX=8}HUhH*`H;iH^Y$oWKv57tBAO!xu5W$ftMsV`QvvoFH5L zz!!DH7j@VlWjIFHxEtUb#P+xkJM>~DEtHNu~ zZ}@3+x@U&Qb3egPGsm7cqrQ(G><@3OBfMb)t;1cP_b$!V^BcIuBl02}oPceCJn#WG zVw>fM54fN{+pI1=#Qw-rCWKEp^T<2xwA0#r=o)s~b5ns1awep2-1*5PU%sU0t^8i) z?l9#mm*=zhZ@v4VKW*K2=ubU)?mE*i{pSAN+6D8-yF}XRN1nd*p|1Yq=|jHp=pUbx z7Mm-*U~cNWQ}v9@9Oaq6`S6vkNgmxM-<}BS^I_ESSYq*D7xDH?7D0#}m7xmQ(U*f~DDeQ@Qu}L@wU%79C zAu_aX&)@kxp5JH(WA@B?-CIm2ZwBpHcta06ce%%~9zL4=$7N>;=W=_CH?WT%un*f} zpRI$>Wv#GJUdm%rhg$vtx z^~^KRZ0|uRKl|*nTfO=9J099ugL3ti`;8rUJE`Y4tduKLUpwbl_d~`shB2kpQ(w8d zVxQH?+{`)c#QHO~bG3Tfq})1{3)Y#nnX|fNWv<$gm9fZIU2XNLFS<&s4mnFl#`=I& zZQ$JANyDo>vqyBN7>?oG9xF?Kc%WZ4LObKhgAL>0NXm_+Ex#=sgE4u=F}^-CymOBAB)vxy=aca=s4B~v$pZ&(6^|CkS=6quv?#J0g?_#p!xI_1nCD~aI zIjF~`irrmXD}BITc?RS?CTV%>JKvN|Q_o(IqkY6Va%6w(fpKD=jHj+K;V0)QY3`$i zuD(+@^HeUauJVj!?aBn}%sjN0RxVA?r>%a>Q~it&Yv$&&HqvlzEPdmF*c|0+uTOo; zv*+fdpKwE-y6WMBx-f-r;^^Y|UwC2vm1&#jn__kF2L{D>%ige+vE^_HTh`$2D!g&e zJ>7rt*W3ldJzt-_$+OtJ`_GSvzw7K1oLRFnJ_Y}Sy^OCRmY;GyhR^Eob@(^NvS#wm z-e-U9n|%A9b5n3{f9-X!1H1M!*j5h5%3(8@lBX|YNo#{oFfHGh+S1?nBB*C?jAwjp zjia4%I$ycA>gb0pkS8q<4q;3nGgvjY_WB`D^}?g@+Pb8TsoWUqnv;3zUpa1MY~!k9 z?edJ7x-hN}WiV;%*a>yu)11g%J8609n=d}7m-R?nk8*8#KH&+h)4BXiekC6i?xo=+ z7=a1-!3KQ589xmVjUkW!sI9Yuvo}r|m;G^{#@_fo%((viHYi(xH=fhP{yBSDpLM&Z zb^quL1~+)dc3Ok^3+%0Y_J!TiC!G+R$_i!SFFZLyvNO?E_6ZTcU z`PkRs2rkuwLwl-RUjod@3(nOI55zEPUe2cUhuFQ3HCUh68l{y-e_E?{)@c6e3qHw$ z-taDn;5?+gdbnuKupt<$*U{u;4#tF6v2o<93-|EqGY;X6wHuc_^o;{#VXelLR-Zi7 zw;wnJqk=Z_$P@?dnR@yO4~P0&Rm?U*-2-1deENQOZHM)e)NfQ0socLuhROp4sv2s=}PZ*d2Z;gpRdX% zF&A8Pw%5)$Vq>^B@VW0_UdBp2J%Ix{K$*`1T_C8ZoNl#;`Y;|D(ywI4meWhh;fG9&&G*1JXFvUC9I}bt zp@;OTT$=n7)W;osBnz@3LtG&*^S~FfBp(=;CngX1%Hh*zJRp0#Qiq&yNL&5KR^d(h zz$fk5MqJN3%hm+@I6;31>;+um4SB^TNhjc%HMy5E4?J=YjgM>(TLVuxZQo$jy`TG? z9^2E&A-@f3f8FilG+dBJxIqr?+T2s(m%UI2@8hT7NzWDB5MV={a7UVK#pF*OvK_E(j`o)>vQA^-37aKP8}qV$_r&Z6Ug(n^!b{_lxpAF^ z&9CPx&f*Qf&$x8Gb<-ha0arKz)Ap2}p`+9ZH^k-zpX6qq>XHqvD6=o&i`ZxRxMK}? z7rVvo(iLQcGvr1#LO3KH9$A+V&WP31k92Gpo6X-POZD|{9s1{|!LNHu-zOW#AKu_R zzS4>I&Yt28onZdHd*ivMJtTXW!W-?Pd&SYuiHGGWg9}*T@8FUBvCpt(KkQxh-(K1$ zlB?Y#p<-{2NIqAz%H*3hT^(=V>*3%=ln4RBV2b88H@#nwvK z(}8%Tj=4*xoIbYKo{_L0Y&IOhhqmVKyYJ>`T>H+JSU25ECwWdrHtcY8j=dmbb&X56 zO5>I>!Y}oGX3z9NM%Eh}L58>h9XQ;QlQ#O*hxL-J zJbX(YInx<%WbJq^kX`(>*dX#!CmbSEToPL+Jz%Zmh%3(6Y${9|gD$o{?}a%}d4_A> z$MJ7`vT1%(7iRc}cxz9{%HHCPxw1o^7h4a#P1fe^ykp+-^#KEHv|z2cC13D4K^y*= zJr2gh7aW0QSd+HrI3O6?Uc0k$2Go|_p;MLnY(KS0U1`Cd<52jd4|Sbo=z6%br`8`n z&>ueATl$UuH)nfJ*Q-Yk8nz$Tv$u3^f$?Ui-LzK|7Jz$2St4tPOVJ0Dzh(M2uJb1A&g zRv+PuJV72G&EDxR++z34!#=}+z~{>6#PXD>pFC;jJ7*gDpyviY!V-C~v(|=d&OOc< z);kW~uxEG!&+hT?gO0O)^ODbh@qF2{Wox3V$%*XATs>H#4`InZT9-A)r+@`@>@}Q; z={P!(?86axg1*Us%!~zR=9E716Ia=BSb=LigtPel<_RNoVfchIu|HyZiynhJbB044 z=mQQYQ&%7#W5`zzUimKYOyBdt^k-doW1snv{U5uhAKX$-zI+hl>kntxos4CD-<~Ng#x%jOPcEX(akiJV5o_e=|?J$ra6u~VM3x!_H>Y|pfbJjvMF$vO7J-mwYfN`}q{_8D&30BQcCzS&Uq$PqWK zt>=q8_%;SzqFgY(eO0bZ9ekj_qy_Vc?x8Di4-eH7Ys-IeRtg-3PqrvEbs{3Ap2fOBo> z1!MK&;-h^qhP3|7MXat^uwQrvYxvH0kTyp60{6x?H}Z5YFgL$v;rR#MLw30O;0Hgr z>G+@i^rvm#J+GG^?t~Ba5l7%%-!NfZd&!5h_v%P1gB{N&-Q(~hJZJX)l0A2xGGCma zi*Otktc=m zu|{%$Z#D-u@@F5dA5X4j;(hdd!d`a)j#f){iW*@iFV1V7rb5B5N?zxvk3IOb%J z*+X|2-d})sb0jnO7O=zC^A+$*-vYiEQ~!dre%S^(!G4fEza`J5={DyTSi}kC(qj5W zT03}GS3C9b0k+_V4PqOevGeELYHs{4>n1zva2K6^^ri1Uwh#KZ7xD#sQ7?O>4}8G^ zI+}hUZ{v}tvB=ii`GasyUixva_WLK!9qg#zdZH`YV!RjA^W;f>u&r->+GlAPu#eU* zZU1mgxqSN)tiYLi$x9n^f*-+|!C3U8b(6Vs5Wfev^-GTAXa3eXF5dXQDw{&q_JxhK z7Ix5l?Fk#M4423p7p%)#$%9?NBXWWT^T!o3mc|k5&z@XfV!ycLky{0YS%x8UO4!DPh%DumA&gN-+b*x1i=EUZhbYHGdSRo7f z8At!x7@Ob3C-3hC_z~6?A4=cW2T#^-z3^ac>$Prkv?gWb4(sF)9U+fCp`-Z==3?J3N9} z?WFCmvXsN7e(euTTZes>CSS33`ek#(`okmX*cbiar2g?lU1Q)r{=lKn+R~AD!>;gs zo#pr++S28@7tn|A#f;<6zZqg&cTnbKeddni&OP)Gd*j^g{>C0;Kgdb2K6_3dS&Ot7 zZc<-f0_;g=|Lvvyh5>2$Vtq?zJbS5M7*S?C>2MT2ea12SYJ9vSd}Q_nf7p9-_PoGzfSiMUKgV4QxzZJ6WFN`X+Q=xfk|!NJ+YfSvKQeR9=Tq?y z+!N(K-x=N6lb?XEd^SfukNwWG5zoQsb>|s-BqUAOE9V>AL-~9q z?cFK3BPUD!lCSw$hq>W|&tmI0H#SGi4#5Ul`wU~Ss2v@SGjIwkcw-!8#^zJvjrzV9 zGmbxZ$j+F~ldx(%utPSv>tQ3E%it1k?1McJ3--}k!y{??BiL^k&=wD@-?^SYn%J4% zotWn^d_up|C1&Hq?snw)O$foiGbz^2Zw}}y_3_&?TK>4PVJFyw6>0LuC3;ple(1ya z#tDIZ6BYqmyx*ay$R{Wb%c zk|!DWztsSvf@h8Hi_~}b21EQ7wgIN-I9!BtbD&${1)tak>tj>MNm?HK(E;j*Q?QeB z1U-qb?hNc3t~zI!55D?d%sBr1+XrlldnNm0-sVrQ*mGDRS9oU|qo1wQ+VCej++Nv# zdvEW^i9HEd+#UN3A@a6PcOZ#zAbTX;2YZ$vKWY1^FYWAk_~zM;zG02sz;)P#8NY+) zTto+Y$IWlp!!Vv`hmQ}ezq{|f|J&dG_T_GOyIYIzbf-I&d*1V&?e{93OZjy;qaPS2 zH~P|ElApcjL%8qj?*-ha!7yIKuDaU54ms)zwqP4>$o^Khx>Z?erIp(Fx46YE+Haft z_ZydAe)%>ZcZ;6A(7|Nx`3U{MkMsP+op1Oewzu#dSz0sQM26(4KeCM;!==catkW+0 zC04GU_GC#9=@a+l!5G_X-LZ!_14q^gN8}*QR*^^Y*fw*^eVS)D?ya*r^ zw0HRIuQP1ys&$g5e#lUo-6C^+!N2~jHF^mC?HoF&rfr{FdjeKbMbNf{p2ShVTJt2nGJ_Q&jw(he*x#d!$p?%#9okz^NMF4 zfxdsYr@+vcop{+w^v zC~Mkr`|Zj%zE;lyyVUoYtIRi8uim9^{?#U1TxN^Np8cZ-%)y$1V|X`T`DCNt++{es z7#CmY3-h!dcGNnpQCs|gck9v*{a~-i(LT|q}~0zelm#u4U~nf2rwSZP5qK$C$P_CCr&!@Br9#`A@Vj4 z_d%X><0tMI69(*`@n8>k;EV6cRyu#>j)2`UzI{~Bdd;8RqzA}F8BWo~?m~TM4sW)t zvO0Ng{mqvH_HQ-@hmJqC{{Bu~)BaVKb<5+zIf1>Gp78_R1M$US zz&?;Od6RMWLaaRdqRg0rJnh*B&opp_yzB*;3V4b$?76w%srvR(dt8JiY1}uKJ1T8p z(Ejl~=oIDFM<#gV-2vYx8^@osF^qWE0-njq+UWsUv{&SeTb?yppFI(iRdgG@A3H)G zxI-p%BRx%DyJNOaGJ$(`CGsHy=_Ls`q`uf3^d&Yw^DsVK;0&(9jd}3r1bd3BY$whL z{7*iB^9=n?2f&SS=vs5X?QL({`g7OK2a*r#htHl>W*=IAXTI96BdT0ZoI9r+ab&$m zu4C{CfAOndm-6qrn>Ad0#Z|4|9((Ryjy<71n>x5YbFZ8|ppL`eTX*fR6t;-(Xnp(~ z_Y=<5Fa<~E<+Hm1*ylId2l~LikSpG?GuCZAYzln>yD%uNJ-*Q2uu6skE*OVi6WE!_ z0y(fh+C-O1v&&?S*T#bZykT>2m=8mr(gCjyL!< zF5dV~6@0Tdo|9NVF0+mF8ZKKW3`VEn7CoA~O?*?wevk|K(1mo0&+Gsl4F`N9`%bRm ziu6Q$(WWoUXKnjHzA$6o*hRc`_Qex?#(f;Je{>+7z~+QI?yuZ`t+?Wf&8MSF`0&<7 zE__+O3|{eJ@qvEfXC1I#^~-CRd)%UDEM#;1QT4ru%I7Vr@9>3s_Sj(K4a--z*sOf| zbDwEG#;l6Z<7+(_H;-9|)OUL~-K03WnLhQwckz4>huI#w z0B>-{I>b1{Z_vgb!x}w7C*qB}ZFN1r@tL2lu643Q)&sNS;*EFxaoN1sK)B;W#bAo zSSv5#-*Y^=+I+-0W1ANrRB-lU!`V!JfN{wN4&$?`qdnb+H*o6QlDnPwW8sgOjP2Vn z?%;~Da7K(n+Sp%v2lIk_{jneTPJUz$FJvutCuM$cX3h3NU()s!AJ~5wW%r$9=s5k; z@q7%r+4BM4!yLz-ds%a5Z`_%fFFne3;}IFN8=hmq3I759VU$jAw$Gh09b#|okC=Yc zSGYiig3ma}-x0_layR}UPx{aH1un=`A9R%VVt5vOrbp>MK06zl8-Z9|^k`bA${l)rD z)t77d{AbIi8?0aUKd9oXV(I83>i7RDXWZ?bws1k~Ys2-|D;rmTgPdU#=G{T#yRps1 zynJSB=qh8GKO2QRd^mO0$r+u!Wq0WkShIJ|PtMi$NLs$Mz;AU#UK- zGpl_iTlVdQ8cwNq&4*PTa8Z3uDeUs~uT{=$P`+I6rqr=sxF>&H@%+<#oU`EQE_b<0 z^HJPc)6v$!AMV$wt#z7r^rE?t7dZ)$p*Wx2so67gn2bB|mDP3rFfV5snAcW0uE7!= zYyZgsSK!7zX=6S9$hp8XdfaiofK}&cdeU0x zcF#9G>*S}MUTwyaHE-Ujr!?NopLa?*Vb1a8$m8no`R!l*_1*R;yY5nd`{~g7+oE+G z_X_%OuEL##Ek3ktvQhm#m%29kgIy(86jKGB{8`_c)pAmlTBz_vSokULz*hS_sxcDBUxAO4g&cnUu- zk1v9AvGax=_~bd7raCVE1DaH|bDhy{5$lkl@~)jr@PUmX_643E{77UDbL z5N@U)vCo2fVs*(Mf7u<_fj3x!If3o5cDOSpEYq9bXK@z5G5E7@-t)k9dL5S5Uw55$ zCSiAWs=8p_iS;+z>6I#@!;h&xQ(f1QN7ry%eMhf;KBmUAk36azaZG)W_=x&`T3x4k zlPQ^Px6Rhgez7FqAv-c>5A;b!&Rp`efpY=xhNZ(l z@`n@W9XgB816$@FKL(!Ye)GXGw$}b=Ydrb52v7J;K3i>3e{Z#(=Xb60aChd6GUt?& z%iL4vm6J}HS5BT+zc+JI{al}Kolws|{Hf!r|29|toVn%HdG+1@x(=8>u0Hqi-oOsq z)pK9fH*}|YkW08>T)J1_AMkr=zJNAw4r+?txI&vS7y%6^y`omsY zzq1xQj{|J7IoSu#wQ$v3aS;FYpMItLAm5(w0r)faH1C!KOZI!I%C0=zRce`*d03Xocet-xNx^d*J}fByH&pKP}j~LY*l?u_X2r8X8WzTDyLOl za$S87j14*Ev;~W}QMq$mwLds=%n5VKoRjMrs$%A(+AkY)>Vngn4O&p;xqrQb{=pSj zlnd(j`1Q9#9cSw;>+eq0dn4m*vDsG}pK$iTdZ%#G$#Wb2=mK-3%ji6|)7oNh=ma|5 znqctFZ&|gh_Rjh}@73PgVrk)>tG~U5`b@RPtH0|VEq>R#S1&Jn`Af_5o?rhBmp8t? zJmZ;9Z~JVo?3;H=z3Y0H6;~+FdUpLclRva(dH)*qJ0JfW)>yOt8w6|B_i@%-qkM3! z`kU7^to4CK{|?q#A6T<|`ZJ#@`|P{u87F)HK| zhE9W1=Ouc?@61g1pYPxJ&4uuW4q&JGzve)futjji_RtUZ$+IUIhhI8QIiApY^o#W> zbB5(l-0_ZgY_T-oTzrQ++~LNc+-I@iOp)@`6$^vqsh74s+s_1j@oiv?A3-lV^TL$- z3!H>EdV@}|K5KCPXm@^9_wQWS!=`w~zh8a-%(G3;EBD)buQKPv6Ut@vH++s}_p0p> zAI}EV__)fKqmHRI@%Z|kn93ErIqc|aUu!;p?S#s!Sx46IhE|^9+$Hs_Iq&3?+8Ey1 zIi${Y-)dvr7r3|M<7~Lm2JIa-yr#SH+V>LZXZyg$ur+)%dW4N(C-LX@ceq`-)19k) zm%r1bA$7#skUKr*jMvBZQr;czc>A)#U6wD)uW;wquXKVu=@nO4lxJV;$vakiW3%0d z9X`9Pv-Y|TXSfkO;SLVHqGPv|RGo$|Ne-lSq@Vl4Jo;YuO zxWW&-!EO0`Wb@)r!k;_z=xX?5W7$r+!9Mb**dV-drge|XPPnI%zWd$p-qP}X7FJkc zg&VQna{v$QkGSU$eTNST+6zA07eSr2ueGiD;RKt*4~>6Dze&>%Y`)*n;Jedj?t|Hy zLl3UsH?H`ed%SzF`VLrq7Y)8on}2G#@~SJ!MHTOOG5@ssZ>OC)zu6Lg+9{QL?8~Xu z|2w&!pXOD&z;8RH+M2o5{>-cOkFR%c^UpZF9A4Xdcjw1H{&6|0eqO*{Rom^^=&_Y+ z-bpyP%AcL{P5}<_pUf4u*j(#iZ|sfxKhHV%c=ptuIkQC`(O=O=i6d|IQfF9fkD{BT zQ(xVr6U$5BPu${`%avEW>UrhB{p$BlkEy?}w(ouoXE@J)!5_BA8OVO&jq?JWx>s;# z;p|K&@Ll-|>suvg>ZUl(Ww| zv&^b}Ih(Y5wE6Ylo2qt&Uv})A6U&K}H^-h>{j+-BIc84%z496#Re6Um?gj8`))7ZG zjvYMvu(Dv`>Ft>g?!bcgM%lTG>%Gk{etvBW=bU*~dw0~`H~V2Nd^CE1o_0@QpWFqK zzdfPr^p(3@cMN=2`cUA@#-B~BoIgfpumP|7Pa3X`CFWPCN3Zg=^{=g1o^%3iKkJ## zE>C&dD&<+vS*5(HmTj^5mSyfqi+-0&KjzQRv^VyhUceiD^NZrAIWt%vU6=3i@l~h8 z-VE9=_J)lo4}KQD!oKs1b3HDROYYj8tMMOi=mmjK;5zT|16Fa1b6x8kV1v^RWW+-4KA_jtqh zTL)fkTF?ESYaVgf>~hUjSGK$6OD_ISnKk>+a%$y(^*HCxt~Pc~ZF5AO*YR`fbCk*n z=Na+QC)8*7l`CTR(fl=brT5cj*ZY7uwcTNrLnqe$4zBZck6^v?tBvyR=Vg~(Qr~|& zv^^hU=lE^*$9~`~|A20@SM&hfv9Dxdj_eKIxXZ9V+Tmt9FI5>uR^%o4>`q5{>hmGo zdE%(pXU`XWmZtmrpl#otPoCG-jXMBiIGxj5tFd(QpzKTMOUvp(O= z+F%hj@xXaN9X<}MO5>Sw{nFv=g7pdP8$65I2qqA4;m)(xU4L(}uEqK1jD@F_-FICi3+wbwH#wU>ys^pdpYYCk(_T0O;0?Wy zd~1}3J(v!z$yr*y{Sc(_i5~OaQSWzq56r(^KqhpMIpmyfUebcJeXt+qGpjzo+HvP? z>pAU9^$vfh@`;asviX*L4E_Rp~c-K2Bq1l)%^_fghm5BL%|LoRrhGmY3eN8jEZ zAuIdhcd>Cge@W-=Q$Osr^ZWy=KjPg+@4pDnN&2IEve&SIZ*(r7R2;t%r(udO%BP?& z=n?yYYxZ>ydZ6;jdlmNZyy{1h!`T(%2USd;cKR8G&2V<%ui=q90k*^Q8n(pUfSAn@ zvpIO=PC&Z@>v>5(`&Vq9`K`0dKHuE0`Buih@ci@I_jG(WmHqSmxV`t>qdq%Yx2?tf zrFrp1`CTv%<796S$eu5!PR={lXWi;M6WN!%Ljkt}Oem+L=pwxH9pwIZWbKXL!N6VT zDtAS}y)%}(3H_5Te2|;@Y`a~(Gu&~}zi&c6thes^%~zy5?FG)*NBY@3;fp;LoWc2H z`m;s>PVG6J>~}P$`wwsE3-+C#W?XuKyrpxmORoG|957Ed)49`KE)2pOUx5x2=pN@* zx`tn+++8+Vl7};>GbP;9BhK%5>o?i^zi>t#`^*1h6X{m_%pWla&jE8zmB-fGYn);0 z*d(&IHoOrUFRBhOM|-7DxWXIjq(jKz*vbW*p%c!mK9OhrVt8dQoKa4kSAUzWKG!_q z_%l>#~!e?+7gFEhLa+l!Qu|D}=WN1%z*m3*js~vFQqIaeb*#E$G z=k6Ti`2rm(&;{0Q-qwek^fbSMF2pJ8!zE`ZcZ$>Pr;!2P%&)o#Z=93aIeLlQ`Dye7 z+wVN%EI{V=0@mqwPi>|u^%i-YEbkL(XQ zobBxgUB>UG&&iHIL1*F?z0TJ7zKP!^vp3<3J+U|Poullpw(Ny>lISz%NBuf0H;mMG zz_;7J{#z*9Y*G$Bq`pIP(4y}e;6A^ZzNV+)iw%J(=O|dwzcredGl8|jm@`CpGu`&a z+Wkg^^S8Ab+Z`ILkvqAv0rYpC)mpc?k}a&m8~>N@rtE*ZyYpa8tE*h#ZAdEl$6KW- zQ_);S;}mi&ryFmn6@wZjlf%^ti2(xSqBnyWP0S$k)Fg}vGYLXBbPIGhJpet?-82n! zfDi~%LjnY7LX&}@(W0WUG_YO2_wZQTx6Yvl`kX`gRqeCiaSv-f&suA*_x-*Dlkml^ zSU>PPI7F}XVBUBr1`y+^*${q5zhVG-=zpu!K0X;QpC;F1yzVhJ+uAWEoWUb~@J-4-Bs=-!jB}iz-J@YnsL7BXDDQ zCbPc&FSWJ*v%W@-fX|U*<8#Dmp4AL%|7A-DeqFw}{)b$xdFKD!rET*~H@6E*e%3g? zlk=r}w-nA!Z})AvXUOj@sOLw%>*HNf^4(PZnw#;9rTH7rJL!H=7&W=xDyWEQ_bfZmei=}$roPNe9haB{> zr4#?RO-y1wY@Bi79G#k*bL_0P@P;p?J6vP4x zu3JO1Nqnz$1wNQt^3d|S#%(?AxR{8J=!=aaC$<}J*b8xi7(jdEOD=T8|5(4!3!dO} z#)(VTdXB>axy47}F56~|X$KFj`*9_48eQNvP6s#U$KRNf9H(`qd06iaHoJK5`!JrJ z@HcF`cRykImYY5|#4Q^u_lzTaj=UM4lQoT;+3Mnochx)xl}q!EjojL>=Bc%h9Klt^ zDZa_OY#XY*rB~MPa8@2cE=>MGoVK~%K`?G>D*n#zet5QoBd&uj`589FIN3e265}X| zz469A9peJupzzh?hBI_z?Fj2~4A$J_<2QhC#(Vnoxy5u=J@*kSV{5A!g7K7bCy0rZ21^2K5TK9s+458p%=WP&HS zs!cT>S!4Kr4pV)^7vzYmd~)8+)F#a13jMQH{95mCzC&K;MuCGTO)gE#uthM3d( z2Cj}OuJJeGB;OBk|7~Sc*A_o+yY=&f-1r>NG5Lt()e@hn`{$j>t62-lyLsjOOGdcG7Vt%QgI5Z>!N$ARb1V3y2bk8^m^ap@+9Z4b zcgiRJ#{4ZUc?81P11z;5C)U#=ha2xgKOfEoMjt@urTA!{@1C2i7$xRqbB zzVXZxcjU3;v&3xHHgZ}x#6DQVh_kF)#A}}6h}Xnre33Yf4^b~GS@1oJmM$CkcG;3E z2A|{oG`zZaL9P9Zx5g(QKd<^17qUBY(B4<$YkY6vh{|=C4?9KPeVhh=^oJvG7tTZm zj?0yh9a#nMys_4XW$QLJ zSlq?7SnH4l8Nwo&lDBb+jre2v7CI#pyvITMW&`;wIMg0Jj(Nl9(zU*=N!8}hPsdld z-}f2#7&?T7$eYvMGvN?`*$gT))~B;$$q zYVIq}z~AQ2-8ige*>8SSOeg1j?RAxdem33`k9jsDPUDNz;xaLt+%(>Z&s>LV&QHX5 zs|NohUK0l@$EAzNd>o5X3x$^O6~eOc2PuQ|ge|H>!H z```@u;Z5&v!W(k4#vyxHg=euWp5(V1*-6)sKl?}iY><7tv9BN86MrnMcWX{Pt=^5T z^^JG?HeOrrz1EzK$GaW;jk$QffPeEV?leDqHde=si)}YoG7wAR)pYYFb^>q6RqO=& zFlwB9g`A$)Eo%s46T`6?VpiPeFRjh26&x1>v6Z+4xA8adNfzXXA2`Q=Ei9pwk>Wo2R_C- z{eGv$`i4z-_$v<%@1=>CHWmLo_moH4Ts~q=aYVc&K1)7rW3AZ}ui=LLviMB=HoTWs zcov_@r(LBZ*fTg4H?XJPU!rp|q-QqQI-Xwn2)w~P`mrx|gCB8~ z{Fyk-`o?eih}&>aEUhnn;F$Nga77#gV~J7NP5sj+e}jweGhWAM+TY0ilST4=Fe+DI z&T@+4Q|I8Atl1W^COxqcFy}m5BsSw)$%*ZzGxKsES-4kR1^@gG|L(Xk&^J8OuY2L# z8XvFqCr+c^etknvY+HB(58^as3}Kk==4AMU}5)$Sbn_MWHj=zQ|TxkGM^4`r*_C-Nm5b`L*1Pcct(!7X+f_Ho8> zyrEmNB1?9w=Z!Un=kj9lycbAT{I&M9n|PiL^z2$b0Z-U5y3lTT13$(oPCMhw)7vSh zeWH068gJ;D>~X=|V;AK)-2-26B6bsd;0e3xoVoBb?8bESCjQ2nj=y1R%!_?77I6~a z3+FHu|4T>aMJDDzp8P(}vw3u44a7$96}SZZ0P zIk^&i!WrkulAmIKj6<8`Y+Io-$HXLboApg{6v2`{*f!%f4*mw$6C=^pamO4zk$rKQJcDw{ zf{TaW_efuS-il@QyLH7A-)A_c?1*Pga^^6c*hU_Q9dnK^fXCP$wi%zypPu;dtZ(2O zF3FJ|_zHGizK0$ZJ^)7WjZJb++)Q6|Dz3sCvZsUOH*ITE>_P{4GVtc~xx>4De2p<1 zA6}}(44%P?1H#YXkgmkoFv=Ea6Ej;xw{{T{6L8a<;}GcQG%SkcHwoIxaXa zer1#7cX7uY*j;+mh8Wwm)&cYd>+ZuBJ{|7aDYbn*T_0qoxE^ommd$dEZM3ZoT*GJM zU@L7Wf69)UL;Q{05uLy$%orcvYV7Pp@&b579&9Ro+V`DK`2pBnTRHaY3*&NHa>IVZ zhA(1E@Q+{MZ#Nc~wVO=iI`K-?#BD9IkqY({FtE z{ZF>pcX8yOeTR-8<=3rU#Q)^QuFwVEGI>1#6H95?rfcHu?!e+U*%{BKyXI8GZo8|GeP zaE#rB4SIGAZ`ft};a{yOW|B9a-&hCZ2LER~#L)V|Mf>a`y;#$@U%@;3?52L$ zBiOd$%F5}f6t=K$Gqvz?>+nerT5hq6wcSwb^MLr)sP$Z z&i|HLi!3Nz55F-`xc0op`)i)-EG>UAY<*j~wyG zI)!e<6ZB#3?qzGl@8TlXVi4<7;dV`1K!C{KgI#y zc*M5AgMGZAYj%!)*+Kd@4?ao3vG^PFvX;ST#c?*mIOzpvv#joOyIk?yLj^U8} zfO+yS#={5PdP_aOS-Yk!obP`UEF5r+PuG>Nfou23Hz?WHkM?j5M|^km+%wM@)>Ow9 zR?VNh*&_GwEn-d>A|rC;NAld0F2Wn~V`t12&lK1vL%!B==h^PqGk$_T_y#)Ho}3&T z3h!`%L*&m+YD*h@5?dusWB;^CAGpGP(GR?fwY4+64_&-D{HTu%zrQ$t-o)<}iZyW& zCtWX|pcC_@JG|k8#0IcQzkC#(SyLE0jE;YcJ6PYHHGdP{@WW1zTk|!4GRHSfwMf!ez&CZaR6Bya3*?SH_4ZWX-Pd$#NFf z4!Gkyo9tSCg$-d__#AR`54`g|WI*3!O4jtnPO=T!hdagGlLIhcb9cX(3_pwk-fb&l zEjojD+z~H`>)1Onq;cT3efWSKW6{5{=pSa}QR54fL!}e>2DXiDr%QUlB^ZtF;R;XH zx$7+7oT68NW;VZStubMABiDTG!c9`FR2e`s+$p-ke;tiPJEwz$C7+Q+&Ms**P*11F~mq51ojq&5Qi$ zi!R{_Zu#xVmQ5jRg`5=En~Q76nty;xc9CCB5pNl@7?*zN%JqtNwK1-p!{y5^y}X@$ z&Kd3a6OU{s%sqb4iTICSG*+0xU)-@yFuwSH`Y=aarBi$|Hgk{7n6BqH@ipQ-?@W?8 z4)cj(0`tQk&r8@aYd!vkuOKI}iTTl``{2nu$coOz4D1B|!M=&L*%9{1{$$1c9j9vr z#`}`vskJ2@hJ)1Ewy)?{VQ1k+tivwj3_O^V@w0ERXzt!$!+(B9tiaCGmFr-Ujj-){ zaVc3SVnBK)AM*%C&D;JdHNO+ff1X<7$$4{jow*D#Wnp&xx{3dHZQak7>zfYP(%02^ zan><1Hx_Zf@ye+gzwsKk@wtyK;ZC3IfcOgD%?}6E3hd!R)-ipZ?OdDt)Y?+q-xv4v z=lW~r)q6GPoO@>b&`18fei!PHcG{_@4?1x_d(4Kj)8-6k<^WUX%)a52n9p3?gP;7* zO!=FhH}oOakz*krF^V;&vA`Ff!;i2%Y%x5r!OrpXuBCsxVYl#~Ph}glAqL|2Q`>fr zBA#Ve#C~igo+@G^@vJ$JH|$?}cjD z^UvYknrrJf2v*kbEUcS|2ffF+;X1$7v9`VgSN~J`n)O4T?yqVLudTK4@Gix#OvF3k zjkTOP@lkv$-mn|-P4s0Qjhk@CR)}li&Nbx5U$G^LaoNcfv4?YPBRTPPYHgAS3_At` z3JhqEy>zemSz%B0OIEhk`|o#P+viQc+g`u-|7k}ZedMs-fteGIpF8+bc8R>%1#=RE zurFi}2ga{2SjA8Aqkj1twt6am)BB5E%@L%HJ`YWP{3SW7VVRuZ-dx1h~;pLzwy0VGO;$5k5to>ZFOJpl0Kpb-v^b`Q7hV_54xf|ZHRdlYd|&G{+IHa zpJ_k#qkFa=_`(0terB&9Z-*aoSX;hq;(w&#oAI)BY?*EGnKhhsfOv=vVZZQ%e`OcM ziSX9*Civb}er)eP&poXx*%dkwi&@{WV{|}2>$x z<_~vxrP!v=?AwN6HM^yjpO3FlYoAQCuZAJAQ^(%rnB$JA`vOOJ4de2E@Srbkx|grv zJH$M22Agm}_i`7oY0iIKehm+epB)vSDc*x#xqf5Ydfb7ny?#!6;G{omm#^Ji-+^9H z{^Qzq>B@EO{v+Pr+8bWfzI@yvZQEbGvHkr4uWpZg_|5HK-}dtM^c!B%o~ir(<@aCS zHePUA+jQF(+x~@(*Zy>UNB`A7F>q9TBNpVNjh)>vNAebn$!CZI%$4tE>u^NufLF%r z7@rlnjb+6jDRi!Ai|rR1(Xqnrn?KB`U8C^b@ke};Z5(#ozVh1tRqu4XruKiX-Xs0> z*R{hA|C>R!@M2E%%Vxojal;$gi&fZOh{{sU@U$fWG zwzt0REw%st0ZVWX({#qJi)F-zv~d+wiKUGxFsV$lKb{arRfuAJ|sJ`4`W>q+Rg0m47|zu;JcQPCj+8%Q%CRiQlbJ z^<~WndpJt|_=i_wB6EiawoUv1lQZ#uaq-6Q7$iT8uVgB>#O~t-jHs>2jg{=46oZ%40@P@re{7pyNC4Z&grY{__=AeUM-gWNl?{y#f!-(zvT-r{p9deg<;nTGZ zZ}^?HlsUTIe6(qu;#x5ZzOa=zl-Qo`_+as)*g!7JnrV2K^QtxNONaeQ`(E+pp5yiW&?fuj#9@VO-E-FA?W*G0he{Ve@~VnOU$y68^MB#zCe{@Ef%u9o6GI!X@v{&3Loep& z8a4v|=?{i+-&!H^4{ySg)Zq}Fvo~bvT)2{LvU7~?`%2nUr#<@e{027UBmee- z`VPQ;2MqR^%@v!$6CU6?yM!CAHwT=7F?PW*IMcpZLM}o7{derQ7jNz8`N{m2brfuw z2Q2W7WB?<4t?}YHTV?IfX2X{`$yL!Eo5oIO*hdm{khgN2|gMYw55F|`+TOhJp&S7Iw#(oYie`hbKuFE0p7%> z;sHKa9MAW_pjbgXFW&Z>e{|(LYFDkjw%zkL`?glRx%-%d+miJ+R9;}!@cT5&iw9dj zc}VeQPJ8I&zi8k1=~A`*EGUwI;(ETnJ}y*gA)O=9kz9b`OUl2XkV};gSr= zPa&^59`IB|pl1I~zVp}^f5!(tU>=S*!nYDs_deaXq*p}0= z{$bBz2ebi8+I3F<;srV4nfN^UV=)~_TTMOe|)(GNo&wI2n{2s@~>)Qjz?mzgO zd*{}BerxN0bgZo$+M4>G(Q7ughmJpJ@HY>9;{UhDKlXd=dvAJC`{#GOvORkEJKKFn zysQ25+g~xnai2f$*mnJ`w+}I|-{?2iAO5i)9r6JFPXnHaT_OW|Gj6s_tcm~Z09o^s zSxd8N>>{1e0nE}J8S*KK$>glbkbK1DnRO}s$@lO}A=7%?I zld*|O#K>|{Y*?^vymTZk-~+77#A))uc!P6nh`a~+vdLM~WIci-3cZk#cES;LIAeQU z*jLw+uj|y=*1q<{UF2>|e2j64fz62y_!;9e4>*H=y5>jd7H+Ip@x~flUWk3b8|{#h zdwefoNqw(o>FR6RV{;E~?YF zTINrGt^LzGU)k1OR_{Ju&n|5QpvglRwq|p!A?` z`6|AHUuTQx0=^aYBF}u;G8`fUu?w!lq}WCtfsVE?zr^ee_;>s=eZkZ*VpZn!_k z)Z_i}v2eukv|-zEx>ndW_6$}MbHY1$;}krz&Bg_DbjA0vgZM(X)~eyve)Aryo`?dBvbJ{;1^7HNMAN%8W`>`Kr zH_kh!J#fr>+9Q9yPkZ#p{n{<(ozxzC|8KN^f9p%zBZvM@+giTn;g7$)JzQ;ky|&k1 zbWXiPy{?`3iMj0$OQr|x|L*pdx4yak)X)C(AY1Y^E;xx#6_=W`d6B`8=kPoK^&SiV z$!5H+o&k{q4!WOBiQVdXK=#4=m>d0m_|hK>XL7$<+tH1fg8bnGzW8i$3O%u9_#a)H z6JNvsDCWRt(kDB}kFg)_kqaRYG0;r##(F2b5i7%scou)6`i9>w2w&^$VSY!nIeQ`YJvOQHz&)U;24#0!)!-w@T?uv=!1o%HT zi9d%i+lpMgeSL_5%njH1UbVK3ec`2-wN-0Z5BqCZRXnueb8Yo!Kh?hT2QP0=zH_hk z;NkCRPrv15?ddn}(b})fX-^#Vx^~04C$(*d{Az2z{G#^E-gDZ$r+l#8cItmEAIL7~;x2Cs-mhb=OH@`WoDb2;ZVUCNz#ByR$c5HZet?u
uVXiV>Zo?lDIac|&iZ6~=-79+uN?RP+I{6i?yUUsvZeL?m-FVf zZAbjy_Tb0=sNJ%#ev9#%4eh4W{<=MM)H~XP^<3w{!}e{D9ItFGuF{yEWSfg!N*S>hi?QQ85%i1Fkf4zP6(I?t%U#R_M zR}B9%EplPg$VMyy!*topnSAMJ%#D5@7s9C=cg%h6SM0-!^@(^491zpC+cUe!&eK7pL*%-Y1cl;RpFeGB8K{ zFh{(>Gq|QdaW#3tC|d%%);hNFhOJ@yn{C~bwSxP-_rGY_(t$g0y?oWg z?>b#x&o!5Ay16Yazq{a?`ajW|ztAqPeA;E}t{?u7=Ve#d^OYNJtu@b0ZE>}+dd>QF z>FW9)B^&B@1#h^yU9z&?RoGPPoNMbI-}S6~f^qmCA$WUPVQEdh$3w>EZ7jHA48{lZ z>bvf|yM6VmUmf1Fd-Snw?a6=m_J4AV9e^$JV6XXO{s$K6$2yKrfP3xHV`2cuQXD7u zem|UZz4Ls4{6hF5ZWrsar+5Mf`m{#mL(P?b`KaV2*l3*LhxitGHhl5iS$i-_ukfcG zcAuS_iF{g~-{1{f4S#YW{L2s%7iX-A$<*4+TA5wP9kPRMvW~x}8}=OD;F(Rp8!}5? zl>fmU`f{9X^^cR{qUendw4a(j*e~%QJ0!OE?hAi{FP^oqp>&ei%-Y0y%y(+|lH|>B zf-Q$h$HfN*_bqV7YvN?~Vkw_~dtI#H)U{_3D++{;%bwONMyM z_i@}OcJlvp1{<@wa@2+U)s=64AJpc`hv2tyim}z?MJMFNUpbC5 zbO@K;6%dDu|IAr?e2{tb2XHRd#vw9w4Vk*0ZKOX%PS`O#z-iYSmy&guw#0)tX+1^W zVn^eFG58vA2-}5W_Ik*56^D!ym*C2^w(-SrCH`mSs)@FIN7MJ6S1hkQ+KS@CvmEkW z=+$fLw*cxqoL;@6VyHR~`->`8x^kuOtgUQUEGdpyztsI#mH%2(Yo_HDTfw~lQ{aC` zu|faI2lrQ-&WA5*^T2I=sr^qYIL^3j`yR6S;GOHpfz6|Dc3bSGus38V&kLJm3IE>3 zq+9WUd{FG9ZJ4mG5S!B-8zzqG^Yu9Dxbrw^?a3dR8|)YxTZl6XT(O}rC0@fB1#Ub~ zgc~~NgV<}>HAcMgI}P3)n~4wJkOiE=H*)a;2YS9=*fKfEWU^> zA`iHwU;1XV*dqQ_oQ4DRhLh|bdqQtGL5JkTFZYFSr%(5|*FOKvp5Yt+1`qgw1L1CX ztTq<-F+SK-j1j+Z$(Yry!z*pUntK=4Zy7IIaz*`rnoEY?v++OBF1+ZHc4_^#ncoyx zaM8ShGyZ=K8Njmtnd~>i7S{iPo_ERowyZca#5oJ=*?FD6=;Hc*X4MNWt7r1{-8KD? z6-;?gXWsma+M>mm5BIs=n8K&{M`JbqD~qSL#d6`gxtOo-i#g|B$Hb-dX{~PE$sTz= zLiS{Co8IX|p$~de$Q>V?Pkzg@d@+Ul=tdjtB%RPVjs+|H49?>Zyx=6?!VX&lh`qv5 zYksjZWgXW-Lsy*~Z))6-5He#iSf`0X7V95x5*EWGv}vKmhLAil^r z#hzq9|8#8r@MqrcX9LWMeAqsEvfhrpbFDn1!q(uTF(~@LL2<5FL4HhIu|s5vw_-Ii z*0=j$1unF04HKV&2e2=Ggc*2_?{N>#7^8b()qVDDI|i%zGgq;L`r^X>XD+DU$f!K8 za~DU;9%)m)6{!g&+%q+@v(Mz`T{w&#C)|6X__ThT;N0^+GyION zHqNhW_2vF^Yo2VB{>ju_GFGw`&l;;(&;ObAe`VMOxin*3UOsPe@zH$g!1d0{BjPDt zvhAMZku@8tu;*-{ZLu`FYTGe*=7-dd!;f`_bK;Ud-XizN(s;%C(YrQq$D9?IpliHw zPJ8gjHi}EUFNv4dNy#51HW~l_q}@fJK0eE~|HA<9kwxND_9yul=WrS);2E~zg)Zq& zjX#6#3Zro1TDG76f^X;P8;0o)p7{i8Iok@n!!bY6=ZsvB53Ysztnb-a_qbQzZ~zZ< z)7xU$rz^OLkIcAX2yX1F$pW`rr@$_KYBRQ49e&w{JzO(C#~rh;9el>ezK4h=hc3dB1Y>s4RjQS$S#I)q-INwG`#zQA+ z_t1xX)b23{*SbgBxkv3fGB$qWFfO(i&*a0*gK) zYa4ui;)y4wWnB~gg)?gZn}JSo*4iJ|t>MI?Y@6b@iu{hHb8L{Rd7tn zaogmSLe^vtm*FM7nG>7lK5erT`XRg6LbZM8ZL9SO@8%KztyDMp7ghwzveQ`V-(Vp@2{b|?r(SiBEykg972dB2( zn>l1V++#1?Yi`Dlf8?e%X7|H^v1<<=;Dmm~Sg=pe_Qh=K%#(h!L)YY^o%kZ0!Uz6H z5%;*4Eyp|04RFg^LLd1K8_f7$JTvhLj~;&b;ocFPvk?*c@^pPdc*CmeDP_!#HfS=deMqFhFKvYK2_b4n;rYO@`#=I(Cb%f(`q@ z1lh+Y;kPm9117>3V^HjCk8jnsq8~;7j%$OzGbV9>HEYhh9^cs*x)Q&T2fM@Oi5(Pe zsmTnUw8viZ>(1r8YqTv6)*kt2j}G*szqIK(a$%RvMQuNAXwxx}XnoVb92& zA2v@M&?hX%=is61or}E9f&3MG(3coO|HkQ~{3VAB|Bt@=)Ug(Fc*c!1@{|UYw>$m%Bc4eEddHnImhjjx#L+^O( z9U{39@6Y3p*itUuJjjZS*b1>5-jFF7lLuP|v#^a%b;L}(DK=hZMO|D`{2 zQ@hW&U8C5>eQ~zy$r%Sy!X3JDUT%y`VOak#Z@gl1cpwAE*>=atnS5|Ieh%kS$k%pw zg17eZgRP?%dc_-KU@M%XUwHBVAYs7V$<93FcI8^Fx8ButSVS@M;B=ga?`FkdpE6LN7*tOvt#`QF|4PP=yy z=!y=*4Kj*Ngl+c7F}_L-A9R6>xJ3u9(HEK1A6>&F8^k}#S9;!NUg8byxiH+Hk#V?8BdST*tPMo#zegsW@GY@A{z& z+R!VroZaHo~a=MgjaAVMspwR`OY`{ z?b!|9(6@H!i%#Hx&Ea##zGb}se7WU+W|9Se%7)N646?=EVKPtesNp#~DIdtD8LMKP ze2#hZ=j0$)!l(M~j_*bCS)QfgpWl)Y`*}x~e94vF!4Gn#Q`?Tw39Q@48@e}Ec%c`^ zVB0>wl9-JCa8sXrC4S+c>y-F7e!}?G@^rSXjns)N^ubSLp8k)b>-FbY{3EQp*Ku)% z_jj}@c4P;&XRgL!-|<|l9k#(Sb0!0I>;=Awhn?4szRZz4)Y^<~&)CVyG3Uvd4Aj~d zyRqZqZngtg$vbk1ts;BvXy19`*B(8k#&5^*EB)|6+EU{?U*Y#W6u+(Vl9#+>cvl8z zVIN0uhQEP*-`mGqoZxfwt`yvOM#L_9cH~~;7jN*ze7`vFsi&TLzO>lx{oYC7FZgM8 z44&Bsz5>trM&rZ*&p$ms7N6h@+hl%x4gW$;?qv(|Tee~fd^8{SjQ#K|+Ha(=i||h; zFhI9-2pi#!^V+3v7^O%0blmmuMPE6t9k|2~cACFrx8ifcoz&WbbH9J9jd0a@+iDna z9nM&H;)DKk+&SBdHt_0eU;EndzdeF&IO_|n;iLa;?j0ce?(Iut!2WvoR6I-$`c^C2 zSF=}sTgNj6wd1Y}mdQ$SjQxW@zR-DmGzZ&i`mk-h^kHn~rXMoVj`nTikbSlI+_tgc z0^5rx`~*8HhT-GHF5VIGo|)&QZ~;d!#n)*EAJ}ME^NxULsj%p~H+&A>Yfp}fU#4T& zBh%^p9zpN7yay=0vHnUdZ490vn-|`&8Qx)X4el6k62g7A3U>l!I^D(P8)1F|3}7RMeQ16_>$U~ zjfp*Xe~$Mx%s59U_uqg2;3r_(y{=O$u8Xafx7V)mO;^ zR$P(1o%?VF&s;A~WoyMM^v!R7qyE>H=PTYt7oS+;i$U<%+DV+l7s7-*1N_lF-vU#{ z?0W=kA+Eq0oM@l#Wb?J%V|%-fV<#>cx9j~;>lBz^t6|i5aGvilPWHl@!8@9GCU=KY(Y=n6P2^T`+Q(e;S-F7{bZA^t;M7Jj=VmVhLPxpIAv-V!Zx0pwj2=tjFBRK6}9DS+~iXz%`rA=NLO*NmkZ4a7gy> zhBMZ8<_YuUZte7~Z+&avPS2Zd+qMmT;Gyf2t7QM!DSllH%r1&y>4kr{R^i*pPh3dn z>>}=nb&`jP&4W|pmzNMf(}iQfu(8`$vfbCV`;z-`YBFA>E!U+?b}Za;O*qu^rRPz2 zgA3scjyN8^ggdZH7PwSe>(QpgL*m3>^ zPV_0C;lA1Ym>d596Um9n8L?0Nh&WSRiZ^^KOyWDAieqYd59e^hN4)*r?|ygiIsBSF z*aR`HIL+FSFMuKUlegpA6{q_yD%*sAbZjjAzwyv7`N~s^i|{6RavTQu8?qIj;6QA7 zu;%(;e5yLw&UyPi*ZSkxcHVY>ukF6zivHEv&c5;CiT$208Fzfjm?ObG%;Q7iC_Hel zSXW+COhHDlu8^hJ!MOPg*RnP2q*zNH)HPxV*ZF_F;$Z6z`ryCPAKl5(c?Vh_KI|s_ zi#_CUy-RGL?}q^~mbDE&$OquH^)IfTc6kiwQl`yVo3(Z?tdzuHg%;_r%(`MV@IFC*)7W zJAA*ju5~!Sk2C#;PUsTP@Jg}%F0gAU<<{mYs?c#~s+Ep{5;@L7zaHFqbIK{lOt>%0AX=`xnPBN4S*dId1z&;z_xbQy{EU9!fd0i`VhOex_tfHS_K&_j z3uDW1hP|HZy_#TV=x8_n*^xG_P3XWp|xnMnYj)f!kZL4!G+u08%`Z~N|2l`^XDR56l zu1%Zza=mkSU|nu~V19DzYIAhlz1D^K4kkJAL*fMTqZ`M_(=j#@{^^wLaYf-fU|-yT zlh(P`mvUq{qlO!{jr`$A-pGBPttiIDF5{)R%d=p%*fG3O#5UqIcyZh{Y$`j*_iWj+ zWxI^%vEFZQ_r@K(V6rx0m+gU5eh0?M#T@pmwLe>6TW->v@G9#Zb7gPX3CBI_5!1=d z$&rz#Tq6DOHP)+qF8$;~M~MfF&sxct_&B~wzwD*F1gx`})`)lrC;AmX&_8?yf3eTm zhdWs0SMUX%=NHD{5Uy}UJmP%t-D7^doognmdrtJ*8H?KZ@IbL2zSuSf<8jP9`3l>{ z;Jh`4xu>?Rz^>Z!c=ik)W2^AQJdMY7#tr*oB+uq?$r?3rGd-|pY-^7jxmYn8Zi=D# z1UXSQj%_tQeAl*oDnBANr%%{`rM)W$B@Txz{aarhRDMW%Fhb|>$QJbRdoVMbb!0(4 z@J1HmC-cG^ST`4Vv}Ti^!7Fxyd@@J($zQTr^dx4*7q-MX^20@bif+hTPLb`SCm;GD zAGIQW6ys@&Kf#r)^~^%vfG=X#JSWsPKf~|Q5v+v^aE23#V{mGoup|CAH?}W!j$XwE zu7g`O48WOMJ20GWID}irQ)}CPFdZKWr?77v!FR58EN$Qg%)vc-V0~%brg)Aa9wIk$ z(>8mjuq$fk_+mDSjiWdEa@@Mm_r1tbyh~QrjO54mv2T1oKauq+j41ZS-R?0DT$Fo& z5#zLWkx!y8@w>GZF5rc{oNLu&Ew^AzB%Wbk<(I@WFo!dIn0<4aDn6S{-lT8#1TMvC zVoPfUK0Y=Dme>ojP|VdeVa3F;}VcY15T;ycrRK#1xog6S7v48X}?{6I9?Qloj%tpbz zw%8Unk6t~;2!C+Snk2bv`h$O1V4tjQaG!0}k0O3gd_ynVV59jTI$%4*h_Ua%>kFmk zZ|F}vX)VAevk80#KVt6i4*xjfoH?@>?3w(5`SMld!^bM*z&?_vbrTy;{_>vWPM>@V zS;Iff(<6CXEAk)KxBL{j=6!4ObUd-2T)$_lo(-_WvF+@ldBx7dj{e}$?=Q>gc`kuB zd+f2t@Y~R8=jj**jVai+Hnyh51JA2*{mWne^6(6g4fh=l*z{bF-G21ZM~Cz7QP_K2 za;;nne&e9=;fsAZvKGkrjM12k({ESfkGRJ?=!B0kPBOt2>--ckvv`QU$jbHPO!m2l zKFGy-ou42}*WxwKn?If7m3D#w_sJ8nwfO0NKEgg;n}<9CAA~D%*q&{`lrf3d@D&z3 zm#~h}Kbr+>%AMhqI8DGZ+ixqK%ov~JvL!w)DTAr6(#}9=p z`9}pFonyoKB=|K(zKU#cR{!4nBR@DZM%ZIZ@WxsK9>vr6f+w)Ake9;FkeL|BvoQPQ zPIquhR_qa*MyC9iIFK!)EBJK{-OB%1Yq*~cWfR#pTwy2a%o>LsHC{H%deazj90oiC zq*I(#U8J&pN_-6NbZsR#L?&*UZ$=5R?^28Z_$@Sz)FYd<++)8YxAAF%-_6={1 z$Jp30?|SGP2FY82N%59KSFnfAVmfP5ybxF7FO1Nw7{Rj){1Fow2Oq{S&?{do=Ef7T zjyO%6>%+fTH}EZTitLfxyj(l~w6!?o8fy$6yn}iEQQPbe-{O5CTnS#u9ByIQ_{45m zQ_Dzd{%IV2-VtepFJI*^!2YA7j;*wZd+(cjEM`OTg*oJxe zF!8O}12%9^o8+opdcu+TCEtgj57)s4JE0%G1@`!AyyH{F?LLmbkUrKF>~hAK@v*P) z=otuI^S^AOIG+E7e;8EgSUeYbkq=qXA$^h&o!}WPh`;3D@J9}Yf6^Yg!-Mzxj0ZPe z&#u9>nl9*-?bEjP7`^ci`hi0@l7C=-?c?ID1p>WT2 z))es1Usx~cLp&iiv#!y%b&;5kJ+hX@t*p(&K91YhuKW2jw!{2!1}@o=%*R~Jl^ua) zwhZ3bC9x4Xn6J+oXE^Z!_!I8R1bcY9WGUP|bWKFNIO3(C>cn^;8 z%sDoV&c*Zi!XMF%*bmO=itU0Mc9uSs*g)-y4PeC@M+^}hx?a&gyP!Yg!g>AhH)22d zCI|ObMy}!mdZ9~tqI*6` z>;bn*FbsR*cVom0n4t^T$L_*_aao6ImpyvFB@Q}7@qd?NP0P(9>K&^=P+^@zd z_lc|73fJj7W5jVh=J(0VJ@`YWWW{dK2|lxXbeg<43@E*hZR3q=*&TDzt{6~TY^zv8 zoQfCxwzYtHTO*2ljYX~{K1SU?BZt}9{@^j)?NwPG&Xg-bfXqxdTJExh2f*)D#A zErV}*q6<9058L5L_(DhYf@8+ac8E{lp8aK8V8{DTIAOf{!WSQQ+L-jg_Q03->*Z9! zn_zM_d=6X(+rxmL{nK;Vd7MdZNc=$#*0j0Tn%Mfl+SYu?#@zGCb^X5Utf5^?UhFf> z@bT93^do*2oZ*aP za0^pxG@Rl=co+Yq*1mJ>cYK25Dg8aR72|MUU&0S5SkukZEA32Jp^K6K06*kNJQQ`qQ5PvD|v36yv_yP47GNTIdNCK-;xp_y$Hu}Gxn*r54`BV6_Skhe8sjTC+uikZ^iO*n6%z`5d9Ke+|jUo<*$>dRCUCEk39iIlb{8aW+=bG%} zMv7x8xlSD}>KiwV%NW$g?|9Bxr|}i$$li-}*dDofSjUO@3K$j7vFW&g7tZnR@h4^!{wO=jEQ^Y-Q|K!X@nSrEsWi{UqMR1svxC^3I&` z>D#^TbIoM)ne13(!FRzWyUx#xnaM5~@I02?G6ym@W^*uR+lq0E`@Hv%&yMIl*QSo{ zJojaPjY}TQSd5K6U88L^z4zFe47+K2I_iFo#%SH8_HmB?6QkKSHt&##MSb`cF?6?aZ~~d#Bn@n_1&!e|y*GyzBYXaXcOQ(Ivmbe&J2*0v*H#;6GjPQ;Czv zJZ;$CnGZgs9T@apP1Xlu8+gmddDrKoPjiXi%JJ#0^$UO2U$}$ce4(*rEXM7*1b^uG z)_OMXy^-nocn^kN*i*U*f5IDn2yetaWa~4TzW953X1n=`#QE%X;{B|Fw}-u8Zb$2J zd*l7*YG-GzeXe%cio~SmtXRuf!&sAyu@_x>pCx$&vX`e($Ui#Z=j3p#b9d&`>#5%! zx4|aoH!8$6u$Q<82E#Emd#>czxUHGk#}_$te#KhP+{l;T=0AO$w?ADmCHo>~&$=V} zithLjYYuCEIp6Uo`mpxFF?Kol5%a*0wSl$1br6hLCumc@))LlO-cgW~5R33}aAKV$ zZp26S7r({*@MWBAbNHUMxpRp%gS*5$;w|wG|LQ))cIK+>#7X*dpSI*UjK`XX9QX#~ zHSWDD_a??NC;cT4Mh4)$hJI)|y~ z%6@bQ7w{~nH5q@6MULA&>@KV*{EOTG%q0&jk0l2zf0bNT@?2Tpi$}7ix30IQ&$?c& zT0S`Id-($Ee6`%yR7D=ZI!(#8+P#js&vuIT^r5fhBy7tqq+~3{Zw(_?9Ilu@|D^UD zVX}|+L-Z@xhLhwM-l*+chv6eUpY~vYZO;3fp8eo6o9%-);-g?R_sv$F`KK12 zc|PO!8XS`oU>C4Pd9^^9@+(FR-PnTPjD*lPDDR%3hl z7SD{5|4kkKsQYIslkMYPe@*`^Chg>W?pKGa=~M0an6Jsfr4E19dB&v1o7AqA^CP>& zYU0x5I{2jEv6mYx(zWN1c*C!geR3aa{t<6*OKh^cKf#@LVSw$^4?FIenAn@0#4Dfv z`r*I};EyrMm3W^->}22CC-0|@$2{JDzEhw>F%Er)GqHKNL$|Qc2D3$SFFyS^;DdkX zAHB=qJt6O0dgoZ1au9Og{WD5j$a9lCrwR5`hZB7r?5l(Sa0Hff%z68YT=lrdi5zo2 zT;b;w`>C^?bMC_-v72{~d>_s;Hs7i9UKbl4IraLXQ+Z%>$2re|$UZTon(Wyf@3M)H zdpVEg-nYH0+hRujBq!ke>E;*w?P|Nzbsu@)v-r%~jBmtoc?|i%#B1q)EWaFozBT)2 z{VF#ro@Vc2^Thq$ukq|p%))QLsP8`Tjow`l|4#Lxlkid>c`hSPN-QJ}3ub#x1hYNn zae_`2;|TY9KH{Q%=M)@tpFYx-e)_(&mt*0O{*6tHrm+3uy39eyvS6pS{~!IVk;E^VrAzaV~JW#~7R-7z^nZ6pV1jK-@^#kjn`grnX? z5Ua|=kfEGnWUC#;KD*@}xQK3cr@o)7E$eBx>-&`}cdWPT&(+3EoZ}PuapMZ7@g48u zpZhsvJN5G&F+j3B1Cu8A*j1Atc&*kyt^UMLa#Z~OST)DQip?`CN zd1H4!KI>2awp}wGpJNJq8@IWbhdSIMFYWn$C0^qUj*IbpmqAY6HEc>`9BvvTUp*Ti z>zh6o1s~!e+zVpw9O;h_v|fdS*h9LdSB1XCK;j-)RI{CI zIy>$fc*UI*eCfBd?>hL3orTd97|b?|rWjYS%4gs$4&j-5*nf7O|H3uCP8`P%Xe0Nh zjlQ37L>*4Mk3Gn_%p+sVakW0l$Mt-VH9vohGh$iq!if!CgCD(I-OI=E`LUVN$xPL- zAl8(V@~nnUx4to+;3V}-jcKxe`Z0);jL~~(@ja8>GZV+y9dQ`D3lDHkuP_7S_TkMo zoQi*7Bph%Lo6LWt#7?J%TW!E~Fa%Fi6&TZ>0$;{}ch)M_0`SeZ$Orgdjcwy`&U07o z8duur>$IKPIeybUY=M2_&788GxunK5V`4k-21n$g6AzMM`pEcQkI(Lv2bhkW^Uh#K zUR1nejSZ798l0xynf9jh+I~E-ZGwB=0z+i1m!Y9Ia36Ie8D&9eH-ugZ7<)n5jCD-)V@o~n4M|dMQfH(XGj(9c_KY=&C@4`>8`No&L zf&R8va_wZ}$Qb)Na}Hnh7nzWeZME~>E9K{KW-|Hry4c;@JKEolUbnl)H&uHXPwJ_z zo6h6U)pySE*?#jwyy2ZIxCnQ89N6BK5=;eO+8ARlSj#oJhyCYYaEEWfoA?`e6gT0G z7)Y+bdJWI}c6Mbu^WKqr$8&Kn8-z3BP0zX`>*>_>WNr8TPPW}2o9zDCI%Ztsv5ohq zdu)Aw{kDFyF9+%Ua?fwXVbAqW{m!0)pPhN1-?e!cOsvGecy9o2PHoFecn&MK#}>-1 zdEdvo7kQ6Ct`rvYZe`XO>bwV&n!Qi4Mo;nXfi=B4?-r-_?m^yd>9-y8u20^9wvRX7 zpRpdtmEParjXqps4dFLsQrn)ac-E8RyVM8O_lf1?*bI5OsfzDhdyeGUrsDaQytDTj n6m8Mjy9;%9ccDtD0(JKmx8hD)T#A$676OEXAfzZE&(vK1^{;>3JT`Movz)1Y7}N6pJIsIt zrn28tX*)2D|EQ0VOXA5Er_SQSKmYm9LQf@@zA0g%cJfCNZpvJiWF3o>$6)zn^8Ihw z%|inA^sj&YYx(VOe_MX@o8OdQ|N7VE`s=T6LHd`!{AIcBy6ei%fBy6Gv!DH}{NyJ; zDL?-4kIRpK^rP~_AO5ghbImp7>Z`9VS6y{gx$?>@Tlm2beo%zVF1xIS?|=XME&ks3 zzE^}xF1e&!dg-MtzWCyc%Xhx>ofa;-=%RArg%_3!F1X;vaQ^w{7va3~&MW7hd+tbr zzVvCVemvuwPeMOW^X=D@HOZ5<-mIT2$c2mqG84$}+H0?E^8Cdwe$iwbdEaou4MngA z_Tu-y|9$!6AOBeX^rt_SKmYm9E&Sy#e`$MVzxwdEzx}NYhQI&)?$(LS|pk3NdR9}CCVWPUp zPrpkV@3`ZRWmwo@haJjI3EOYKeM#7EyX}hLbJCN=>SZkH!TDrugR;oyZ+zn$Mc8GR zUCM5|?N)Z*efP4*9($C%_S&oLv(G-|o8SCq*>Au7%KrQBe`7fCzyr&nhaOrEKm70( zjyU3oa`e$hm*bB=zRaCF_r`GY$tRZu3l@~qPd~k!dFGkrTi^Ot(?xW~cfb4HrpxFy zdIHb!%in)FvxqO`Ku6ib4}IuEZEyD6bI%+1XYalDZhK^(gzQznUk4m;K=EbP1QzGZPCYZ?~!>z1BsAk*{#_dYLaz?B5t!69MSU3YDq z3ctj3p1th#3+`^a?Y1p!v&}YT>#etLVaqMIEL&``McI7w&C6H6`qi@Orkl2~$tIhW zjW*h-Y`Ecu<;!3Ga{1J!K2;VjT-bcq+ya`DX8b|NEOxvtQZcU{W5= zz-XvKujh;M{&R0PK8t-G3_X7Pw0c?hAeQ^j(t|-i;Z@&f()#Yx>2I>QpHJ4HkEDmi%Jr!%Hp1t~+dA?qb}3op8bl z&HmyE-}SxkeQ&chWC#1f9L&OV@UF~RCFc_77wMd1`gHR8b58QokA9OD3(4z0hf~Uu z7Mou<)TalzqfXW&-Q!Pw{~6{|H~8zZn7mh)#PeK3~(Ge(~tTuXFD>ZIJ~tSM!Q zVMdL-GjLMJmio=%9p)YP;w(_!s^go(yOn& zdMoGWzWwcQFK>F&o60-i`Ofl&H@u;|^{sDhZC?A@*S31{^rhabUiGThhdkxD`pQ?n zvX#q|*8UZ*ctv^r>tEmUrHyHR+N`?js;!<_o8+r6&luv@z3z3b4{3E$U%qtuH}^hI zpDBOyo8R2Vke9SF^AVfBv|#;WG9e!^oh+@4T&2l&jWyON?|%2Y%UWx#Rn}Z{%~mGQ zXF5z89;M}d<};sZX?f1%pZLTlTHY7G_{H+cPkyp|gFDyPk=}Av&b>(UI+~+>Gtg^}~ zEq=;Vp3>^5C;hzVJ+D0NX-{i;&wlo^Tc6^mKmF1yTX zt**BERwg#Kw(4bW^uO`+!G36~u6o*Ar?hh15R_}r2C72_^2AxMHu{e3Q*IpftvO@r z2hNnksqwUdH)CcE@F}f-vLcgUQa#+(ufEhJ7yTK>x^YpNd1)^#RxXdMrRA9udCOCV zU&$j&GSZ*e7y`M7$)6nbZ!P9w&dSN-BOm!lvq8?H-ZjbJvdovv%{vuy=FDk689Pcg zYz_I+5!RPIA`9y$2Xevz^0l{cLq_nfjyC)Ua)MiZswXcPlO{vhRY$+_?71MVPvwI7 zq+GxJXXS9Ce{10iT@g zl!ph(jcfm2@q=|AQAR)?QyPRjWE(&{Q_Pt9MM=i0^;d-nm(_ztk8 z9hpdL!?(aw<<1s-bV1sALVxP0s|}gLgL3^?yYj5lnCc1E14rgyp3>@(5gzCp@55>R zS~FRwAAO;neKAjaWxd9Q@0`&iH*KspZR~@6CO`7g-hQfYTy>Sp^I5*M@yt_RbgjOX zY3J0Of3=hLeuy^Cuj;|QbFFi;&-zhUTleqoft-VtJ1;v!8=vfyX`?TB z%G|4%xAxljEFY)zt)6^o^WI>C4O%~BmAuS_tn^Pd+W8D)#aIr)*7I`Awe zM;s$pm?cAd0_Wjpbcy{{Zf|fwUpT71G4zSwaBolT9q!o+v34*JKEjf^>=EwZFg<4M z@EW$Poh;5e>#Q5^se7!>B!|Kmb8vTt-{eNuL`RT~v^A0eTWXDBdd@zP8(i22dG?Fk ztlR#QyM4zE!Cj6v+6wj_mQo+~^#xl3%(}CIak%x4ly-0k^ZHJ?IxtVa=N&0~pq{xH z!&u>(x#`QgrC}NV;6omKnKL}Vp!LB3T>(4x+*;IwHS2~YeQ6`#`p8b3)YW(NgE^5i zOh>Pfk+GCnlXmz*w({`FXY10AoWdvTx7O$c<&hKpploGU;vIcUI z7rDT?b!dlI)=Ca!n6=8YcI9LPKe&NI{5bfMFHc|EyoBd;LF~6-;aKTXZvLz?2%xP>1AcG3fs}ubhCW#Fu}L_o{`ym zd4e|K2(0LvOoMT{(mtA(IooSzQZnabySI`rmM_n}kaMj&D0cJ7PkwSs+e7zA>bWO+ z%wrzY%H1dBu1FqxtG_2c@rmuMt3UO%b04T5cT(E$Yt>Wc&Pre8sGf59k9yRjTA%L5 zlxv^6FJl>#@8aG{9s0_g=`rQjr(D1AZH+Jpr{qFb_zjQtL%w~s56bY5-cV-$l+j0I zPxs&${3_Rm4#6>c4IkByzENgBV8fW&Wv}F=OuoJ1=dowjEVdSVYL500_q3liYgYUI z!?MO5?*f>cIa&+ZTNhnoZ>&MEH*iZIxsNB)U>gT=r!Nif@nO`(2fVR2+Te(C{!BQc z91rOj_2HU5Cr`M+Vfcd^X*h#D_25??48x>#z^pmLl{-~>z}=E^{b?&tnKfH0Y-+0x zercQWl;;jv9A86U+UQ#yY4gyhxyhHO4K8V?|E$Not-A7*o3k-94{OE~>(y3$a~8Bw z7EUX}MfKFDAK=nGvwp3aTrx-F!Kym!15Crey~GcDL8sw|y}}(q8V;2Ej2HG3*L>Di zTDf|B0(ttUXZ07~4QI>)-^~l|aEA{UuK8Y4Z*ThaOqm_$n&^=DyX4NU(r@k>*=2zY z=qNJ64ejVB0oG%Ga7wwnJPX5@*cbKjNuJnedm(Kcc(B(Gd)UL8-ZUTkZhr7brUKnA z4_;ux9>BgbZS*5gxpm-)Hu_Sgo-)`}2CuLVN8t+&z&;Map?;Oi*It>=u&*z9IA=c6 zcm><)n7evn^GTm#pUn-QjH3^2;5;1Dmw75zpY1Y+v@$j(ZOv0#duWcvjJ<+W!5Y)v z9AQ~M#)WbC&bf`fxA!;_-6n0X;R0Xmcle@=ouWteCDxz1_MOkj&$WNnFZgT?&I-XF z-^X`?JX6fyO!LMan(rXtI-3Hs{84kKJJ}sFqz7;XFW3xx;e(NB_@bO#@r7K(_&~9RcFhl#@IXCygOP9uK42A|lv|fQ zf)99uN%&C**5#|GKXt=DxH1O;*6>rAATK(`e3XYnI4Z4AefzAA{=*^V<`mu1&)8D|9{GWI#1Fz*`h8jXb4N~3L{~e%kSAX`eu_M6 z!vS2t8*(HMJc4_eS0)cH@IyX-j6Tu^cfui`$=$oN;f?*VSH`uMo=e-mM?B&YO<%*C zV2{`2iTUDhll#cG5zV=T(pBv z@{|2Am7|Z;uFJ;=AoAI<&t{%SNta58K zU-h&Vi|+`z0?n2aa$i_6JAs17GYhUg3&*Y>hNMs||fk zCiWgztiid-8OPq)^Ak@z@y73s&NP2~r`;UM60g})`$*30B)Pc<2oK=iT5*7UTn(tzbH3_PNjVxew~4j&y>&KCN$`^#y0?1J2~9yl*3|Zt~R8 zPMh?fa&h|T^U^kX>Le}B`1q|%kj~iVn=)hKP1Yb^TAp^waWdudQm(G?| zMV>xzi@x%XxiWotcbzZDCv!JsKj|HIkAK475VPg-`=G23DNCNu(tMJ^Sebk}SX!Jq zWDsA5jFibsx{u`x^kV8L?}M^F(5*g;6WAMhu#-G#`GP!g^7xJ^W5cAyf-*KEpQV$R ze0$uNODhY<9{SLSwl&yOb_GZ1LiWSn@ws5Z-pA(1V_W3q9Hc(J$a60MYwF2^FFbOO z$6vG$WC(}O+Ri!bkGa$H?tf?UkB?>_aGX7aXZQSY<6ecXF<-U;=5dDnV4wV8A77NI z!{?I55#=}}t)9NMmG-?1Tu~o)*c{(4As_ngKKHp#(~&rWH{yhF$7kt$PFhUveR*G& z^1-~+&p3UouFu2z?em7U(Y9YhpH@G8_w&(b|2gT@&*zEav==9+YhT658;nyf=J&Bp z_yfcCk`9D_dxsbJV(;+9e&ZEiN*)`6BR=DXJa!0QP5}T<%_jqTVaYmC5n~2wVfj@K^`I@)$8tlV3Igy#bmV__(KrZSj3t!}^hcjdz zzTlWN4)M{H_@4K?XSv_~?$>myy|&MMF<577VspX;c{rkO?1?>= z=d%!-B*v5U7aPSVWb@nw&@t-y4jf!MgWFHPFE>+vzQ=j&vBx%i;xHZHdxi9b`O!1( z(LATe1^9-2a=-^RhaBk{Wn_k1((2%la``wCTNU3-9=_n1aoJzzD84vfo2?7($x0e$ z`n-IGtEuo|ux-YZukW6FgM3g2-Ujoh`nl&q(y*>yaq{|jSbp-vOA>HKpL`-`EBBf9 z0$1!SEWo>19^6Z_HTc5DNW+Slu2nBK2R~ptMR@UQt5c|P=A5WG}`*5W1+a3;UyCmF5yVQlr zq$i8jgL8F){hlL9FRncG(qH--#vzaSBhA%fkbC zY?5+ZfhBEm1;>0=p65F9@PzE$OTmM_oPD*Y-*NbqQ%?Cm?|P1tL(W!k?VieftP}p( zL$;EQ#C!5l&wB9&KjM#tFY&?ff!uIIj6>`So1+i>(Z+YtaS@lot%DCfxY@S{Jm3NC zPR-f0=L}f}%SlfZhXYF+>lc2~KI7(dVs%n4={{C3pW${`I(dCOtZtA0KHc*`em?hg zk{;wr>JQ6zU%=Mf>t6S2IEXIfYuQ7%zz2H^+xAl#+X4@8E-yAo9=j6GN#lv2FI-Z` z9RWRV|KU=7zmGtl=Us-QjykIS-tIX0lCk`5Fn!_t#~#ADv$=a{b_Q?g8Fz(#dw~qd zi2V3h_+y=7dCJ3q*cW}WDdgPq1($Hk_{LNpm)L0cXlxEX!|R}ZnTRWk%MaG4Gw!iJ zUFmROs&UViomuBMccfb2xv!mvLH|!1kV2ZAzaZXZjiHjWUmb!f`9s!#xMGXj&nC)UZdoVzNV8i>3h2P()T-6 z&ZK?MoupHSEBC$cecN+qWq80x7o_biEW?exg?IKx9{j^QT+xO2qD^cLzNn`SzTg_4 z&S&l1OBvJMu5!QYrLFTXJOJK+JC)S5MNY6d_)XsmfxHvm9mGWSHy5b(|qfQ6k zQ;oxy!8L%7xZ8Bt22swW6%EFCwr$E?qLR&!ZG}TL3SzU zA!i+Hf=irJm%cSNy^1${70>Kv;?M6MxHET#aZf=m*6-c|Z#;W5ck9IsIt4GR!S80l zzx%i&k36z*McQ`}4m<3ymX9m)_@eUtX2I;)vv1^;{_seDUg)=S1iFw+ZYw-3bd~=N^6N%X1L2Vqb7d8|MJ{z$aXD&X|cm z|K5=2jMnMh8vYdD$vY6%YJT`c56}~A4O!ujwc-NK_`N9n!56j%U)UODd^UVZ8rR4m z_au0uZ~971&yX?yfIXg!H!wMj15?cl)<+#r7iZFM@IKWz{Gm_X3wZB|&4Fk872gb2 z^8SnaFYmO$OwROt7Ww=ccMHnxEkA~?gbDU0oTIaGP=HD6f=_nJSadeOkN-}W&(!;! z-c^8KHrTxddBpd0-)EiP7oZEw8?Wdab-ZV1ooq9IEquWlzZ(}$`S*r!2w(6^J-oq7 z_YHK8zS(WEbpLIi+LzJ~?=M$WwK z4O^rxPLLfgdDqc7McSDa@6^L9=Vtc?u%RDmXMY^>S=v0*^?fxQp;KXxe#d8bSN-p+ zjnnSpX9=PIGi$;2ehUuq{n4-pj!mer{~Hbz7gltQC)ND14%0 z@RLsB1LKtXILT(Q>wE+DhTJCOjk4(uJ>Ex|o-WR$-yZ)lAU62V9R*Qa_)BT z;NN8&=U={G>faWi3#nl-Yc){b-5p5Q)_4d$P+ zz5F|TqKC-EJ&G~)&HnT0^hw6{%RM+=;Mh3MIFoU3l$`1N3eKk^URu6nE^y0+un(T4 z;fei&2Q~+eaRXNP25gQr-q5Sz4IJ4^{l))>3HPmh4LpP`Sn;gKIL=4*11@3Ac(CG* zP9N?Z{5vw^{=*&b#*qJKT(>xQCm!Mzu z1l%AKwuzl_E+Rv=j6dvNz**Ej;7fQTJ(K+D@jl9Qa5^3Fl5%IzPqRMfOYb}R4x8^L zunBmBFZLB)=zU|r3Y)~Xzy&+uj)1*kgYbr(fqDB1C+>`LR)z)qVOx}`XRn=S=wUda zvz;r9=iKdlJ`;cb%~JB9Bdpa~gFM&|YxPbazFQ9&;k&y5a`XNgoq{9o5%8Z4VMqCR zKGQpN4qMJ2^_iTNvDx$r&ghdbhd1_*&8O$cd0FC3Z;wXF8+}a&XY{!wydm>pb@}M_ zf#1yL*e~`07T{f)-psiL&w{1i-pC7Yj3tc=&bqNPY>hPhNt-u}@N0}=JiLJ&m~gM= z-`bstKmTUAxzQ19fID&=abAagXF2mHC$>fm=hEt%yLDQZdjYnuM`sB3#9FnX ze{e~jdq;N#`o$UCaUX#<_rCYN+xWwHBX2PDm>ySp)ZFR&BcD-*CE-tB$JqGr@P|LV zamO8)yJuYC%fo=Z&)t_hB=vjVus8H1eDULy;|;q6WA>hZj*sk&Kqu=H4(Lrh!6SZ} zv#>L@z4uILCV0b7JNMjko6pHNf@QuEU(#I7+dkqzo~@7)mcvc^%xqvTBASI>>f(%Qf+ zpV{3e8$qXfUVtzBCb-~t^G(<#dg_&f0BykwmGhUYkKPQJ6|*;c;8?ObjRI884&SC~Iv4A@XdHGyF4W zInQ5k1sAo&KODg==O%nJoOtvpQsOZj{aizC*uwLDjybxVSBpsM#Y)*)8l=V z>0x})zx>3~d_KuXEJw$=0$&)H~j+Ff;!g9N3#Z4 zXG7@?vS35lEd42Wzl~SUN$~CL#Ga8eS=k46&OJ3BY{#4qV}fw7umUx<7O8%HFs~fE~PHxBS~|yUdYI zCNI2!b2djCYw*lPIqc7!JGWeV>80h8OD<{8Lw)wH06V2U8L=;T<$16>6y?h39&+Iu zc-H1lLY*by#$VQ<14eX&!p#Mg5d!1sd(X8;`G+go?;p^XVY&Qo~f z+=^;wgwzwXJDV*!3TTKS7mpt1*X{zHb+b@xF$_MkRSVDedM5?a|>>| zZ^R4hreoMC{GhMg3FtfZ@g%{C)i^u}cf|ZOWAc;f zRnKp{GiV><8^q_f_prkM;-}?&L$A6sfD!nDv)CK{zrMW3$S-4G=v>_4@4%9N%mq(i z2)6h&#&ZY77A{z@VC=s!;I{?njPS-Y81{xOmbO-RTF&#hf@9_l{|`e)Ja)`EN_Zk%`(2Vt*8$ zbDw5Rw%>QwS6XSM#vQokbKpy2_r`oPIu>5&J!kp;d=o!Sx&EDBopI?*J_f&x?*~hC zIV{4Y^*G-cgO8&w-Z=ZfEuY^xeVl)OlY{LuKL6C**b;MxeQSj!@?#J9S9F1xuE7oG zpgikkL&(qlTOaTve8L-db@ThwfBSlawi z@I@cS7x(vR&L#Y2&!Opkdj;q0j`NN4z5TRT-p3t$e&Yxfft>$ck>T9vq-I z*bnl_GaLNyyo=t!FSbeAc}L*8;wnDkiuL0UeWJ`cl+F21djVY3FPj4+ z`lWx}G4YMmWpC(V=Nt1F_ix(VaKjCaAI@I5ZQgj}%z-y}z@FlQ=aJ@2zc_>77<*w& zerrP+?2GgKif+;mx$*n-2a9+kkN?GP`RxJg&-X&%ioNGov+MY)ZXbf_nTUh?>4>Mo znY0}Rcfuccy)e#KqsL&D-sD%of><7|xFcf&>{re=_F14uaZ0(q>0{XN9Y+1JC9nW{ z&H&QR0M^8A!ICo%d!r7Wj5m1O|GS0busfbdI=j0kfonWxFWEtJj!l49_K>aN)AC8J z4d?lD*6v(n4ekYfr^ojbJd--8UJ?2Bdx%-q})_8-f z)4>^i2K!TuZwhz#SA+G9Z$5OYdriI?oZ^V{P3{HgJ9k3fLBtE(a>gCBH$89kjf?#C zJTs?e1x1ju%4ZiS&%)`9d3+EfYmG#jh_<=9% zjB*@-cRrZ4lcV*=*Ya=v3cgEf4ByE+{q)nDEyEjkAfB_bW4Io)WXHIE%1Ju{V4c zKAJNQ9f>z|p|QM^k@_h3IAddVa$PVvAq}#_-U&+?l|=@Hqc`$CNKd zR?cwbVQ#pME4V~2;CAl1aEIN&d1oTJz`2m_um*P|xRiS{ca(y1a^a)m4td%;e9ZZV z?jb{G8-9VYacf!TOt|@@{Jq%B{fW8g?9d<~}2XepXK6u=}p?2MM*R{JZbHiVA zaxdWC!d&qNr|Akl6x%>n&LiFz(1uR2CVnp)ghSSqd&TGx<#pj*q-iqS?A6mFBbP1zVQau*$400us5(^|DESy+`i(4vp#zg z--<2{Z|GG1OV9V=osXv-o{^{f5%nY{AQx`R=jrClu>MEIA$v2*usE&^mh-*bKYK2KH|~S!H|G)W3(#}k zt#TH@3w(&Z32$%*Z|FmNOE&Il=}$UXKX`*H@WAJRGkxL>yTfNuhBtIFOyZ4qQ9Mr> z$6wwb{n3wp)Ybrl=Eg^2AK46c#QNwC_@-Ojqgf9hlO4f%GVz`P+3`c^ojku`k9^i1 z4?PPN_<`()GY+o0LnA+Wj4pu_=STa*-c7~USlcjMk0WnX+(|#9;?E#A@>%~$i;YWf z!8$v_{@Xjx3-}&z&VRS}o=G^1s2h8eXW{fD9E3Myk2h?E=bkXd)`UBFArA)A7k-62tl{fGa%#SL*soa78VQqSTOq4d~ z>_wgl*gJa$2e3|`I^XES{gN{btlMw<&lj`jbf-Ip@QPi6hxzm8Hyh$?!`IQLdjYnE zUUi0`kJ&kP2o9Cg*{}<@h-62Vk3xWE=QX)<~}I0mu#a zoQDLu#k%MVYsNu(ie1vTHsq*18<%s9-^*qHjp@A(cArkUDL&b-y5q1dlZ`Pd&ZM7V zJXo5%LB8liUSet3biVQ46YlWG-Rsb?IKw}Hd43aK@U6x6fnJ1**dk@@41Rgezz3l# zVas_b_ql8t-q5#rgMV~6&Z-B8@|3}tXC34G`|fwY+wLUdL$bU0V1DN8`9A#sv@GyD|zpo3vR zd+&w{xfh@}-O;i$YzYj(lQf;{jLq)g4SU1ZX#-EjQ#S73?7ZND3z}Ta&HC`(odw&9 zGjs*qkb|=gTSHFLc+dVuHf##+JB>Xx$M|XR&Q3TJnj3DgGwuwnM_?z&DtF5GLO-yd{8oM+A6FnxzMekWF}jKF z!AH8r*%EKuN0SRX?;I-Fx9R2$UQLGgQI_?%A4Pg;crvU_xPvp{k32YcW^pIrPK`dK z-|U++I{l9mun!ZSr+6-bOLQPVj9sEL;YFaE;l;f`+G%U=wP9~?gN-s5c%h5=i0l%) zz>#VaHesLN%@ZFlsk9^|WO5>FEJHL~ex^$28igkI0#s1JK>@uF< z4h~trby_>SieG$Q9I^+3zUR%G*Y?7={_StSKji(Waq#BbTBNy2|n`E@Cx?$ zf%F+)$#1`m>+fIx`d7QlaP~Gf-oP(g$Y(QGHqbf4{5`X=X1*G`D|r5o3q4%bXb1q>^_y(}fN8_)-h5ZX(U|Slma0GX7L7sH%3H*mA`pfx7tPf{k*rb2y za?j5Bb^3)f=Lli4-?|;O-{w2n-c^Q8bAxYd!+WxE_F#wjYSQik=oLDJ{s@1_Kyc^o zUJV!cr}C`5zdMvKkDk$<|4Z-im+{Abn1lT?FLy4ZxQAX^UQLGiQIz$VA4Pg;IWw#s z{>bO2IdAfr?T0f7-wU>J04DH3Y;Wb;Q#epA4;G~DdpN9U z*qr!n{WE{|gB;~)gCk@u#u0o`jwjl)?b-BQ3N z4Ax8iK2DpP%8_tjN!q3Euo!NKfx??{+$O@)PW-{lg7=2NUot z4~JkQ9H0;Fu{O9sFQ-ghX*P$wvG;g}Q)2eUKGEsULwq3|#4+aqzP`C$b=6f9!F=TT zcw;T@=G{Z{p`5qa4IFY_fnULAI)n^hpIpcQ&-5YIR$7@D&&U)v)WxyX#SitB$Brp; z_rfN*=jR`KS74Z**5f%imp90T)C*Vo&uQ1kgMAF^Pn`s9lNQ5S|2gT2^3z_q00T)6 z#`@~Zr8DMW-Pj6sCJO5GV-9}Sj=v`G<@wFHL#M((_yXV3a1SGNDn7vkI|2vipMQQk zyW$nx_k7Ww4a)gOTfESgPw#w#H~8v2&3}O%zQ5-ho&}BbH=_Lph&AC2Tj-2p?YZ~h zKhpDTvoj3*hZE#Rhmei5Af4}PlA|;^;tReh4_~ZXnjFyuoC9Ll?3bx-Xk3JyF<6Oz?%BTI?aTLW@CKi7 z#on_;@?eC$VN+ny{WadW7choz_K73J*TVlbuo+k_Jv@=$0jx;Riv-CtE{OZg5 zyd~+QZ`-GP9!->1f1axt6d{j@vZ`Oa+)aA41SJPhI|cn}9aiGv59Q!jbJNB_Cc zlO7h}ZnBVZQfIJ_lqX+3>BN0n-NAnPyuq@xwchk6P8)G#k-YTL*HZ^Bz3=2*8~3hk zi@mkia34J?-#)@Z@}2K-0j_c%Krgc?>gAqIIUA#n`uM00Zn&F*Mdt|ihA(H#*dp`u zJO1PP!yEX=8}HUhH*`H;iH^Y$oWKv57tBAO!xu5W$ftMsV`QvvoFH5L zz!!DH7j@VlWjIFHxEtUb#P+xkJM>~DEtHNu~ zZ}@3+x@U&Qb3egPGsm7cqrQ(G><@3OBfMb)t;1cP_b$!V^BcIuBl02}oPceCJn#WG zVw>fM54fN{+pI1=#Qw-rCWKEp^T<2xwA0#r=o)s~b5ns1awep2-1*5PU%sU0t^8i) z?l9#mm*=zhZ@v4VKW*K2=ubU)?mE*i{pSAN+6D8-yF}XRN1nd*p|1Yq=|jHp=pUbx z7Mm-*U~cNWQ}v9@9Oaq6`S6vkNgmxM-<}BS^I_ESSYq*D7xDH?7D0#}m7xmQ(U*f~DDeQ@Qu}L@wU%79C zAu_aX&)@kxp5JH(WA@B?-CIm2ZwBpHcta06ce%%~9zL4=$7N>;=W=_CH?WT%un*f} zpRI$>Wv#GJUdm%rhg$vtx z^~^KRZ0|uRKl|*nTfO=9J099ugL3ti`;8rUJE`Y4tduKLUpwbl_d~`shB2kpQ(w8d zVxQH?+{`)c#QHO~bG3Tfq})1{3)Y#nnX|fNWv<$gm9fZIU2XNLFS<&s4mnFl#`=I& zZQ$JANyDo>vqyBN7>?oG9xF?Kc%WZ4LObKhgAL>0NXm_+Ex#=sgE4u=F}^-CymOBAB)vxy=aca=s4B~v$pZ&(6^|CkS=6quv?#J0g?_#p!xI_1nCD~aI zIjF~`irrmXD}BITc?RS?CTV%>JKvN|Q_o(IqkY6Va%6w(fpKD=jHj+K;V0)QY3`$i zuD(+@^HeUauJVj!?aBn}%sjN0RxVA?r>%a>Q~it&Yv$&&HqvlzEPdmF*c|0+uTOo; zv*+fdpKwE-y6WMBx-f-r;^^Y|UwC2vm1&#jn__kF2L{D>%ige+vE^_HTh`$2D!g&e zJ>7rt*W3ldJzt-_$+OtJ`_GSvzw7K1oLRFnJ_Y}Sy^OCRmY;GyhR^Eob@(^NvS#wm z-e-U9n|%A9b5n3{f9-X!1H1M!*j5h5%3(8@lBX|YNo#{oFfHGh+S1?nBB*C?jAwjp zjia4%I$ycA>gb0pkS8q<4q;3nGgvjY_WB`D^}?g@+Pb8TsoWUqnv;3zUpa1MY~!k9 z?edJ7x-hN}WiV;%*a>yu)11g%J8609n=d}7m-R?nk8*8#KH&+h)4BXiekC6i?xo=+ z7=a1-!3KQ589xmVjUkW!sI9Yuvo}r|m;G^{#@_fo%((viHYi(xH=fhP{yBSDpLM&Z zb^quL1~+)dc3Ok^3+%0Y_J!TiC!G+R$_i!SFFZLyvNO?E_6ZTcU z`PkRs2rkuwLwl-RUjod@3(nOI55zEPUe2cUhuFQ3HCUh68l{y-e_E?{)@c6e3qHw$ z-taDn;5?+gdbnuKupt<$*U{u;4#tF6v2o<93-|EqGY;X6wHuc_^o;{#VXelLR-Zi7 zw;wnJqk=Z_$P@?dnR@yO4~P0&Rm?U*-2-1deENQOZHM)e)NfQ0socLuhROp4sv2s=}PZ*d2Z;gpRdX% zF&A8Pw%5)$Vq>^B@VW0_UdBp2J%Ix{K$*`1T_C8ZoNl#;`Y;|D(ywI4meWhh;fG9&&G*1JXFvUC9I}bt zp@;OTT$=n7)W;osBnz@3LtG&*^S~FfBp(=;CngX1%Hh*zJRp0#Qiq&yNL&5KR^d(h zz$fk5MqJN3%hm+@I6;31>;+um4SB^TNhjc%HMy5E4?J=YjgM>(TLVuxZQo$jy`TG? z9^2E&A-@f3f8FilG+dBJxIqr?+T2s(m%UI2@8hT7NzWDB5MV={a7UVK#pF*OvK_E(j`o)>vQA^-37aKP8}qV$_r&Z6Ug(n^!b{_lxpAF^ z&9CPx&f*Qf&$x8Gb<-ha0arKz)Ap2}p`+9ZH^k-zpX6qq>XHqvD6=o&i`ZxRxMK}? z7rVvo(iLQcGvr1#LO3KH9$A+V&WP31k92Gpo6X-POZD|{9s1{|!LNHu-zOW#AKu_R zzS4>I&Yt28onZdHd*ivMJtTXW!W-?Pd&SYuiHGGWg9}*T@8FUBvCpt(KkQxh-(K1$ zlB?Y#p<-{2NIqAz%H*3hT^(=V>*3%=ln4RBV2b88H@#nwvK z(}8%Tj=4*xoIbYKo{_L0Y&IOhhqmVKyYJ>`T>H+JSU25ECwWdrHtcY8j=dmbb&X56 zO5>I>!Y}oGX3z9NM%Eh}L58>h9XQ;QlQ#O*hxL-J zJbX(YInx<%WbJq^kX`(>*dX#!CmbSEToPL+Jz%Zmh%3(6Y${9|gD$o{?}a%}d4_A> z$MJ7`vT1%(7iRc}cxz9{%HHCPxw1o^7h4a#P1fe^ykp+-^#KEHv|z2cC13D4K^y*= zJr2gh7aW0QSd+HrI3O6?Uc0k$2Go|_p;MLnY(KS0U1`Cd<52jd4|Sbo=z6%br`8`n z&>ueATl$UuH)nfJ*Q-Yk8nz$Tv$u3^f$?Ui-LzK|7Jz$2St4tPOVJ0Dzh(M2uJb1A&g zRv+PuJV72G&EDxR++z34!#=}+z~{>6#PXD>pFC;jJ7*gDpyviY!V-C~v(|=d&OOc< z);kW~uxEG!&+hT?gO0O)^ODbh@qF2{Wox3V$%*XATs>H#4`InZT9-A)r+@`@>@}Q; z={P!(?86axg1*Us%!~zR=9E716Ia=BSb=LigtPel<_RNoVfchIu|HyZiynhJbB044 z=mQQYQ&%7#W5`zzUimKYOyBdt^k-doW1snv{U5uhAKX$-zI+hl>kntxos4CD-<~Ng#x%jOPcEX(akiJV5o_e=|?J$ra6u~VM3x!_H>Y|pfbJjvMF$vO7J-mwYfN`}q{_8D&30BQcCzS&Uq$PqWK zt>=q8_%;SzqFgY(eO0bZ9ekj_qy_Vc?x8Di4-eH7Ys-IeRtg-3PqrvEbs{3Ap2fOBo> z1!MK&;-h^qhP3|7MXat^uwQrvYxvH0kTyp60{6x?H}Z5YFgL$v;rR#MLw30O;0Hgr z>G+@i^rvm#J+GG^?t~Ba5l7%%-!NfZd&!5h_v%P1gB{N&-Q(~hJZJX)l0A2xGGCma zi*Otktc=m zu|{%$Z#D-u@@F5dA5X4j;(hdd!d`a)j#f){iW*@iFV1V7rb5B5N?zxvk3IOb%J z*+X|2-d})sb0jnO7O=zC^A+$*-vYiEQ~!dre%S^(!G4fEza`J5={DyTSi}kC(qj5W zT03}GS3C9b0k+_V4PqOevGeELYHs{4>n1zva2K6^^ri1Uwh#KZ7xD#sQ7?O>4}8G^ zI+}hUZ{v}tvB=ii`GasyUixva_WLK!9qg#zdZH`YV!RjA^W;f>u&r->+GlAPu#eU* zZU1mgxqSN)tiYLi$x9n^f*-+|!C3U8b(6Vs5Wfev^-GTAXa3eXF5dXQDw{&q_JxhK z7Ix5l?Fk#M4423p7p%)#$%9?NBXWWT^T!o3mc|k5&z@XfV!ycLky{0YS%x8UO4!DPh%DumA&gN-+b*x1i=EUZhbYHGdSRo7f z8At!x7@Ob3C-3hC_z~6?A4=cW2T#^-z3^ac>$Prkv?gWb4(sF)9U+fCp`-Z==3?J3N9} z?WFCmvXsN7e(euTTZes>CSS33`ek#(`okmX*cbiar2g?lU1Q)r{=lKn+R~AD!>;gs zo#pr++S28@7tn|A#f;<6zZqg&cTnbKeddni&OP)Gd*j^g{>C0;Kgdb2K6_3dS&Ot7 zZc<-f0_;g=|Lvvyh5>2$Vtq?zJbS5M7*S?C>2MT2ea12SYJ9vSd}Q_nf7p9-_PoGzfSiMUKgV4QxzZJ6WFN`X+Q=xfk|!NJ+YfSvKQeR9=Tq?y z+!N(K-x=N6lb?XEd^SfukNwWG5zoQsb>|s-BqUAOE9V>AL-~9q z?cFK3BPUD!lCSw$hq>W|&tmI0H#SGi4#5Ul`wU~Ss2v@SGjIwkcw-!8#^zJvjrzV9 zGmbxZ$j+F~ldx(%utPSv>tQ3E%it1k?1McJ3--}k!y{??BiL^k&=wD@-?^SYn%J4% zotWn^d_up|C1&Hq?snw)O$foiGbz^2Zw}}y_3_&?TK>4PVJFyw6>0LuC3;ple(1ya z#tDIZ6BYqmyx*ay$R{Wb%c zk|!DWztsSvf@h8Hi_~}b21EQ7wgIN-I9!BtbD&${1)tak>tj>MNm?HK(E;j*Q?QeB z1U-qb?hNc3t~zI!55D?d%sBr1+XrlldnNm0-sVrQ*mGDRS9oU|qo1wQ+VCej++Nv# zdvEW^i9HEd+#UN3A@a6PcOZ#zAbTX;2YZ$vKWY1^FYWAk_~zM;zG02sz;)P#8NY+) zTto+Y$IWlp!!Vv`hmQ}ezq{|f|J&dG_T_GOyIYIzbf-I&d*1V&?e{93OZjy;qaPS2 zH~P|ElApcjL%8qj?*-ha!7yIKuDaU54ms)zwqP4>$o^Khx>Z?erIp(Fx46YE+Haft z_ZydAe)%>ZcZ;6A(7|Nx`3U{MkMsP+op1Oewzu#dSz0sQM26(4KeCM;!==catkW+0 zC04GU_GC#9=@a+l!5G_X-LZ!_14q^gN8}*QR*^^Y*fw*^eVS)D?ya*r^ zw0HRIuQP1ys&$g5e#lUo-6C^+!N2~jHF^mC?HoF&rfr{FdjeKbMbNf{p2ShVTJt2nGJ_Q&jw(he*x#d!$p?%#9okz^NMF4 zfxdsYr@+vcop{+w^v zC~Mkr`|Zj%zE;lyyVUoYtIRi8uim9^{?#U1TxN^Np8cZ-%)y$1V|X`T`DCNt++{es z7#CmY3-h!dcGNnpQCs|gck9v*{a~-i(LT|q}~0zelm#u4U~nf2rwSZP5qK$C$P_CCr&!@Br9#`A@Vj4 z_d%X><0tMI69(*`@n8>k;EV6cRyu#>j)2`UzI{~Bdd;8RqzA}F8BWo~?m~TM4sW)t zvO0Ng{mqvH_HQ-@hmJqC{{Bu~)BaVKb<5+zIf1>Gp78_R1M$US zz&?;Od6RMWLaaRdqRg0rJnh*B&opp_yzB*;3V4b$?76w%srvR(dt8JiY1}uKJ1T8p z(Ejl~=oIDFM<#gV-2vYx8^@osF^qWE0-njq+UWsUv{&SeTb?yppFI(iRdgG@A3H)G zxI-p%BRx%DyJNOaGJ$(`CGsHy=_Ls`q`uf3^d&Yw^DsVK;0&(9jd}3r1bd3BY$whL z{7*iB^9=n?2f&SS=vs5X?QL({`g7OK2a*r#htHl>W*=IAXTI96BdT0ZoI9r+ab&$m zu4C{CfAOndm-6qrn>Ad0#Z|4|9((Ryjy<71n>x5YbFZ8|ppL`eTX*fR6t;-(Xnp(~ z_Y=<5Fa<~E<+Hm1*ylId2l~LikSpG?GuCZAYzln>yD%uNJ-*Q2uu6skE*OVi6WE!_ z0y(fh+C-O1v&&?S*T#bZykT>2m=8mr(gCjyL!< zF5dV~6@0Tdo|9NVF0+mF8ZKKW3`VEn7CoA~O?*?wevk|K(1mo0&+Gsl4F`N9`%bRm ziu6Q$(WWoUXKnjHzA$6o*hRc`_Qex?#(f;Je{>+7z~+QI?yuZ`t+?Wf&8MSF`0&<7 zE__+O3|{eJ@qvEfXC1I#^~-CRd)%UDEM#;1QT4ru%I7Vr@9>3s_Sj(K4a--z*sOf| zbDwEG#;l6Z<7+(_H;-9|)OUL~-K03WnLhQwckz4>huI#w z0B>-{I>b1{Z_vgb!x}w7C*qB}ZFN1r@tL2lu643Q)&sNS;*EFxaoN1sK)B;W#bAo zSSv5#-*Y^=+I+-0W1ANrRB-lU!`V!JfN{wN4&$?`qdnb+H*o6QlDnPwW8sgOjP2Vn z?%;~Da7K(n+Sp%v2lIk_{jneTPJUz$FJvutCuM$cX3h3NU()s!AJ~5wW%r$9=s5k; z@q7%r+4BM4!yLz-ds%a5Z`_%fFFne3;}IFN8=hmq3I759VU$jAw$Gh09b#|okC=Yc zSGYiig3ma}-x0_layR}UPx{aH1un=`A9R%VVt5vOrbp>MK06zl8-Z9|^k`bA${l)rD z)t77d{AbIi8?0aUKd9oXV(I83>i7RDXWZ?bws1k~Ys2-|D;rmTgPdU#=G{T#yRps1 zynJSB=qh8GKO2QRd^mO0$r+u!Wq0WkShIJ|PtMi$NLs$Mz;AU#UK- zGpl_iTlVdQ8cwNq&4*PTa8Z3uDeUs~uT{=$P`+I6rqr=sxF>&H@%+<#oU`EQE_b<0 z^HJPc)6v$!AMV$wt#z7r^rE?t7dZ)$p*Wx2so67gn2bB|mDP3rFfV5snAcW0uE7!= zYyZgsSK!7zX=6S9$hp8XdfaiofK}&cdeU0x zcF#9G>*S}MUTwyaHE-Ujr!?NopLa?*Vb1a8$m8no`R!l*_1*R;yY5nd`{~g7+oE+G z_X_%OuEL##Ek3ktvQhm#m%29kgIy(86jKGB{8`_c)pAmlTBz_vSokULz*hS_sxcDBUxAO4g&cnUu- zk1v9AvGax=_~bd7raCVE1DaH|bDhy{5$lkl@~)jr@PUmX_643E{77UDbL z5N@U)vCo2fVs*(Mf7u<_fj3x!If3o5cDOSpEYq9bXK@z5G5E7@-t)k9dL5S5Uw55$ zCSiAWs=8p_iS;+z>6I#@!;h&xQ(f1QN7ry%eMhf;KBmUAk36azaZG)W_=x&`T3x4k zlPQ^Px6Rhgez7FqAv-c>5A;b!&Rp`efpY=xhNZ(l z@`n@W9XgB816$@FKL(!Ye)GXGw$}b=Ydrb52v7J;K3i>3e{Z#(=Xb60aChd6GUt?& z%iL4vm6J}HS5BT+zc+JI{al}Kolws|{Hf!r|29|toVn%HdG+1@x(=8>u0Hqi-oOsq z)pK9fH*}|YkW08>T)J1_AMkr=zJNAw4r+?txI&vS7y%6^y`omsY zzq1xQj{|J7IoSu#wQ$v3aS;FYpMItLAm5(w0r)faH1C!KOZI!I%C0=zRce`*d03Xocet-xNx^d*J}fByH&pKP}j~LY*l?u_X2r8X8WzTDyLOl za$S87j14*Ev;~W}QMq$mwLds=%n5VKoRjMrs$%A(+AkY)>Vngn4O&p;xqrQb{=pSj zlnd(j`1Q9#9cSw;>+eq0dn4m*vDsG}pK$iTdZ%#G$#Wb2=mK-3%ji6|)7oNh=ma|5 znqctFZ&|gh_Rjh}@73PgVrk)>tG~U5`b@RPtH0|VEq>R#S1&Jn`Af_5o?rhBmp8t? zJmZ;9Z~JVo?3;H=z3Y0H6;~+FdUpLclRva(dH)*qJ0JfW)>yOt8w6|B_i@%-qkM3! z`kU7^to4CK{|?q#A6T<|`ZJ#@`|P{u87F)HK| zhE9W1=Ouc?@61g1pYPxJ&4uuW4q&JGzve)futjji_RtUZ$+IUIhhI8QIiApY^o#W> zbB5(l-0_ZgY_T-oTzrQ++~LNc+-I@iOp)@`6$^vqsh74s+s_1j@oiv?A3-lV^TL$- z3!H>EdV@}|K5KCPXm@^9_wQWS!=`w~zh8a-%(G3;EBD)buQKPv6Ut@vH++s}_p0p> zAI}EV__)fKqmHRI@%Z|kn93ErIqc|aUu!;p?S#s!Sx46IhE|^9+$Hs_Iq&3?+8Ey1 zIi${Y-)dvr7r3|M<7~Lm2JIa-yr#SH+V>LZXZyg$ur+)%dW4N(C-LX@ceq`-)19k) zm%r1bA$7#skUKr*jMvBZQr;czc>A)#U6wD)uW;wquXKVu=@nO4lxJV;$vakiW3%0d z9X`9Pv-Y|TXSfkO;SLVHqGPv|RGo$|Ne-lSq@Vl4Jo;YuO zxWW&-!EO0`Wb@)r!k;_z=xX?5W7$r+!9Mb**dV-drge|XPPnI%zWd$p-qP}X7FJkc zg&VQna{v$QkGSU$eTNST+6zA07eSr2ueGiD;RKt*4~>6Dze&>%Y`)*n;Jedj?t|Hy zLl3UsH?H`ed%SzF`VLrq7Y)8on}2G#@~SJ!MHTOOG5@ssZ>OC)zu6Lg+9{QL?8~Xu z|2w&!pXOD&z;8RH+M2o5{>-cOkFR%c^UpZF9A4Xdcjw1H{&6|0eqO*{Rom^^=&_Y+ z-bpyP%AcL{P5}<_pUf4u*j(#iZ|sfxKhHV%c=ptuIkQC`(O=O=i6d|IQfF9fkD{BT zQ(xVr6U$5BPu${`%avEW>UrhB{p$BlkEy?}w(ouoXE@J)!5_BA8OVO&jq?JWx>s;# z;p|K&@Ll-|>suvg>ZUl(Ww| zv&^b}Ih(Y5wE6Ylo2qt&Uv})A6U&K}H^-h>{j+-BIc84%z496#Re6Um?gj8`))7ZG zjvYMvu(Dv`>Ft>g?!bcgM%lTG>%Gk{etvBW=bU*~dw0~`H~V2Nd^CE1o_0@QpWFqK zzdfPr^p(3@cMN=2`cUA@#-B~BoIgfpumP|7Pa3X`CFWPCN3Zg=^{=g1o^%3iKkJ## zE>C&dD&<+vS*5(HmTj^5mSyfqi+-0&KjzQRv^VyhUceiD^NZrAIWt%vU6=3i@l~h8 z-VE9=_J)lo4}KQD!oKs1b3HDROYYj8tMMOi=mmjK;5zT|16Fa1b6x8kV1v^RWW+-4KA_jtqh zTL)fkTF?ESYaVgf>~hUjSGK$6OD_ISnKk>+a%$y(^*HCxt~Pc~ZF5AO*YR`fbCk*n z=Na+QC)8*7l`CTR(fl=brT5cj*ZY7uwcTNrLnqe$4zBZck6^v?tBvyR=Vg~(Qr~|& zv^^hU=lE^*$9~`~|A20@SM&hfv9Dxdj_eKIxXZ9V+Tmt9FI5>uR^%o4>`q5{>hmGo zdE%(pXU`XWmZtmrpl#otPoCG-jXMBiIGxj5tFd(QpzKTMOUvp(O= z+F%hj@xXaN9X<}MO5>Sw{nFv=g7pdP8$65I2qqA4;m)(xU4L(}uEqK1jD@F_-FICi3+wbwH#wU>ys^pdpYYCk(_T0O;0?Wy zd~1}3J(v!z$yr*y{Sc(_i5~OaQSWzq56r(^KqhpMIpmyfUebcJeXt+qGpjzo+HvP? z>pAU9^$vfh@`;asviX*L4E_Rp~c-K2Bq1l)%^_fghm5BL%|LoRrhGmY3eN8jEZ zAuIdhcd>Cge@W-=Q$Osr^ZWy=KjPg+@4pDnN&2IEve&SIZ*(r7R2;t%r(udO%BP?& z=n?yYYxZ>ydZ6;jdlmNZyy{1h!`T(%2USd;cKR8G&2V<%ui=q90k*^Q8n(pUfSAn@ zvpIO=PC&Z@>v>5(`&Vq9`K`0dKHuE0`Buih@ci@I_jG(WmHqSmxV`t>qdq%Yx2?tf zrFrp1`CTv%<796S$eu5!PR={lXWi;M6WN!%Ljkt}Oem+L=pwxH9pwIZWbKXL!N6VT zDtAS}y)%}(3H_5Te2|;@Y`a~(Gu&~}zi&c6thes^%~zy5?FG)*NBY@3;fp;LoWc2H z`m;s>PVG6J>~}P$`wwsE3-+C#W?XuKyrpxmORoG|957Ed)49`KE)2pOUx5x2=pN@* zx`tn+++8+Vl7};>GbP;9BhK%5>o?i^zi>t#`^*1h6X{m_%pWla&jE8zmB-fGYn);0 z*d(&IHoOrUFRBhOM|-7DxWXIjq(jKz*vbW*p%c!mK9OhrVt8dQoKa4kSAUzWKG!_q z_%l>#~!e?+7gFEhLa+l!Qu|D}=WN1%z*m3*js~vFQqIaeb*#E$G z=k6Ti`2rm(&;{0Q-qwek^fbSMF2pJ8!zE`ZcZ$>Pr;!2P%&)o#Z=93aIeLlQ`Dye7 z+wVN%EI{V=0@mqwPi>|u^%i-YEbkL(XQ zobBxgUB>UG&&iHIL1*F?z0TJ7zKP!^vp3<3J+U|Poullpw(Ny>lISz%NBuf0H;mMG zz_;7J{#z*9Y*G$Bq`pIP(4y}e;6A^ZzNV+)iw%J(=O|dwzcredGl8|jm@`CpGu`&a z+Wkg^^S8Ab+Z`ILkvqAv0rYpC)mpc?k}a&m8~>N@rtE*ZyYpa8tE*h#ZAdEl$6KW- zQ_);S;}mi&ryFmn6@wZjlf%^ti2(xSqBnyWP0S$k)Fg}vGYLXBbPIGhJpet?-82n! zfDi~%LjnY7LX&}@(W0WUG_YO2_wZQTx6Yvl`kX`gRqeCiaSv-f&suA*_x-*Dlkml^ zSU>PPI7F}XVBUBr1`y+^*${q5zhVG-=zpu!K0X;QpC;F1yzVhJ+uAWEoWUb~@J-4-Bs=-!jB}iz-J@YnsL7BXDDQ zCbPc&FSWJ*v%W@-fX|U*<8#Dmp4AL%|7A-DeqFw}{)b$xdFKD!rET*~H@6E*e%3g? zlk=r}w-nA!Z})AvXUOj@sOLw%>*HNf^4(PZnw#;9rTH7rJL!H=7&W=xDyWEQ_bfZmei=}$roPNe9haB{> zr4#?RO-y1wY@Bi79G#k*bL_0P@P;p?J6vP4x zu3JO1Nqnz$1wNQt^3d|S#%(?AxR{8J=!=aaC$<}J*b8xi7(jdEOD=T8|5(4!3!dO} z#)(VTdXB>axy47}F56~|X$KFj`*9_48eQNvP6s#U$KRNf9H(`qd06iaHoJK5`!JrJ z@HcF`cRykImYY5|#4Q^u_lzTaj=UM4lQoT;+3Mnochx)xl}q!EjojL>=Bc%h9Klt^ zDZa_OY#XY*rB~MPa8@2cE=>MGoVK~%K`?G>D*n#zet5QoBd&uj`589FIN3e265}X| zz469A9peJupzzh?hBI_z?Fj2~4A$J_<2QhC#(Vnoxy5u=J@*kSV{5A!g7K7bCy0rZ21^2K5TK9s+458p%=WP&HS zs!cT>S!4Kr4pV)^7vzYmd~)8+)F#a13jMQH{95mCzC&K;MuCGTO)gE#uthM3d( z2Cj}OuJJeGB;OBk|7~Sc*A_o+yY=&f-1r>NG5Lt()e@hn`{$j>t62-lyLsjOOGdcG7Vt%QgI5Z>!N$ARb1V3y2bk8^m^ap@+9Z4b zcgiRJ#{4ZUc?81P11z;5C)U#=ha2xgKOfEoMjt@urTA!{@1C2i7$xRqbB zzVXZxcjU3;v&3xHHgZ}x#6DQVh_kF)#A}}6h}Xnre33Yf4^b~GS@1oJmM$CkcG;3E z2A|{oG`zZaL9P9Zx5g(QKd<^17qUBY(B4<$YkY6vh{|=C4?9KPeVhh=^oJvG7tTZm zj?0yh9a#nMys_4XW$QLJ zSlq?7SnH4l8Nwo&lDBb+jre2v7CI#pyvITMW&`;wIMg0Jj(Nl9(zU*=N!8}hPsdld z-}f2#7&?T7$eYvMGvN?`*$gT))~B;$$q zYVIq}z~AQ2-8ige*>8SSOeg1j?RAxdem33`k9jsDPUDNz;xaLt+%(>Z&s>LV&QHX5 zs|NohUK0l@$EAzNd>o5X3x$^O6~eOc2PuQ|ge|H>!H z```@u;Z5&v!W(k4#vyxHg=euWp5(V1*-6)sKl?}iY><7tv9BN86MrnMcWX{Pt=^5T z^^JG?HeOrrz1EzK$GaW;jk$QffPeEV?leDqHde=si)}YoG7wAR)pYYFb^>q6RqO=& zFlwB9g`A$)Eo%s46T`6?VpiPeFRjh26&x1>v6Z+4xA8adNfzXXA2`Q=Ei9pwk>Wo2R_C- z{eGv$`i4z-_$v<%@1=>CHWmLo_moH4Ts~q=aYVc&K1)7rW3AZ}ui=LLviMB=HoTWs zcov_@r(LBZ*fTg4H?XJPU!rp|q-QqQI-Xwn2)w~P`mrx|gCB8~ z{Fyk-`o?eih}&>aEUhnn;F$Nga77#gV~J7NP5sj+e}jweGhWAM+TY0ilST4=Fe+DI z&T@+4Q|I8Atl1W^COxqcFy}m5BsSw)$%*ZzGxKsES-4kR1^@gG|L(Xk&^J8OuY2L# z8XvFqCr+c^etknvY+HB(58^as3}Kk==4AMU}5)$Sbn_MWHj=zQ|TxkGM^4`r*_C-Nm5b`L*1Pcct(!7X+f_Ho8> zyrEmNB1?9w=Z!Un=kj9lycbAT{I&M9n|PiL^z2$b0Z-U5y3lTT13$(oPCMhw)7vSh zeWH068gJ;D>~X=|V;AK)-2-26B6bsd;0e3xoVoBb?8bESCjQ2nj=y1R%!_?77I6~a z3+FHu|4T>aMJDDzp8P(}vw3u44a7$96}SZZ0P zIk^&i!WrkulAmIKj6<8`Y+Io-$HXLboApg{6v2`{*f!%f4*mw$6C=^pamO4zk$rKQJcDw{ zf{TaW_efuS-il@QyLH7A-)A_c?1*Pga^^6c*hU_Q9dnK^fXCP$wi%zypPu;dtZ(2O zF3FJ|_zHGizK0$ZJ^)7WjZJb++)Q6|Dz3sCvZsUOH*ITE>_P{4GVtc~xx>4De2p<1 zA6}}(44%P?1H#YXkgmkoFv=Ea6Ej;xw{{T{6L8a<;}GcQG%SkcHwoIxaXa zer1#7cX7uY*j;+mh8Wwm)&cYd>+ZuBJ{|7aDYbn*T_0qoxE^ommd$dEZM3ZoT*GJM zU@L7Wf69)UL;Q{05uLy$%orcvYV7Pp@&b579&9Ro+V`DK`2pBnTRHaY3*&NHa>IVZ zhA(1E@Q+{MZ#Nc~wVO=iI`K-?#BD9IkqY({FtE z{ZF>pcX8yOeTR-8<=3rU#Q)^QuFwVEGI>1#6H95?rfcHu?!e+U*%{BKyXI8GZo8|GeP zaE#rB4SIGAZ`ft};a{yOW|B9a-&hCZ2LER~#L)V|Mf>a`y;#$@U%@;3?52L$ zBiOd$%F5}f6t=K$Gqvz?>+nerT5hq6wcSwb^MLr)sP$Z z&i|HLi!3Nz55F-`xc0op`)i)-EG>UAY<*j~wyG zI)!e<6ZB#3?qzGl@8TlXVi4<7;dV`1K!C{KgI#y zc*M5AgMGZAYj%!)*+Kd@4?ao3vG^PFvX;ST#c?*mIOzpvv#joOyIk?yLj^U8} zfO+yS#={5PdP_aOS-Yk!obP`UEF5r+PuG>Nfou23Hz?WHkM?j5M|^km+%wM@)>Ow9 zR?VNh*&_GwEn-d>A|rC;NAld0F2Wn~V`t12&lK1vL%!B==h^PqGk$_T_y#)Ho}3&T z3h!`%L*&m+YD*h@5?dusWB;^CAGpGP(GR?fwY4+64_&-D{HTu%zrQ$t-o)<}iZyW& zCtWX|pcC_@JG|k8#0IcQzkC#(SyLE0jE;YcJ6PYHHGdP{@WW1zTk|!4GRHSfwMf!ez&CZaR6Bya3*?SH_4ZWX-Pd$#NFf z4!Gkyo9tSCg$-d__#AR`54`g|WI*3!O4jtnPO=T!hdagGlLIhcb9cX(3_pwk-fb&l zEjojD+z~H`>)1Onq;cT3efWSKW6{5{=pSa}QR54fL!}e>2DXiDr%QUlB^ZtF;R;XH zx$7+7oT68NW;VZStubMABiDTG!c9`FR2e`s+$p-ke;tiPJEwz$C7+Q+&Ms**P*11F~mq51ojq&5Qi$ zi!R{_Zu#xVmQ5jRg`5=En~Q76nty;xc9CCB5pNl@7?*zN%JqtNwK1-p!{y5^y}X@$ z&Kd3a6OU{s%sqb4iTICSG*+0xU)-@yFuwSH`Y=aarBi$|Hgk{7n6BqH@ipQ-?@W?8 z4)cj(0`tQk&r8@aYd!vkuOKI}iTTl``{2nu$coOz4D1B|!M=&L*%9{1{$$1c9j9vr z#`}`vskJ2@hJ)1Ewy)?{VQ1k+tivwj3_O^V@w0ERXzt!$!+(B9tiaCGmFr-Ujj-){ zaVc3SVnBK)AM*%C&D;JdHNO+ff1X<7$$4{jow*D#Wnp&xx{3dHZQak7>zfYP(%02^ zan><1Hx_Zf@ye+gzwsKk@wtyK;ZC3IfcOgD%?}6E3hd!R)-ipZ?OdDt)Y?+q-xv4v z=lW~r)q6GPoO@>b&`18fei!PHcG{_@4?1x_d(4Kj)8-6k<^WUX%)a52n9p3?gP;7* zO!=FhH}oOakz*krF^V;&vA`Ff!;i2%Y%x5r!OrpXuBCsxVYl#~Ph}glAqL|2Q`>fr zBA#Ve#C~igo+@G^@vJ$JH|$?}cjD z^UvYknrrJf2v*kbEUcS|2ffF+;X1$7v9`VgSN~J`n)O4T?yqVLudTK4@Gix#OvF3k zjkTOP@lkv$-mn|-P4s0Qjhk@CR)}li&Nbx5U$G^LaoNcfv4?YPBRTPPYHgAS3_At` z3JhqEy>zemSz%B0OIEhk`|o#P+viQc+g`u-|7k}ZedMs-fteGIpF8+bc8R>%1#=RE zurFi}2ga{2SjA8Aqkj1twt6am)BB5E%@L%HJ`YWP{3SW7VVRuZ-dx1h~;pLzwy0VGO;$5k5to>ZFOJpl0Kpb-v^b`Q7hV_54xf|ZHRdlYd|&G{+IHa zpJ_k#qkFa=_`(0terB&9Z-*aoSX;hq;(w&#oAI)BY?*EGnKhhsfOv=vVZZQ%e`OcM ziSX9*Civb}er)eP&poXx*%dkwi&@{WV{|}2>$x z<_~vxrP!v=?AwN6HM^yjpO3FlYoAQCuZAJAQ^(%rnB$JA`vOOJ4de2E@Srbkx|grv zJH$M22Agm}_i`7oY0iIKehm+epB)vSDc*x#xqf5Ydfb7ny?#!6;G{omm#^Ji-+^9H z{^Qzq>B@EO{v+Pr+8bWfzI@yvZQEbGvHkr4uWpZg_|5HK-}dtM^c!B%o~ir(<@aCS zHePUA+jQF(+x~@(*Zy>UNB`A7F>q9TBNpVNjh)>vNAebn$!CZI%$4tE>u^NufLF%r z7@rlnjb+6jDRi!Ai|rR1(Xqnrn?KB`U8C^b@ke};Z5(#ozVh1tRqu4XruKiX-Xs0> z*R{hA|C>R!@M2E%%Vxojal;$gi&fZOh{{sU@U$fWG zwzt0REw%st0ZVWX({#qJi)F-zv~d+wiKUGxFsV$lKb{arRfuAJ|sJ`4`W>q+Rg0m47|zu;JcQPCj+8%Q%CRiQlbJ z^<~WndpJt|_=i_wB6EiawoUv1lQZ#uaq-6Q7$iT8uVgB>#O~t-jHs>2jg{=46oZ%40@P@re{7pyNC4Z&grY{__=AeUM-gWNl?{y#f!-(zvT-r{p9deg<;nTGZ zZ}^?HlsUTIe6(qu;#x5ZzOa=zl-Qo`_+as)*g!7JnrV2K^QtxNONaeQ`(E+pp5yiW&?fuj#9@VO-E-FA?W*G0he{Ve@~VnOU$y68^MB#zCe{@Ef%u9o6GI!X@v{&3Loep& z8a4v|=?{i+-&!H^4{ySg)Zq}Fvo~bvT)2{LvU7~?`%2nUr#<@e{027UBmee- z`VPQ;2MqR^%@v!$6CU6?yM!CAHwT=7F?PW*IMcpZLM}o7{derQ7jNz8`N{m2brfuw z2Q2W7WB?<4t?}YHTV?IfX2X{`$yL!Eo5oIO*hdm{khgN2|gMYw55F|`+TOhJp&S7Iw#(oYie`hbKuFE0p7%> z;sHKa9MAW_pjbgXFW&Z>e{|(LYFDkjw%zkL`?glRx%-%d+miJ+R9;}!@cT5&iw9dj zc}VeQPJ8I&zi8k1=~A`*EGUwI;(ETnJ}y*gA)O=9kz9b`OUl2XkV};gSr= zPa&^59`IB|pl1I~zVp}^f5!(tU>=S*!nYDs_deaXq*p}0= z{$bBz2ebi8+I3F<;srV4nfN^UV=)~_TTMOe|)(GNo&wI2n{2s@~>)Qjz?mzgO zd*{}BerxN0bgZo$+M4>G(Q7ughmJpJ@HY>9;{UhDKlXd=dvAJC`{#GOvORkEJKKFn zysQ25+g~xnai2f$*mnJ`w+}I|-{?2iAO5i)9r6JFPXnHaT_OW|Gj6s_tcm~Z09o^s zSxd8N>>{1e0nE}J8S*KK$>glbkbK1DnRO}s$@lO}A=7%?I zld*|O#K>|{Y*?^vymTZk-~+77#A))uc!P6nh`a~+vdLM~WIci-3cZk#cES;LIAeQU z*jLw+uj|y=*1q<{UF2>|e2j64fz62y_!;9e4>*H=y5>jd7H+Ip@x~flUWk3b8|{#h zdwefoNqw(o>FR6RV{;E~?YF zTINrGt^LzGU)k1OR_{Ju&n|5QpvglRwq|p!A?` z`6|AHUuTQx0=^aYBF}u;G8`fUu?w!lq}WCtfsVE?zr^ee_;>s=eZkZ*VpZn!_k z)Z_i}v2eukv|-zEx>ndW_6$}MbHY1$;}krz&Bg_DbjA0vgZM(X)~eyve)Aryo`?dBvbJ{;1^7HNMAN%8W`>`Kr zH_kh!J#fr>+9Q9yPkZ#p{n{<(ozxzC|8KN^f9p%zBZvM@+giTn;g7$)JzQ;ky|&k1 zbWXiPy{?`3iMj0$OQr|x|L*pdx4yak)X)C(AY1Y^E;xx#6_=W`d6B`8=kPoK^&SiV z$!5H+o&k{q4!WOBiQVdXK=#4=m>d0m_|hK>XL7$<+tH1fg8bnGzW8i$3O%u9_#a)H z6JNvsDCWRt(kDB}kFg)_kqaRYG0;r##(F2b5i7%scou)6`i9>w2w&^$VSY!nIeQ`YJvOQHz&)U;24#0!)!-w@T?uv=!1o%HT zi9d%i+lpMgeSL_5%njH1UbVK3ec`2-wN-0Z5BqCZRXnueb8Yo!Kh?hT2QP0=zH_hk z;NkCRPrv15?ddn}(b})fX-^#Vx^~04C$(*d{Az2z{G#^E-gDZ$r+l#8cItmEAIL7~;x2Cs-mhb=OH@`WoDb2;ZVUCNz#ByR$c5HZet?uuVXiV>Zo?lDIac|&iZ6~=-79+uN?RP+I{6i?yUUsvZeL?m-FVf zZAbjy_Tb0=sNJ%#ev9#%4eh4W{<=MM)H~XP^<3w{!}e{D9ItFGuF{yEWSfg!N*S>hi?QQ85%i1Fkf4zP6(I?t%U#R_M zR}B9%EplPg$VMyy!*topnSAMJ%#D5@7s9C=cg%h6SM0-!^@(^491zpC+cUe!&eK7pL*%-Y1cl;RpFeGB8K{ zFh{(>Gq|QdaW#3tC|d%%);hNFhOJ@yn{C~bwSxP-_rGY_(t$g0y?oWg z?>b#x&o!5Ay16Yazq{a?`ajW|ztAqPeA;E}t{?u7=Ve#d^OYNJtu@b0ZE>}+dd>QF z>FW9)B^&B@1#h^yU9z&?RoGPPoNMbI-}S6~f^qmCA$WUPVQEdh$3w>EZ7jHA48{lZ z>bvf|yM6VmUmf1Fd-Snw?a6=m_J4AV9e^$JV6XXO{s$K6$2yKrfP3xHV`2cuQXD7u zem|UZz4Ls4{6hF5ZWrsar+5Mf`m{#mL(P?b`KaV2*l3*LhxitGHhl5iS$i-_ukfcG zcAuS_iF{g~-{1{f4S#YW{L2s%7iX-A$<*4+TA5wP9kPRMvW~x}8}=OD;F(Rp8!}5? zl>fmU`f{9X^^cR{qUendw4a(j*e~%QJ0!OE?hAi{FP^oqp>&ei%-Y0y%y(+|lH|>B zf-Q$h$HfN*_bqV7YvN?~Vkw_~dtI#H)U{_3D++{;%bwONMyM z_i@}OcJlvp1{<@wa@2+U)s=64AJpc`hv2tyim}z?MJMFNUpbC5 zbO@K;6%dDu|IAr?e2{tb2XHRd#vw9w4Vk*0ZKOX%PS`O#z-iYSmy&guw#0)tX+1^W zVn^eFG58vA2-}5W_Ik*56^D!ym*C2^w(-SrCH`mSs)@FIN7MJ6S1hkQ+KS@CvmEkW z=+$fLw*cxqoL;@6VyHR~`->`8x^kuOtgUQUEGdpyztsI#mH%2(Yo_HDTfw~lQ{aC` zu|faI2lrQ-&WA5*^T2I=sr^qYIL^3j`yR6S;GOHpfz6|Dc3bSGus38V&kLJm3IE>3 zq+9WUd{FG9ZJ4mG5S!B-8zzqG^Yu9Dxbrw^?a3dR8|)YxTZl6XT(O}rC0@fB1#Ub~ zgc~~NgV<}>HAcMgI}P3)n~4wJkOiE=H*)a;2YS9=*fKfEWU^> zA`iHwU;1XV*dqQ_oQ4DRhLh|bdqQtGL5JkTFZYFSr%(5|*FOKvp5Yt+1`qgw1L1CX ztTq<-F+SK-j1j+Z$(Yry!z*pUntK=4Zy7IIaz*`rnoEY?v++OBF1+ZHc4_^#ncoyx zaM8ShGyZ=K8Njmtnd~>i7S{iPo_ERowyZca#5oJ=*?FD6=;Hc*X4MNWt7r1{-8KD? z6-;?gXWsma+M>mm5BIs=n8K&{M`JbqD~qSL#d6`gxtOo-i#g|B$Hb-dX{~PE$sTz= zLiS{Co8IX|p$~de$Q>V?Pkzg@d@+Ul=tdjtB%RPVjs+|H49?>Zyx=6?!VX&lh`qv5 zYksjZWgXW-Lsy*~Z))6-5He#iSf`0X7V95x5*EWGv}vKmhLAil^r z#hzq9|8#8r@MqrcX9LWMeAqsEvfhrpbFDn1!q(uTF(~@LL2<5FL4HhIu|s5vw_-Ii z*0=j$1unF04HKV&2e2=Ggc*2_?{N>#7^8b()qVDDI|i%zGgq;L`r^X>XD+DU$f!K8 za~DU;9%)m)6{!g&+%q+@v(Mz`T{w&#C)|6X__ThT;N0^+GyION zHqNhW_2vF^Yo2VB{>ju_GFGw`&l;;(&;ObAe`VMOxin*3UOsPe@zH$g!1d0{BjPDt zvhAMZku@8tu;*-{ZLu`FYTGe*=7-dd!;f`_bK;Ud-XizN(s;%C(YrQq$D9?IpliHw zPJ8gjHi}EUFNv4dNy#51HW~l_q}@fJK0eE~|HA<9kwxND_9yul=WrS);2E~zg)Zq& zjX#6#3Zro1TDG76f^X;P8;0o)p7{i8Iok@n!!bY6=ZsvB53Ysztnb-a_qbQzZ~zZ< z)7xU$rz^OLkIcAX2yX1F$pW`rr@$_KYBRQ49e&w{JzO(C#~rh;9el>ezK4h=hc3dB1Y>s4RjQS$S#I)q-INwG`#zQA+ z_t1xX)b23{*SbgBxkv3fGB$qWFfO(i&*a0*gK) zYa4ui;)y4wWnB~gg)?gZn}JSo*4iJ|t>MI?Y@6b@iu{hHb8L{Rd7tn zaogmSLe^vtm*FM7nG>7lK5erT`XRg6LbZM8ZL9SO@8%KztyDMp7ghwzveQ`V-(Vp@2{b|?r(SiBEykg972dB2( zn>l1V++#1?Yi`Dlf8?e%X7|H^v1<<=;Dmm~Sg=pe_Qh=K%#(h!L)YY^o%kZ0!Uz6H z5%;*4Eyp|04RFg^LLd1K8_f7$JTvhLj~;&b;ocFPvk?*c@^pPdc*CmeDP_!#HfS=deMqFhFKvYK2_b4n;rYO@`#=I(Cb%f(`q@ z1lh+Y;kPm9117>3V^HjCk8jnsq8~;7j%$OzGbV9>HEYhh9^cs*x)Q&T2fM@Oi5(Pe zsmTnUw8viZ>(1r8YqTv6)*kt2j}G*szqIK(a$%RvMQuNAXwxx}XnoVb92& zA2v@M&?hX%=is61or}E9f&3MG(3coO|HkQ~{3VAB|Bt@=)Ug(Fc*c!1@{|UYw>$m%Bc4eEddHnImhjjx#L+^O( z9U{39@6Y3p*itUuJjjZS*b1>5-jFF7lLuP|v#^a%b;L}(DK=hZMO|D`{2 zQ@hW&U8C5>eQ~zy$r%Sy!X3JDUT%y`VOak#Z@gl1cpwAE*>=atnS5|Ieh%kS$k%pw zg17eZgRP?%dc_-KU@M%XUwHBVAYs7V$<93FcI8^Fx8ButSVS@M;B=ga?`FkdpE6LN7*tOvt#`QF|4PP=yy z=!y=*4Kj*Ngl+c7F}_L-A9R6>xJ3u9(HEK1A6>&F8^k}#S9;!NUg8byxiH+Hk#V?8BdST*tPMo#zegsW@GY@A{z& z+R!VroZaHo~a=MgjaAVMspwR`OY`{ z?b!|9(6@H!i%#Hx&Ea##zGb}se7WU+W|9Se%7)N646?=EVKPtesNp#~DIdtD8LMKP ze2#hZ=j0$)!l(M~j_*bCS)QfgpWl)Y`*}x~e94vF!4Gn#Q`?Tw39Q@48@e}Ec%c`^ zVB0>wl9-JCa8sXrC4S+c>y-F7e!}?G@^rSXjns)N^ubSLp8k)b>-FbY{3EQp*Ku)% z_jj}@c4P;&XRgL!-|<|l9k#(Sb0!0I>;=Awhn?4szRZz4)Y^<~&)CVyG3Uvd4Aj~d zyRqZqZngtg$vbk1ts;BvXy19`*B(8k#&5^*EB)|6+EU{?U*Y#W6u+(Vl9#+>cvl8z zVIN0uhQEP*-`mGqoZxfwt`yvOM#L_9cH~~;7jN*ze7`vFsi&TLzO>lx{oYC7FZgM8 z44&Bsz5>trM&rZ*&p$ms7N6h@+hl%x4gW$;?qv(|Tee~fd^8{SjQ#K|+Ha(=i||h; zFhI9-2pi#!^V+3v7^O%0blmmuMPE6t9k|2~cACFrx8ifcoz&WbbH9J9jd0a@+iDna z9nM&H;)DKk+&SBdHt_0eU;EndzdeF&IO_|n;iLa;?j0ce?(Iut!2WvoR6I-$`c^C2 zSF=}sTgNj6wd1Y}mdQ$SjQxW@zR-DmGzZ&i`mk-h^kHn~rXMoVj`nTikbSlI+_tgc z0^5rx`~*8HhT-GHF5VIGo|)&QZ~;d!#n)*EAJ}ME^NxULsj%p~H+&A>Yfp}fU#4T& zBh%^p9zpN7yay=0vHnUdZ490vn-|`&8Qx)X4el6k62g7A3U>l!I^D(P8)1F|3}7RMeQ16_>$U~ zjfp*Xe~$Mx%s59U_uqg2;3r_(y{=O$u8Xafx7V)mO;^ zR$P(1o%?VF&s;A~WoyMM^v!R7qyE>H=PTYt7oS+;i$U<%+DV+l7s7-*1N_lF-vU#{ z?0W=kA+Eq0oM@l#Wb?J%V|%-fV<#>cx9j~;>lBz^t6|i5aGvilPWHl@!8@9GCU=KY(Y=n6P2^T`+Q(e;S-F7{bZA^t;M7Jj=VmVhLPxpIAv-V!Zx0pwj2=tjFBRK6}9DS+~iXz%`rA=NLO*NmkZ4a7gy> zhBMZ8<_YuUZte7~Z+&avPS2Zd+qMmT;Gyf2t7QM!DSllH%r1&y>4kr{R^i*pPh3dn z>>}=nb&`jP&4W|pmzNMf(}iQfu(8`$vfbCV`;z-`YBFA>E!U+?b}Za;O*qu^rRPz2 zgA3scjyN8^ggdZH7PwSe>(QpgL*m3>^ zPV_0C;lA1Ym>d596Um9n8L?0Nh&WSRiZ^^KOyWDAieqYd59e^hN4)*r?|ygiIsBSF z*aR`HIL+FSFMuKUlegpA6{q_yD%*sAbZjjAzwyv7`N~s^i|{6RavTQu8?qIj;6QA7 zu;%(;e5yLw&UyPi*ZSkxcHVY>ukF6zivHEv&c5;CiT$208Fzfjm?ObG%;Q7iC_Hel zSXW+COhHDlu8^hJ!MOPg*RnP2q*zNH)HPxV*ZF_F;$Z6z`ryCPAKl5(c?Vh_KI|s_ zi#_CUy-RGL?}q^~mbDE&$OquH^)IfTc6kiwQl`yVo3(Z?tdzuHg%;_r%(`MV@IFC*)7W zJAA*ju5~!Sk2C#;PUsTP@Jg}%F0gAU<<{mYs?c#~s+Ep{5;@L7zaHFqbIK{lOt>%0AX=`xnPBN4S*dId1z&;z_xbQy{EU9!fd0i`VhOex_tfHS_K&_j z3uDW1hP|HZy_#TV=x8_n*^xG_P3XWp|xnMnYj)f!kZL4!G+u08%`Z~N|2l`^XDR56l zu1%Zza=mkSU|nu~V19DzYIAhlz1D^K4kkJAL*fMTqZ`M_(=j#@{^^wLaYf-fU|-yT zlh(P`mvUq{qlO!{jr`$A-pGBPttiIDF5{)R%d=p%*fG3O#5UqIcyZh{Y$`j*_iWj+ zWxI^%vEFZQ_r@K(V6rx0m+gU5eh0?M#T@pmwLe>6TW->v@G9#Zb7gPX3CBI_5!1=d z$&rz#Tq6DOHP)+qF8$;~M~MfF&sxct_&B~wzwD*F1gx`})`)lrC;AmX&_8?yf3eTm zhdWs0SMUX%=NHD{5Uy}UJmP%t-D7^doognmdrtJ*8H?KZ@IbL2zSuSf<8jP9`3l>{ z;Jh`4xu>?Rz^>Z!c=ik)W2^AQJdMY7#tr*oB+uq?$r?3rGd-|pY-^7jxmYn8Zi=D# z1UXSQj%_tQeAl*oDnBANr%%{`rM)W$B@Txz{aarhRDMW%Fhb|>$QJbRdoVMbb!0(4 z@J1HmC-cG^ST`4Vv}Ti^!7Fxyd@@J($zQTr^dx4*7q-MX^20@bif+hTPLb`SCm;GD zAGIQW6ys@&Kf#r)^~^%vfG=X#JSWsPKf~|Q5v+v^aE23#V{mGoup|CAH?}W!j$XwE zu7g`O48WOMJ20GWID}irQ)}CPFdZKWr?77v!FR58EN$Qg%)vc-V0~%brg)Aa9wIk$ z(>8mjuq$fk_+mDSjiWdEa@@Mm_r1tbyh~QrjO54mv2T1oKauq+j41ZS-R?0DT$Fo& z5#zLWkx!y8@w>GZF5rc{oNLu&Ew^AzB%Wbk<(I@WFo!dIn0<4aDn6S{-lT8#1TMvC zVoPfUK0Y=Dme>ojP|VdeVa3F;}VcY15T;ycrRK#1xog6S7v48X}?{6I9?Qloj%tpbz zw%8Unk6t~;2!C+Snk2bv`h$O1V4tjQaG!0}k0O3gd_ynVV59jTI$%4*h_Ua%>kFmk zZ|F}vX)VAevk80#KVt6i4*xjfoH?@>?3w(5`SMld!^bM*z&?_vbrTy;{_>vWPM>@V zS;Iff(<6CXEAk)KxBL{j=6!4ObUd-2T)$_lo(-_WvF+@ldBx7dj{e}$?=Q>gc`kuB zd+f2t@Y~R8=jj**jVai+Hnyh51JA2*{mWne^6(6g4fh=l*z{bF-G21ZM~Cz7QP_K2 za;;nne&e9=;fsAZvKGkrjM12k({ESfkGRJ?=!B0kPBOt2>--ckvv`QU$jbHPO!m2l zKFGy-ou42}*WxwKn?If7m3D#w_sJ8nwfO0NKEgg;n}<9CAA~D%*q&{`lrf3d@D&z3 zm#~h}Kbr+>%AMhqI8DGZ+ixqK%ov~JvL!w)DTAr6(#}9=p z`9}pFonyoKB=|K(zKU#cR{!4nBR@DZM%ZIZ@WxsK9>vr6f+w)Ake9;FkeL|BvoQPQ zPIquhR_qa*MyC9iIFK!)EBJK{-OB%1Yq*~cWfR#pTwy2a%o>LsHC{H%deazj90oiC zq*I(#U8J&pN_-6NbZsR#L?&*UZ$=5R?^28Z_$@Sz)FYd<++)8YxAAF%-_6={1 z$Jp30?|SGP2FY82N%59KSFnfAVmfP5ybxF7FO1Nw7{Rj){1Fow2Oq{S&?{do=Ef7T zjyO%6>%+fTH}EZTitLfxyj(l~w6!?o8fy$6yn}iEQQPbe-{O5CTnS#u9ByIQ_{45m zQ_Dzd{%IV2-VtepFJI*^!2YA7j;*wZd+(cjEM`OTg*oJxe zF!8O}12%9^o8+opdcu+TCEtgj57)s4JE0%G1@`!AyyH{F?LLmbkUrKF>~hAK@v*P) z=otuI^S^AOIG+E7e;8EgSUeYbkq=qXA$^h&o!}WPh`;3D@J9}Yf6^Yg!-Mzxj0ZPe z&#u9>nl9*-?bEjP7`^ci`hi0@l7C=-?c?ID1p>WT2 z))es1Usx~cLp&iiv#!y%b&;5kJ+hX@t*p(&K91YhuKW2jw!{2!1}@o=%*R~Jl^ua) zwhZ3bC9x4Xn6J+oXE^Z!_!I8R1bcY9WGUP|bWKFNIO3(C>cn^;8 z%sDoV&c*Zi!XMF%*bmO=itU0Mc9uSs*g)-y4PeC@M+^}hx?a&gyP!Yg!g>AhH)22d zCI|ObMy}!mdZ9~tqI*6` z>;bn*FbsR*cVom0n4t^T$L_*_aao6ImpyvFB@Q}7@qd?NP0P(9>K&^=P+^@zd z_lc|73fJj7W5jVh=J(0VJ@`YWWW{dK2|lxXbeg<43@E*hZR3q=*&TDzt{6~TY^zv8 zoQfCxwzYtHTO*2ljYX~{K1SU?BZt}9{@^j)?NwPG&Xg-bfXqxdTJExh2f*)D#A zErV}*q6<9058L5L_(DhYf@8+ac8E{lp8aK8V8{DTIAOf{!WSQQ+L-jg_Q03->*Z9! zn_zM_d=6X(+rxmL{nK;Vd7MdZNc=$#*0j0Tn%Mfl+SYu?#@zGCb^X5Utf5^?UhFf> z@bT93^do*2oZ*aP za0^pxG@Rl=co+Yq*1mJ>cYK25Dg8aR72|MUU&0S5SkukZEA32Jp^K6K06*kNJQQ`qQ5PvD|v36yv_yP47GNTIdNCK-;xp_y$Hu}Gxn*r54`BV6_Skhe8sjTC+uikZ^iO*n6%z`5d9Ke+|jUo<*$>dRCUCEk39iIlb{8aW+=bG%} zMv7x8xlSD}>KiwV%NW$g?|9Bxr|}i$$li-}*dDofSjUO@3K$j7vFW&g7tZnR@h4^!{wO=jEQ^Y-Q|K!X@nSrEsWi{UqMR1svxC^3I&` z>D#^TbIoM)ne13(!FRzWyUx#xnaM5~@I02?G6ym@W^*uR+lq0E`@Hv%&yMIl*QSo{ zJojaPjY}TQSd5K6U88L^z4zFe47+K2I_iFo#%SH8_HmB?6QkKSHt&##MSb`cF?6?aZ~~d#Bn@n_1&!e|y*GyzBYXaXcOQ(Ivmbe&J2*0v*H#;6GjPQ;Czv zJZ;$CnGZgs9T@apP1Xlu8+gmddDrKoPjiXi%JJ#0^$UO2U$}$ce4(*rEXM7*1b^uG z)_OMXy^-nocn^kN*i*U*f5IDn2yetaWa~4TzW953X1n=`#QE%X;{B|Fw}-u8Zb$2J zd*l7*YG-GzeXe%cio~SmtXRuf!&sAyu@_x>pCx$&vX`e($Ui#Z=j3p#b9d&`>#5%! zx4|aoH!8$6u$Q<82E#Emd#>czxUHGk#}_$te#KhP+{l;T=0AO$w?ADmCHo>~&$=V} zithLjYYuCEIp6Uo`mpxFF?Kol5%a*0wSl$1br6hLCumc@))LlO-cgW~5R33}aAKV$ zZp26S7r({*@MWBAbNHUMxpRp%gS*5$;w|wG|LQ))cIK+>#7X*dpSI*UjK`XX9QX#~ zHSWDD_a??NC;cT4Mh4)$hJI)|y~ z%6@bQ7w{~nH5q@6MULA&>@KV*{EOTG%q0&jk0l2zf0bNT@?2Tpi$}7ix30IQ&$?c& zT0S`Id-($Ee6`%yR7D=ZI!(#8+P#js&vuIT^r5fhBy7tqq+~3{Zw(_?9Ilu@|D^UD zVX}|+L-Z@xhLhwM-l*+chv6eUpY~vYZO;3fp8eo6o9%-);-g?R_sv$F`KK12 zc|PO!8XS`oU>C4Pd9^^9@+(FR-PnTPjD*lPDDR%3hl z7SD{5|4kkKsQYIslkMYPe@*`^Chg>W?pKGa=~M0an6Jsfr4E19dB&v1o7AqA^CP>& zYU0x5I{2jEv6mYx(zWN1c*C!geR3aa{t<6*OKh^cKf#@LVSw$^4?FIenAn@0#4Dfv z`r*I};EyrMm3W^->}22CC-0|@$2{JDzEhw>F%Er)GqHKNL$|Qc2D3$SFFyS^;DdkX zAHB=qJt6O0dgoZ1au9Og{WD5j$a9lCrwR5`hZB7r?5l(Sa0Hff%z68YT=lrdi5zo2 zT;b;w`>C^?bMC_-v72{~d>_s;Hs7i9UKbl4IraLXQ+Z%>$2re|$UZTon(Wyf@3M)H zdpVEg-nYH0+hRujBq!ke>E;*w?P|Nzbsu@)v-r%~jBmtoc?|i%#B1q)EWaFozBT)2 z{VF#ro@Vc2^Thq$ukq|p%))QLsP8`Tjow`l|4#Lxlkid>c`hSPN-QJ}3ub#x1hYNn zae_`2;|TY9KH{Q%=M)@tpFYx-e)_(&mt*0O{*6tHrm+3uy39eyvS6pS{~!IVk;E^VrAzaV~JW#~7R-7z^nZ6pV1jK-@^#kjn`grnX? z5Ua|=kfEGnWUC#;KD*@}xQK3cr@o)7E$eBx>-&`}cdWPT&(+3EoZ}PuapMZ7@g48u zpZhsvJN5G&F+j3B1Cu8A*j1Atc&*kyt^UMLa#Z~OST)DQip?`CN zd1H4!KI>2awp}wGpJNJq8@IWbhdSIMFYWn$C0^qUj*IbpmqAY6HEc>`9BvvTUp*Ti z>zh6o1s~!e+zVpw9O;h_v|fdS*h9LdSB1XCK;j-)RI{CI zIy>$fc*UI*eCfBd?>hL3orTd97|b?|rWjYS%4gs$4&j-5*nf7O|H3uCP8`P%Xe0Nh zjlQ37L>*4Mk3Gn_%p+sVakW0l$Mt-VH9vohGh$iq!if!CgCD(I-OI=E`LUVN$xPL- zAl8(V@~nnUx4to+;3V}-jcKxe`Z0);jL~~(@ja8>GZV+y9dQ`D3lDHkuP_7S_TkMo zoQi*7Bph%Lo6LWt#7?J%TW!E~Fa%Fi6&TZ>0$;{}ch)M_0`SeZ$Orgdjcwy`&U07o z8duur>$IKPIeybUY=M2_&788GxunK5V`4k-21n$g6AzMM`pEcQkI(Lv2bhkW^Uh#K zUR1nejSZ798l0xynf9jh+I~E-ZGwB=0z+i1m!Y9Ia36Ie8D&9eH-ugZ7<)n5jCD-)V@o~n4M|dMQfH(XGj(9c_KY=&C@4`>8`No&L zf&R8va_wZ}$Qb)Na}Hnh7nzWeZME~>E9K{KW-|Hry4c;@JKEolUbnl)H&uHXPwJ_z zo6h6U)pySE*?#jwyy2ZIxCnQ89N6BK5=;eO+8ARlSj#oJhyCYYaEEWfoA?`e6gT0G z7)Y+bdJWI}c6Mbu^WKqr$8&Kn8-z3BP0zX`>*>_>WNr8TPPW}2o9zDCI%Ztsv5ohq zdu)Aw{kDFyF9+%Ua?fwXVbAqW{m!0)pPhN1-?e!cOsvGecy9o2PHoFecn&MK#}>-1 zdEdvo7kQ6Ct`rvYZe`XO>bwV&n!Qi4Mo;nXfi=B4?-r-_?m^yd>9-y8u20^9wvRX7 zpRpdtmEParjXqps4dFLsQrn)ac-E8RyVM8O_lf1?*bI5OsfzDhdyeGUrsDaQytDTj n6m8 inline const display_t * getPixelPtr(coord_t x, coord_t y) const { -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) x = width - x - 1; y = height - y - 1; #endif @@ -210,7 +210,7 @@ class BitmapBuffer: public BitmapBufferBase inline display_t * getPixelPtr(coord_t x, coord_t y) { -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) x = width - x - 1; y = height - y - 1; #endif diff --git a/radio/src/gui/480x272/lcd.h b/radio/src/gui/480x272/lcd.h index dfb42e829..d6c8dfa11 100644 --- a/radio/src/gui/480x272/lcd.h +++ b/radio/src/gui/480x272/lcd.h @@ -156,7 +156,7 @@ void putsChnLetter(coord_t x, coord_t y, uint8_t idx, LcdFlags attr); #define DOTTED 0x55 #define STASHED 0x33 -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) #define PIXEL_PTR(x, y) &displayBuf[(LCD_H*LCD_W-1) - (y)*LCD_W - (x)] #else #define PIXEL_PTR(x, y) &displayBuf[(y)*LCD_W + (x)] diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index ebda41874..4fc0a1e75 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1686,7 +1686,10 @@ bool menuModelSetup(event_t event) if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) optionValue = 50 + 5 * optionValue; - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); if (attr) { int8_t min, max; getMultiOptionValues(multi_proto, min, max); diff --git a/radio/src/gui/480x272/radio_diagkeys.cpp b/radio/src/gui/480x272/radio_diagkeys.cpp index 9e26ebe86..31a3941a4 100644 --- a/radio/src/gui/480x272/radio_diagkeys.cpp +++ b/radio/src/gui/480x272/radio_diagkeys.cpp @@ -73,7 +73,7 @@ bool menuRadioDiagKeys(event_t event) #if defined(ROTARY_ENCODER_NAVIGATION) coord_t y = MENU_HEADER_HEIGHT + FH * (8 - KEY_START); lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ROTARY_ENCODER); - lcdDrawNumber(70, y, rotencValue, 0); + lcdDrawNumber(70, y, rotencValue / ROTARY_ENCODER_GRANULARITY, 0); #endif return true; diff --git a/radio/src/gui/480x272/radio_setup.cpp b/radio/src/gui/480x272/radio_setup.cpp index 0eb630928..f3cdb9a61 100644 --- a/radio/src/gui/480x272/radio_setup.cpp +++ b/radio/src/gui/480x272/radio_setup.cpp @@ -65,6 +65,9 @@ enum menuRadioSetupItems { ITEM_SETUP_BACKLIGHT_DELAY, ITEM_SETUP_BRIGHTNESS, ITEM_SETUP_DIM_LEVEL, +#if defined(KEYS_BACKLIGHT_GPIO) + ITEM_SETUP_KEYS_BACKLIGHT, +#endif ITEM_SETUP_FLASH_BEEP, CASE_PWR_BUTTON_PRESS(ITEM_SETUP_PWR_OFF_SPEED) #if defined(PXX2) @@ -110,6 +113,11 @@ bool menuRadioSetup(event_t event) #else #define FAI_CHOICE_ROW #endif +#if defined(KEYS_BACKLIGHT_GPIO) + #define BACKLIGHT_ROWS LABEL(BACKLIGHT), 0, 0, 0, 0, 0, 0, +#else + #define BACKLIGHT_ROWS LABEL(BACKLIGHT), 0, 0, 0, 0, 0, +#endif MENU(STR_MENURADIOSETUP, RADIO_ICONS, menuTabGeneral, MENU_RADIO_SETUP, ITEM_SETUP_MAX, { 2|NAVIGATION_LINE_BY_LINE, 2|NAVIGATION_LINE_BY_LINE, 1|NAVIGATION_LINE_BY_LINE, // Date Time Bat range @@ -117,7 +125,7 @@ bool menuRadioSetup(event_t event) CASE_VARIO(LABEL(VARIO)) CASE_VARIO(0) CASE_VARIO(0) CASE_VARIO(0) CASE_VARIO(0) CASE_HAPTIC(LABEL(HAPTIC)) CASE_HAPTIC(0) CASE_HAPTIC(0) CASE_HAPTIC(0) LABEL(ALARMS), 0, 0, 0, 0, - LABEL(BACKLIGHT), 0, 0, 0, 0, 0, + BACKLIGHT_ROWS CASE_PWR_BUTTON_PRESS(0) CASE_PXX2(0) // owner registration ID CASE_GPS(LABEL(GPS)) @@ -373,7 +381,7 @@ bool menuRadioSetup(event_t event) break; case ITEM_SETUP_BACKLIGHT_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE); + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH , y, STR_MODE); g_eeGeneral.backlightMode = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_VBLMODE, g_eeGeneral.backlightMode, e_backlight_mode_off, e_backlight_mode_on, attr, event); break; @@ -388,7 +396,6 @@ bool menuRadioSetup(event_t event) editName(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID, event, attr); break; #endif - case ITEM_SETUP_BACKLIGHT_DELAY: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLDELAY); lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.lightAutoOff*5, attr|LEFT, 0, NULL, "s"); @@ -405,6 +412,13 @@ bool menuRadioSetup(event_t event) g_eeGeneral.blOffBright = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.blOffBright, BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, attr); break; +#if defined(KEYS_BACKLIGHT_GPIO) + case ITEM_SETUP_KEYS_BACKLIGHT: + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH , y, STR_KEYS_BACKLIGHT); + g_eeGeneral.keysBacklight = editCheckBox(g_eeGeneral.keysBacklight, RADIO_SETUP_2ND_COLUMN, y, attr, event ) ; + break; +#endif + #if defined(PWR_BUTTON_PRESS) case ITEM_SETUP_PWR_OFF_SPEED: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PWR_OFF_DELAY); diff --git a/radio/src/gui/480x272/themes/darkblue.cpp b/radio/src/gui/480x272/themes/darkblue.cpp index 05a8b2e1f..ec4b51fb2 100644 --- a/radio/src/gui/480x272/themes/darkblue.cpp +++ b/radio/src/gui/480x272/themes/darkblue.cpp @@ -139,6 +139,8 @@ class DarkblueTheme: public Theme delete calibHorus; #if defined(RADIO_T16) calibHorus = BitmapBuffer::load(getThemePath("t16.bmp")); +#elif defined(RADIO_T18) + calibHorus = BitmapBuffer::load(getThemePath("t18.bmp")); #elif defined(RADIO_TX16S) calibHorus = BitmapBuffer::load(getThemePath("tx16s.bmp")); #elif defined(PCBX10) diff --git a/radio/src/gui/480x272/themes/default.cpp b/radio/src/gui/480x272/themes/default.cpp index b90ccd1a2..740f30e39 100644 --- a/radio/src/gui/480x272/themes/default.cpp +++ b/radio/src/gui/480x272/themes/default.cpp @@ -176,6 +176,8 @@ class DefaultTheme: public Theme delete calibHorus; #if defined(RADIO_T16) calibHorus = BitmapBuffer::load(getThemePath("t16.bmp")); +#elif defined(RADIO_T18) + calibHorus = BitmapBuffer::load(getThemePath("t18.bmp")); #elif defined(RADIO_TX16S) calibHorus = BitmapBuffer::load(getThemePath("tx16s.bmp")); #elif defined(PCBX10) diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 16929cc7e..59153faa7 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -921,7 +921,8 @@ const char* mm_options_strings::options[] = { STR_MULTI_TELEMETRY, STR_MULTI_SERVOFREQ, STR_MULTI_MAX_THROW, - STR_MULTI_RFCHAN + STR_MULTI_RFCHAN, + STR_MULTI_RFPOWER }; const mm_protocol_definition multi_protocols[] = { diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 3186e8975..77bb1b894 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -2073,6 +2073,10 @@ int main() inline uint32_t PWR_PRESS_SHUTDOWN_DELAY() { + // Instant off when both power button are pressed + if (pwrForcePressed()) + return 0; + return (2 - g_eeGeneral.pwrOffSpeed) * 100; } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index ba7aefbdf..d73027a6e 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -536,7 +536,7 @@ bool setTrimValue(uint8_t phase, uint8_t idx, int trim); #if defined(PCBSKY9X) #define ROTARY_ENCODER_GRANULARITY (2 << g_eeGeneral.rotarySteps) -#elif defined(RADIO_FAMILY_T16) +#elif defined(RADIO_FAMILY_T16) && !defined(RADIO_T18) #define ROTARY_ENCODER_GRANULARITY (1) #else #define ROTARY_ENCODER_GRANULARITY (2) diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 5f2cca40f..e13eaf2fb 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -666,6 +666,10 @@ inline void getMultiOptionValues(int8_t multi_proto, int8_t & min, int8_t & max) min = -1; max = 84; break; + case MODULE_SUBTYPE_MULTI_FRSKY_R9: + min = 0; // 10mW + max = 5; // 300mW + break; default: min = -128; max = 127; diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index 645e527d3..c54e185a1 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -128,6 +128,8 @@ const char * getBasename(const char * path); #define OTX_FOURCC 0x3478746F // otx for X12S #elif defined(RADIO_T16) #define OTX_FOURCC 0x3F78746F // otx for Jumper T16 +#elif defined(RADIO_T18) + #define OTX_FOURCC 0x4078746F // otx for Jumper T18 #elif defined(RADIO_TX16S) #define OTX_FOURCC 0x3878746F // otx for Radiomaster TX16S #elif defined(PCBX10) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index 6360b9676..62eb5a7eb 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -24,6 +24,8 @@ // not needed #elif defined(RADIO_T16) const int8_t adcDirection[NUM_ANALOGS] = {1,-1,1,-1, 1,1,1, -1,1, -1,1}; +#elif defined(RADIO_T18) + const int8_t adcDirection[NUM_ANALOGS] = { 1,-1,1,-1, -1,1,-1, -1,1, -1,1 }; #elif defined(RADIO_TX16S) const int8_t adcDirection[NUM_ANALOGS] = {1,-1,1,-1, 1,1,1, -1,1, -1,1}; #elif defined(PCBX10) diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index 59027be27..cd337fb7f 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -197,7 +197,8 @@ int main() RCC_AHB1PeriphClockCmd(PWR_RCC_AHB1Periph | KEYS_RCC_AHB1Periph | LCD_RCC_AHB1Periph | BACKLIGHT_RCC_AHB1Periph | - AUX_SERIAL_RCC_AHB1Periph | AUX2_SERIAL_RCC_AHB1Periph | I2C_RCC_AHB1Periph | + AUX_SERIAL_RCC_AHB1Periph | AUX2_SERIAL_RCC_AHB1Periph | + I2C_RCC_AHB1Periph | KEYS_BACKLIGHT_RCC_AHB1Periph | SD_RCC_AHB1Periph, ENABLE); RCC_APB1PeriphClockCmd(ROTARY_ENCODER_RCC_APB1Periph | LCD_RCC_APB1Periph | BACKLIGHT_RCC_APB1Periph | diff --git a/radio/src/targets/common/arm/stm32/pwr_driver.cpp b/radio/src/targets/common/arm/stm32/pwr_driver.cpp index f488d19fa..220b5fdfd 100644 --- a/radio/src/targets/common/arm/stm32/pwr_driver.cpp +++ b/radio/src/targets/common/arm/stm32/pwr_driver.cpp @@ -51,6 +51,12 @@ void pwrInit() GPIO_InitStructure.GPIO_Pin = PWR_SWITCH_GPIO_PIN; GPIO_Init(PWR_SWITCH_GPIO, &GPIO_InitStructure); +#if defined(PWR_EXTRA_SWITCH_GPIO) + // PWR Extra switch + GPIO_InitStructure.GPIO_Pin = PWR_EXTRA_SWITCH_GPIO_PIN; + GPIO_Init(PWR_EXTRA_SWITCH_GPIO, &GPIO_InitStructure); +#endif + #if defined(PCBREV_HARDCODED) hardwareOptions.pcbrev = PCBREV_HARDCODED; #elif defined(PCBREV_GPIO_PIN) @@ -101,9 +107,20 @@ void pwrOff() GPIO_ResetBits(PWR_ON_GPIO, PWR_ON_GPIO_PIN); } +#if defined(PWR_EXTRA_SWITCH_GPIO) +bool pwrForcePressed() +{ + return (GPIO_ReadInputDataBit(PWR_SWITCH_GPIO, PWR_SWITCH_GPIO_PIN) == Bit_RESET && GPIO_ReadInputDataBit(PWR_EXTRA_SWITCH_GPIO, PWR_EXTRA_SWITCH_GPIO_PIN) == Bit_RESET); +} +#endif + bool pwrPressed() { +#if defined(PWR_EXTRA_SWITCH_GPIO) + return (GPIO_ReadInputDataBit(PWR_SWITCH_GPIO, PWR_SWITCH_GPIO_PIN) == Bit_RESET || GPIO_ReadInputDataBit(PWR_EXTRA_SWITCH_GPIO, PWR_EXTRA_SWITCH_GPIO_PIN) == Bit_RESET); +#else return GPIO_ReadInputDataBit(PWR_SWITCH_GPIO, PWR_SWITCH_GPIO_PIN) == Bit_RESET; +#endif } void pwrResetHandler() diff --git a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp index debe8793d..00b3c76f8 100644 --- a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp +++ b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp @@ -71,7 +71,7 @@ void rotaryEncoderInit() void rotaryEncoderCheck() { -#if defined(RADIO_FAMILY_T16) +#if defined(RADIO_FAMILY_T16) && !defined(RADIO_T18) static uint8_t state = 0; uint8_t pins = ROTARY_ENCODER_POSITION(); diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 4fa4238de..20348968f 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -71,6 +71,13 @@ if (PCB STREQUAL X10) set(AUX2_SERIAL ON) endif() set(INTERNAL_GPS_BAUDRATE "9600" CACHE STRING "Baud rate for internal GPS") + elseif (PCBREV STREQUAL T18) + set(FLAVOUR t18) + set(LUA_EXPORT lua_export_t16) + add_definitions(-DRADIO_T18) + add_definitions(-DRADIO_FAMILY_T16) + option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) + option(BLUETOOTH "Support for bluetooth module" OFF) else() set(FLAVOUR x10) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) diff --git a/radio/src/targets/horus/backlight_driver.cpp b/radio/src/targets/horus/backlight_driver.cpp index e141cf5dd..13f73f1ee 100644 --- a/radio/src/targets/horus/backlight_driver.cpp +++ b/radio/src/targets/horus/backlight_driver.cpp @@ -32,6 +32,15 @@ void backlightInit() GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource, BACKLIGHT_GPIO_AF); +#if defined(KEYS_BACKLIGHT_GPIO) + GPIO_InitStructure.GPIO_Pin = KEYS_BACKLIGHT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(KEYS_BACKLIGHT_GPIO, &GPIO_InitStructure); +#endif + // TIMER init #if defined(PCBX12S) && PCBREV >= 13 BACKLIGHT_TIMER->ARR = 100; @@ -72,6 +81,15 @@ void backlightEnable(uint8_t dutyCycle) BACKLIGHT_TIMER->CCR3 = BACKLIGHT_LEVEL_MAX - dutyCycle; #endif +#if defined(KEYS_BACKLIGHT_GPIO) && !defined(BOOT) + if (dutyCycle == 0 || g_eeGeneral.keysBacklight == 0) { + GPIO_ResetBits(KEYS_BACKLIGHT_GPIO, KEYS_BACKLIGHT_GPIO_PIN); + } + else { + GPIO_SetBits(KEYS_BACKLIGHT_GPIO, KEYS_BACKLIGHT_GPIO_PIN); + } +#endif + if (dutyCycle == 0) { BACKLIGHT_TIMER->BDTR &= ~TIM_BDTR_MOE; } diff --git a/radio/src/targets/horus/board.cpp b/radio/src/targets/horus/board.cpp index 49a0f13f7..cb379e6fe 100644 --- a/radio/src/targets/horus/board.cpp +++ b/radio/src/targets/horus/board.cpp @@ -103,6 +103,7 @@ void boardInit() LED_RCC_AHB1Periph | LCD_RCC_AHB1Periph | BACKLIGHT_RCC_AHB1Periph | + KEYS_BACKLIGHT_RCC_AHB1Periph | SD_RCC_AHB1Periph | AUDIO_RCC_AHB1Periph | KEYS_RCC_AHB1Periph | diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 44d8a5d10..41a8f96ee 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -492,6 +492,11 @@ void pwrOn(); void pwrOff(); void pwrResetHandler(); bool pwrPressed(); +#if defined(PWR_EXTRA_SWITCH_GPIO) + bool pwrForcePressed(); +#else + #define pwrForcePressed() false +#endif uint32_t pwrPressedDuration(); // USB Charger @@ -555,6 +560,10 @@ void usbJoystickUpdate(); #define USB_NAME "Jumper T16" #define USB_MANUFACTURER 'J', 'u', 'm', 'p', 'e', 'r', ' ', ' ' /* 8 bytes */ #define USB_PRODUCT 'T', '1', '6', ' ', ' ', ' ', ' ', ' ' /* 8 Bytes */ +#elif defined(RADIO_T18) + #define USB_NAME "Jumper T18" + #define USB_MANUFACTURER 'J', 'u', 'm', 'p', 'e', 'r', ' ', ' ' /* 8 bytes */ + #define USB_PRODUCT 'T', '1', '8', ' ', ' ', ' ', ' ', ' ' /* 8 Bytes */ #elif defined(RADIO_TX16S) #define USB_NAME "RadioMas TX16S" #define USB_MANUFACTURER 'R', 'a', 'd', 'i', 'o', 'M', 'a', 's' /* 8 bytes */ diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 5e7cd04c2..12b2a4630 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -322,7 +322,7 @@ #define ADC_TRANSFER_COMPLETE() (ADC_DMA->LISR & DMA_LISR_TCIF0) #if defined(RADIO_TX16S) #define ADC_VREF_PREC2 330 - #elif defined(RADIO_T16) + #elif defined(RADIO_T16) || defined(RADIO_T18) #define ADC_VREF_PREC2 300 #else #define ADC_VREF_PREC2 250 @@ -330,11 +330,21 @@ #endif // Power +#if defined(RADIO_T18) +#define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_GPIOB +#define PWR_ON_GPIO GPIOJ +#define PWR_ON_GPIO_PIN GPIO_Pin_1 // PJ.01 +#define PWR_SWITCH_GPIO GPIOJ +#define PWR_SWITCH_GPIO_PIN GPIO_Pin_0 // PJ.00 +#define PWR_EXTRA_SWITCH_GPIO GPIOB +#define PWR_EXTRA_SWITCH_GPIO_PIN GPIO_Pin_0 // PB.00 +#else #define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOJ #define PWR_ON_GPIO GPIOJ #define PWR_ON_GPIO_PIN GPIO_Pin_1 // PJ.01 #define PWR_SWITCH_GPIO GPIOJ #define PWR_SWITCH_GPIO_PIN GPIO_Pin_0 // PJ.00 +#endif // USB Charger #if defined(USB_CHARGER) @@ -514,6 +524,9 @@ #define LCD_GPIO_NRST GPIOI #define LCD_GPIO_PIN_NRST GPIO_Pin_10 // PI.10 #endif +#if defined(PCBX10) && !defined(RADIO_T18) && !defined(SIMU) + #define LCD_VERTICAL_INVERT +#endif #define LTDC_IRQ_PRIO 4 #define DMA_SCREEN_IRQ_PRIO 6 @@ -549,6 +562,14 @@ #define BACKLIGHT_GPIO_AF GPIO_AF_TIM8 #define BACKLIGHT_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) #endif +#if defined(RADIO_T18) + #define KEYS_BACKLIGHT_RCC_AHB1Periph RCC_AHB1Periph_GPIOC + #define KEYS_BACKLIGHT_GPIO GPIOC + #define KEYS_BACKLIGHT_GPIO_PIN GPIO_Pin_4 // PC.04 + #define KEYS_BACKLIGHT_GPIO_PinSource GPIO_PinSource4 +#else + #define KEYS_BACKLIGHT_RCC_AHB1Periph 0 +#endif // SD #define SD_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_DMA2) diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 444994d02..42d2c3837 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -20,14 +20,25 @@ #include "opentx.h" -#define HBP 42 -#define VBP 12 +#if defined(RADIO_T18) + #define HBP 43 + #define VBP 12 -#define HSW 2 -#define VSW 10 + #define HSW 2 + #define VSW 4 -#define HFP 3 -#define VFP 2 + #define HFP 8 + #define VFP 8 +#else + #define HBP 42 + #define VBP 12 + + #define HSW 2 + #define VSW 10 + + #define HFP 3 + #define VFP 2 +#endif #define LCD_FIRST_LAYER 0 #define LCD_SECOND_LAYER 1 @@ -315,7 +326,11 @@ void LCD_Init(void) { /* Reset the LCD --------------------------------------------------------*/ LCD_NRSTConfig(); +#if defined(RADIO_T18) // T18 seems to have eractic display issue if NRST is ever driven low + NRST_HIGH(); +#else lcd_reset(); +#endif /* Configure the LCD Control pins */ LCD_AF_GPIOConfig(); @@ -383,7 +398,7 @@ void lcdInit(void) void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { -#if defined(PCBX10) +#if defined(LCD_VERTICAL_INVERT) x = destw - (x + w); y = desth - (y + h); #endif @@ -415,7 +430,7 @@ void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, ui void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h) { -#if defined(PCBX10) +#if defined(LCD_VERTICAL_INVERT) x = destw - (x + w); y = desth - (y + h); srcx = srcw - (srcx + w); @@ -458,7 +473,7 @@ void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h) { -#if defined(PCBX10) +#if defined(LCD_VERTICAL_INVERT) x = destw - (x + w); y = desth - (y + h); srcx = srcw - (srcx + w); diff --git a/radio/src/targets/sky9x/board.h b/radio/src/targets/sky9x/board.h index 4a8587194..8b1c7275c 100644 --- a/radio/src/targets/sky9x/board.h +++ b/radio/src/targets/sky9x/board.h @@ -377,6 +377,7 @@ void pwrOn(); uint32_t pwrCheck(); bool pwrPressed(); #define UNEXPECTED_SHUTDOWN() (g_eeGeneral.unexpectedShutdown) +#define pwrForcePressed() (false) // EEPROM driver #define EEPROM_SIZE (4*1024*1024/8) diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 7dd0050a5..62ad32a5f 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -648,6 +648,7 @@ bool pwrPressed(); uint32_t pwrPressedDuration(); #endif void pwrResetHandler(); +#define pwrForcePressed() false #if defined(SIMU) #define UNEXPECTED_SHUTDOWN() false diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index cfe9830b7..65462c576 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -1639,6 +1639,7 @@ #define BACKLIGHT_GPIO_PinSource GPIO_PinSource8 #define BACKLIGHT_GPIO_AF GPIO_AF_TIM10 #endif +#define KEYS_BACKLIGHT_RCC_AHB1Periph 0 // LCD driver #if defined(PCBX9E) diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index ab57c5a33..43dccb554 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -96,6 +96,7 @@ ISTR(INPUTNAMES); #if defined(MULTIMODULE) ISTR(MULTI_PROTOCOLS); +ISTR(MULTI_POWER); #endif #if defined(BLUETOOTH) @@ -654,6 +655,7 @@ const char STR_GYRO_MAX[] = TR_GYRO_MAX; #if defined(PWM_BACKLIGHT) || defined(COLORLCD) const char STR_BLONBRIGHTNESS[] = TR_BLONBRIGHTNESS; const char STR_BLOFFBRIGHTNESS[] = TR_BLOFFBRIGHTNESS; +const char STR_KEYS_BACKLIGHT[] = TR_KEYS_BACKLIGHT; #endif const char STR_TIMEZONE[] = TR_TIMEZONE; @@ -694,6 +696,7 @@ const char STR_BIND_OK[] = TR_BIND_OK; const char STR_MULTI_CUSTOM[] = TR_MULTI_CUSTOM; const char STR_MULTI_RFTUNE[] = TR_MULTI_RFTUNE; +const char STR_MULTI_RFPOWER[] = TR_MULTI_RFPOWER; const char STR_MULTI_TELEMETRY[] = TR_MULTI_TELEMETRY; const char STR_MULTI_VIDFREQ[] = TR_MULTI_VIDFREQ; const char STR_MULTI_OPTION[] = TR_MULTI_OPTION; diff --git a/radio/src/translations.h b/radio/src/translations.h index a4b9fbb32..f428b3057 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -159,10 +159,8 @@ extern const char STR_XJT_ACCST_RF_PROTOCOLS[]; extern const char STR_ISRM_RF_PROTOCOLS[]; extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; - -#if defined(MULTIMODULE) extern const char STR_MULTI_PROTOCOLS[]; -#endif +extern const char STR_MULTI_POWER[]; #if defined(AFHDS3) extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; @@ -305,6 +303,7 @@ extern const char STR_BLDELAY[]; #if defined(PWM_BACKLIGHT) || defined(COLORLCD) extern const char STR_BLONBRIGHTNESS[]; extern const char STR_BLOFFBRIGHTNESS[]; +extern const char STR_KEYS_BACKLIGHT[]; #endif extern const char STR_SPLASHSCREEN[]; @@ -399,6 +398,7 @@ extern const char STR_MULTI_FIXEDID[]; extern const char STR_MULTI_OPTION[]; extern const char STR_MULTI_VIDFREQ[]; extern const char STR_MULTI_RFTUNE[]; +extern const char STR_MULTI_RFPOWER[]; extern const char STR_MULTI_TELEMETRY[]; extern const char STR_MULTI_AUTOBIND[]; extern const char STR_DISABLE_CH_MAP[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 7ae453e04..d6563c16c 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -522,6 +522,7 @@ #define TR_BLDELAY INDENT"Zhasnout po" #define TR_BLONBRIGHTNESS TR3(INDENT"Jas Zap.", INDENT"Jas Zap.", INDENT"Jas zapnutého LCD") #define TR_BLOFFBRIGHTNESS TR3(INDENT"Jas Vyp.", INDENT"Jas Vyp.", INDENT"Jas vypnutého LCD") +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Barva" #define TR_SPLASHSCREEN TR("úvodní logo", "Zobrazit úvodní logo") #define TR_PWR_ON_DELAY "Pwr On delay" @@ -628,6 +629,7 @@ #define TR_RECEIVER_NUM TR("RX číslo", "Číslo přijímače") #define TR_RECEIVER "Přijímač" #define TR_MULTI_RFTUNE TR(INDENT "Ladění frek", INDENT "Jemné ladění frek. RF") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetrie" #define TR_MULTI_VIDFREQ TR(INDENT "Freq. videa", INDENT "Frekvence videa") #define TR_RFPOWER "Výkon RF" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index e0e4eb437..f816a7269 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -524,6 +524,7 @@ #define TR_BLDELAY INDENT "Dauer" #define TR_BLONBRIGHTNESS INDENT "An-Helligkeit" #define TR_BLOFFBRIGHTNESS INDENT "Aus-Helligkeit" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Farbe" #define TR_SPLASHSCREEN TR("Startbild Ein", "Startbild Anzeigedauer") #define TR_PWR_ON_DELAY "Pwr On delay" @@ -630,6 +631,7 @@ #define TR_RECEIVER_NUM TR("Empf Nr.", "Empfänger Nummer") #define TR_RECEIVER "Empfänger" #define TR_MULTI_RFTUNE TR(INDENT "RF Freq.", INDENT "RF Freq. Feintuning") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR(INDENT "Vid. Freq.", INDENT "Video Frequenz") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index a270a1f34..f902dfa60 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -523,6 +523,7 @@ #define TR_BLDELAY INDENT "Duration" #define TR_BLONBRIGHTNESS INDENT "ON brightness" #define TR_BLOFFBRIGHTNESS INDENT "OFF brightness" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Color" #define TR_SPLASHSCREEN "Splash screen" #define TR_PWR_ON_DELAY "Pwr On delay" @@ -629,6 +630,7 @@ #define TR_RECEIVER_NUM TR("RxNum", "Receiver No.") #define TR_RECEIVER "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 822ace072..d7dbd2b15 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -522,6 +522,7 @@ #define TR_BLDELAY INDENT"Duración" #define TR_BLONBRIGHTNESS INDENT"MAS brillo" #define TR_BLOFFBRIGHTNESS INDENT"MENOS brillo" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Color" #define TR_SPLASHSCREEN "Pantalla inicio" #define TR_PWR_ON_DELAY TR("Atraso On", "Atraso encendido") @@ -631,6 +632,7 @@ #define TR_RECEIVER_NUM TR("Num Rx", "Receptor No.") #define TR_RECEIVER "Receptor" #define TR_MULTI_RFTUNE TR("Sint.freq.", "RF Freq. sint.fina") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetría" #define TR_MULTI_VIDFREQ TR("Freq.vídeo", "Frecuencia vídeo") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index ec28eabcb..579eb25bf 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -536,6 +536,7 @@ #define TR_BLDELAY INDENT"Duration" #define TR_BLONBRIGHTNESS INDENT"ON Brightness" #define TR_BLOFFBRIGHTNESS INDENT"OFF Brightness" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Color" #define TR_SPLASHSCREEN "Splash screen" #define TR_PWR_ON_DELAY "Pwr On delay" @@ -647,6 +648,7 @@ #define TR_RECEIVER_NUM TR("RxNum", "Receiver No.") #define TR_RECEIVER "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 466effb38..a914772d5 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -542,6 +542,7 @@ #define TR_BLDELAY INDENT "Durée" #define TR_BLONBRIGHTNESS INDENT "Luminosité ON" #define TR_BLOFFBRIGHTNESS INDENT "Luminosité OFF" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Couleur" #define TR_SPLASHSCREEN "Logo d'accueil" #define TR_PWR_ON_DELAY "Délai btn ON" @@ -650,6 +651,7 @@ #define TR_RECEIVER_NUM TR("NumRx", "No. récepteur") #define TR_RECEIVER "Récept." #define TR_MULTI_RFTUNE TR(INDENT "Ajust.fréq", INDENT "Ajustement fréq.") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Télémétrie" #define TR_MULTI_VIDFREQ TR(INDENT "Fréq. vidéo", INDENT "Fréquence vidéo") #define TR_RFPOWER TR("Puiss. RF", "Puissance RF") diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index cea9b2716..126955a6d 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -544,6 +544,7 @@ #define TR_BLDELAY INDENT "Durata" #define TR_BLONBRIGHTNESS TR(INDENT "Lumin. ON", INDENT "Luminosità ON") #define TR_BLOFFBRIGHTNESS TR(INDENT "Lumin. OFF", INDENT "Luminosità OFF") +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Colore" #define TR_SPLASHSCREEN TR("Sch. avvio", "Schermata d'avvio") #define TR_PWR_ON_DELAY "Rit. accens." @@ -649,6 +650,7 @@ #define TR_RECEIVER_NUM TR("RxNum", "Ricevente N.") #define TR_RECEIVER "Ricevente" #define TR_MULTI_RFTUNE TR("Tune RF", "Tune fine Freq. RF") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetria" #define TR_MULTI_VIDFREQ TR("Freq. video", "Frequenza video") #define TR_RFPOWER "Potenza RF" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 5c4310875..6c1138094 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -525,6 +525,7 @@ #define TR_BLDELAY INDENT "Duur" #define TR_BLONBRIGHTNESS INDENT "Aan-Helderheid" #define TR_BLOFFBRIGHTNESS INDENT "Uit-Helderheid" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Kleur" #define TR_SPLASHSCREEN "Startscherm Aan" #define TR_PWR_ON_DELAY "Pwr On delay" @@ -635,6 +636,7 @@ #define TR_RECEIVER_NUM TR("RxNum", "Receiver Nr.") #define TR_RECEIVER "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 9f3efa84f..38b03063a 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -543,6 +543,7 @@ #define TR_BLDELAY INDENT"Czas trwania" #define TR_BLONBRIGHTNESS INDENT"Jasnośc wł." #define TR_BLOFFBRIGHTNESS INDENT"Jasność wył." +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Color" #define TR_SPLASHSCREEN "Logo ekranu" #define TR_PWR_ON_DELAY "Pwr On delay" @@ -648,6 +649,7 @@ #define TR_RECEIVER_NUM TR("Nr RX", "Nr odbiornika") #define TR_RECEIVER INDENT "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index e11cd03e6..e6a3ecfb1 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -529,6 +529,7 @@ #define TR_BLDELAY INDENT "Tempo Backlight" #define TR_BLONBRIGHTNESS INDENT "ON Brightness" #define TR_BLOFFBRIGHTNESS INDENT "OFF Brightness" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_BLCOLOR INDENT "Color" #define TR_SPLASHSCREEN "Splash screen" #define TR_PWR_ON_DELAY "Pwr On delay" @@ -639,6 +640,7 @@ #define TR_RECEIVER "Receiver" #define TR_SYNCMENU "Sync [MENU]" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index ab1e44f3b..079127692 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -542,6 +542,7 @@ #define TR_BLDELAY INDENT "Av efter" #define TR_BLONBRIGHTNESS INDENT "På Ljusstyrka" #define TR_BLOFFBRIGHTNESS INDENT "Av Ljusstyrka" +#define TR_KEYS_BACKLIGHT "Keys backlight" #define TR_SPLASHSCREEN "Startbild" #define TR_PWR_ON_DELAY "Pwr On delay" #define TR_PWR_OFF_DELAY "Pwr Off delay" @@ -648,6 +649,7 @@ #define TR_RECEIVER "Receiver" #define TR_SYNCMENU "Synk [MENU]" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") +#define TR_MULTI_RFPOWER "RF power" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 97a431879..32acdcb84 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -125,8 +125,10 @@ #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 ""SFHSS\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 ""FlyZone""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyarte""ESky-v2""DSM RX\0""JJRC345""Q90C\0 " -#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 ""SFHSS\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 ""FlyZone""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 " +#define LEN_MULTI_POWER "\005" +#define TR_MULTI_POWER "10mW\0""25mW\0""50mW\0""100mW""200mW""300mW""500mW""1W\0 ""2W\0 " #define LEN_AFHDS3_PROTOCOLS "\x008" #define TR_AFHDS3_PROTOCOLS "PWM/IBUS""PWM/SBUS""PPM/IBUS""PPM/SBUS" diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index 76bade5b6..487fe1f03 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -148,6 +148,11 @@ def main(): cmake_options["PCBREV"] = "T16" firmware_options = options_jumper_t16 maxsize = 2 * 1024 * 1024 + elif board_name == "t18": + cmake_options["PCB"] = "X10" + cmake_options["PCBREV"] = "T18" + firmware_options = options_jumper_t16 + maxsize = 2 * 1024 * 1024 elif board_name == "tx16s": cmake_options["PCB"] = "X10" cmake_options["PCBREV"] = "TX16S" diff --git a/tools/build-companion-nightly.sh b/tools/build-companion-nightly.sh index 98d6653e6..fbf8f7166 100755 --- a/tools/build-companion-nightly.sh +++ b/tools/build-companion-nightly.sh @@ -106,6 +106,10 @@ cmake ${COMMON_OPTIONS} -DPCB=X10 -DPCBREV=TX16S ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X10 -DPCBREV=T18 ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X10 -DPCBREV=EXPRESS ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt diff --git a/tools/build-jumper.py b/tools/build-jumper.py index ab3fb9e45..6e7f3d733 100755 --- a/tools/build-jumper.py +++ b/tools/build-jumper.py @@ -25,6 +25,12 @@ boards = { "PCBREV": "T16", "INTERNAL_MODULE_MULTI": "YES", "DEFAULT_MODE": "2", + }, + "T18": { + "PCB": "X10", + "PCBREV": "T18", + "INTERNAL_MODULE_MULTI": "YES", + "DEFAULT_MODE": "2", } } From 10243f2436331145946401bf2591cea792050c79 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Jun 2020 21:48:33 +0200 Subject: [PATCH 013/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 92fd0a455..039e0a70f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1770,3 +1770,4 @@ Peter Sarcher Steve Schoonover Francisco Orcera Exposito Alan Powell +Martin Eriksson From 82869c8500db6d5259f9bc33cbb59618b82ef9fd Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Jun 2020 07:28:37 +0200 Subject: [PATCH 014/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 039e0a70f..dcd43cb24 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1771,3 +1771,4 @@ Steve Schoonover Francisco Orcera Exposito Alan Powell Martin Eriksson +Wayne J Podaima From e1abf2c80be4d952e463ee02a893f9cf47a70a20 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Jun 2020 21:26:40 +0200 Subject: [PATCH 015/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index dcd43cb24..af1172e2e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1772,3 +1772,4 @@ Francisco Orcera Exposito Alan Powell Martin Eriksson Wayne J Podaima +Tom Wilkinson From 149627fd14dfbb20c85fea33b33ea3ee637cf604 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Jun 2020 10:27:41 +0200 Subject: [PATCH 016/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index af1172e2e..3cf148f8b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -8,6 +8,7 @@ Romolo Manfredini (romoloman) Martin Hotař (mhotar) Maxim (Max) Paperno (mpaperno) Arne Schwabe (schwabe) +Daniel Gorbea (dgatf) [Translators] [fr] Sebastien Charpentier (LapinFou) @@ -1773,3 +1774,4 @@ Alan Powell Martin Eriksson Wayne J Podaima Tom Wilkinson +Fred Ceratt From 77f4c18f4c55f007d91872c59a72f94a27b343fe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Jun 2020 12:14:46 +0200 Subject: [PATCH 017/267] Frsky production script updated --- tools/build-frsky.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/build-frsky.py b/tools/build-frsky.py index 5a5cb26ac..6e3b7de85 100755 --- a/tools/build-frsky.py +++ b/tools/build-frsky.py @@ -76,7 +76,7 @@ boards = { "SBUS": "NO", "DEFAULT_MODE": "2", }, - "X9D+": { + "X9D+2019": { "PCB": "X9D+", "PCBREV": "2019", "LUA": "NO_MODEL_SCRIPTS", @@ -87,6 +87,14 @@ boards = { "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, + "X9D+": { + "PCB": "X9D+", + "LUA": "NO_MODEL_SCRIPTS", + "GVARS": "YES", + "MULTIMODULE": "NO", + "CROSSFIRE": "NO", + "DEFAULT_MODE": "2", + }, "X9E": { "PCB": "X9E", "LUA": "NO_MODEL_SCRIPTS", From 0ff6c0e5aeb0d09b2fac4122ea019676b14f36bb Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 24 Jun 2020 12:35:28 +0200 Subject: [PATCH 018/267] Fix nightlies (#7765) --- radio/src/datastructs.h | 4 ---- radio/src/pulses/afhds3.cpp | 2 +- radio/src/pulses/afhds3.h | 6 +++--- radio/src/targets/horus/lcd_driver.cpp | 8 ++++---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 876ae9875..9f114c5f8 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -744,12 +744,8 @@ PACK(struct RadioData { uint8_t backlightMode:3; int8_t antennaMode:2; uint8_t disableRtcWarning:1; -#if defined(KEYS_BACKLIGHT_GPIO) uint8_t keysBacklight:1; int8_t spare1:1; -#else - int8_t spare1:2; -#endif NOBACKUP(TrainerData trainer); NOBACKUP(uint8_t view); // index of view in main screen NOBACKUP(BUZZER_FIELD); /* 2bits */ diff --git a/radio/src/pulses/afhds3.cpp b/radio/src/pulses/afhds3.cpp index bd3f577a3..bcfe8d3cf 100644 --- a/radio/src/pulses/afhds3.cpp +++ b/radio/src/pulses/afhds3.cpp @@ -341,7 +341,7 @@ void PulsesData::putFrame(COMMAND command, FRAME_TYPE frame, uint8_t* data, uint if (frameIndex == nullptr) { frameIndex = &this->frame_index; } - uint8_t buffer[] = { FrameAddress, *frameIndex, frame, command }; + uint8_t buffer[] = {FrameAddress, *frameIndex, frame, command}; putBytes(buffer, 4); //payload diff --git a/radio/src/pulses/afhds3.h b/radio/src/pulses/afhds3.h index 774293b65..fab057854 100644 --- a/radio/src/pulses/afhds3.h +++ b/radio/src/pulses/afhds3.h @@ -43,7 +43,7 @@ // 1s = 1 000 000 us // 1000000/115200 = 8,68 us // actual timer is ticking in 0,5 us = 8,68*2 = 17,36 because it must be integer take 17 -// difference 1000000 / x = 8.5 --> x = 117 647 = difference = 1.2 % +// difference 1000000 / x = 8.5 --> x = 117 647 = difference = 1.2 % // allowed half a bit difference on the last bit -- should be fine #define AFHDS3_BAUDRATE 115200 //Because timer is ticking with 0.5us @@ -176,7 +176,7 @@ enum DeviceAddress MODULE = 0x03, }; -enum FRAME_TYPE +enum FRAME_TYPE: uint8_t { REQUEST_GET_DATA = 0x01, //Get data response: ACK + DATA REQUEST_SET_EXPECT_DATA = 0x02, //Set data response: ACK + DATA @@ -187,7 +187,7 @@ enum FRAME_TYPE NOT_USED = 0xff }; -enum COMMAND +enum COMMAND: uint8_t { MODULE_READY = 0x01, MODULE_STATE = 0x02, diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 42d2c3837..751aa88cd 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -153,6 +153,9 @@ static void delay3(uint32_t nCount) static void lcd_reset(void) { +#if defined(RADIO_T18) // T18 screen has issues if NRST is ever brought low + NRST_HIGH(); +#else NRST_HIGH(); delay3(1); @@ -161,6 +164,7 @@ static void lcd_reset(void) NRST_HIGH(); delay3(30); +#endif } void LCD_Init_LTDC(void) @@ -326,11 +330,7 @@ void LCD_Init(void) { /* Reset the LCD --------------------------------------------------------*/ LCD_NRSTConfig(); -#if defined(RADIO_T18) // T18 seems to have eractic display issue if NRST is ever driven low - NRST_HIGH(); -#else lcd_reset(); -#endif /* Configure the LCD Control pins */ LCD_AF_GPIOConfig(); From 8c7a3eabd0115481dc75ad2ebc7c29224f2862ee Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Jun 2020 12:42:43 +0200 Subject: [PATCH 019/267] Frsky production script updated. Compilation fixes --- radio/src/gui/480x272/model_setup.cpp | 8 +++- tools/build-frsky.py | 66 ++++----------------------- 2 files changed, 15 insertions(+), 59 deletions(-) diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 4fc0a1e75..264685b2d 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1801,13 +1801,15 @@ bool menuModelSetup(event_t event) #endif #if defined(MULTIMODULE) || defined(AFHDS3) -#if defined(HARDWARE_INTERNAL_MODULE) +#if defined(MULTIMODULE) && defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS: #endif #if defined(AFHDS3) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: #endif +#if defined(MULTIMODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: +#endif { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_STATUS); @@ -1817,13 +1819,15 @@ bool menuModelSetup(event_t event) break; } -#if defined(HARDWARE_INTERNAL_MODULE) +#if defined(MULTIMODULE) && defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS: #endif #if defined(AFHDS3) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_POWER_STATUS: #endif +#if defined(MULTIMODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: +#endif { #if defined(AFHDS3) if (isModuleAFHDS3(moduleIdx)) { diff --git a/tools/build-frsky.py b/tools/build-frsky.py index 6e3b7de85..fc33d2b80 100755 --- a/tools/build-frsky.py +++ b/tools/build-frsky.py @@ -11,149 +11,93 @@ import tempfile boards = { "XLITE_FCC": { "PCB": "XLITE", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "MODULE_SIZE_STD": "NO", "PPM": "NO", "DSM2": "NO", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "SBUS": "NO", }, "XLITE_LBT": { "PCB": "XLITE", "MODULE_PROTOCOL_D8": "NO", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "MODULE_SIZE_STD": "NO", "PPM": "NO", "DSM2": "NO", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "SBUS": "NO", }, "XLITES": { "PCB": "XLITES", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "AUTOUPDATE": "YES", "PXX1": "YES", "XJT": "NO", "MODULE_SIZE_STD": "NO", "PPM": "NO", "DSM2": "NO", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "SBUS": "NO", }, "X9LITE": { "PCB": "X9LITE", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "AUTOUPDATE": "YES", "PXX1": "YES", "XJT": "NO", "MODULE_SIZE_STD": "NO", "PPM": "NO", "DSM2": "NO", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "SBUS": "NO", "DEFAULT_MODE": "2", }, "X9LITES": { "PCB": "X9LITES", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "AUTOUPDATE": "YES", "PXX1": "YES", "XJT": "NO", "MODULE_SIZE_STD": "NO", "PPM": "NO", "DSM2": "NO", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "SBUS": "NO", "DEFAULT_MODE": "2", }, "X9D+2019": { "PCB": "X9D+", "PCBREV": "2019", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "AUTOUPDATE": "YES", "PXX1": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X9D+": { "PCB": "X9D+", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X9E": { "PCB": "X9E", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X7_FCC": { "PCB": "X7", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X7_LBT": { "PCB": "X7", "MODULE_PROTOCOL_D8": "NO", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X7ACCESS": { "PCB": "X7", "PCBREV": "ACCESS", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", "AUTOUPDATE": "YES", "PXX1": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X10S": { "PCB": "X10", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X10SExpress": { "PCB": "X10", "PCBREV": "EXPRESS", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, "X12S": { "PCB": "X12S", - "LUA": "NO_MODEL_SCRIPTS", - "GVARS": "YES", - "MULTIMODULE": "NO", - "CROSSFIRE": "NO", "DEFAULT_MODE": "2", }, } @@ -163,13 +107,21 @@ translations = [ "CZ" ] +common_options = { + "MULTIMODULE": "NO", + "CROSSFIRE": "NO", + "AFHDS3": "NO", + "GVARS": "YES", + "LUA": "NO_MODEL_SCRIPTS", +} + def timestamp(): return datetime.datetime.now().strftime("%y%m%d") def build(board, translation, srcdir): - cmake_options = " ".join(["-D%s=%s" % (key, value) for key, value in boards[board].items()]) + cmake_options = " ".join(["-D%s=%s" % (key, value) for key, value in list(boards[board].items()) + list(common_options.items())]) cwd = os.getcwd() if not os.path.exists("output"): os.mkdir("output") From a2b6150934f1805590840ee7e966f4a50e21d9b1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 24 Jun 2020 13:31:46 +0200 Subject: [PATCH 020/267] Increase travis coverage --- tools/commit-tests.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index 92b5542f0..c1c97237e 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -97,7 +97,7 @@ fi if [[ " X9LITES X9LITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9LITES rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X9LITES -DHELI=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X9LITES -DHELI=YES -DGVARS=YES -DMULTIMODULE=NO "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -187,7 +187,7 @@ fi if [[ " T16 COLORLCD ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on T16 boards rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X10 -DPCBREV=T16 -DHELI=YES -DLUA=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X10 -DPCBREV=T16 -DHELI=YES -DLUA=YES -DGVARS=YES -DAFHDS3=NO "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio @@ -205,7 +205,7 @@ fi if [[ " X12S COLORLCD ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X12S rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=X12S -DHELI=YES -DLUA=YES -DGVARS=YES "${SRCDIR}" + cmake "${COMMON_OPTIONS}" -DPCB=X12S -DHELI=YES -DLUA=YES -DGVARS=YES -DMULTIMODULE=NO "${SRCDIR}" make -j"${CORES}" ${FIRMARE_TARGET} make -j"${CORES}" libsimulator make -j"${CORES}" tests-radio From 7b3a77e4cfa4a20ea45ff53890e68bb9e0e2c146 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 25 Jun 2020 00:19:04 +0200 Subject: [PATCH 021/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3cf148f8b..1faa26219 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1775,3 +1775,4 @@ Martin Eriksson Wayne J Podaima Tom Wilkinson Fred Ceratt +Stefan Grunenberg From ed1c1f06964d479be8c88bd78a01422438aec4c2 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 25 Jun 2020 08:59:18 +0200 Subject: [PATCH 022/267] Fix AUX/AUX2 power handling. This fixes #7751 --- .../targets/common/arm/stm32/aux_serial_driver.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index 8d24c00df..88fc32cb8 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -101,11 +101,12 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) #if defined(CROSSFIRE) if (protocol == PROTOCOL_TELEMETRY_CROSSFIRE) { auxSerialSetup(CROSSFIRE_TELEM_MIRROR_BAUDRATE, false); - AUX_SERIAL_POWER_OFF(); + AUX_SERIAL_POWER_ON(); break; } #endif auxSerialSetup(FRSKY_TELEM_MIRROR_BAUDRATE, false); + AUX_SERIAL_POWER_ON(); break; #if defined(DEBUG) || defined(CLI) @@ -118,7 +119,7 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) case UART_MODE_TELEMETRY: if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { auxSerialSetup(FRSKY_D_BAUDRATE, true); - AUX_SERIAL_POWER_OFF(); + AUX_SERIAL_POWER_ON(); } break; @@ -292,12 +293,12 @@ void aux2SerialInit(unsigned int mode, unsigned int protocol) #if defined(CROSSFIRE) if (protocol == PROTOCOL_TELEMETRY_CROSSFIRE) { aux2SerialSetup(CROSSFIRE_TELEM_MIRROR_BAUDRATE, false); - AUX2_SERIAL_POWER_OFF(); + AUX2_SERIAL_POWER_ON(); break; } #endif aux2SerialSetup(FRSKY_TELEM_MIRROR_BAUDRATE, false); - AUX2_SERIAL_POWER_OFF(); + AUX2_SERIAL_POWER_ON(); break; #if defined(DEBUG) || defined(CLI) @@ -310,7 +311,7 @@ void aux2SerialInit(unsigned int mode, unsigned int protocol) case UART_MODE_TELEMETRY: if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { aux2SerialSetup(FRSKY_D_BAUDRATE, true); - AUX2_SERIAL_POWER_OFF(); + AUX2_SERIAL_POWER_ON(); } break; From 55e642bc04bd0bf23c44826aead7021898483679 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 25 Jun 2020 09:26:08 +0200 Subject: [PATCH 023/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1faa26219..016395ecd 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1776,3 +1776,4 @@ Wayne J Podaima Tom Wilkinson Fred Ceratt Stefan Grunenberg +Aram Parsamyan From e203211482b9f107bc8a36bc1de27f29bff05ab2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 25 Jun 2020 21:23:49 +0200 Subject: [PATCH 024/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 016395ecd..b11780727 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1777,3 +1777,4 @@ Tom Wilkinson Fred Ceratt Stefan Grunenberg Aram Parsamyan +Wolfgang Rauser From feb5a072375ebed30d7cda64417c17653205f784 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 26 Jun 2020 01:39:40 +0200 Subject: [PATCH 025/267] Fix T18 missing internal multi --- radio/util/build-firmware.py | 2 +- radio/util/fwoptions.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index 487fe1f03..11b85be7d 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -151,7 +151,7 @@ def main(): elif board_name == "t18": cmake_options["PCB"] = "X10" cmake_options["PCBREV"] = "T18" - firmware_options = options_jumper_t16 + firmware_options = options_jumper_t18 maxsize = 2 * 1024 * 1024 elif board_name == "tx16s": cmake_options["PCB"] = "X10" diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index cae773561..7227b3cef 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -235,6 +235,19 @@ options_jumper_t16 = { "afhds3": ("AFHDS3", "YES", "NO") } +options_jumper_t18 = { + "noheli": ("HELI", "NO", "YES"), + "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), + "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), + "nogvars": ("GVARS", "NO", "YES"), + "faimode": ("FAI", "YES", None), + "faichoice": ("FAI", "CHOICE", None), + "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), + "bluetooth": ("BLUETOOTH", "YES", "NO"), + "afhds3": ("AFHDS3", "YES", "NO") +} + options_radiomaster_tx16s = { "noheli": ("HELI", "NO", "YES"), "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), From 331ae68d293c788ac8edf4c834043fa42cfbfb55 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 26 Jun 2020 10:05:39 +0200 Subject: [PATCH 026/267] Offer AFHDS3 on T18 --- companion/src/firmwares/opentx/opentxinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index ea13bdb18..82c1b5728 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -1283,7 +1283,7 @@ void registerOpenTxFirmwares() addOpenTxFrskyOptions(firmware); firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); /* 9XR-Pro */ firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO); From 9b674010a88fad963fb6c17f33abf5b7cc6024a8 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 26 Jun 2020 10:23:05 +0200 Subject: [PATCH 027/267] Add AFHDS3 by default to radios with enough RAM for testing --- .../src/firmwares/opentx/opentxinterface.cpp | 16 +++++++------- radio/util/build-firmware.py | 7 +++++-- radio/util/fwoptions.py | 21 +++++++++++++------ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 82c1b5728..4bbeb43b3 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -1174,7 +1174,7 @@ void registerOpenTxFirmwares() firmware = new OpenTxFirmware("opentx-x9d+2019", Firmware::tr("FrSky Taranis X9D+ 2019"), BOARD_TARANIS_X9DP_2019); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); /* FrSky Taranis X9D board */ firmware = new OpenTxFirmware("opentx-x9d", Firmware::tr("FrSky Taranis X9D"), BOARD_TARANIS_X9D); @@ -1208,13 +1208,13 @@ void registerOpenTxFirmwares() firmware = new OpenTxFirmware("opentx-x7", Firmware::tr("FrSky Taranis X7 / X7S"), BOARD_TARANIS_X7); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); + addOpenTxRfOptions(firmware, EU + FLEX); /* FrSky X7 Access board */ firmware = new OpenTxFirmware("opentx-x7access", Firmware::tr("FrSky Taranis X7 / X7S Access"), BOARD_TARANIS_X7_ACCESS); addOpenTxTaranisOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); /* FrSky X-Lite S/PRO board */ firmware = new OpenTxFirmware("opentx-xlites", Firmware::tr("FrSky Taranis X-Lite S/PRO"), BOARD_TARANIS_XLITES); @@ -1234,13 +1234,13 @@ void registerOpenTxFirmwares() addOpenTxFrskyOptions(firmware); firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement")); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); + addOpenTxRfOptions(firmware, EU + FLEX); /* FrSky X10 Express board */ firmware = new OpenTxFirmware("opentx-x10express", Firmware::tr("FrSky Horus X10 Express / X10S Express"), BOARD_X10_EXPRESS); addOpenTxFrskyOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); /* FrSky X12 (Horus) board */ firmware = new OpenTxFirmware("opentx-x12s", Firmware::tr("FrSky Horus X12S"), BOARD_HORUS_X12S); @@ -1248,7 +1248,7 @@ void registerOpenTxFirmwares() firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement")); firmware->addOption("pcbdev", Firmware::tr("Use ONLY with first DEV pcb version")); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3); + addOpenTxRfOptions(firmware, EU + FLEX); /* Jumper T12 board */ firmware = new OpenTxFirmware("opentx-t12", QCoreApplication::translate("Firmware", "Jumper T12 / T12 Pro"), BOARD_JUMPER_T12); @@ -1266,13 +1266,13 @@ void registerOpenTxFirmwares() addOpenTxFrskyOptions(firmware); firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module")); firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); registerOpenTxFirmware(firmware); /* Radiomaster TX16S board */ firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16s / TX16s Hall / TX16s Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module")); static const Firmware::Option opt_internal_gps("internalgps", Firmware::tr("Support internal GPS")); firmware->addOptionsGroup({opt_bt, opt_internal_gps}); diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index 11b85be7d..c27e6b58a 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -93,12 +93,14 @@ def main(): maxsize = 65536 * 8 elif options[optcount] == "x7": cmake_options["PCB"] = "X7" - firmware_options = options_taranis_x9dp + cmake_options["AFHDS3"] = "YES" + firmware_options = options_taranis_x7 maxsize = 65536 * 8 elif options[optcount] == "x7access": cmake_options["PCB"] = "X7" cmake_options["PCBREV"] = "ACCESS" - firmware_options = options_taranis_x9dp + cmake_options["AFHDS3"] = "YES" + firmware_options = options_taranis_x7 maxsize = 65536 * 8 elif board_name == "xlite": cmake_options["PCB"] = "XLITE" @@ -119,6 +121,7 @@ def main(): elif board_name == "x9d+2019": cmake_options["PCB"] = "X9D+" cmake_options["PCBREV"] = "2019" + cmake_options["AFHDS3"] = "YES" firmware_options = options_taranis_x9dp maxsize = 65536 * 8 elif board_name == "x9e": diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index 7227b3cef..fe5331cb0 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -97,6 +97,21 @@ options_taranis_x9dp = { "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "afhds3": ("AFHDS3", "YES", "NO") } +options_taranis_x7 = { + "noheli": ("HELI", "NO", "YES"), + "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), + "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), + "nogvars": ("GVARS", "NO", "YES"), + "sqt5font": ("FONT", "SQT5", None), + "noras": ("RAS", "NO", "YES"), + "faimode": ("FAI", "YES", None), + "faichoice": ("FAI", "CHOICE", None), + "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), + "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), + "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), + "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), +} options_taranis_x9lite = { "noheli": ("HELI", "NO", "YES"), @@ -177,7 +192,6 @@ options_horus_x12s = { "pcbdev": ("PCBREV", "10", None), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)], - "afhds3": ("AFHDS3", "YES", "NO") } options_horus_x10 = { @@ -191,7 +205,6 @@ options_horus_x10 = { "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)], - "afhds3": ("AFHDS3", "YES", "NO") } options_horus_x10express = { @@ -205,7 +218,6 @@ options_horus_x10express = { "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), - "afhds3": ("AFHDS3", "YES", "NO") } options_jumper_t12 = { @@ -232,7 +244,6 @@ options_jumper_t16 = { "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "internalmulti": ("INTERNAL_MODULE_MULTI", "YES", "NO"), "bluetooth": ("BLUETOOTH", "YES", "NO"), - "afhds3": ("AFHDS3", "YES", "NO") } options_jumper_t18 = { @@ -245,7 +256,6 @@ options_jumper_t18 = { "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "bluetooth": ("BLUETOOTH", "YES", "NO"), - "afhds3": ("AFHDS3", "YES", "NO") } options_radiomaster_tx16s = { @@ -259,5 +269,4 @@ options_radiomaster_tx16s = { "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "bluetooth": ("BLUETOOTH", "YES", "NO"), "internalgps": ("INTERNAL_GPS", "YES", "NO"), - "afhds3": ("AFHDS3", "YES", "NO") } \ No newline at end of file From e53e87a1edbfd94c102711f3951f328d0749172b Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 26 Jun 2020 10:27:06 +0200 Subject: [PATCH 028/267] Cleanup internalppm remains --- companion/src/firmwares/opentx/opentxinterface.cpp | 2 -- radio/util/fwoptions.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 4bbeb43b3..1570c59b2 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -734,8 +734,6 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board); case PULSES_PXX_XJT_D8: return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board); - case PULSES_PPM: - return id.contains("internalppm"); case PULSES_ACCESS_ISRM: case PULSES_ACCST_ISRM_D16: return IS_ACCESS_RADIO(board, id); diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index fe5331cb0..3bcdd25e8 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -74,7 +74,6 @@ options_taranis_x9d = { "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), @@ -91,7 +90,6 @@ options_taranis_x9dp = { "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), @@ -107,7 +105,6 @@ options_taranis_x7 = { "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), @@ -172,7 +169,6 @@ options_taranis_x9e = { "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "internalppm": ("INTERNAL_MODULE_PPM", "YES", "NO"), "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "horussticks": ("STICKS", "HORUS", "STANDARD"), From a57f794c5a0e6ad20a74c459a0556addc623ad2f Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 26 Jun 2020 10:32:51 +0200 Subject: [PATCH 029/267] Sync options with companion --- radio/util/fwoptions.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index 3bcdd25e8..140cf4b47 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -71,10 +71,10 @@ options_taranis_x9d = { "haptic": ("HAPTIC", "YES", "NO"), "nogvars": ("GVARS", "NO", "YES"), "sqt5font": ("FONT", "SQT5", None), + "noras": ("RAS", "NO", "YES"), "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "afhds3": ("AFHDS3", "YES", "NO") @@ -90,7 +90,6 @@ options_taranis_x9dp = { "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "afhds3": ("AFHDS3", "YES", "NO") @@ -101,11 +100,9 @@ options_taranis_x7 = { "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), "nogvars": ("GVARS", "NO", "YES"), "sqt5font": ("FONT", "SQT5", None), - "noras": ("RAS", "NO", "YES"), "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), } @@ -115,15 +112,11 @@ options_taranis_x9lite = { "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), "nogvars": ("GVARS", "NO", "YES"), - "autoupdate": ("AUTOUPDATE", "YES", None), "sqt5font": ("FONT", "SQT5", None), - "noras": ("RAS", "NO", "YES"), "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), - "internalpxx1": ("INTERNAL_MODULE_PXX1", "YES", "NO"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "afhds3": ("AFHDS3", "YES", "NO") } @@ -137,7 +130,6 @@ options_taranis_xlite = { "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "afhds3": ("AFHDS3", "YES", "NO") @@ -148,14 +140,11 @@ options_taranis_xlites = { "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), "nogvars": ("GVARS", "NO", "YES"), - "autoupdate": ("AUTOUPDATE", "YES", None), "sqt5font": ("FONT", "SQT5", None), "faimode": ("FAI", "YES", None), "faichoice": ("FAI", "CHOICE", None), "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), - "shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"), "eu": ("MODULE_PROTOCOL_D8", "NO", "YES"), - "internalpxx1": ("INTERNAL_MODULE_PXX1", "YES", "NO"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "afhds3": ("AFHDS3", "YES", "NO") } From 112f507f4740c9a377f9390a03e83857f8bb9957 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 26 Jun 2020 10:34:51 +0200 Subject: [PATCH 030/267] Revert "Offer AFHDS3 on T18" This reverts commit 331ae68d293c788ac8edf4c834043fa42cfbfb55. --- companion/src/firmwares/opentx/opentxinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 1570c59b2..72734a4d5 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -1281,7 +1281,7 @@ void registerOpenTxFirmwares() addOpenTxFrskyOptions(firmware); firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); /* 9XR-Pro */ firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO); From 971156b6efb3879e985e0595365c4424331f347f Mon Sep 17 00:00:00 2001 From: Marco Robustini Date: Sat, 27 Jun 2020 09:40:52 +0200 Subject: [PATCH 031/267] Update it.h.txt (#7774) --- radio/src/translations/it.h.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 126955a6d..74acd82c9 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -958,12 +958,12 @@ #define TR_UNIT "Unita" #define TR_TELEMETRY_NEWSENSOR INDENT "Aggiungi nuovo sensore" #define TR_CHANNELRANGE INDENT "Numero Canali" -#define TR_AFHDS3_RX_FREQ TR("RX freq.", "RX frequency") -#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetry") +#define TR_AFHDS3_RX_FREQ TR("Freq. RX", "Frequenza RX") +#define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetria") #define TR_AFHDS3_ONE_TO_MANY "Multicast" -#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") -#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") +#define TR_AFHDS3_ACTUAL_POWER TR("Pot. att.", "Potenza attuale") +#define TR_AFHDS3_POWER_SOURCE TR("Sorg. al.", "Sorgente alimentazione") +#define TR_RXFREQUENCY TR(INDENT "Freq. RX", INDENT "Frequenza RX") #define TR_ANTENNACONFIRM1 "ANTENNA EST." #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" From d019741625ebce33a47ea0273440097d2bbb6c89 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 27 Jun 2020 10:06:47 +0200 Subject: [PATCH 032/267] Remove unused string --- radio/src/translations.cpp | 1 - radio/src/translations.h | 1 - radio/src/translations/cz.h.txt | 1 - radio/src/translations/de.h.txt | 1 - radio/src/translations/en.h.txt | 1 - radio/src/translations/es.h.txt | 1 - radio/src/translations/fi.h.txt | 1 - radio/src/translations/fr.h.txt | 1 - radio/src/translations/it.h.txt | 3 ++- radio/src/translations/nl.h.txt | 1 - radio/src/translations/pl.h.txt | 1 - radio/src/translations/pt.h.txt | 1 - radio/src/translations/se.h.txt | 1 - 13 files changed, 2 insertions(+), 13 deletions(-) diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 43dccb554..3406a412c 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -471,7 +471,6 @@ const char STR_BINDING_1_8_TELEM_OFF[] = TR_BINDING_CH1_8_TELEM_OFF; const char STR_BINDING_9_16_TELEM_ON[] = TR_BINDING_CH9_16_TELEM_ON; const char STR_BINDING_9_16_TELEM_OFF[] = TR_BINDING_CH9_16_TELEM_OFF; const char STR_CHANNELRANGE[] = TR_CHANNELRANGE; -const char STR_RXFREQUENCY[] = TR_RXFREQUENCY; const char STR_ANTENNACONFIRM1[] = TR_ANTENNACONFIRM1; const char STR_ANTENNACONFIRM2[] = TR_ANTENNACONFIRM2; const char STR_USE_INTERNAL_ANTENNA[] = TR_USE_INTERNAL_ANTENNA; diff --git a/radio/src/translations.h b/radio/src/translations.h index f428b3057..0eceb54bb 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -774,7 +774,6 @@ extern const char STR_BINDING_1_8_TELEM_OFF[]; extern const char STR_BINDING_9_16_TELEM_ON[]; extern const char STR_BINDING_9_16_TELEM_OFF[]; extern const char STR_CHANNELRANGE[]; -extern const char STR_RXFREQUENCY[]; extern const char STR_ANTENNASELECTION[]; extern const char STR_ANTENNACONFIRM1[]; extern const char STR_ANTENNACONFIRM2[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index d6563c16c..fa59c7dfa 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -942,7 +942,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", TR("Power src.", "Power source")) -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Opravdu přepnout?" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index f816a7269..3489f1d68 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -948,7 +948,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Ant. umschalten" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index f902dfa60..d5a335f4d 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -947,7 +947,6 @@ #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE TR(INDENT "Ch. Range", INDENT "Channel Range") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index d7dbd2b15..26c7e0612 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -945,7 +945,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "ANTENA EXT." #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\020" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 579eb25bf..9087fe150 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -961,7 +961,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index a914772d5..a8a22127c 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -963,7 +963,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Vraiment changer?" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\021" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 74acd82c9..c0d7032e8 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -961,9 +961,10 @@ #define TR_AFHDS3_RX_FREQ TR("Freq. RX", "Frequenza RX") #define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetria") #define TR_AFHDS3_ONE_TO_MANY "Multicast" +#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") +#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") #define TR_AFHDS3_ACTUAL_POWER TR("Pot. att.", "Potenza attuale") #define TR_AFHDS3_POWER_SOURCE TR("Sorg. al.", "Sorgente alimentazione") -#define TR_RXFREQUENCY TR(INDENT "Freq. RX", INDENT "Frequenza RX") #define TR_ANTENNACONFIRM1 "ANTENNA EST." #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 6c1138094..3c79e2d69 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -949,7 +949,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "Antennes wisselen?" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 38b03063a..a7f7734d9 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -962,7 +962,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index e6a3ecfb1..72e7dd3ad 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -952,7 +952,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 079127692..be03b5f94 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -962,7 +962,6 @@ #define TR_AFHDS3_ONE_TO_MANY "Multicast" #define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") #define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") -#define TR_RXFREQUENCY TR(INDENT "RX Freq.", INDENT "RX Frequency") #define TR_ANTENNACONFIRM1 "EXT. ANTENNA" #if defined(PCBX12S) #define LEN_ANTENNA_MODES "\023" From ab2ec8828393afd82183580aa24e3bcfbd6bca6d Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 27 Jun 2020 10:07:59 +0200 Subject: [PATCH 033/267] Fix IT --- radio/src/translations/it.h.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c0d7032e8..e67a7c354 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -961,8 +961,6 @@ #define TR_AFHDS3_RX_FREQ TR("Freq. RX", "Frequenza RX") #define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetria") #define TR_AFHDS3_ONE_TO_MANY "Multicast" -#define TR_AFHDS3_ACTUAL_POWER TR("Act. pow", "Actual power") -#define TR_AFHDS3_POWER_SOURCE TR("Power src.", "Power source") #define TR_AFHDS3_ACTUAL_POWER TR("Pot. att.", "Potenza attuale") #define TR_AFHDS3_POWER_SOURCE TR("Sorg. al.", "Sorgente alimentazione") #define TR_ANTENNACONFIRM1 "ANTENNA EST." From d903036dd882910ab8d2b0c2dcf81736f79f1463 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 27 Jun 2020 13:10:08 +0200 Subject: [PATCH 034/267] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index b11780727..727bbe185 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1778,3 +1778,6 @@ Fred Ceratt Stefan Grunenberg Aram Parsamyan Wolfgang Rauser +John Wall +Martin Haggas +Denis Kelly From adbfd63f2be4b75e988d6ec3cd2fba2c9f25ab03 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 27 Jun 2020 15:42:56 +0200 Subject: [PATCH 035/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 727bbe185..dfbcaf170 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1781,3 +1781,4 @@ Wolfgang Rauser John Wall Martin Haggas Denis Kelly +James Houck From 45f46d0b42beeda9d002babbaa74e8d651fb9b15 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 28 Jun 2020 00:33:17 +0200 Subject: [PATCH 036/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index dfbcaf170..9c0df159e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1782,3 +1782,5 @@ John Wall Martin Haggas Denis Kelly James Houck +Brian Stephanik +William McNeice From 090a266463060d4ee401aa7e81bc936406acffe8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 28 Jun 2020 10:04:20 +0200 Subject: [PATCH 037/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9c0df159e..ffc211bae 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1784,3 +1784,4 @@ Denis Kelly James Houck Brian Stephanik William McNeice +Sven De Boeck From 7b01eb0722962e2fed0e3c5d5580fcd6e7acd38c Mon Sep 17 00:00:00 2001 From: MRC3742 Date: Mon, 29 Jun 2020 01:05:29 -0400 Subject: [PATCH 038/267] Updates for MPM version 1.3.1.24 (#7779) --- companion/src/firmwares/multiprotocols.cpp | 10 ++++++---- radio/src/gui/gui_common.cpp | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/companion/src/firmwares/multiprotocols.cpp b/companion/src/firmwares/multiprotocols.cpp index f6ab2ac12..f370caeaf 100644 --- a/companion/src/firmwares/multiprotocols.cpp +++ b/companion/src/firmwares/multiprotocols.cpp @@ -41,7 +41,7 @@ static const QStringList STR_SUBTYPE_FLYSKY {"Standard", "V9x9", "V6x6", "V91 static const QStringList STR_SUBTYPE_HUBSAN {"H107", "H301", "H501"}; static const QStringList STR_SUBTYPE_FRSKY {"D16", "D8", "D16 8ch", "V8", "D16 EU-LBT", "D16 EU-LBT 8ch", "D8 Cloned", "D16 Cloned"}; static const QStringList STR_SUBTYPE_HISKY {"Standard", "HK310"}; -static const QStringList STR_SUBTYPE_V2X2 {"Standard", "JXD506"}; +static const QStringList STR_SUBTYPE_V2X2 {"Standard", "JXD506", "MR101"}; static const QStringList STR_SUBTYPE_DSM {"DSM2 22ms", "DSM2 11ms", "DSMX 22ms", "DSMX 11ms"}; static const QStringList STR_SUBTYPE_DEVO {"8 Channel", "10 Channel", "12 Channel", "6 Channel", "7 Channel"}; static const QStringList STR_SUBTYPE_YD717 {"Standard", "Skywalker", "Syma X4", "XINXUN", "NIHUI"}; @@ -71,6 +71,7 @@ static const QStringList STR_SUBTYPE_TRAXXAS {"6519 RX"}; static const QStringList STR_SUBTYPE_E01X {"E012", "E015", "E016H"}; static const QStringList STR_SUBTYPE_V911S {"Standard", "E119"}; static const QStringList STR_SUBTYPE_GD00X {"GD V1", "GD V2"}; +static const QStringList STR_SUBTYPE_V761 {"3 Channel", "4 Channel"}; static const QStringList STR_SUBTYPE_REDPINE {"Fast", "Slow"}; static const QStringList STR_SUBTYPE_POTENSIC {"A20 Firefly"}; static const QStringList STR_SUBTYPE_ZSX {"JJRC ZSX-280"}; @@ -80,7 +81,7 @@ static const QStringList STR_SUBTYPE_HOTT {"Sync", "No_Sync"}; static const QStringList STR_SUBTYPE_FX816 {"P38"}; static const QStringList STR_SUBTYPE_PELIKAN {"Pro", "Lite"}; static const QStringList STR_SUBTYPE_XK {"X450", "X420"}; -static const QStringList STR_SUBTYPE_XN297DUMP {"250K", "1M", "2M", "AUTO"}; +static const QStringList STR_SUBTYPE_XN297DUMP {"250K", "1M", "2M", "AUTO", "NRF"}; static const QStringList STR_SUBTYPE_FRSKYX2 {"D16", "D16 8ch", "D16 EU-LBT", "D16 EU-LBT 8ch", "D16 Cloned"}; static const QStringList STR_SUBTYPE_FRSKYR9 {"915 MHz", "868 MHz", "915 MHz 8-Channel", "868 MHz 8-Channel"}; static const QStringList STR_SUBTYPE_PROPEL {"74-Z"}; @@ -97,7 +98,7 @@ const Multiprotocols multiProtocols { {MODULE_SUBTYPE_MULTI_HUBSAN, 2, false, STR_SUBTYPE_HUBSAN, STR_MULTI_VIDFREQ}, {MODULE_SUBTYPE_MULTI_FRSKY, 7, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_HISKY, 1, false, STR_SUBTYPE_HISKY, nullptr}, - {MODULE_SUBTYPE_MULTI_V2X2, 1, false, STR_SUBTYPE_V2X2, nullptr}, + {MODULE_SUBTYPE_MULTI_V2X2, 2, false, STR_SUBTYPE_V2X2, nullptr}, {MODULE_SUBTYPE_MULTI_DSM2, 3, false, STR_SUBTYPE_DSM, nullptr}, {MODULE_SUBTYPE_MULTI_DEVO, 4, false, STR_SUBTYPE_DEVO, STR_MULTI_FIXEDID}, {MODULE_SUBTYPE_MULTI_YD717, 4, false, STR_SUBTYPE_YD717, nullptr}, @@ -130,6 +131,7 @@ const Multiprotocols multiProtocols { {MODULE_SUBTYPE_MULTI_E01X, 2, false, STR_SUBTYPE_E01X, STR_MULTI_OPTION}, {MODULE_SUBTYPE_MULTI_V911S, 1, false, STR_SUBTYPE_V911S, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_GD00X, 1, false, STR_SUBTYPE_GD00X, STR_MULTI_RFTUNE}, + {MODULE_SUBTYPE_MULTI_V761, 1, false, STR_SUBTYPE_V761, nullptr}, {MODULE_SUBTYPE_MULTI_KF606, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_REDPINE, 1, false, STR_SUBTYPE_REDPINE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_POTENSIC, 0, false, STR_SUBTYPE_POTENSIC, nullptr}, @@ -140,7 +142,7 @@ const Multiprotocols multiProtocols { {MODULE_SUBTYPE_MULTI_FX816, 0, false, STR_SUBTYPE_FX816, nullptr}, {MODULE_SUBTYPE_MULTI_PELIKAN, 1, false, STR_SUBTYPE_PELIKAN, nullptr}, {MODULE_SUBTYPE_MULTI_XK, 1, false, STR_SUBTYPE_XK, STR_MULTI_RFTUNE}, - {MODULE_SUBTYPE_MULTI_XN297DUMP, 3, false, STR_SUBTYPE_XN297DUMP, nullptr}, + {MODULE_SUBTYPE_MULTI_XN297DUMP, 4, false, STR_SUBTYPE_XN297DUMP, nullptr}, {MODULE_SUBTYPE_MULTI_FRSKYX2, 4, true, STR_SUBTYPE_FRSKYX2, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_FRSKY_R9, 3, true, STR_SUBTYPE_FRSKYR9, nullptr}, {MODULE_SUBTYPE_MULTI_PROPEL, 0, false, STR_SUBTYPE_PROPEL, nullptr}, diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 59153faa7..19b591f66 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -866,7 +866,7 @@ const char STR_SUBTYPE_FLYSKY[] = "\004""Std\0""V9x9""V6x6""V912""CX20"; const char STR_SUBTYPE_HUBSAN[] = "\004""H107""H301""H501"; const char STR_SUBTYPE_FRSKY[] = "\011""D16\0 ""D8\0 ""D16 8ch\0 ""V8\0 ""LBT(EU)\0 ""LBT 8ch\0 ""D8Cloned\0""D16Cloned"; const char STR_SUBTYPE_HISKY[] = "\005""Std\0 ""HK310"; -const char STR_SUBTYPE_V2X2[] = "\006""Std\0 ""JXD506"; +const char STR_SUBTYPE_V2X2[] = "\006""Std\0 ""JXD506""MR101\0"; const char STR_SUBTYPE_DSM[] = "\006""2 22ms""2 11ms""X 22ms""X 11ms"; const char STR_SUBTYPE_DEVO[] = "\004""8ch\0""10ch""12ch""6ch\0""7ch\0"; const char STR_SUBTYPE_YD717[] = "\007""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 "; @@ -896,6 +896,7 @@ const char STR_SUBTYPE_TRAXXAS[] = "\004""6519"; const char STR_SUBTYPE_E01X[] = "\005""E012\0""E015\0""E016H"; const char STR_SUBTYPE_V911S[] = "\004""Std\0""E119"; const char STR_SUBTYPE_GD00X[] = "\005""GD_V1""GD_V2"; +const char STR_SUBTYPE_V761[] = "\003""3ch""4ch"; const char STR_SUBTYPE_REDPINE[] = "\004""Fast""Slow"; const char STR_SUBTYPE_POTENSIC[] = "\003""A20"; const char STR_SUBTYPE_ZSX[] = "\007""280JJRC"; @@ -905,7 +906,7 @@ const char STR_SUBTYPE_HOTT[] = "\007""Sync\0 ""No_Sync"; const char STR_SUBTYPE_FX816[] = "\003""P38"; const char STR_SUBTYPE_PELIKAN[] = "\004""Pro\0""Lite"; const char STR_SUBTYPE_XK[] = "\004""X450""X420"; -const char STR_SUBTYPE_XN297DUMP[] = "\004""250K""1M\0 ""2M\0 ""AUTO"; +const char STR_SUBTYPE_XN297DUMP[] = "\004""250K""1M\0 ""2M\0 ""AUTO""NRF\0"; const char STR_SUBTYPE_FRSKYX2[] = "\011""D16\0 ""D16 8ch\0 ""LBT(EU)\0 ""LBT 8ch\0 ""D16Cloned"; const char STR_SUBTYPE_FRSKYR9[] = "\007""915MHz\0""868MHz\0""915 8ch""868 8ch"; const char STR_SUBTYPE_PROPEL[] = "\004""74-Z"; @@ -931,7 +932,7 @@ const mm_protocol_definition multi_protocols[] = { {MODULE_SUBTYPE_MULTI_HUBSAN, 2, false, false, STR_SUBTYPE_HUBSAN, STR_MULTI_VIDFREQ}, {MODULE_SUBTYPE_MULTI_FRSKY, 7, false, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_HISKY, 1, false, true, STR_SUBTYPE_HISKY, nullptr}, - {MODULE_SUBTYPE_MULTI_V2X2, 1, false, false, STR_SUBTYPE_V2X2, nullptr}, + {MODULE_SUBTYPE_MULTI_V2X2, 2, false, false, STR_SUBTYPE_V2X2, nullptr}, {MODULE_SUBTYPE_MULTI_DSM2, 3, false, true, STR_SUBTYPE_DSM, STR_MULTI_MAX_THROW}, {MODULE_SUBTYPE_MULTI_DEVO, 4, false, true, STR_SUBTYPE_DEVO, STR_MULTI_FIXEDID}, {MODULE_SUBTYPE_MULTI_YD717, 4, false, false, STR_SUBTYPE_YD717, nullptr}, @@ -964,6 +965,7 @@ const mm_protocol_definition multi_protocols[] = { {MODULE_SUBTYPE_MULTI_E01X, 2, false, false, STR_SUBTYPE_E01X, STR_MULTI_OPTION}, {MODULE_SUBTYPE_MULTI_V911S, 1, false, false, STR_SUBTYPE_V911S, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_GD00X, 1, false, false, STR_SUBTYPE_GD00X, STR_MULTI_RFTUNE}, + {MODULE_SUBTYPE_MULTI_V761, 1, false, false, STR_SUBTYPE_V761, nullptr}, {MODULE_SUBTYPE_MULTI_KF606, 0, false, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_REDPINE, 1, false, false, STR_SUBTYPE_REDPINE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_POTENSIC, 0, false, false, STR_SUBTYPE_POTENSIC, nullptr}, @@ -974,7 +976,7 @@ const mm_protocol_definition multi_protocols[] = { {MODULE_SUBTYPE_MULTI_FX816, 0, false, false, STR_SUBTYPE_FX816, nullptr}, {MODULE_SUBTYPE_MULTI_PELIKAN, 1, false, false, STR_SUBTYPE_PELIKAN, nullptr}, {MODULE_SUBTYPE_MULTI_XK, 1, false, false, STR_SUBTYPE_XK, STR_MULTI_RFTUNE}, - {MODULE_SUBTYPE_MULTI_XN297DUMP, 3, false, false, STR_SUBTYPE_XN297DUMP, STR_MULTI_RFCHAN}, + {MODULE_SUBTYPE_MULTI_XN297DUMP, 4, false, false, STR_SUBTYPE_XN297DUMP, STR_MULTI_RFCHAN}, {MODULE_SUBTYPE_MULTI_FRSKYX2, 4, true, false, STR_SUBTYPE_FRSKYX2, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_FRSKY_R9, 3, true, false, STR_SUBTYPE_FRSKYR9, nullptr}, {MODULE_SUBTYPE_MULTI_PROPEL, 0, false, false, STR_SUBTYPE_PROPEL, nullptr}, From 1b462058ad53776374e17c456a65816a81967d3f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 29 Jun 2020 13:08:22 +0200 Subject: [PATCH 039/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ffc211bae..b6c560345 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1785,3 +1785,4 @@ James Houck Brian Stephanik William McNeice Sven De Boeck +SB Europe Ltd From e8fc959f188644d3b798bd67d3f0b292a5c4fc5b Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 29 Jun 2020 13:39:00 +0200 Subject: [PATCH 040/267] Change SBUS voltage warning for TX16S --- radio/src/gui/480x272/model_setup.cpp | 4 ++++ radio/src/translations.cpp | 1 + radio/src/translations.h | 1 + radio/src/translations/cz.h.txt | 1 + radio/src/translations/de.h.txt | 1 + radio/src/translations/en.h.txt | 1 + radio/src/translations/es.h.txt | 1 + radio/src/translations/fi.h.txt | 1 + radio/src/translations/fr.h.txt | 1 + radio/src/translations/it.h.txt | 1 + radio/src/translations/nl.h.txt | 1 + radio/src/translations/pl.h.txt | 1 + radio/src/translations/pt.h.txt | 1 + radio/src/translations/se.h.txt | 1 + 14 files changed, 17 insertions(+) diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 264685b2d..dee079d96 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1705,8 +1705,12 @@ bool menuModelSetup(event_t event) lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON); } else if (isModuleSBUS(moduleIdx)) { +#if defined(RADIO_TX16S) // TX16S has 5v output on CPPM pin + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_WARN_5VOLTS); +#else lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WARN_BATTVOLTAGE); drawValueWithUnit(MODEL_SETUP_4TH_COLUMN, y, getBatteryVoltage(), UNIT_VOLTS, attr|PREC2|LEFT); +#endif } break; } diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 3406a412c..4d65e6840 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -357,6 +357,7 @@ const char STR_SPECTRUM_ANALYSER_EXT[] = TR_SPECTRUM_ANALYSER_EXT; const char STR_SPECTRUM_ANALYSER_INT[] = TR_SPECTRUM_ANALYSER_INT; const char STR_WAITING_FOR_RX[] = TR_WAITING_FOR_RX; const char STR_WAITING_FOR_TX[] = TR_WAITING_FOR_TX; +const char STR_WARN_5VOLTS[] = TR_WARN_5VOLTS; const char STR_CAT_NOT_EMPTY[] = TR_CAT_NOT_EMPTY; const char STR_WARNING[] = TR_WARNING; const char STR_THROTTLEWARN[] = TR_THROTTLEWARN; diff --git a/radio/src/translations.h b/radio/src/translations.h index 0eceb54bb..f99520cd7 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -541,6 +541,7 @@ extern const char STR_SPECTRUM_ANALYSER_EXT[]; extern const char STR_SPECTRUM_ANALYSER_INT[]; extern const char STR_WAITING_FOR_RX[]; extern const char STR_WAITING_FOR_TX[]; +extern const char STR_WARN_5VOLTS[]; #if defined(SDCARD) extern const char STR_BACKUP_MODEL[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index fa59c7dfa..adaa7d30e 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -441,6 +441,7 @@ #define TR_PPMFRAME INDENT "PPM modulace" #define TR_REFRESHRATE TR(INDENT "Obnovit", INDENT "Obn. frekv.") #define STR_WARN_BATTVOLTAGE TR(INDENT "Výstup VBAT: ", INDENT "Varování: výstupní hodnota VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequence" #define TR_SWITCH "Spínač" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 3489f1d68..e6c6eb431 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -443,6 +443,7 @@ #define TR_PPMFRAME INDENT "PPM-Frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH TR("Schalt.", "Schalter") diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index d5a335f4d..706197f33 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -442,6 +442,7 @@ #define TR_PPMFRAME INDENT "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH "Switch" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 26c7e0612..714fcc217 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -441,6 +441,7 @@ #define TR_PPMFRAME "Trama PPM" #define TR_REFRESHRATE TR(INDENT "Refresco", INDENT "Velocidad refresco") #define STR_WARN_BATTVOLTAGE TR(INDENT "Salida es VBAT: ", INDENT "Aviso: señal salida es VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequencia" #define TR_SWITCH TR("Interr.", "Interruptor") diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 9087fe150..90ce833cb 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -455,6 +455,7 @@ #define TR_PPMFRAME TR("PPM frame", INDENT "PPM frame") #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH "Switch" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index a8a22127c..d4a1f5f29 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -461,6 +461,7 @@ #define TR_PPMFRAME INDENT "Trame PPM" #define TR_REFRESHRATE INDENT "Période" #define STR_WARN_BATTVOLTAGE TR(INDENT "Signal est VBAT: ", INDENT "ATTENTION: signal est à VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Fréquence" #define TR_SWITCH TR("Inter", "Interrupteur") diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index e67a7c354..734789283 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -463,6 +463,7 @@ #define TR_PPMFRAME INDENT "Frame PPM" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Uscita VBAT: ", INDENT "Att.: livel. uscita VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequenza" #define TR_SWITCH "Inter." diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 3c79e2d69..64af03eea 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -442,6 +442,7 @@ #define TR_PPMFRAME INDENT "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH TR("Schak.", "Schakelaar") diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index a7f7734d9..5ba5bd258 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -462,6 +462,7 @@ #define TR_PPMFRAME INDENT "Ramka PPM" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH "Przełą" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 72e7dd3ad..e07e3ca78 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -448,6 +448,7 @@ #define TR_PPMFRAME "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH "Chave" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index be03b5f94..c55d64650 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -461,6 +461,7 @@ #define TR_PPMFRAME INDENT "PPM-paket" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") +#define TR_WARN_5VOLTS "Warning: output level is 5 volts" #define TR_MS "ms" #define TR_FREQUENCY INDENT "Frequency" #define TR_SWITCH "Brytare" From ee18c6bcc759d03dcd8df5bdcdfdd1a0c93982ff Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 29 Jun 2020 19:16:32 +0200 Subject: [PATCH 041/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index b6c560345..5e0e76735 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1786,3 +1786,4 @@ Brian Stephanik William McNeice Sven De Boeck SB Europe Ltd +Evening Shade Pottery From a325b2b612069f3985fb371c14ad4fd9491c018a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 29 Jun 2020 19:17:04 +0200 Subject: [PATCH 042/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5e0e76735..f2c4aa017 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1787,3 +1787,4 @@ William McNeice Sven De Boeck SB Europe Ltd Evening Shade Pottery +D van Oostveen From c95d662266d2500c3e8def3e4a2b5e924f91df2d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 30 Jun 2020 08:04:27 +0200 Subject: [PATCH 043/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index f2c4aa017..6d5552c25 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1788,3 +1788,4 @@ Sven De Boeck SB Europe Ltd Evening Shade Pottery D van Oostveen +Michael Grosso From 423aa47e31d42dcdf3c0a725bfc875b979cb9344 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 30 Jun 2020 09:06:35 +0200 Subject: [PATCH 044/267] IT translation --- radio/src/translations/it.h.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 734789283..034bcd7db 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -461,11 +461,11 @@ #endif #define TR_PROTO TR(INDENT "Protoc.", INDENT "Protocollo") #define TR_PPMFRAME INDENT "Frame PPM" -#define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") -#define STR_WARN_BATTVOLTAGE TR(INDENT "Uscita VBAT: ", INDENT "Att.: livel. uscita VBAT: ") -#define TR_WARN_5VOLTS "Warning: output level is 5 volts" +#define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") +#define STR_WARN_BATTVOLTAGE TR(INDENT "Uscita VBAT: ", INDENT "Att.: livel. uscita VBAT: ") +#define TR_WARN_5VOLTS "Attenzione: il livello d'uscita è 5 Volts" #define TR_MS "ms" -#define TR_FREQUENCY INDENT "Frequenza" +#define TR_FREQUENCY INDENT "Frequenza" #define TR_SWITCH "Inter." #define TR_TRIMS "Trims" #define TR_FADEIN "Diss.In" From 34ba94818a045301578b433f1d02814910828ec1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 30 Jun 2020 20:42:30 +0200 Subject: [PATCH 045/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 6d5552c25..200c09ac5 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1789,3 +1789,4 @@ SB Europe Ltd Evening Shade Pottery D van Oostveen Michael Grosso +Carlos Marcal From e43d5641a25a4f1af7477c1aa0c91a3c45d5c1d2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 30 Jun 2020 21:16:39 +0200 Subject: [PATCH 046/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 200c09ac5..b0ddf486e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1790,3 +1790,5 @@ Evening Shade Pottery D van Oostveen Michael Grosso Carlos Marcal +d@taweb + From 5624cac22260d0f64e8ae049c19ffee8dbe9b1f7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 1 Jul 2020 08:07:02 +0200 Subject: [PATCH 047/267] Update CREDITS.txt --- CREDITS.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CREDITS.txt b/CREDITS.txt index b0ddf486e..d5683fc90 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1791,4 +1791,4 @@ D van Oostveen Michael Grosso Carlos Marcal d@taweb - +Justin Digby From 3f4a80dd92ed8560b00bc9240f448a40f5aee775 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 1 Jul 2020 08:25:09 +0200 Subject: [PATCH 048/267] Fix TX16S defaut pots This fixes #7783 --- radio/src/targets/horus/board.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 41a8f96ee..0364e938f 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -386,7 +386,11 @@ enum Analogs { #define SLIDER2 SLIDER_FRONT_RIGHT #define DEFAULT_SWITCH_CONFIG (SWITCH_TOGGLE << 14) + (SWITCH_3POS << 12) + (SWITCH_2POS << 10) + (SWITCH_3POS << 8) + (SWITCH_3POS << 6) + (SWITCH_3POS << 4) + (SWITCH_3POS << 2) + (SWITCH_3POS << 0) -#define DEFAULT_POTS_CONFIG (POT_WITH_DETENT << 4) + (POT_MULTIPOS_SWITCH << 2) + (POT_WITHOUT_DETENT << 0) +#if defined(RADIO_FAMILY_T16) + #define DEFAULT_POTS_CONFIG (POT_WITH_DETENT << 4) + (POT_MULTIPOS_SWITCH << 2) + (POT_WITH_DETENT << 0) +#else + #define DEFAULT_POTS_CONFIG (POT_WITH_DETENT << 4) + (POT_MULTIPOS_SWITCH << 2) + (POT_WITHOUT_DETENT << 0) +#endif #if defined(PCBX12S) #define DEFAULT_SLIDERS_CONFIG (SLIDER_WITH_DETENT << 3) + (SLIDER_WITH_DETENT << 2) + (SLIDER_WITH_DETENT << 1) + (SLIDER_WITH_DETENT << 0) From b1382a992599e96e1610ddaa0d0796e0d4d08c8e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 1 Jul 2020 14:58:53 +0200 Subject: [PATCH 049/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d5683fc90..d8f4c408d 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1792,3 +1792,4 @@ Michael Grosso Carlos Marcal d@taweb Justin Digby +Hermann Vocat From 96892db80bbcaa801f70a84844d3fc403274c78f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Jul 2020 00:02:34 +0200 Subject: [PATCH 050/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d8f4c408d..31fba06a8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1793,3 +1793,4 @@ Carlos Marcal d@taweb Justin Digby Hermann Vocat +Michael Francini From 2f1206f399ee7bd1714bddcf14b23f444d515cba Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Jul 2020 08:08:48 +0200 Subject: [PATCH 051/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 31fba06a8..3908d8c2b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1794,3 +1794,4 @@ d@taweb Justin Digby Hermann Vocat Michael Francini +Michael Chambers From 4e7f6e4f29754b1443dca351c8c5c79d098b15a7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Jul 2020 18:39:13 +0200 Subject: [PATCH 052/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3908d8c2b..9a83e8102 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1795,3 +1795,4 @@ Justin Digby Hermann Vocat Michael Francini Michael Chambers +Harold H Youngren From ecee4638f67593445d077aba5e6c8f7c1863ed42 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 3 Jul 2020 08:06:43 +0200 Subject: [PATCH 053/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9a83e8102..ba2751063 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1796,3 +1796,4 @@ Hermann Vocat Michael Francini Michael Chambers Harold H Youngren +Yann Orselli From 3ebf8096812652f04d6a5cc3360e7e933bd604ff Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 3 Jul 2020 19:07:39 +0200 Subject: [PATCH 054/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ba2751063..957fd1916 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1797,3 +1797,4 @@ Michael Francini Michael Chambers Harold H Youngren Yann Orselli +Dieter Schaad From 0f13580f3d9cc8d017df5ccdc9ecef38eed6155c Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 3 Jul 2020 19:36:15 +0200 Subject: [PATCH 055/267] Add missing radios This fixes #7789 --- tools/build-companion-release.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/build-companion-release.sh b/tools/build-companion-release.sh index 34757a3e8..b73ce8ed2 100755 --- a/tools/build-companion-release.sh +++ b/tools/build-companion-release.sh @@ -62,6 +62,10 @@ cmake ${COMMON_OPTIONS} -DPCB=X7 ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=ACCESS ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X7 -DPCBREV=T12 ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt @@ -102,6 +106,10 @@ cmake ${COMMON_OPTIONS} -DPCB=X10 -DPCBREV=TX16S ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt +cmake ${COMMON_OPTIONS} -DPCB=X10 -DPCBREV=T18 ${SRCDIR} +make -j${JOBS} libsimulator +rm CMakeCache.txt + cmake ${COMMON_OPTIONS} -DPCB=X10 -DPCBREV=EXPRESS ${SRCDIR} make -j${JOBS} libsimulator rm CMakeCache.txt From 531784d43336dc2c92fadfeb9dd0c337dc63a720 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 3 Jul 2020 21:19:36 +0200 Subject: [PATCH 056/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 957fd1916..70c7667c0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1798,3 +1798,4 @@ Michael Chambers Harold H Youngren Yann Orselli Dieter Schaad +Ernst Camenzind From b50ec5355adfa0f5913f6c067b20fb97329c3013 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 4 Jul 2020 10:41:16 +0200 Subject: [PATCH 057/267] Multi DSM string clarification --- companion/src/firmwares/multiprotocols.cpp | 2 +- radio/src/gui/gui_common.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/multiprotocols.cpp b/companion/src/firmwares/multiprotocols.cpp index f370caeaf..c8c18e6e5 100644 --- a/companion/src/firmwares/multiprotocols.cpp +++ b/companion/src/firmwares/multiprotocols.cpp @@ -42,7 +42,7 @@ static const QStringList STR_SUBTYPE_HUBSAN {"H107", "H301", "H501"}; static const QStringList STR_SUBTYPE_FRSKY {"D16", "D8", "D16 8ch", "V8", "D16 EU-LBT", "D16 EU-LBT 8ch", "D8 Cloned", "D16 Cloned"}; static const QStringList STR_SUBTYPE_HISKY {"Standard", "HK310"}; static const QStringList STR_SUBTYPE_V2X2 {"Standard", "JXD506", "MR101"}; -static const QStringList STR_SUBTYPE_DSM {"DSM2 22ms", "DSM2 11ms", "DSMX 22ms", "DSMX 11ms"}; +static const QStringList STR_SUBTYPE_DSM {"DSM2 1F", "DSM2 2F", "DSMX 1F", "DSMX 2F"}; static const QStringList STR_SUBTYPE_DEVO {"8 Channel", "10 Channel", "12 Channel", "6 Channel", "7 Channel"}; static const QStringList STR_SUBTYPE_YD717 {"Standard", "Skywalker", "Syma X4", "XINXUN", "NIHUI"}; static const QStringList STR_SUBTYPE_KN {"WLtoys", "FeiLun"}; diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 19b591f66..6cbc287d2 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -867,7 +867,7 @@ const char STR_SUBTYPE_HUBSAN[] = "\004""H107""H301""H501"; const char STR_SUBTYPE_FRSKY[] = "\011""D16\0 ""D8\0 ""D16 8ch\0 ""V8\0 ""LBT(EU)\0 ""LBT 8ch\0 ""D8Cloned\0""D16Cloned"; const char STR_SUBTYPE_HISKY[] = "\005""Std\0 ""HK310"; const char STR_SUBTYPE_V2X2[] = "\006""Std\0 ""JXD506""MR101\0"; -const char STR_SUBTYPE_DSM[] = "\006""2 22ms""2 11ms""X 22ms""X 11ms"; +const char STR_SUBTYPE_DSM[] = "\004""2 1F""2 2F""X 1F""X 2F"; const char STR_SUBTYPE_DEVO[] = "\004""8ch\0""10ch""12ch""6ch\0""7ch\0"; const char STR_SUBTYPE_YD717[] = "\007""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 "; const char STR_SUBTYPE_KN[] = "\006""WLtoys""FeiLun"; From 3da925602065f9731dea2f40ad6ca946fb6b0381 Mon Sep 17 00:00:00 2001 From: ignaciobfp Date: Sat, 4 Jul 2020 13:01:03 +0200 Subject: [PATCH 058/267] Add missing tts sounds in spanish, minor fixes (#7777) Add missing tts spanish sounds (fixes #7770), minor improvements to make some lines more accurate (most cases) or just more natural --- radio/util/tts_es.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/radio/util/tts_es.py b/radio/util/tts_es.py index 01d28d3dd..897b6b9e4 100755 --- a/radio/util/tts_es.py +++ b/radio/util/tts_es.py @@ -61,24 +61,27 @@ for s, f in [("me tienes abandonada", "inactiv"), ("trim centrado", "midtrim"), ("potenciómetro centrado", "midpot"), ("trim al máximo", "maxtrim"), - ("trim al mínima", "mintrim"), + ("trim al mínimo", "mintrim"), ("20 segundos", "timer20"), ("30 segundos", "timer30"), - ("assorbimiento elevado", "highmah"), - ("temperatura elevada", "hightemp"), + ("consumo alto", "highmah"), + ("temperatura alta", "hightemp"), ("Alarma A1", "a1_org"), ("A1 crítica", "a1_red"), ("Alarma A2", "a2_org"), ("A2 crítica", "a2_red"), ("Bienvenido a open t equis", "hello"), - ("recepcion de señal baja", "rssi_org"), - ("recepción de señal crítica", "rssi_red"), + ("señal baja", "rssi_org"), + ("señal crítica", "rssi_red"), ("Problemas con la antena del transmisor", "swr_red"), ("Sin telemetría", "telemko"), ("Telemetría disponible", "telemok"), - ("servo overload", "servoko"), - ("power overload", "rxko"), - ("El receptor esta conectado", "modelpwr"), + ("sobrecarga de servo", "servoko"), + ("sobrecarga de potencia", "rxko"), + ("El receptor aún está encendido", "modelpwr"), + ("cronómetro uno terminado", "timovr1"), + ("cronómetro dos terminado", "timovr2"), + ("cronómetro tres terminado", "timovr3"), ]: systemSounds.append((s, filename(f))) for i, s in enumerate(["cronómetro", "cronómetro", "transmisión", "recepción", "A1", "A2", "altitud", "motor", @@ -102,14 +105,14 @@ for i, (s, f) in enumerate([("tren arriba.", "gearup"), ("modo de velocidad", "spdmod"), ("modo de térmica", "thmmod"), ("modo de vuelo normal", "nrmmod"), - ("fase de vuelo 1", "fltmd1"), - ("fase de vuelo 2", "fltmd2"), - ("fase de vuelo 3", "fltmd3"), - ("fase de vuelo 4", "fltmd4"), - ("fase de vuelo 5", "fltmd5"), - ("fase de vuelo 6", "fltmd6"), - ("fase de vuelo 7", "fltmd7"), - ("fase de vuelo 8", "fltmd8"), - ("fase de vuelo 9", "fltmd9"), + ("modo de vuelo 1", "fltmd1"), + ("modo de vuelo 2", "fltmd2"), + ("modo de vuelo 3", "fltmd3"), + ("modo de vuelo 4", "fltmd4"), + ("modo de vuelo 5", "fltmd5"), + ("modo de vuelo 6", "fltmd6"), + ("modo de vuelo 7", "fltmd7"), + ("modo de vuelo 8", "fltmd8"), + ("modo de vuelo 9", "fltmd9"), ]): sounds.append((s, filename(f))) From 1f720c02e357acc367a59dc8199f2a9b13084a6f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 4 Jul 2020 15:15:03 +0200 Subject: [PATCH 059/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 70c7667c0..50fa70501 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1799,3 +1799,4 @@ Harold H Youngren Yann Orselli Dieter Schaad Ernst Camenzind +Ciobanu Rivelino Costel From fc8072812b968ffab97b55c46666f7333f84c6a4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 5 Jul 2020 10:16:35 +0200 Subject: [PATCH 060/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 50fa70501..9003f776a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1800,3 +1800,5 @@ Yann Orselli Dieter Schaad Ernst Camenzind Ciobanu Rivelino Costel +David Jolly +Aram Parsamyan From 68a4e1a05c5a32bcd2ad4083d6cae39987ab8831 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 5 Jul 2020 15:30:53 +0200 Subject: [PATCH 061/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9003f776a..6f0583635 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1802,3 +1802,4 @@ Ernst Camenzind Ciobanu Rivelino Costel David Jolly Aram Parsamyan +Stewart Davies From 0f79f07187629ccf4eb8abd79cd993949084b55d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Jul 2020 08:48:36 +0200 Subject: [PATCH 062/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 6f0583635..60208a640 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1803,3 +1803,4 @@ Ciobanu Rivelino Costel David Jolly Aram Parsamyan Stewart Davies +Karsten Schwarz From 06a8e03eeba1abec4fa8a9ec7836613337f4f605 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Jul 2020 14:11:23 +0200 Subject: [PATCH 063/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 60208a640..617db9b03 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1804,3 +1804,4 @@ David Jolly Aram Parsamyan Stewart Davies Karsten Schwarz +Robert Mckay From 6535b652bcad7a048b243a2bc14ce20a41f5db8b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Jul 2020 16:11:00 +0200 Subject: [PATCH 064/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 617db9b03..7a5dcbf9e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1805,3 +1805,4 @@ Aram Parsamyan Stewart Davies Karsten Schwarz Robert Mckay +Michael Oeschger From 6d3996ab5442656eb991203672a942c71849b31f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 7 Jul 2020 08:59:12 +0200 Subject: [PATCH 065/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7a5dcbf9e..1ae419b6b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1806,3 +1806,4 @@ Stewart Davies Karsten Schwarz Robert Mckay Michael Oeschger +Randy Ling From 2af5881f3e19c81bc222448d57e3fa04f8fe8daf Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 7 Jul 2020 14:57:34 +0200 Subject: [PATCH 066/267] Improve checkbox visibility on darkblue theme --- radio/src/gui/480x272/themes/darkblue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/480x272/themes/darkblue.cpp b/radio/src/gui/480x272/themes/darkblue.cpp index ec4b51fb2..02fa5055b 100644 --- a/radio/src/gui/480x272/themes/darkblue.cpp +++ b/radio/src/gui/480x272/themes/darkblue.cpp @@ -35,7 +35,7 @@ class DarkblueTheme: public Theme lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = WHITE; lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = RGB(32, 34, 42); lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE; - lcdColorTable[LINE_COLOR_INDEX] = GREY; + lcdColorTable[LINE_COLOR_INDEX] = LIGHTGREY; lcdColorTable[SCROLLBOX_COLOR_INDEX] = WHITE; lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = DARKGREY; lcdColorTable[MENU_TITLE_COLOR_INDEX] = WHITE; From 94ba22a74d10d124e631837f49ff95e8240efc2e Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 7 Jul 2020 15:35:09 +0200 Subject: [PATCH 067/267] Improve FAI sensor handling (#7801) Display specific FAI message on forbiden sensors --- radio/src/gui/common/colorlcd/draw_functions.cpp | 5 +++++ radio/src/gui/common/stdlcd/draw_functions.cpp | 5 +++++ radio/src/options.h | 6 +++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/common/colorlcd/draw_functions.cpp b/radio/src/gui/common/colorlcd/draw_functions.cpp index c75ae824e..3fe07f0e6 100644 --- a/radio/src/gui/common/colorlcd/draw_functions.cpp +++ b/radio/src/gui/common/colorlcd/draw_functions.cpp @@ -202,6 +202,11 @@ void drawSensorCustomValue(coord_t x, coord_t y, uint8_t sensor, int32_t value, return; } + if (IS_FAI_FORBIDDEN(MIXSRC_FIRST_TELEM + 3 * sensor)) { + lcdDrawText(x, y, "FAI mode", flags); + return; + } + TelemetryItem & telemetryItem = telemetryItems[sensor]; TelemetrySensor & telemetrySensor = g_model.telemetrySensors[sensor]; diff --git a/radio/src/gui/common/stdlcd/draw_functions.cpp b/radio/src/gui/common/stdlcd/draw_functions.cpp index 1bcf7286c..524b06a7b 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.cpp +++ b/radio/src/gui/common/stdlcd/draw_functions.cpp @@ -353,6 +353,11 @@ void drawSensorCustomValue(coord_t x, coord_t y, uint8_t sensor, int32_t value, return; } + if (IS_FAI_FORBIDDEN(MIXSRC_FIRST_TELEM + 3 * sensor)) { + lcdDrawText(x, y, "FAI mode", flags); + return; + } + TelemetryItem & telemetryItem = telemetryItems[sensor]; TelemetrySensor & telemetrySensor = g_model.telemetrySensors[sensor]; diff --git a/radio/src/options.h b/radio/src/options.h index e8bf634c1..b9d2a0075 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -22,7 +22,7 @@ #ifndef OPENTX_OPTIONS_H #define OPENTX_OPTIONS_H -static const char * options[] = { +static const char * const options[] = { #if defined(AUTOUPDATE) "autoupdate", #endif @@ -33,10 +33,10 @@ static const char * options[] = { "eu", #endif #if defined(FAI) - "faimode", + "FAImode", #endif #if defined(FAI_CHOICE) - "faichoice", + "FAIchoice", #endif #if defined(MODULE_PROTOCOL_FLEX) "flexr9m", From 0eb203e82774c965a2ebf6503eaedf09d726a220 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 7 Jul 2020 15:56:18 +0200 Subject: [PATCH 068/267] Tx16s sbus in (#7787) Enable SBUS trainer input --- radio/src/gui/480x272/radio_hardware.cpp | 4 +- radio/src/gui/gui_common.cpp | 18 +++++++ radio/src/gui/gui_common.h | 2 + .../common/arm/stm32/aux_serial_driver.cpp | 54 +++++++++++++------ radio/src/targets/horus/CMakeLists.txt | 9 ++++ radio/src/targets/horus/board.h | 3 ++ radio/src/targets/horus/trainer_driver.cpp | 16 ++++++ radio/src/tasks.cpp | 2 +- radio/src/trainer.cpp | 7 +++ 9 files changed, 96 insertions(+), 19 deletions(-) diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp index 3ba908ad9..8020bf983 100644 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ b/radio/src/gui/480x272/radio_hardware.cpp @@ -372,7 +372,7 @@ bool menuRadioHardware(event_t event) #if defined(RADIO_TX16S) lcdDrawText(lcdNextPos, y, " (TTL)"); #endif - g_eeGeneral.auxSerialMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_AUX_SERIAL_MODES, g_eeGeneral.auxSerialMode, 0, UART_MODE_MAX, attr, event); + g_eeGeneral.auxSerialMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_AUX_SERIAL_MODES, g_eeGeneral.auxSerialMode, 0, UART_MODE_MAX, attr, event, isAux1ModeAvailable); if (attr && checkIncDec_Ret) { auxSerialInit(g_eeGeneral.auxSerialMode, modelTelemetryProtocol()); } @@ -385,7 +385,7 @@ bool menuRadioHardware(event_t event) #if defined(RADIO_TX16S) lcdDrawText(lcdNextPos, y, " (TTL)"); #endif - g_eeGeneral.aux2SerialMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_AUX_SERIAL_MODES, g_eeGeneral.aux2SerialMode, 0, UART_MODE_MAX, attr, event); + g_eeGeneral.aux2SerialMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_AUX_SERIAL_MODES, g_eeGeneral.aux2SerialMode, 0, UART_MODE_MAX, attr, event, isAux2ModeAvailable); if (attr && checkIncDec_Ret) { aux2SerialInit(g_eeGeneral.aux2SerialMode, modelTelemetryProtocol()); } diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 6cbc287d2..b491d84b4 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -385,6 +385,24 @@ bool isSwitchAvailable(int swtch, SwitchContext context) return true; } +bool isAux1ModeAvailable(int mode) +{ +#if defined(AUX2_SERIAL) + if (mode == UART_MODE_SBUS_TRAINER) + return g_eeGeneral.aux2SerialMode != UART_MODE_SBUS_TRAINER; +#endif + return true; +} + +bool isAux2ModeAvailable(int mode) +{ +#if defined(AUX_SERIAL) + if (mode == UART_MODE_SBUS_TRAINER) + return g_eeGeneral.auxSerialMode != UART_MODE_SBUS_TRAINER; +#endif + return true; +} + bool isSwitchAvailableInLogicalSwitches(int swtch) { return isSwitchAvailable(swtch, LogicalSwitchesContext); diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index c7229f5c7..d1fdb8fa1 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -70,6 +70,8 @@ bool isSourceAvailableInGlobalFunctions(int source); bool isSourceAvailableInCustomSwitches(int source); bool isSourceAvailableInResetSpecialFunction(int index); bool isSourceAvailableInGlobalResetSpecialFunction(int index); +bool isAux1ModeAvailable(int mode); +bool isAux2ModeAvailable(int mode); bool isSwitchAvailableInLogicalSwitches(int swtch); bool isSwitchAvailableInCustomFunctions(int swtch); bool isSwitchAvailableInMixes(int swtch); diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index 88fc32cb8..e646c30d9 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -21,12 +21,16 @@ #include "opentx.h" #include "targets/horus/board.h" +#if defined(SBUS) +extern Fifo trainerSbusFifo; +#endif + #if defined(AUX_SERIAL) uint8_t auxSerialMode = 0; Fifo auxSerialTxFifo; AuxSerialRxFifo auxSerialRxFifo __DMA (AUX_SERIAL_DMA_Stream_RX); -void auxSerialSetup(unsigned int baudrate, bool dma) +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t lenght = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; @@ -49,9 +53,9 @@ void auxSerialSetup(unsigned int baudrate, bool dma) #endif USART_InitStructure.USART_BaudRate = baudrate; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_WordLength = lenght; + USART_InitStructure.USART_StopBits = stop; + USART_InitStructure.USART_Parity = parity; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(AUX_SERIAL_USART, &USART_InitStructure); @@ -123,6 +127,11 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) } break; + case UART_MODE_SBUS_TRAINER: + auxSerialSetup(SBUS_BAUDRATE, false, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b + AUX_SERIAL_POWER_ON(); + break; + case UART_MODE_LUA: auxSerialSetup(DEBUG_BAUDRATE, false); AUX_SERIAL_POWER_ON(); @@ -144,9 +153,7 @@ void auxSerialPutc(char c) void auxSerialSbusInit() { - auxSerialSetup(SBUS_BAUDRATE, true); - AUX_SERIAL_USART->CR1 |= USART_CR1_M | USART_CR1_PCE ; - AUX_SERIAL_POWER_ON(); + auxSerialInit(UART_MODE_SBUS_TRAINER, 0); } void auxSerialStop() @@ -201,10 +208,15 @@ extern "C" void AUX_SERIAL_USART_IRQHandler(void) uint32_t status = AUX_SERIAL_USART->SR; while (status & (USART_FLAG_RXNE | USART_FLAG_ERRORS)) { uint8_t data = AUX_SERIAL_USART->DR; + UNUSED(data); if (!(status & USART_FLAG_ERRORS)) { #if defined(LUA) & !defined(CLI) if (luaRxFifo && auxSerialMode == UART_MODE_LUA) luaRxFifo->push(data); +#endif +#if !defined(BOOT) + if (auxSerialMode == UART_MODE_SBUS_TRAINER) + trainerSbusFifo.push(data); #endif } status = AUX_SERIAL_USART->SR; @@ -218,7 +230,7 @@ uint8_t aux2SerialMode = 0; Fifo aux2SerialTxFifo; AuxSerialRxFifo aux2SerialRxFifo __DMA (AUX2_SERIAL_DMA_Stream_RX); -void aux2SerialSetup(unsigned int baudrate, bool dma) +void aux2SerialSetup(unsigned int baudrate, bool dma, uint16_t lenght = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; @@ -241,9 +253,9 @@ void aux2SerialSetup(unsigned int baudrate, bool dma) #endif USART_InitStructure.USART_BaudRate = baudrate; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_WordLength = lenght; + USART_InitStructure.USART_StopBits = stop; + USART_InitStructure.USART_Parity = parity; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(AUX2_SERIAL_USART, &USART_InitStructure); @@ -315,6 +327,11 @@ void aux2SerialInit(unsigned int mode, unsigned int protocol) } break; + case UART_MODE_SBUS_TRAINER: + aux2SerialSetup(SBUS_BAUDRATE, false, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b + AUX2_SERIAL_POWER_ON(); + break; + case UART_MODE_LUA: aux2SerialSetup(DEBUG_BAUDRATE, false); AUX2_SERIAL_POWER_ON(); @@ -336,9 +353,7 @@ void aux2SerialPutc(char c) void aux2SerialSbusInit() { - aux2SerialSetup(SBUS_BAUDRATE, true); - AUX2_SERIAL_USART->CR1 |= USART_CR1_M | USART_CR1_PCE ; - AUX2_SERIAL_POWER_ON(); + aux2SerialInit(UART_MODE_SBUS_TRAINER, 0); } void aux2SerialStop() @@ -394,10 +409,17 @@ extern "C" void AUX2_SERIAL_USART_IRQHandler(void) uint32_t status = AUX2_SERIAL_USART->SR; while (status & (USART_FLAG_RXNE | USART_FLAG_ERRORS)) { uint8_t data = AUX2_SERIAL_USART->DR; + UNUSED(data); if (!(status & USART_FLAG_ERRORS)) { -#if defined(LUA) - if (luaRxFifo && aux2SerialMode == UART_MODE_LUA) +#if defined(LUA) & !defined(CLI) + if (luaRxFifo && aux2SerialMode == UART_MODE_LUA) { luaRxFifo->push(data); + } +#endif +#if !defined(BOOT) + if (aux2SerialMode == UART_MODE_SBUS_TRAINER) { + trainerSbusFifo.push(data); + } #endif } status = AUX2_SERIAL_USART->SR; diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 20348968f..220bb5b9e 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -66,6 +66,7 @@ if (PCB STREQUAL X10) set(HARDWARE_TOUCH YES) set(USB_CHARGER YES) set(DEFAULT_THEME "DARKBLUE") + set(SBUS ON) set(AUX_SERIAL ON) if (NOT BLUETOOTH AND NOT INTERNAL_GPS) set(AUX2_SERIAL ON) @@ -254,6 +255,14 @@ if (AUX2_SERIAL) set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.cpp) endif() +if (SBUS) + add_definitions(-DSBUS) + set(SRC + ${SRC} + sbus.cpp + ) +endif() + set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} ${LCD_DRIVER} diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 0364e938f..e4e99d40f 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -197,6 +197,9 @@ void stop_trainer_ppm(); void init_trainer_capture(); void stop_trainer_capture(); +// SBUS +int sbusGetByte(uint8_t * byte); + // Keys driver enum EnumKeys { diff --git a/radio/src/targets/horus/trainer_driver.cpp b/radio/src/targets/horus/trainer_driver.cpp index d5b34cc1e..f039536ff 100644 --- a/radio/src/targets/horus/trainer_driver.cpp +++ b/radio/src/targets/horus/trainer_driver.cpp @@ -20,6 +20,10 @@ #include "opentx.h" +#if defined(SBUS) +Fifo trainerSbusFifo; +#endif + void trainerSendNextFrame(); void init_trainer_ppm() @@ -152,3 +156,15 @@ extern "C" void TRAINER_TIMER_IRQHandler() trainerSendNextFrame(); } } + +int sbusGetByte(uint8_t * byte) +{ + switch (currentTrainerMode) { +#if defined(AUX_SERIAL) + case TRAINER_MODE_MASTER_BATTERY_COMPARTMENT: + return trainerSbusFifo.pop(*byte); +#endif + default: + return false; + } +} \ No newline at end of file diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 0e7bd7a09..533d1bb33 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -107,7 +107,7 @@ TASK_FUNCTION(mixerTask) s_pulses_paused = true; while (true) { -#if defined(PCBTARANIS) && defined(SBUS) +#if defined(SBUS) && !defined(PCBSKY9X) // SBUS trainer processSbusInput(); #endif diff --git a/radio/src/trainer.cpp b/radio/src/trainer.cpp index 0af1901b7..27a237b54 100644 --- a/radio/src/trainer.cpp +++ b/radio/src/trainer.cpp @@ -115,9 +115,16 @@ void checkTrainerSettings() #if defined(TRAINER_BATTERY_COMPARTMENT) case TRAINER_MODE_MASTER_BATTERY_COMPARTMENT: +#if defined(AUX_SERIAL) if (g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER) auxSerialSbusInit(); else +#endif +#if defined(AUX2_SERIAL) + if (g_eeGeneral.aux2SerialMode == UART_MODE_SBUS_TRAINER) + aux2SerialSbusInit(); + else +#endif init_trainer_capture(); break; #endif From 9a639b3fb12d24d311f00351ec76e7bc7bfd1782 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Tue, 7 Jul 2020 14:56:40 +0100 Subject: [PATCH 069/267] Fix model setup menu for 9x (#7775) --- radio/src/gui/128x64/model_setup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index f6b1773fa..3dfca9645 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -416,6 +416,7 @@ void menuModelSetup(event_t event) LABEL(ExternalModule), MODULE_TYPE_ROWS(EXTERNAL_MODULE), + MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) From 4d6a8e063aeca989ba03469b1c5056ee69ceec45 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 7 Jul 2020 15:57:23 +0200 Subject: [PATCH 070/267] Bring back radio type on version screen (#7768) --- radio/src/gui/480x272/radio_version.cpp | 14 +++++++------- radio/src/gui/common/stdlcd/radio_version.cpp | 4 ++-- radio/src/opentx.h | 1 + radio/src/stamp.cpp | 1 + 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/radio/src/gui/480x272/radio_version.cpp b/radio/src/gui/480x272/radio_version.cpp index 33ab73607..63301619a 100644 --- a/radio/src/gui/480x272/radio_version.cpp +++ b/radio/src/gui/480x272/radio_version.cpp @@ -186,13 +186,13 @@ bool menuRadioVersion(event_t event) getCPUUniqueID(id); SIMPLE_MENU(STR_MENUVERSION, RADIO_ICONS, menuTabGeneral, MENU_RADIO_VERSION, 1); - - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, vers_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + FH, date_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 2*FH, time_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 3*FH, eeprom_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 4*FH, "UID:"); - lcdDrawText(MENUS_MARGIN_LEFT + 64, MENU_CONTENT_TOP + 4*FH, id); + lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, fw_stamp); + lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + FH, vers_stamp); + lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 2*FH, date_stamp); + lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 3*FH, time_stamp); + lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 4*FH, eeprom_stamp); + lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 5*FH, "UID:"); + lcdDrawText(MENUS_MARGIN_LEFT + 64, MENU_CONTENT_TOP + 5*FH, id); coord_t y = MENU_CONTENT_TOP + 5*FH; lcdDrawText(MENUS_MARGIN_LEFT, y, "OPTS:"); diff --git a/radio/src/gui/common/stdlcd/radio_version.cpp b/radio/src/gui/common/stdlcd/radio_version.cpp index ce6e30e59..d6128ecb7 100644 --- a/radio/src/gui/common/stdlcd/radio_version.cpp +++ b/radio/src/gui/common/stdlcd/radio_version.cpp @@ -222,7 +222,7 @@ void menuRadioModulesVersion(event_t event) enum MenuRadioVersionItems { ITEM_RADIO_VERSION_FIRST = HEADER_LINE - 1, -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBTARANIS) ITEM_RADIO_FIRMWARE_OPTIONS, #endif #if defined(PXX2) @@ -250,7 +250,7 @@ void menuRadioVersion(event_t event) y += 2; -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBTARANIS) lcdDrawText(INDENT_WIDTH, y, BUTTON(TR_FIRMWARE_OPTIONS), menuVerticalPosition == ITEM_RADIO_FIRMWARE_OPTIONS ? INVERS : 0); y += FH; if (menuVerticalPosition == ITEM_RADIO_FIRMWARE_OPTIONS && event == EVT_KEY_BREAK(KEY_ENTER)) { diff --git a/radio/src/opentx.h b/radio/src/opentx.h index d73027a6e..0d21d5df5 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -678,6 +678,7 @@ inline int calcRESXto100(int x) #if defined(COLORLCD) +extern const char fw_stamp[]; extern const char vers_stamp[]; extern const char date_stamp[]; extern const char time_stamp[]; diff --git a/radio/src/stamp.cpp b/radio/src/stamp.cpp index e8b67bdff..ad28e4037 100644 --- a/radio/src/stamp.cpp +++ b/radio/src/stamp.cpp @@ -45,6 +45,7 @@ #endif #if defined(COLORLCD) + const char fw_stamp[] = "FW" TAB ": opentx-" FLAVOUR; const char vers_stamp[] = "VERS" TAB ": " VERSION DISPLAY_VERSION " (" GIT_STR ")"; const char date_stamp[] = "DATE" TAB ": " DATE; const char time_stamp[] = "TIME" TAB ": " TIME; From 2bca354029d312eb063a70e595c4bfddbc96c69c Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 7 Jul 2020 17:04:43 +0200 Subject: [PATCH 071/267] Please Travis (#7802) --- radio/src/targets/horus/CMakeLists.txt | 6 +++--- radio/src/targets/horus/trainer_driver.cpp | 2 +- radio/src/targets/taranis/CMakeLists.txt | 1 + radio/src/targets/taranis/trainer_driver.cpp | 2 +- radio/src/tasks.cpp | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 220bb5b9e..525c8a4ad 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -66,7 +66,7 @@ if (PCB STREQUAL X10) set(HARDWARE_TOUCH YES) set(USB_CHARGER YES) set(DEFAULT_THEME "DARKBLUE") - set(SBUS ON) + set(SBUS_TRAINER ON) set(AUX_SERIAL ON) if (NOT BLUETOOTH AND NOT INTERNAL_GPS) set(AUX2_SERIAL ON) @@ -255,8 +255,8 @@ if (AUX2_SERIAL) set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.cpp) endif() -if (SBUS) - add_definitions(-DSBUS) +if (SBUS_TRAINER) + add_definitions(-DSBUS_TRAINER) set(SRC ${SRC} sbus.cpp diff --git a/radio/src/targets/horus/trainer_driver.cpp b/radio/src/targets/horus/trainer_driver.cpp index f039536ff..83ec7a542 100644 --- a/radio/src/targets/horus/trainer_driver.cpp +++ b/radio/src/targets/horus/trainer_driver.cpp @@ -20,7 +20,7 @@ #include "opentx.h" -#if defined(SBUS) +#if defined(SBUS_TRAINER) Fifo trainerSbusFifo; #endif diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 82dbcef91..b00c7dfba 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -335,6 +335,7 @@ endif() add_definitions(-DPCBTARANIS) add_definitions(-DAUDIO -DVOICE -DRTCLOCK) +add_definitions(-DSBUS_TRAINER) set(SRC ${SRC} diff --git a/radio/src/targets/taranis/trainer_driver.cpp b/radio/src/targets/taranis/trainer_driver.cpp index 06a006899..01f6e47bf 100644 --- a/radio/src/targets/taranis/trainer_driver.cpp +++ b/radio/src/targets/taranis/trainer_driver.cpp @@ -329,7 +329,7 @@ void stop_trainer_module_sbus() } #endif -#if defined(SBUS) +#if defined(SBUS_TRAINER) int sbusGetByte(uint8_t * byte) { switch (currentTrainerMode) { diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 533d1bb33..e9dcfac45 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -107,7 +107,7 @@ TASK_FUNCTION(mixerTask) s_pulses_paused = true; while (true) { -#if defined(SBUS) && !defined(PCBSKY9X) +#if defined(SBUS_TRAINER) // SBUS trainer processSbusInput(); #endif From 044c3bbf86a757afbb82ab3732ac48551851883a Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 8 Jul 2020 14:55:59 +0200 Subject: [PATCH 072/267] Multi R9/DSM (#7804) --- companion/src/modeledit/setup.cpp | 2 +- radio/src/gui/128x64/model_setup.cpp | 55 ++++++++++++++++++++------- radio/src/gui/212x64/model_setup.cpp | 51 +++++++++++++++++++------ radio/src/gui/480x272/model_setup.cpp | 54 +++++++++++++++++++------- radio/src/pulses/multi.cpp | 18 ++++----- radio/src/telemetry/spektrum.cpp | 53 +++++++++++++++----------- radio/src/telemetry/telemetry.h | 6 ++- 7 files changed, 167 insertions(+), 72 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 3fc74a3e7..90036bc57 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -570,7 +570,7 @@ void ModulePanel::update() ui->lowPower->setVisible(mask & MASK_MULTIMODULE); ui->autoBind->setVisible(mask & MASK_MULTIMODULE); if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2) - ui->autoBind->setText(tr("Autodetect Format")); + ui->autoBind->setVisible(false); else ui->autoBind->setText(tr("Bind on channel")); diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 3dfca9645..c8eda1387 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1541,18 +1541,35 @@ void menuModelSetup(event_t event) } } + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; - - if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); - else - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + optionValue = optionValue & 0x01; + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + } + else { + if (min == 0 && max == 1) + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } if (attr) { - int8_t min, max; - getMultiOptionValues(multi_proto, min, max); - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max); + CHECK_INCDEC_MODELVAR(event, optionValue, min, max); + if (checkIncDec_Ret) { + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFE) + optionValue; + } + else { + g_model.moduleData[moduleIdx].multi.optionValue = optionValue; + } + } } } #endif @@ -1659,10 +1676,22 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) - g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_DSM_AUTODTECT, attr, event); - else - g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_AUTOBIND, attr, event); + if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { + int8_t value = (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; + lcdDrawText(INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, value ? 11 : 22, attr); + lcdDrawText(lcdNextPos, y, "ms", attr); + if (attr) { + CHECK_INCDEC_MODELVAR(event, value, 0, 1); + if (checkIncDec_Ret) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFD) + (value << 1); + } + } + } + else { + g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, + STR_MULTI_AUTOBIND, attr, event); + } break; #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 829a7eee9..2f67a3dcb 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1306,17 +1306,35 @@ void menuModelSetup(event_t event) } } - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); - else - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + optionValue = optionValue & 0x01; + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + } + else { + if (min == 0 && max == 1) + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } if (attr) { - int8_t min, max; - getMultiOptionValues(multi_proto, min, max); - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max); + CHECK_INCDEC_MODELVAR(event, optionValue, min, max); + if (checkIncDec_Ret) { + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFE) + optionValue; + } + else { + g_model.moduleData[moduleIdx].multi.optionValue = optionValue; + } + } } } #endif @@ -1375,10 +1393,21 @@ void menuModelSetup(event_t event) #if defined (MULTIMODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_DSM_AUTODTECT, attr, event); - else + if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { + int8_t value = (g_model.moduleData[EXTERNAL_MODULE].multi.optionValue & 0x02) >> 1; + lcdDrawText(INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, value ? 11 : 22, attr); + lcdDrawText(lcdNextPos, y, "ms", attr); + if (attr) { + CHECK_INCDEC_MODELVAR(event, value, 0, 1); + if (checkIncDec_Ret) { + g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = (g_model.moduleData[EXTERNAL_MODULE].multi.optionValue & 0xFD) + (value << 1); + } + } + } + else { g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_AUTOBIND, attr, event); + } break; case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM: diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index dee079d96..99bd1492c 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1576,7 +1576,7 @@ bool menuModelSetup(event_t event) if (attr && l_posHorz>0) { if (s_editMode>0) { if (l_posHorz == 1) { - if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx)) { + if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || isModuleAFHDS3(moduleIdx) || IS_R9_MULTI(moduleIdx)) { if (event == EVT_KEY_BREAK(KEY_ENTER)) { #if defined(AFHDS3) if (isModuleAFHDS3(moduleIdx)) { @@ -1683,17 +1683,35 @@ bool menuModelSetup(event_t event) } } - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) + if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); - else - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + optionValue = optionValue & 0x01; + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, LEFT | attr, event); + } + else { + if (min == 0 && max == 1) + editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, LEFT | attr, event); + else + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); + } if (attr) { - int8_t min, max; - getMultiOptionValues(multi_proto, min, max); - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max); + CHECK_INCDEC_MODELVAR(event, optionValue, min, max); + if (checkIncDec_Ret) { + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFE) + optionValue; + } + else { + g_model.moduleData[moduleIdx].multi.optionValue = optionValue; + } + } } } #endif @@ -1761,11 +1779,21 @@ bool menuModelSetup(event_t event) case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: #endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_DSM_AUTODTECT); - else + if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { + int8_t value = (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, value ? 11 : 22, attr, 0, "", "ms"); + if (attr) { + CHECK_INCDEC_MODELVAR(event, value, 0, 1); + if (checkIncDec_Ret) { + g_model.moduleData[moduleIdx].multi.optionValue = (g_model.moduleData[moduleIdx].multi.optionValue & 0xFD) + (value << 1); + } + } + } + else { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_AUTOBIND); - g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); + g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); + } break; #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index cfcff15d0..c8170f217 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -178,7 +178,7 @@ void setupPulsesMulti(uint8_t moduleIdx) if (getMultiModuleStatus(moduleIdx).isValid()) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); if (status.minor >= 3 && !(status.flags & 0x80)) { //Version 1.3.x.x or more and Buffer not full - if (IS_D16_MULTI(moduleIdx) && moduleState[moduleIdx].mode == MODULE_MODE_BIND) { + if ((IS_D16_MULTI(moduleIdx) || IS_R9_MULTI(moduleIdx)) && moduleState[moduleIdx].mode == MODULE_MODE_BIND) { sendD16BindOption(moduleIdx);//1 byte of additional data } #if defined(LUA) @@ -313,15 +313,14 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) // rfProtocol if (type == MODULE_SUBTYPE_MULTI_DSM2 +1 ) { - // Autobinding should always be done in DSMX 11ms - if (g_model.moduleData[moduleIdx].multi.autoBindMode && moduleState[moduleIdx].mode == MODULE_MODE_BIND) - subtype = MM_RF_DSM2_SUBTYPE_AUTO; - - // Multi module in DSM mode wants the number of channels to be used as option value - if (optionValue) - optionValue = 0x80 | sentModuleChannels(moduleIdx); // Max throw + // Multi module in DSM mode wants the number of channels to be used as option value along with other flags + if (optionValue & 0x01) + optionValue = 0x80; // Max throw else - optionValue = sentModuleChannels(moduleIdx); + optionValue = 0x00; + if (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) + optionValue |= 0x40; // 11ms servo refresh + optionValue |= sentModuleChannels(moduleIdx); //add number of channels } // Special treatment for the FrSky entry... @@ -336,7 +335,6 @@ void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) if (g_model.moduleData[moduleIdx].getMultiProtocol() == MM_RF_CUSTOM_SELECTED) type = g_model.moduleData[moduleIdx].getMultiProtocol(); - uint8_t headerByte = 0x55; // header, byte 0, 0x55 for proto 0-31, 0x54 for proto 32-63 if (type & 0x20) diff --git a/radio/src/telemetry/spektrum.cpp b/radio/src/telemetry/spektrum.cpp index 60c9f2329..285cd8ac2 100644 --- a/radio/src/telemetry/spektrum.cpp +++ b/radio/src/telemetry/spektrum.cpp @@ -468,17 +468,17 @@ void processSpektrumPacket(const uint8_t *packet) // "I" here means the multi module /* -0-3 4 bytes -> Cyrf ID of the TX xor 0xFF but you don't care as I've checked it already... -4 1 byte -> RX version but you don't care... +0-3 4 bytes -> Cyrf ID of the TX xor 0xFF but don't care... +4 1 byte -> RX version but don't care... 5 1 byte -> number of channels, example 0x06=6 channels 6 1 byte -> max DSM type allowed: - 0x01 => 22ms 1024 DSM2 1 packet => number of channels is <8 and no telemetry - 0x02 => 22ms 1024 DSM2 2 packets => either a number of channel >7 or telemetry enable RX - 0x12 => 11ms 2048 DSM2 2 packets => can be any number of channels with/without telemetry -> this mode might be supported following Mike's trials, note the channels should be duplicated between the packets which is not the case today - 0xa2 => 22ms 2048 DSMX 1 packet => number of channels is <8 and no telemetry - 0xb2 => 11ms 2048 DSMX => can be any number of channels with/without telemetry -> this mode is only half supported since the channels should be duplicated between the packets which is not the case but might be supported following Mike's trials + 0x01 => 1024 DSM2 1 packet => number of channels is <8 and no telemetry + 0x02 => 1024 DSM2 2 packets => either a number of channel >7 or telemetry enable RX + 0x12 => 2048 DSM2 2 packets => can be any number of channels with/without telemetry -> this mode might be supported following Mike's trials, note the channels should be duplicated between the packets which is not the case today + 0xa2 => 2048 DSMX 1 packet => number of channels is <8 and no telemetry + 0xb2 => 2048 DSMX 2 packets => can be any number of channels with/without telemetry -> this mode is only half supported since the channels should be duplicated between the packets which is not the case but might be supported following Mike's trials 7 0x00: not sure of the use of this byte since I've always seen it at 0... -8-9 2 bytes CRC but you don't care as I've checked it already... +8-9 2 bytes CRC but don't care... Examples: DSM #Chan RXver Inductrix 0xa2 07 1 @@ -488,23 +488,32 @@ void processSpektrumPacket(const uint8_t *packet) void processDSMBindPacket(uint8_t module, const uint8_t *packet) { uint32_t debugval; - if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2 - && g_model.moduleData[module].multi.autoBindMode) { - + if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2 && g_model.moduleData[module].subType == MM_RF_DSM2_SUBTYPE_AUTO) { + // Only sets channel etc when in DSM/AUTO mode int channels = packet[5]; - // Only sets channel etc when in DSM multi mode + if (channels > 12) { + channels = 12; + } + else if (channels < 3) { + channels = 3; + } g_model.moduleData[module].channelsCount = channels - 8; - // bool use11ms = (packet[8] & 0x10) ; - if (packet[6] >= 0xb2) - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; - else if (packet[6] >= 0xa2) - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_22; - else if (packet[6] >= 0x12) - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; - else - g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_22; - + switch(packet[6]) { + case 0xa2: + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_22; + break; + case 0x12: + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; + break; + case 0x01: + case 0x02: + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_22; + break; + default: // 0xb2 or unknown + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; + break; + } storageDirty(EE_MODEL); } diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 74671bbd4..d35198cdc 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -112,16 +112,18 @@ extern uint8_t telemetryProtocol; #if defined (MULTIMODULE) #define IS_D16_MULTI(module) (((g_model.moduleData[module].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY) && (g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16 || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_8CH || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_LBT || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH || g_model.moduleData[module].subType == MM_RF_FRSKY_SUBTYPE_D16_CLONED)) \ || (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_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)) #if defined(HARDWARE_INTERNAL_MODULE) - #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && (IS_D16_MULTI(INTERNAL_MODULE)||IS_D16_MULTI(EXTERNAL_MODULE)))) + #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && (IS_D16_MULTI(INTERNAL_MODULE) || IS_D16_MULTI(EXTERNAL_MODULE) || IS_R9_MULTI(INTERNAL_MODULE) || IS_R9_MULTI(EXTERNAL_MODULE)))) #else - #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && IS_D16_MULTI(EXTERNAL_MODULE))) + #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT || (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE && (IS_D16_MULTI(EXTERNAL_MODULE) || IS_R9_MULTI(EXTERNAL_MODULE)))) #endif #else #define IS_D16_MULTI(module) false + #define IS_R9_MULTI(module) false #define IS_HOTT_MULTI(module) false #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) #define IS_RX_MULTI(module) false From 003c621694586a33840923398b100c52ad544d62 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 9 Jul 2020 14:09:14 +0200 Subject: [PATCH 073/267] Fix some multi option field --- radio/src/gui/128x64/model_setup.cpp | 2 +- radio/src/gui/212x64/model_setup.cpp | 2 +- radio/src/gui/480x272/model_setup.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index c8eda1387..4319ed8dc 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1550,7 +1550,7 @@ void menuModelSetup(event_t event) else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); } - if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); } diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 2f67a3dcb..36c7783bc 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1315,7 +1315,7 @@ void menuModelSetup(event_t event) else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); } - if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); } diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 99bd1492c..2d7ae7de1 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1692,7 +1692,7 @@ bool menuModelSetup(event_t event) else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); } - if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, LEFT | attr, event); } From c8c6a52bfd144ad2cf426465217d060b9a52fc83 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 9 Jul 2020 19:44:36 +0200 Subject: [PATCH 074/267] Fix companion not allowing internal multi edit for t12 --- companion/src/firmwares/opentx/opentxinterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 72734a4d5..75aab7b64 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -589,7 +589,7 @@ int OpenTxFirmware::getCapability(::Capability capability) case NumModules: return 2; case NumFirstUsableModule: - return (IS_JUMPER_T12(board) ? 1 : 0); + return (IS_JUMPER_T12(board) && !id.contains("internalmulti") ? 1 : 0); case HasModuleR9MFlex: return id.contains("flexr9m"); case HasModuleR9MMini: @@ -731,9 +731,9 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) return true; case PULSES_PXX_XJT_X16: case PULSES_PXX_XJT_LR12: - return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board); + return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_JUMPER_T12(board); case PULSES_PXX_XJT_D8: - return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board); + return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_JUMPER_T12(board); case PULSES_ACCESS_ISRM: case PULSES_ACCST_ISRM_D16: return IS_ACCESS_RADIO(board, id); From 37f64e7d9df87e9e01ae27a88d5c52cfbf51e9aa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 9 Jul 2020 22:10:33 +0200 Subject: [PATCH 075/267] Update CREDITS.txt --- CREDITS.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1ae419b6b..2d6eec3ba 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1807,3 +1807,13 @@ Karsten Schwarz Robert Mckay Michael Oeschger Randy Ling +Jonathan Wise +David Gardner +Christophe Judas +Lachlan Peters +Patrick C Kutschke +Thomas Halenbeck +Barry Wright +Nicolas Ducher +Giampiero Sintoni +Sidney B Harden From 15c9fa5afd471451551e2507b1edb05fdf53d31c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 10 Jul 2020 11:40:38 +0200 Subject: [PATCH 076/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 2d6eec3ba..704a7bec5 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1817,3 +1817,4 @@ Barry Wright Nicolas Ducher Giampiero Sintoni Sidney B Harden +John Moody From a5aca74aa59dbcb7e95aaab88ab849e9ec5ff911 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 10 Jul 2020 15:58:56 +0200 Subject: [PATCH 077/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 704a7bec5..0d2984b9a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1818,3 +1818,5 @@ Nicolas Ducher Giampiero Sintoni Sidney B Harden John Moody +Sylvain Cubizolles + From e80e5ff4029af8abecab75949dc6b6db085c0ea4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 11 Jul 2020 09:46:49 +0200 Subject: [PATCH 078/267] Update CREDITS.txt --- CREDITS.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CREDITS.txt b/CREDITS.txt index 0d2984b9a..3672053db 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1819,4 +1819,5 @@ Giampiero Sintoni Sidney B Harden John Moody Sylvain Cubizolles - +Adam Kouse +FCG Kas From fed0a0a8259008d23bc3a0760dd6e0d2421fffce Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 13 Jul 2020 10:36:10 +0200 Subject: [PATCH 079/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3672053db..0c0a21c7f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1821,3 +1821,5 @@ John Moody Sylvain Cubizolles Adam Kouse FCG Kas +Dennis Marinus +James Donelson From cab661879c9acec9f7fde1e2344c15e1df08976f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 13 Jul 2020 10:49:14 +0200 Subject: [PATCH 080/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0c0a21c7f..d4a300738 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1823,3 +1823,4 @@ Adam Kouse FCG Kas Dennis Marinus James Donelson +Stefan Hassler From fd166ae252a10f037ed93ae022b785a806c00040 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 13 Jul 2020 17:36:55 +0200 Subject: [PATCH 081/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d4a300738..cb19c3328 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1824,3 +1824,4 @@ FCG Kas Dennis Marinus James Donelson Stefan Hassler +Reinhard Prix From 387f938abde48d1baa4dea483c26338598c4aaea Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 15 Jul 2020 10:13:00 +0200 Subject: [PATCH 082/267] Update CREDITS.txt --- CREDITS.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index cb19c3328..0809943ea 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1825,3 +1825,7 @@ Dennis Marinus James Donelson Stefan Hassler Reinhard Prix +Jeffrey Johnson +Michael Conway +Ryan Buffington +Thomas Horns From d45475871d06b717a7ae682c48fc03dc9cae2d40 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 15 Jul 2020 17:09:54 +0200 Subject: [PATCH 083/267] Cosmetics --- radio/src/targets/horus/hal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 12b2a4630..dd8f6ec70 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -855,7 +855,7 @@ #define INTMODULE_HEARTBEAT #define INTMODULE_HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD #define INTMODULE_HEARTBEAT_GPIO GPIOD -#define INTMODULE_HEARTBEAT_GPIO_PIN GPIO_Pin_12 +#define INTMODULE_HEARTBEAT_GPIO_PIN GPIO_Pin_12 // PD.12 #define INTMODULE_HEARTBEAT_EXTI_PortSource EXTI_PortSourceGPIOD #define INTMODULE_HEARTBEAT_EXTI_PinSource GPIO_PinSource12 #define INTMODULE_HEARTBEAT_EXTI_LINE EXTI_Line12 From 9ef510d8485ab68f5b53789d9c42d478d60e1df3 Mon Sep 17 00:00:00 2001 From: pascallanger Date: Thu, 16 Jul 2020 09:27:56 +0200 Subject: [PATCH 084/267] Update DSM autobind behavior (#7820) Authored-by: Pascal Langer --- radio/src/telemetry/spektrum.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/radio/src/telemetry/spektrum.cpp b/radio/src/telemetry/spektrum.cpp index 285cd8ac2..aab694db0 100644 --- a/radio/src/telemetry/spektrum.cpp +++ b/radio/src/telemetry/spektrum.cpp @@ -497,7 +497,6 @@ void processDSMBindPacket(uint8_t module, const uint8_t *packet) else if (channels < 3) { channels = 3; } - g_model.moduleData[module].channelsCount = channels - 8; switch(packet[6]) { case 0xa2: @@ -505,6 +504,9 @@ void processDSMBindPacket(uint8_t module, const uint8_t *packet) break; case 0x12: g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; + if (channels == 7) { + channels = 12; // change the number of channels if 7 + } break; case 0x01: case 0x02: @@ -512,8 +514,15 @@ void processDSMBindPacket(uint8_t module, const uint8_t *packet) break; default: // 0xb2 or unknown g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; + if (channels == 7) { + channels = 12; // change the number of channels if 7 + } break; } + + g_model.moduleData[module].channelsCount = channels - 8; + g_model.moduleData[module].multi.optionValue &= 0xFD; // clear the 11ms servo refresh rate flag + storageDirty(EE_MODEL); } From a561ce815ec0179a7d5b401e9074db2226eeb267 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 17 Jul 2020 10:28:37 +0200 Subject: [PATCH 085/267] Fix slave PPM detail row missing when BT is not declared --- radio/src/gui/480x272/model_setup.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 2d7ae7de1..556b1f2bf 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -535,7 +535,6 @@ inline uint8_t MODULE_TYPE_ROWS(int moduleIdx) #if defined(BLUETOOTH) #define TRAINER_CHANNELS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)1 : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? (uint8_t)0 : HIDDEN_ROW)) -#define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW) #define IF_BT_TRAINER_ON(x) (g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER ? (uint8_t)(x) : HIDDEN_ROW) #define TRAINER_BLUETOOTH_M_ROW ((bluetooth.distantAddr[0] == '\0' || bluetooth.state == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1) #define TRAINER_BLUETOOTH_S_ROW (bluetooth.distantAddr[0] == '\0' ? HIDDEN_ROW : LABEL()) @@ -543,9 +542,9 @@ inline uint8_t MODULE_TYPE_ROWS(int moduleIdx) #define TRAINER_ROWS LABEL(Trainer), 0, IF_BT_TRAINER_ON(TRAINER_BLUETOOTH_ROW), TRAINER_CHANNELS_ROW, TRAINER_PPM_PARAMS_ROW #else #define TRAINER_CHANNELS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)1 : HIDDEN_ROW) -#define TRAINER_PPM_PARAMS_ROW (HIDDEN_ROW) #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_CHANNELS_ROW, TRAINER_PPM_PARAMS_ROW #endif +#define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW) #if defined(PXX2) #define REGISTRATION_ID_ROWS uint8_t((isDefaultModelRegistrationID() || (warningText && popupFunc == runPopupRegister)) ? HIDDEN_ROW : READONLY_ROW), From 5e4c301e78aaf7a0d028f06eed0faf1d4040f74f Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 17 Jul 2020 10:48:07 +0200 Subject: [PATCH 086/267] Fix popup title not been cleaned --- radio/src/gui/480x272/menus.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/gui/480x272/menus.h b/radio/src/gui/480x272/menus.h index 0e13b09b9..290ff43ac 100644 --- a/radio/src/gui/480x272/menus.h +++ b/radio/src/gui/480x272/menus.h @@ -562,6 +562,7 @@ inline void CLEAR_POPUP() { warningText = nullptr; warningInfoText = nullptr; + popupMenuTitle = nullptr; popupMenuHandler = nullptr; popupMenuItemsCount = 0; // TODO ? popupFunc = nullptr; From 6d640339ae1e7f4400a52fc094477d420427bb35 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 17 Jul 2020 11:14:02 +0200 Subject: [PATCH 087/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0809943ea..4a2579db2 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1829,3 +1829,5 @@ Jeffrey Johnson Michael Conway Ryan Buffington Thomas Horns +Michael Carnes +William Hallman From e662eece8c967cd03ab6f534ba28502488c2f13f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Jul 2020 14:05:51 +0200 Subject: [PATCH 088/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4a2579db2..fe4d6b420 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1831,3 +1831,5 @@ Ryan Buffington Thomas Horns Michael Carnes William Hallman +James Snodgrass +Michal Mesaros From 7a1feb0d9a5a566c48afda1eabfd1c7d47715fe3 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Mon, 20 Jul 2020 21:49:44 +0200 Subject: [PATCH 089/267] Fix some trainer/BT availability issues --- companion/src/firmwares/boards.h | 5 +++++ companion/src/generaledit/hardware.cpp | 2 +- companion/src/modeledit/setup.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index ed5ab9e25..9a703e492 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -328,6 +328,11 @@ inline bool HAS_EXTERNAL_ANTENNA(Board::Type board) return (board == Board::BOARD_X10 || board == Board::BOARD_HORUS_X12S || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board))); } +inline bool IS_TARANIS_X9DP_2019(Board::Type board) +{ + return (board == Board::BOARD_TARANIS_X9DP_2019); +} + inline bool IS_ACCESS_RADIO(Board::Type board, 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) || diff --git a/companion/src/generaledit/hardware.cpp b/companion/src/generaledit/hardware.cpp index 4f530aa78..78d0ec75d 100644 --- a/companion/src/generaledit/hardware.cpp +++ b/companion/src/generaledit/hardware.cpp @@ -172,7 +172,7 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings ui->txCurrentCalibrationLabel->hide(); } - if (IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_FAMILY_HORUS_OR_T16(board)) { + 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); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 90036bc57..898596a6c 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -206,7 +206,7 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul ui->label_module->setText(ModuleData::indexToString(moduleIdx, firmware)); if (moduleIdx < 0) { - if (!IS_TARANIS(firmware->getBoard())) { + if (!IS_TARANIS(firmware->getBoard()) || IS_ACCESS_RADIO(firmware->getBoard(), Firmware::getCurrentVariant()->getId())) { ui->trainerMode->setItemData(TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE, 0, Qt::UserRole - 1); ui->trainerMode->setItemData(TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE, 0, Qt::UserRole - 1); ui->trainerMode->setItemData(TRAINER_MODE_MASTER_BATTERY_COMPARTMENT, 0, Qt::UserRole - 1); From b896d7dab2915c4394042923cccf0ccc3e619969 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 21 Jul 2020 10:44:59 +0200 Subject: [PATCH 090/267] Add source --- radio/src/lua/api_general.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 99dd8aeab..3ee1bf1ae 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1742,6 +1742,7 @@ const luaR_value_entry opentxConstants[] = { { "VALUE", INPUT_TYPE_VALUE }, { "SOURCE", INPUT_TYPE_SOURCE }, { "REPLACE", MLTPX_REP }, + { "MIXSRC_MAX", MIXSRC_MAX }, { "MIXSRC_FIRST_INPUT", MIXSRC_FIRST_INPUT }, { "MIXSRC_Rud", MIXSRC_Rud }, { "MIXSRC_Ele", MIXSRC_Ele }, From 0b4619090078a09a322d5a3458ba6ee0b28f4bd4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 22 Jul 2020 11:56:33 +0200 Subject: [PATCH 091/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index fe4d6b420..c4998d2b4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1833,3 +1833,4 @@ Michael Carnes William Hallman James Snodgrass Michal Mesaros +James Lindner From da45929216c2925760634ea40cd31d6e2418d1da Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 23 Jul 2020 14:11:01 +0200 Subject: [PATCH 092/267] Fix companion compile issue --- companion/src/firmwares/boards.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 9a703e492..2ecf3c2fe 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -333,7 +333,7 @@ inline bool IS_TARANIS_X9DP_2019(Board::Type board) return (board == Board::BOARD_TARANIS_X9DP_2019); } -inline bool IS_ACCESS_RADIO(Board::Type board, QString &id) +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"))); From 9f203a70e424ce6e582224c742d023e1a03f3900 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 23 Jul 2020 19:31:59 +0200 Subject: [PATCH 093/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c4998d2b4..81e5cb199 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1834,3 +1834,4 @@ William Hallman James Snodgrass Michal Mesaros James Lindner +Laurence Gerrard From c3e5ea530356a38dee775e735bb8f0fc1e0954f2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 23 Jul 2020 23:28:09 +0200 Subject: [PATCH 094/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 81e5cb199..c5316e763 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1835,3 +1835,5 @@ James Snodgrass Michal Mesaros James Lindner Laurence Gerrard +Nico Wagner +Christoph Rössner From 1e19277291d3c6157334db8cf9f80498b4d63b40 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 24 Jul 2020 08:34:37 +0200 Subject: [PATCH 095/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c5316e763..9a6bc797e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1837,3 +1837,4 @@ James Lindner Laurence Gerrard Nico Wagner Christoph Rössner +Ruben Flores From ac35ae9ea4756ebf97614c890af5cdd3f7a6c519 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 26 Jul 2020 13:20:37 +0200 Subject: [PATCH 096/267] Fix SF alignement issue (#7846) --- radio/src/gui/480x272/model_special_functions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/gui/480x272/model_special_functions.cpp b/radio/src/gui/480x272/model_special_functions.cpp index fdc063dc9..d8876a35d 100644 --- a/radio/src/gui/480x272/model_special_functions.cpp +++ b/radio/src/gui/480x272/model_special_functions.cpp @@ -24,7 +24,7 @@ #define MODEL_SPECIAL_FUNC_2ND_COLUMN 120 #define MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT (lcdNextPos + 5) #define MODEL_SPECIAL_FUNC_3RD_COLUMN 295 -#define MODEL_SPECIAL_FUNC_4TH_COLUMN 440 +#define MODEL_SPECIAL_FUNC_4TH_COLUMN 463 #define MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF 450 void onCustomFunctionsFileSelectionMenu(const char * result) @@ -403,13 +403,13 @@ bool menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF else if (HAS_REPEAT_PARAM(func)) { if (active) CFN_PLAY_REPEAT(cfn) = checkIncDec(event, CFN_PLAY_REPEAT(cfn)==CFN_PLAY_REPEAT_NOSTART?-1:CFN_PLAY_REPEAT(cfn), -1, 60/CFN_PLAY_REPEAT_MUL, eeFlags); if (CFN_PLAY_REPEAT(cfn) == 0) { - lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN+2, y, "1x", attr); + lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN, y, "1x", attr|RIGHT); } else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { - lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN-1, y, "!1x", attr); + lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN, y, "!1x", attr|RIGHT); } else { - lcdDrawNumber(MODEL_SPECIAL_FUNC_4TH_COLUMN+12, y, CFN_PLAY_REPEAT(cfn)*CFN_PLAY_REPEAT_MUL, attr|RIGHT, 0, NULL, "s"); + lcdDrawNumber(MODEL_SPECIAL_FUNC_4TH_COLUMN, y, CFN_PLAY_REPEAT(cfn)*CFN_PLAY_REPEAT_MUL, attr|RIGHT, 0, NULL, "s"); } } else if (attr) { From 9dd8ca8e2508c0f5ab63364dd63be58923b69eee Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 27 Jul 2020 09:36:58 +0200 Subject: [PATCH 097/267] Add missing option display --- radio/src/options.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/options.h b/radio/src/options.h index b9d2a0075..741c38b77 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -41,6 +41,9 @@ static const char * const options[] = { #if defined(MODULE_PROTOCOL_FLEX) "flexr9m", #endif +#if defined(AFHDS3) + "afhds3", +#endif #if !defined(GVARS) "nogvars", #endif From 02c4886525a6dfcbe49020d531faaee0ebdcbfd1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 28 Jul 2020 19:21:33 +0200 Subject: [PATCH 098/267] Move to proper I2C handling (#7848) * Rework I2C handling --- radio/src/gui/480x272/radio_diaganas.cpp | 5 +- radio/src/targets/horus/tp_gt911.cpp | 642 ++++++++++++++--------- radio/src/targets/horus/tp_gt911.h | 227 +------- 3 files changed, 408 insertions(+), 466 deletions(-) diff --git a/radio/src/gui/480x272/radio_diaganas.cpp b/radio/src/gui/480x272/radio_diaganas.cpp index ae452f7ae..19e50914f 100644 --- a/radio/src/gui/480x272/radio_diaganas.cpp +++ b/radio/src/gui/480x272/radio_diaganas.cpp @@ -19,7 +19,8 @@ */ #include "opentx.h" -#if defined(RADIO_TX16S) +#if defined(HARDWARE_TOUCH) +#include "touch.h" #include "tp_gt911.h" #include #endif @@ -71,7 +72,7 @@ bool menuRadioDiagAnalogs(event_t event) lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 7 * FH, STR_TOUCH_PANEL); } - if (touchPanelEvent) { + if (touchPanelEventOccured()) { touchPanelRead(); lcdDrawNumber(lcdNextPos + 1, MENU_CONTENT_TOP + 7 * FH, touchState.x); lcdDrawText(lcdNextPos, MENU_CONTENT_TOP + 7 * FH, ", "); diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index 5c4929e95..3bb3ec21d 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -18,54 +18,204 @@ * GNU General Public License for more details. */ +#include "opentx.h" #include "tp_gt911.h" +#include "touch.h" + +//GT911 param table +const uint8_t TOUCH_GT911_Cfg[] = + { + GT911_CFG_NUMER, // 0x8047 Config version + 0xE0, // 0x8048 X output map : x 480 + 0x01, + 0x10, // 0x804A Y ouptut max : y 272 + 0x01, + GT911_MAX_TP, // 0x804C Touch number + 0x3C, // 0x804D Module switch 1 : bit4= xy change Int mode + 0x20, // 0x804E Module switch 2 + 0x22, // 0x804F Shake_Count + 0x0A, // 0x8050 Filter + 0x28, // 0x8051 Larger touch + 0x0F, // 0x8052 Noise reduction + 0x5A, // 0x8053 Screen touch level + 0x3C, // 0x8054 Screen touch leave + 0x03, // 0x8055 Low power control + 0x05, // 0x8056 Refresh rate + 0x00, // 0x8057 X threshold + 0x00, // 0x8058 Y threshold + 0x00, // 0x8059 Reserved + 0x00, // 0x805A Reserved + 0x11, // 0x805B Space (top, bottom) + 0x11, // 0x805C Space (left, right) + 0x08, // 0x805D Mini filter + 0x18, // 0x805E Strech R0 + 0x1A, // 0x805F Strech R1 + 0x1E, // 0x8060 Strech R2 + 0x14, // 0x8061 Strech RM + 0x87, // 0x8062 Drv groupA num + 0x29, // 0x8063 Drv groupB num + 0x0A, // 0x8064 Sensor num + 0xCF, // 0x8065 FreqA factor + 0xD1, // 0x8066 FreqB factor + 0xB2, // 0x8067 Panel bit freq + 0x04, + 0x00, // 0x8069 Reserved + 0x00, + 0x00, // 0x806B Panel tx gain + 0xD8, // 0x806C Panel rx gain + 0x02, // 0x806D Panel dump shift + 0x1D, // 0x806E Drv frame control + 0x00, // 0x806F Charging level up + 0x01, // 0x8070 Module switch 3 + 0x00, // 0x8071 Gesture distance + 0x00, // 0x8072 Gesture long press time + 0x00, // 0x8073 X/Y slope adjust + 0x00, // 0x8074 Gesture control + 0x00, // 0x8075 Gesture switch 1 + 0x00, // 0x8076 Gesture switch 2 + 0x00, // 0x8077 Gesture refresh rate + 0x00, // 0x8078 Gesture touch level + 0x00, // 0x8079 New green wake up level + 0xB4, // 0x807A Freq hopping start + 0xEF, // 0x807B Freq hopping end + 0x94, // 0x807C Noise detect time + 0xD5, // 0x807D Hopping flag + 0x02, // 0x807E Hopping flag + 0x07, // 0x807F Noise threshold + 0x00, // 0x8080 Nois min threshold old + 0x00, // 0x8081 Reserved + 0x04, // 0x8082 Hopping sensor group + 0x6E, // 0x8083 Hopping Seg1 normalize + 0xB9, // 0x8084 Hopping Seg1 factor + 0x00, // 0x8085 Main clock adjust + 0x6A, // 0x8086 Hopping Seg2 normalize + 0xC4, // 0x8087 Hopping Seg2 factor + 0x00, // 0x8088 Reserved + 0x66, // 0x8089 Hopping Seg3 normalize + 0xCF, // 0x808A Hopping Seg3 factor + 0x00, // 0x808B Reserved + 0x62, // 0x808C Hopping Seg4 normalize + 0xDB, // 0x808D Hopping Seg4 factor + 0x00, // 0x808E Reserved + 0x5E, // 0x808F Hopping Seg5 normalize + 0xE8, // 0x8090 Hopping Seg5 factor + 0x00, // 0x8091 Reserved + 0x5E, // 0x8092 Hopping Seg6 normalize + 0x00, // 0x8093 Key1 + 0x00, // 0x8094 Key2 + 0x00, // 0x8095 Key3 + 0x00, // 0x8096 Key4 + 0x00, // 0x8097 Key area + 0x00, // 0x8098 Key touch level + 0x00, // 0x8099 Key leave level + 0x00, // 0x809A Key sens + 0x00, // 0x809B Key sens + 0x00, // 0x809C Key restrain + 0x00, // 0x809D Key restrain time + 0x00, // 0x809E Large gesture touch + 0x00, // 0x809F Reserved + 0x00, // 0x80A0 Reserved + 0x00, // 0x80A1 Hotknot noise map + 0x00, // 0x80A2 Link threshold + 0x00, // 0x80A3 Pxy threshold + 0x00, // 0x80A4 GHot dump shift + 0x00, // 0x80A5 GHot rx gain + 0x00, // 0x80A6 Freg gain + 0x00, // 0x80A7 Freg gain 1 + 0x00, // 0x80A8 Freg gain 2 + 0x00, // 0x80A9 Freg gain 3 + 0x00, // 0x80AA Reserved + 0x00, // 0x80AB Reserved + 0x00, // 0x80AC Reserved + 0x00, // 0x80AD Reserved + 0x00, // 0x80AE Reserved + 0x00, // 0x80AF Reserved + 0x00, // 0x80B0 Reserved + 0x00, // 0x80B1 Reserved + 0x00, // 0x80B2 Reserved + 0x00, // 0x80B3 Combine dis + 0x00, // 0x80B4 Split set + 0x00, // 0x80B5 Reserved + 0x00, // 0x80B6 Reserved + 0x14, // 0x80B7 Sensor CH0 + 0x12, // 0x80B8 Sensor CH1 + 0x10, // 0x80B9 Sensor CH2 + 0x0E, // 0x80BA Sensor CH3 + 0x0C, // 0x80BB Sensor CH4 + 0x0A, // 0x80BC Sensor CH5 + 0x08, // 0x80BD Sensor CH6 + 0x06, // 0x80BE Sensor CH7 + 0x04, // 0x80BF Sensor CH8 + 0x02, // 0x80C0 Sensor CH9 + 0xFF, // 0x80C1 Sensor CH10 + 0xFF, // 0x80C2 Sensor CH11 + 0xFF, // 0x80C3 Sensor CH12 + 0xFF, // 0x80C4 Sensor CH13 + 0x00, // 0x80C5 Reserved + 0x00, // 0x80C6 Reserved + 0x00, // 0x80C7 Reserved + 0x00, // 0x80C8 Reserved + 0x00, // 0x80C9 Reserved + 0x00, // 0x80CA Reserved + 0x00, // 0x80CB Reserved + 0x00, // 0x80CC Reserved + 0x00, // 0x80CD Reserved + 0x00, // 0x80CE Reserved + 0x00, // 0x80CF Reserved + 0x00, // 0x80D0 Reserved + 0x00, // 0x80D1 Reserved + 0x00, // 0x80D2 Reserved + 0x00, // 0x80D3 Reserved + 0x00, // 0x80D4 Reserved + 0x28, // 0x80D5 Driver CH0 + 0x26, // 0x80D6 Driver CH1 + 0x24, // 0x80D7 Driver CH2 + 0x22, // 0x80D8 Driver CH3 + 0x21, // 0x80D9 Driver CH4 + 0x20, // 0x80DA Driver CH5 + 0x1F, // 0x80DB Driver CH6 + 0x1E, // 0x80DC Driver CH7 + 0x1D, // 0x80DD Driver CH8 + 0x0C, // 0x80DE Driver CH9 + 0x0A, // 0x80DF Driver CH10 + 0x08, // 0x80E0 Driver CH11 + 0x06, // 0x80E1 Driver CH12 + 0x04, // 0x80E2 Driver CH13 + 0x02, // 0x80E3 Driver CH14 + 0x00, // 0x80E4 Driver CH15 + 0xFF, // 0x80E5 Driver CH16 + 0xFF, // 0x80E6 Driver CH17 + 0xFF, // 0x80E7 Driver CH18 + 0xFF, // 0x80E8 Driver CH19 + 0xFF, // 0x80E9 Driver CH20 + 0xFF, // 0x80EA Driver CH21 + 0xFF, // 0x80EB Driver CH22 + 0xFF, // 0x80EC Driver CH23 + 0xFF, // 0x80ED Driver CH24 + 0xFF, // 0x80EE Driver CH25 + 0x00, // 0x80EF Reserved + 0x00, // 0x80F0 Reserved + 0x00, // 0x80F1 Reserved + 0x00, // 0x80F2 Reserved + 0x00, // 0x80F3 Reserved + 0x00, // 0x80F4 Reserved + 0x00, // 0x80F5 Reserved + 0x00, // 0x80F6 Reserved + 0x00, // 0x80F7 Reserved + 0x00, // 0x80F8 Reserved + 0x00, // 0x80F9 Reserved + 0x00, // 0x80FA Reserved + 0x00, // 0x80FB Reserved + 0x00, // 0x80FC Reserved + 0x00, // 0x80FD Reserved + 0x00 // 0x80FE Reserved + }; uint8_t touchGT911Flag = 0; -uint8_t touchPanelEvent = 0; +uint8_t touchEventOccured = 0; struct TouchData touchData; struct TouchState touchState; -void GT911_INT_Change(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - - GPIO_InitStructure.GPIO_Pin = TOUCH_INT_GPIO_PIN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_Init(TOUCH_INT_GPIO, &GPIO_InitStructure); -} - -uint8_t GT911_Send_Cfg(uint8_t mode) -{ - uint8_t buf[2]; - uint8_t i = 0; - buf[0] = 0; - buf[1] = mode; - for (i = 0; i < sizeof(GT911_Cfg); i++) - buf[0] += GT911_Cfg[i];//check sum - - buf[0] = (~buf[0]) + 1; - gt911WriteRegister(GT_CFGS_REG, (uint8_t *) GT911_Cfg, sizeof(GT911_Cfg));// - gt911WriteRegister(GT_CHECK_REG, buf, 2);//write checksum - return 0; -} - -static void I2C_Config(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - RCC_AHB1PeriphClockCmd(I2C_RCC_AHB1Periph, ENABLE); - GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(I2C_GPIO, &GPIO_InitStructure); - - GPIO_SetBits(I2C_GPIO, I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN); -} - static void TOUCH_AF_ExtiStop(void) { SYSCFG_EXTILineConfig(TOUCH_INT_EXTI_PortSource, TOUCH_INT_EXTI_PinSource1); @@ -99,7 +249,6 @@ static void TOUCH_AF_ExtiConfig(void) EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); - NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TOUCH_INT_EXTI_IRQn1; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; @@ -130,174 +279,158 @@ static void TOUCH_AF_GPIOConfig(void) GPIO_ResetBits(TOUCH_INT_GPIO, TOUCH_INT_GPIO_PIN); } -void SDA_IN(void) +void TOUCH_AF_INT_Change(void) { GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PIN; + + GPIO_InitStructure.GPIO_Pin = TOUCH_INT_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(I2C_GPIO, &GPIO_InitStructure); + GPIO_Init(TOUCH_INT_GPIO, &GPIO_InitStructure); } -void SDA_OUT(void) +void I2C_Init() { + I2C_DeInit(I2C); + GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + I2C_InitTypeDef I2C_InitStructure; + I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; + I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; + I2C_InitStructure.I2C_OwnAddress1 = 0x00; + I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; + I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; + I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; + I2C_Init(I2C, &I2C_InitStructure); + I2C_Cmd(I2C, ENABLE); + + GPIO_PinAFConfig(I2C_GPIO, I2C_SCL_GPIO_PinSource, I2C_GPIO_AF); + GPIO_PinAFConfig(I2C_GPIO, I2C_SDA_GPIO_PinSource, I2C_GPIO_AF); + + GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(I2C_GPIO, &GPIO_InitStructure); } -void I2C_Start(void) +bool I2C_WaitEvent(uint32_t event) { - SDA_OUT(); - I2C_SDA_H(); - I2C_SCL_H(); - delay_us(2); - I2C_SDA_L();//START:when CLK is high,DATA change form high to low - delay_us(2); - I2C_SCL_L();// - delay_us(2); -} - -void I2C_Stop(void) -{ - SDA_OUT(); - I2C_SCL_L(); - I2C_SDA_L();//STOP:when CLK is high DATA change form low to high - delay_us(2); - I2C_SCL_H(); - I2C_SDA_H();// - delay_us(2); -} - -uint8_t I2C_Wait_Ack(void) -{ - uint8_t ucErrTime = 0; - SDA_IN(); - I2C_SDA_H(); - delay_us(2); - I2C_SCL_H(); - while (READ_SDA) { - ucErrTime++; - if (ucErrTime > 166) { - I2C_Stop(); - return 1; - } - delay_us(1); + uint32_t timeout = I2C_TIMEOUT_MAX; + while (!I2C_CheckEvent(I2C, event)) { + if ((timeout--) == 0) return false; } - I2C_SDA_L(); - I2C_SCL_L(); + return true; +} + +bool I2C_WaitEventCleared(uint32_t event) +{ + uint32_t timeout = I2C_TIMEOUT_MAX; + while (I2C_CheckEvent(I2C, event)) { + if ((timeout--) == 0) return false; + } + return true; +} + +uint8_t I2C_GT911_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) +{ + if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) + return false; + + I2C_GenerateSTART(I2C, ENABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) + return false; + + I2C_Send7bitAddress(I2C, GT_CMD_WR, I2C_Direction_Transmitter); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) + return false; + + I2C_SendData(I2C, (uint8_t)((reg & 0xFF00) >> 8)); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) + return false; + I2C_SendData(I2C, (uint8_t)(reg & 0x00FF)); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) + return false; + + /* While there is data to be written */ + while (len--) { + I2C_SendData(I2C, *buf); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) + return false; + buf++; + } + + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) + return false; + + I2C_GenerateSTOP(I2C, ENABLE); + return true; +} + +bool I2C_GT911_ReadRegister(u16 reg, uint8_t * buf, uint8_t len) +{ + if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) + return false; + + I2C_GenerateSTART(I2C, ENABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) + return false; + + I2C_Send7bitAddress(I2C, GT_CMD_WR, I2C_Direction_Transmitter); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) + return false; + + I2C_SendData(I2C, (uint8_t)((reg & 0xFF00) >> 8)); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) + return false; + I2C_SendData(I2C, (uint8_t)(reg & 0x00FF)); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) + return false; + + I2C_GenerateSTART(I2C, ENABLE); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) + return false; + + I2C_Send7bitAddress(I2C, GT_CMD_RD, I2C_Direction_Receiver); + if (!I2C_WaitEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) + return false; + + if (len > 1) { + I2C_AcknowledgeConfig(I2C, ENABLE); + } + + while (len) { + if (len == 1) { + I2C_AcknowledgeConfig(I2C, DISABLE); + } + if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_RECEIVED)) + return false; + *buf++ = I2C_ReceiveData(I2C); + len--; + } + + I2C_GenerateSTOP(I2C, ENABLE); + return true; +} + +uint8_t I2C_GT911_SendConfig(uint8_t mode) +{ + uint8_t buf[2]; + uint8_t i = 0; + buf[0] = 0; + buf[1] = mode; + for (i = 0; i < sizeof(TOUCH_GT911_Cfg); i++) + buf[0] += TOUCH_GT911_Cfg[i];//check sum + + buf[0] = (~buf[0]) + 1; + I2C_GT911_WriteRegister(GT_CFGS_REG, (uint8_t *) TOUCH_GT911_Cfg, sizeof(TOUCH_GT911_Cfg));// + I2C_GT911_WriteRegister(GT_CHECK_REG, buf, 2);//write checksum return 0; } -void I2C_Ack(void) -{ - I2C_SCL_L(); - SDA_OUT(); - I2C_SDA_L(); - delay_us(2); - I2C_SCL_H(); - delay_us(2); - I2C_SCL_L(); -} - -void I2C_NAck(void) -{ - I2C_SCL_L(); - SDA_OUT(); - I2C_SDA_H(); - delay_us(2); - I2C_SCL_H(); - delay_us(2); - I2C_SCL_L(); -} - -void I2C_Send_Byte(uint8_t txd) -{ - uint8_t t; - - SDA_OUT(); - I2C_SCL_L(); - for (t = 0; t < 8; t++) { - if ((txd & 0x80) >> 7) - I2C_SDA_H(); - else - I2C_SDA_L(); - txd <<= 1; - delay_us(1); - I2C_SCL_H(); - delay_us(2); - I2C_SCL_L(); - delay_us(1); - } -} - -uint8_t I2C_Read_Byte(unsigned char ack) -{ - unsigned char i, receive = 0; - - SDA_IN(); - for (i = 0; i < 8; i++) { - I2C_SCL_L(); - delay_us(2); - I2C_SCL_H(); - receive <<= 1; - if (READ_SDA)receive++; - delay_us(1); - } - if (!ack) - I2C_NAck(); - else - I2C_Ack(); - return receive; -} - -uint8_t gt911WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) -{ - { - uint8_t i; - uint8_t ret = 0; - - I2C_Start(); - I2C_Send_Byte(GT_CMD_WR); //send cmd - I2C_Wait_Ack(); - I2C_Send_Byte(reg >> 8); //send hi - I2C_Wait_Ack(); - I2C_Send_Byte(reg & 0XFF); //send low - I2C_Wait_Ack(); - for (i = 0; i < len; i++) { - I2C_Send_Byte(buf[i]); - ret = I2C_Wait_Ack(); - if (ret)break; - } - I2C_Stop(); - - return ret; - } -} - -void gt911ReadRegister(u16 reg, uint8_t * buf, uint8_t len) -{ - uint8_t i; - - I2C_Start(); - I2C_Send_Byte(GT_CMD_WR); //send addr - I2C_Wait_Ack(); - I2C_Send_Byte(reg >> 8); //send hi - I2C_Wait_Ack(); - I2C_Send_Byte(reg & 0XFF); //send low - I2C_Wait_Ack(); - I2C_Start(); - I2C_Send_Byte(GT_CMD_RD); //send rd - I2C_Wait_Ack(); - - for (i = 0; i < len; i++) { - buf[i] = I2C_Read_Byte(i == (len - 1) ? 0 : 1); //send data - } - I2C_Stop(); -} - void touchPanelDeInit(void) { TOUCH_AF_ExtiStop(); @@ -307,61 +440,70 @@ bool touchPanelInit(void) { uint8_t tmp[4] = {0}; - TRACE("Touchpanel init start ..."); - - TOUCH_AF_GPIOConfig(); //SET RST=OUT INT=OUT INT=LOW - I2C_Config(); - - TPRST_LOW(); - TPINT_HIGH(); - delay_us(200); - - TPRST_HIGH(); - delay_ms(6); - - TPINT_LOW(); - delay_ms(55); - - GT911_INT_Change(); //Set INT INPUT INT=LOW - - delay_ms(50); - - TRACE("Reading Touch registry"); - gt911ReadRegister(GT_PID_REG, tmp, 4); - - if (strcmp((char *) tmp, "911") == 0) //ID==9147 - { - TRACE("GT911 chip detected"); - tmp[0] = 0X02; - gt911WriteRegister(GT_CTRL_REG, tmp, 1); - gt911ReadRegister(GT_CFGS_REG, tmp, 1); - - TRACE("Chip config Ver:%x\r\n",tmp[0]); - if (tmp[0] < GT911_CFG_NUMER) //Config ver - { - TRACE("Sending new config %d", GT911_CFG_NUMER); - GT911_Send_Cfg(1); - } - - delay_ms(10); - tmp[0] = 0X00; - gt911WriteRegister(GT_CTRL_REG, tmp, 1); //end reset - touchGT911Flag = true; - + if (touchGT911Flag) { TOUCH_AF_ExtiConfig(); - return true; } - TRACE("GT911 chip NOT FOUND"); - return false; + else { + TRACE("Touchpanel init start ..."); + + TOUCH_AF_GPIOConfig(); //SET RST=OUT INT=OUT INT=LOW + I2C_Init(); + + TPRST_LOW(); + TPINT_HIGH(); + delay_us(200); + + TPRST_HIGH(); + delay_ms(6); + + TPINT_LOW(); + delay_ms(55); + + TOUCH_AF_INT_Change(); //Set INT INPUT INT=LOW + + delay_ms(50); + + TRACE("Reading Touch registry"); + I2C_GT911_ReadRegister(GT_PID_REG, tmp, 4); + + if (strcmp((char *) tmp, "911") == 0) //ID==9147 + { + TRACE("GT911 chip detected"); + tmp[0] = 0X02; + I2C_GT911_WriteRegister(GT_CTRL_REG, tmp, 1); + I2C_GT911_ReadRegister(GT_CFGS_REG, tmp, 1); + + TRACE("Chip config Ver:%x\r\n", tmp[0]); + if (tmp[0] < GT911_CFG_NUMER) //Config ver + { + TRACE("Sending new config %d", GT911_CFG_NUMER); + I2C_GT911_SendConfig(1); + } + + delay_ms(10); + tmp[0] = 0X00; + I2C_GT911_WriteRegister(GT_CTRL_REG, tmp, 1); //end reset + touchGT911Flag = true; + + TOUCH_AF_ExtiConfig(); + + return true; + } + TRACE("GT911 chip NOT FOUND"); + return false; + } } void touchPanelRead() { uint8_t state = 0; - gt911ReadRegister(GT911_READ_XY_REG, &state, 1); - touchPanelEvent = false; + if (!touchEventOccured) + return; + + touchEventOccured = false; + I2C_GT911_ReadRegister(GT911_READ_XY_REG, &state, 1); if ((state & 0x80u) == 0x00) { // not ready @@ -371,7 +513,7 @@ void touchPanelRead() uint8_t pointsCount = (state & 0x0Fu); if (pointsCount > 0 && pointsCount < GT911_MAX_TP) { - gt911ReadRegister(GT911_READ_XY_REG + 1, touchData.data, pointsCount * sizeof(TouchPoint)); + I2C_GT911_ReadRegister(GT911_READ_XY_REG + 1, touchData.data, pointsCount * sizeof(TouchPoint)); if (touchData.pointsCount == 0) { touchState.event = TE_DOWN; touchState.startX = touchState.x = touchData.points[0].x; @@ -399,14 +541,22 @@ void touchPanelRead() } uint8_t zero = 0; - gt911WriteRegister(GT911_READ_XY_REG, &zero, 1); + I2C_GT911_WriteRegister(GT911_READ_XY_REG, &zero, 1); } extern "C" void TOUCH_INT_EXTI_IRQHandler1(void) { if (EXTI_GetITStatus(TOUCH_INT_EXTI_LINE1) != RESET) { - TRACE("TI"); - touchPanelEvent = 1; + if (g_eeGeneral.backlightMode & e_backlight_mode_keys) { + // on touch turn the light on + resetBacklightTimeout(); + } + touchEventOccured = 1; EXTI_ClearITPendingBit(TOUCH_INT_EXTI_LINE1); } } + +bool touchPanelEventOccured() +{ + return touchEventOccured; +} \ No newline at end of file diff --git a/radio/src/targets/horus/tp_gt911.h b/radio/src/targets/horus/tp_gt911.h index 2b5f7d32c..0f9aa98ed 100644 --- a/radio/src/targets/horus/tp_gt911.h +++ b/radio/src/targets/horus/tp_gt911.h @@ -21,218 +21,22 @@ #ifndef _FT5X06_H_ #define _FT5X06_H_ -#include "opentx.h" -#include "touch.h" - #define HAS_TOUCH_PANEL() touchGT911Flag == true -bool touchPanelInit(); -void touchPanelDeInit(); - extern uint8_t touchGT911Flag; -extern uint8_t touchPanelEvent; +extern bool touchPanelInit(void); +extern void touchPanelDeInit(void); -uint8_t gt911WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len); -void gt911ReadRegister(uint16_t reg, uint8_t * buf, uint8_t len); void touchPanelRead(); +bool touchPanelEventOccured(); -#define GT911_MAX_TP 5 -#define GT911_CFG_NUMER 0x6A - -//GT911 param table -const uint8_t GT911_Cfg[] = -{ - GT911_CFG_NUMER, // 0x8047 Config version - 0xE0, // 0x8048 X output map : x 480 - 0x01, - 0x10, // 0x804A Y ouptut max : y 272 - 0x01, - GT911_MAX_TP, // 0x804C Touch number - 0x3C, // 0x804D Module switch 1 : bit4= xy change Int mode - 0x20, // 0x804E Module switch 2 - 0x22, // 0x804F Shake_Count - 0x0A, // 0x8050 Filter - 0x28, // 0x8051 Larger touch - 0x0F, // 0x8052 Noise reduction - 0x5A, // 0x8053 Screen touch level - 0x3C, // 0x8054 Screen touch leave - 0x03, // 0x8055 Low power control - 0x05, // 0x8056 Refresh rate - 0x00, // 0x8057 X threshold - 0x00, // 0x8058 Y threshold - 0x00, // 0x8059 Reserved - 0x00, // 0x805A Reserved - 0x11, // 0x805B Space (top, bottom) - 0x11, // 0x805C Space (left, right) - 0x08, // 0x805D Mini filter - 0x18, // 0x805E Strech R0 - 0x1A, // 0x805F Strech R1 - 0x1E, // 0x8060 Strech R2 - 0x14, // 0x8061 Strech RM - 0x87, // 0x8062 Drv groupA num - 0x29, // 0x8063 Drv groupB num - 0x0A, // 0x8064 Sensor num - 0xCF, // 0x8065 FreqA factor - 0xD1, // 0x8066 FreqB factor - 0xB2, // 0x8067 Panel bit freq - 0x04, - 0x00, // 0x8069 Reserved - 0x00, - 0x00, // 0x806B Panel tx gain - 0xD8, // 0x806C Panel rx gain - 0x02, // 0x806D Panel dump shift - 0x1D, // 0x806E Drv frame control - 0x00, // 0x806F Charging level up - 0x01, // 0x8070 Module switch 3 - 0x00, // 0x8071 Gesture distance - 0x00, // 0x8072 Gesture long press time - 0x00, // 0x8073 X/Y slope adjust - 0x00, // 0x8074 Gesture control - 0x00, // 0x8075 Gesture switch 1 - 0x00, // 0x8076 Gesture switch 2 - 0x00, // 0x8077 Gesture refresh rate - 0x00, // 0x8078 Gesture touch level - 0x00, // 0x8079 New green wake up level - 0xB4, // 0x807A Freq hopping start - 0xEF, // 0x807B Freq hopping end - 0x94, // 0x807C Noise detect time - 0xD5, // 0x807D Hopping flag - 0x02, // 0x807E Hopping flag - 0x07, // 0x807F Noise threshold - 0x00, // 0x8080 Nois min threshold old - 0x00, // 0x8081 Reserved - 0x04, // 0x8082 Hopping sensor group - 0x6E, // 0x8083 Hopping Seg1 normalize - 0xB9, // 0x8084 Hopping Seg1 factor - 0x00, // 0x8085 Main clock adjust - 0x6A, // 0x8086 Hopping Seg2 normalize - 0xC4, // 0x8087 Hopping Seg2 factor - 0x00, // 0x8088 Reserved - 0x66, // 0x8089 Hopping Seg3 normalize - 0xCF, // 0x808A Hopping Seg3 factor - 0x00, // 0x808B Reserved - 0x62, // 0x808C Hopping Seg4 normalize - 0xDB, // 0x808D Hopping Seg4 factor - 0x00, // 0x808E Reserved - 0x5E, // 0x808F Hopping Seg5 normalize - 0xE8, // 0x8090 Hopping Seg5 factor - 0x00, // 0x8091 Reserved - 0x5E, // 0x8092 Hopping Seg6 normalize - 0x00, // 0x8093 Key1 - 0x00, // 0x8094 Key2 - 0x00, // 0x8095 Key3 - 0x00, // 0x8096 Key4 - 0x00, // 0x8097 Key area - 0x00, // 0x8098 Key touch level - 0x00, // 0x8099 Key leave level - 0x00, // 0x809A Key sens - 0x00, // 0x809B Key sens - 0x00, // 0x809C Key restrain - 0x00, // 0x809D Key restrain time - 0x00, // 0x809E Large gesture touch - 0x00, // 0x809F Reserved - 0x00, // 0x80A0 Reserved - 0x00, // 0x80A1 Hotknot noise map - 0x00, // 0x80A2 Link threshold - 0x00, // 0x80A3 Pxy threshold - 0x00, // 0x80A4 GHot dump shift - 0x00, // 0x80A5 GHot rx gain - 0x00, // 0x80A6 Freg gain - 0x00, // 0x80A7 Freg gain 1 - 0x00, // 0x80A8 Freg gain 2 - 0x00, // 0x80A9 Freg gain 3 - 0x00, // 0x80AA Reserved - 0x00, // 0x80AB Reserved - 0x00, // 0x80AC Reserved - 0x00, // 0x80AD Reserved - 0x00, // 0x80AE Reserved - 0x00, // 0x80AF Reserved - 0x00, // 0x80B0 Reserved - 0x00, // 0x80B1 Reserved - 0x00, // 0x80B2 Reserved - 0x00, // 0x80B3 Combine dis - 0x00, // 0x80B4 Split set - 0x00, // 0x80B5 Reserved - 0x00, // 0x80B6 Reserved - 0x14, // 0x80B7 Sensor CH0 - 0x12, // 0x80B8 Sensor CH1 - 0x10, // 0x80B9 Sensor CH2 - 0x0E, // 0x80BA Sensor CH3 - 0x0C, // 0x80BB Sensor CH4 - 0x0A, // 0x80BC Sensor CH5 - 0x08, // 0x80BD Sensor CH6 - 0x06, // 0x80BE Sensor CH7 - 0x04, // 0x80BF Sensor CH8 - 0x02, // 0x80C0 Sensor CH9 - 0xFF, // 0x80C1 Sensor CH10 - 0xFF, // 0x80C2 Sensor CH11 - 0xFF, // 0x80C3 Sensor CH12 - 0xFF, // 0x80C4 Sensor CH13 - 0x00, // 0x80C5 Reserved - 0x00, // 0x80C6 Reserved - 0x00, // 0x80C7 Reserved - 0x00, // 0x80C8 Reserved - 0x00, // 0x80C9 Reserved - 0x00, // 0x80CA Reserved - 0x00, // 0x80CB Reserved - 0x00, // 0x80CC Reserved - 0x00, // 0x80CD Reserved - 0x00, // 0x80CE Reserved - 0x00, // 0x80CF Reserved - 0x00, // 0x80D0 Reserved - 0x00, // 0x80D1 Reserved - 0x00, // 0x80D2 Reserved - 0x00, // 0x80D3 Reserved - 0x00, // 0x80D4 Reserved - 0x28, // 0x80D5 Driver CH0 - 0x26, // 0x80D6 Driver CH1 - 0x24, // 0x80D7 Driver CH2 - 0x22, // 0x80D8 Driver CH3 - 0x21, // 0x80D9 Driver CH4 - 0x20, // 0x80DA Driver CH5 - 0x1F, // 0x80DB Driver CH6 - 0x1E, // 0x80DC Driver CH7 - 0x1D, // 0x80DD Driver CH8 - 0x0C, // 0x80DE Driver CH9 - 0x0A, // 0x80DF Driver CH10 - 0x08, // 0x80E0 Driver CH11 - 0x06, // 0x80E1 Driver CH12 - 0x04, // 0x80E2 Driver CH13 - 0x02, // 0x80E3 Driver CH14 - 0x00, // 0x80E4 Driver CH15 - 0xFF, // 0x80E5 Driver CH16 - 0xFF, // 0x80E6 Driver CH17 - 0xFF, // 0x80E7 Driver CH18 - 0xFF, // 0x80E8 Driver CH19 - 0xFF, // 0x80E9 Driver CH20 - 0xFF, // 0x80EA Driver CH21 - 0xFF, // 0x80EB Driver CH22 - 0xFF, // 0x80EC Driver CH23 - 0xFF, // 0x80ED Driver CH24 - 0xFF, // 0x80EE Driver CH25 - 0x00, // 0x80EF Reserved - 0x00, // 0x80F0 Reserved - 0x00, // 0x80F1 Reserved - 0x00, // 0x80F2 Reserved - 0x00, // 0x80F3 Reserved - 0x00, // 0x80F4 Reserved - 0x00, // 0x80F5 Reserved - 0x00, // 0x80F6 Reserved - 0x00, // 0x80F7 Reserved - 0x00, // 0x80F8 Reserved - 0x00, // 0x80F9 Reserved - 0x00, // 0x80FA Reserved - 0x00, // 0x80FB Reserved - 0x00, // 0x80FC Reserved - 0x00, // 0x80FD Reserved - 0x00 // 0x80FE Reserved -}; +#define GT911_MAX_TP 5 +#define GT911_CFG_NUMER 0x6A //I2C #define GT_CMD_WR 0X28 #define GT_CMD_RD 0X29 - -#define SLAVE_Write_Address 0X28 +#define I2C_TIMEOUT_MAX 1000 //GT9147 #define GT_CTRL_REG 0X8040 @@ -257,8 +61,7 @@ const uint8_t GT911_Cfg[] = #define GT911_CONFIG_CHECKSUM_REG 0x80FF #define GT911_FIRMWARE_VERSION_REG 0x8144 -PACK(typedef struct -{ +PACK(typedef struct { uint8_t track; uint16_t x; uint16_t y; @@ -266,8 +69,7 @@ PACK(typedef struct uint8_t reserved; }) TouchPoint; -PACK(struct TouchData -{ +PACK(struct TouchData { uint8_t pointsCount; union { @@ -276,21 +78,10 @@ PACK(struct TouchData }; }); -extern struct TouchData touchData; - #define TPRST_LOW() do { TOUCH_RST_GPIO->BSRRH = TOUCH_RST_GPIO_PIN; } while(0) #define TPRST_HIGH() do { TOUCH_RST_GPIO->BSRRL = TOUCH_RST_GPIO_PIN; } while(0) #define TPINT_LOW() do { TOUCH_INT_GPIO->BSRRH = TOUCH_INT_GPIO_PIN; } while(0) #define TPINT_HIGH() do { TOUCH_INT_GPIO->BSRRL = TOUCH_INT_GPIO_PIN; } while(0) - -#define I2C_SCL_L() do { I2C_GPIO->BSRRH = I2C_SCL_GPIO_PIN; } while(0) -#define I2C_SCL_H() do { I2C_GPIO->BSRRL = I2C_SCL_GPIO_PIN; } while(0) - -#define I2C_SDA_L() do { I2C_GPIO->BSRRH = I2C_SDA_GPIO_PIN; } while(0) -#define I2C_SDA_H() do { I2C_GPIO->BSRRL = I2C_SDA_GPIO_PIN; } while(0) - -#define READ_SDA GPIO_ReadInputDataBit(I2C_GPIO, I2C_SDA_GPIO_PIN) - -#endif // _FT5X06_H_ +#endif // _FT5X06_H_ \ No newline at end of file From 392a60bffc411a9173448242cf593237f66e940d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 29 Jul 2020 14:49:42 +0200 Subject: [PATCH 099/267] Update CREDITS.txt --- CREDITS.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9a6bc797e..0bfe67cff 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1838,3 +1838,12 @@ Laurence Gerrard Nico Wagner Christoph Rössner Ruben Flores +Jim Cukla +Lee Taylor +Stefan Kuster +Barry Wright +Steven Gilley +Lloyd Evans +Nolan O'Hearne +Jeffrey James N Buffie +James Houck From f41d7c1cdcab569ae8aed92456d6dcc9e7cd9d08 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 29 Jul 2020 22:20:26 +0200 Subject: [PATCH 100/267] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0bfe67cff..3f5d35b3c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1847,3 +1847,6 @@ Lloyd Evans Nolan O'Hearne Jeffrey James N Buffie James Houck +Andreas Arlt +William Walker +Salvador Munoz Vinas From 50645ac8a17c9ad0dd5a9509b291fc8811e07a01 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 30 Jul 2020 10:05:27 +0200 Subject: [PATCH 101/267] Allow Spectrum analyser to start for protocol > 64 --- radio/src/pulses/multi.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index c8170f217..42d57b231 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -167,13 +167,18 @@ void setupPulsesMulti(uint8_t moduleIdx) sendChannels(moduleIdx); // Multi V1.3.X.X -> Send byte 26, Protocol (bits 7 & 6), RX_Num (bits 5 & 4), invert, not used, disable telemetry, disable mapping - sendMulti(moduleIdx, (uint8_t) (((g_model.moduleData[moduleIdx].getMultiProtocol()+3)&0xC0) - | (g_model.header.modelId[moduleIdx] & 0x30) - | (invert[moduleIdx] & 0x08) - //| 0x04 // Future use - | (g_model.moduleData[moduleIdx].multi.disableTelemetry << 1) - | g_model.moduleData[moduleIdx].multi.disableMapping)); - + if (moduleState[moduleIdx].mode == MODULE_MODE_SPECTRUM_ANALYSER) { + sendMulti(moduleIdx, 0); + } + else { + sendMulti(moduleIdx, (uint8_t) (((g_model.moduleData[moduleIdx].getMultiProtocol() + 3) & 0xC0) + | (g_model.header.modelId[moduleIdx] & 0x30) + | (invert[moduleIdx] & 0x08) + //| 0x04 // Future use + | (g_model.moduleData[moduleIdx].multi.disableTelemetry << 1) + | g_model.moduleData[moduleIdx].multi.disableMapping)); + } + // Multi V1.3.X.X -> Send protocol additional data: max 9 bytes if (getMultiModuleStatus(moduleIdx).isValid()) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); From d5bc0db7e11884b95e0a9c0032ea795799309156 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 30 Jul 2020 11:43:21 +0200 Subject: [PATCH 102/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3f5d35b3c..fdc4306a9 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1850,3 +1850,4 @@ James Houck Andreas Arlt William Walker Salvador Munoz Vinas +Chris Geiger Photography From 240d8d0ed01f96203921e24edaa8749bde287c97 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 30 Jul 2020 22:45:17 +0200 Subject: [PATCH 103/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index fdc4306a9..088a34595 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1851,3 +1851,4 @@ Andreas Arlt William Walker Salvador Munoz Vinas Chris Geiger Photography +John F Smith From 886f3a2158ff2cc1608a07a1c7c8e398c6251512 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 31 Jul 2020 15:11:50 +0200 Subject: [PATCH 104/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 088a34595..4bc94504e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1852,3 +1852,5 @@ William Walker Salvador Munoz Vinas Chris Geiger Photography John F Smith +Robert Zecha +Fernando Martins From 6df667f6590b163e16385915df9469c75f4204a7 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 31 Jul 2020 15:18:48 +0200 Subject: [PATCH 105/267] Add some tts (#7853) --- radio/util/tts_en.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/radio/util/tts_en.py b/radio/util/tts_en.py index dff2e29dd..a914ccf21 100755 --- a/radio/util/tts_en.py +++ b/radio/util/tts_en.py @@ -74,6 +74,8 @@ for s, f in [("trim center", "midtrim"), systemSounds.append((s, filename(f))) for i, (s, f) in enumerate([("armed", "armed"), ("disarmed", "disarm"), + ("throttle cut", "thrcut"), + ("throttle active", "thract"), ("gear!, up!", "gearup"), ("gear!, down!", "geardn"), ("flaps!, up!", "flapup"), @@ -119,5 +121,11 @@ for i, (s, f) in enumerate([("armed", "armed"), ("flight mode thermal", "fm-thm"), ("flight mode thermal left", "fm-thl"), ("flight mode thermal right", "fm-thr"), + ("flight mode stabilize", "fm-stb"), + ("flight mode horizon", "fm-hor"), + ("flight mode angle", "fm-ang"), + ("low rate", "ratlow"), + ("medium rate", "ratmed"), + ("high rate", "rathi"), ]): sounds.append((s, filename(f))) From a73a79fb0290bc7a42dbcb11a54ef2ee3f72b023 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Fri, 31 Jul 2020 21:26:33 +0800 Subject: [PATCH 106/267] Wismy/rb3040 fixes (#7847) Fix the ACCX/Y/Z display accuracy Add RX3 status --- radio/src/gui/common/colorlcd/draw_functions.cpp | 4 ++++ radio/src/gui/common/stdlcd/draw_functions.cpp | 4 ++++ radio/src/telemetry/frsky_sport.cpp | 6 +++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/common/colorlcd/draw_functions.cpp b/radio/src/gui/common/colorlcd/draw_functions.cpp index 3fe07f0e6..15aef8e72 100644 --- a/radio/src/gui/common/colorlcd/draw_functions.cpp +++ b/radio/src/gui/common/colorlcd/draw_functions.cpp @@ -251,6 +251,10 @@ void drawSensorCustomValue(coord_t x, coord_t y, uint8_t sensor, int32_t value, "Rx2 Lost", "Rx1 NS", "Rx2 NS", + "Rx3 FS", + "Rx3 LF", + "Rx3 Lost", + "Rx3 NS" }; for (uint8_t i=0; i Date: Fri, 31 Jul 2020 15:57:54 +0200 Subject: [PATCH 107/267] Radio hardware screen improvements (#7843) Move some items in 212 radio right Fix X9E BT mode issue --- radio/src/dataconstants.h | 5 +++++ radio/src/gui/480x272/radio_hardware.cpp | 2 +- radio/src/gui/common/stdlcd/radio_hardware.cpp | 4 ++-- radio/src/translations/en.h.txt | 2 +- radio/src/translations/it.h.txt | 16 ++++++++-------- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index d47b0eeae..512ac301f 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -893,6 +893,11 @@ enum BluetoothModes { BLUETOOTH_OFF, BLUETOOTH_TELEMETRY, BLUETOOTH_TRAINER, +#if defined(PCBX9E) + BLUETOOTH_MAX=BLUETOOTH_TELEMETRY +#else + BLUETOOTH_MAX=BLUETOOTH_TRAINER +#endif }; // PXX2 constants diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp index 8020bf983..ff6d36ade 100644 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ b/radio/src/gui/480x272/radio_hardware.cpp @@ -325,7 +325,7 @@ bool menuRadioHardware(event_t event) #if defined(BLUETOOTH) case ITEM_RADIO_HARDWARE_BLUETOOTH_MODE: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLUETOOTH); - g_eeGeneral.bluetoothMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_BLUETOOTH_MODES, g_eeGeneral.bluetoothMode, BLUETOOTH_OFF, BLUETOOTH_TRAINER, attr, event); + g_eeGeneral.bluetoothMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_BLUETOOTH_MODES, g_eeGeneral.bluetoothMode, BLUETOOTH_OFF, BLUETOOTH_MAX, attr, event); break; case ITEM_RADIO_HARDWARE_BLUETOOTH_PAIRING_CODE: diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index dde563d6a..a2d486193 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -292,7 +292,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #if LCD_W >= 212 #define HW_SETTINGS_COLUMN1 12*FW - #define HW_SETTINGS_COLUMN2 (HW_SETTINGS_COLUMN1 + 6*FW) + #define HW_SETTINGS_COLUMN2 (20*FW - 3) #else #define HW_SETTINGS_COLUMN1 30 #define HW_SETTINGS_COLUMN2 (HW_SETTINGS_COLUMN1 + 5*FW) @@ -569,7 +569,7 @@ void menuRadioHardware(event_t event) lcdDrawTextAlignedLeft(y, STR_BLUETOOTH); lcdDrawTextAtIndex(HW_SETTINGS_COLUMN2, y, STR_BLUETOOTH_MODES, g_eeGeneral.bluetoothMode, attr); if (attr) { - g_eeGeneral.bluetoothMode = checkIncDecGen(event, g_eeGeneral.bluetoothMode, BLUETOOTH_OFF, BLUETOOTH_TRAINER); + g_eeGeneral.bluetoothMode = checkIncDecGen(event, g_eeGeneral.bluetoothMode, BLUETOOTH_OFF, BLUETOOTH_MAX); } break; diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 706197f33..a5a809ec5 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -925,7 +925,7 @@ #define TR_DEL_BUTTON BUTTON(TR("Del", "Delete")) #define TR_RESET_BTN BUTTON("Reset") #define TR_DEBUG "Debug" -#define TR_KEYS_BTN BUTTON(TR("Keys", "Switches")) +#define TR_KEYS_BTN BUTTON("Keys") #define TR_ANALOGS_BTN BUTTON(TR("Anas", "Analogs")) #define TR_TOUCH_NOTFOUND "Touch hardware not found" #define TR_TOUCH_EXIT "Touch screen to exit" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 034bcd7db..5ee5463c1 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -940,11 +940,11 @@ #define TR_DEL_BUTTON BUTTON(TR("Canc.", "Cancella")) #define TR_RESET_BTN "[Reset]" #define TR_DEBUG "Debug" -#define TR_KEYS_BTN BUTTON(TR("SW","Interruttori")) -#define TR_ANALOGS_BTN BUTTON(TR("Analogico","Analogici")) -#define TR_TOUCH_NOTFOUND "Schermo touch non trovato" -#define TR_TOUCH_EXIT "Tocca lo schermo per uscire" -#define TR_CALIBRATION TR("Calibraz.", "Calibrazione") +#define TR_KEYS_BTN BUTTON(TR("SW", "Tasti")) +#define TR_ANALOGS_BTN BUTTON("Analogici") +#define TR_TOUCH_NOTFOUND "Schermo touch non trovato" +#define TR_TOUCH_EXIT "Tocca lo schermo per uscire" +#define TR_CALIBRATION TR("Calibraz.", "Calibrazione") #define TR_SET "[Set]" #define TR_TRAINER TR("Trainer", "Maestro/Allievo") #define TR_CHANS "Canali" @@ -1220,12 +1220,12 @@ #define TR_MENU_OTHER "Altro" #define TR_MENU_INVERT "Inverti" #define TR_JITTER_FILTER "Filtro ADC" -#define TR_RTC_CHECK TR("Controllo RTC", "Controllo voltaggio RTC") -#define TR_AUTH_FAILURE "Auth-failure" +#define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") +#define TR_AUTH_FAILURE "Auth-failure" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" -#define ZSTR_R9PW "R9PW" +#define ZSTR_R9PW "R9PW" #define ZSTR_RAS "SWR" #define ZSTR_A1 "A1" #define ZSTR_A2 "A2" From 40a5e2f030d4647b65b4b4a593356f11085d80ee Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 31 Jul 2020 16:22:41 +0200 Subject: [PATCH 108/267] Flexi theme (#7841) "Flexi" Theme --- CMakeLists.txt | 2 +- radio/src/gui/480x272/colors.h | 5 + radio/src/gui/480x272/theme.cpp | 4 + radio/src/gui/480x272/themes/flexi.cpp | 363 +++++++++++++++++++++++++ radio/src/translations.cpp | 3 + radio/src/translations.h | 3 + radio/src/translations/cz.h.txt | 3 + radio/src/translations/de.h.txt | 3 + radio/src/translations/en.h.txt | 3 + radio/src/translations/es.h.txt | 3 + radio/src/translations/fi.h.txt | 3 + radio/src/translations/fr.h.txt | 3 + radio/src/translations/it.h.txt | 3 + radio/src/translations/nl.h.txt | 3 + radio/src/translations/pl.h.txt | 3 + radio/src/translations/pt.h.txt | 3 + radio/src/translations/se.h.txt | 3 + 17 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 radio/src/gui/480x272/themes/flexi.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 18d2bc919..6908a5419 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "10") 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 "0029") +set(SDCARD_REVISION "0030") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) diff --git a/radio/src/gui/480x272/colors.h b/radio/src/gui/480x272/colors.h index 5d5bf8131..eb6a2c074 100644 --- a/radio/src/gui/480x272/colors.h +++ b/radio/src/gui/480x272/colors.h @@ -29,6 +29,7 @@ #define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) #define WHITE RGB(0xFF, 0xFF, 0xFF) +#define LIGHTWHITE RGB(238, 234, 238) #define BLACK RGB(0, 0, 0) #define YELLOW RGB(0xF0, 0xD0, 0x10) #define BLUE RGB(0x30, 0xA0, 0xE0) @@ -38,6 +39,10 @@ #define RED RGB(229, 32, 30) #define DARKRED RGB(160, 0, 6) #define GREEN RGB(25, 150, 50) +#define LIGHTBROWN RGB(156, 109, 32) +#define DARKBROWN RGB(106, 72, 16) +#define BRIGHTGREEN RGB(0, 180, 60) +#define ORANGE RGB(229, 100, 30) #define OPACITY_MAX 0x0F #define OPACITY(x) ((x)<<24) diff --git a/radio/src/gui/480x272/theme.cpp b/radio/src/gui/480x272/theme.cpp index 27756859a..39c8fe4d9 100644 --- a/radio/src/gui/480x272/theme.cpp +++ b/radio/src/gui/480x272/theme.cpp @@ -19,6 +19,9 @@ */ #include "opentx.h" +#include "storage/modelslist.h" + +extern ModelsList modelslist; const BitmapBuffer * Theme::asterisk = NULL; const BitmapBuffer * Theme::question = NULL; @@ -138,6 +141,7 @@ Theme * getTheme(const char * name) void loadTheme(Theme * new_theme) { TRACE("load theme %s", new_theme->getName()); + modelslist.clear(); theme = new_theme; theme->load(); } diff --git a/radio/src/gui/480x272/themes/flexi.cpp b/radio/src/gui/480x272/themes/flexi.cpp new file mode 100644 index 000000000..55de41628 --- /dev/null +++ b/radio/src/gui/480x272/themes/flexi.cpp @@ -0,0 +1,363 @@ +/* + * 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 "opentx.h" +#include "storage/modelslist.h" + +const ZoneOption OPTIONS_THEME_FLEXI[] = { + { STR_BACKGROUND_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(LIGHTWHITE) }, + { STR_MAIN_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(LIGHTBROWN) }, + { STR_TEXT_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(BLACK) }, + { STR_BAR1_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(ORANGE) }, + { STR_BAR2_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(BRIGHTGREEN) }, + { NULL, ZoneOption::Bool } +}; + +class FlexiTheme: public Theme +{ + public: + FlexiTheme(): + Theme("Flexi", OPTIONS_THEME_FLEXI) + { + } + + void loadColors() const + { + lcdColorTable[TEXT_COLOR_INDEX] = BLACK; + lcdColorTable[TEXT_BGCOLOR_INDEX] = LIGHTWHITE; + lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = LIGHTWHITE; + lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = DARKBROWN; + lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = LIGHTWHITE; + lcdColorTable[LINE_COLOR_INDEX] = GREY; + lcdColorTable[SCROLLBOX_COLOR_INDEX] = DARKBROWN; + lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = LIGHTWHITE; + lcdColorTable[MENU_TITLE_COLOR_INDEX] = LIGHTWHITE; + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = LIGHTWHITE; + lcdColorTable[HEADER_COLOR_INDEX] = LIGHTGREY; + lcdColorTable[ALARM_COLOR_INDEX] = RED; + lcdColorTable[WARNING_COLOR_INDEX] = YELLOW; + lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY; + lcdColorTable[CURVE_AXIS_COLOR_INDEX] = LIGHTGREY; + lcdColorTable[CURVE_COLOR_INDEX] = RED; + lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = DARKBROWN; + lcdColorTable[TITLE_BGCOLOR_INDEX] = DARKBROWN; + lcdColorTable[TRIM_BGCOLOR_INDEX] = LIGHTBROWN; + lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = BLACK; + lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = LIGHTWHITE; + lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = DARKBROWN; + lcdColorTable[HEADER_BGCOLOR_INDEX] = DARKBROWN; // Top bar color + lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = LIGHTBROWN; // background OPENTX icon + lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = GREEN; // icons color + lcdColorTable[OVERLAY_COLOR_INDEX] = LIGHTWHITE; + lcdColorTable[BARGRAPH1_COLOR_INDEX] = ORANGE; + lcdColorTable[BARGRAPH2_COLOR_INDEX] = BRIGHTGREEN; + lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(220, 220, 220); + } + + void loadMenuIcon(uint8_t index, const char * filename) const + { + BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename)); + if (mask) { + menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); + if (menuIconNormal[index]) { + menuIconNormal[index]->clear(HEADER_BGCOLOR); + menuIconNormal[index]->drawMask(0, 0, mask, HEADER_CURRENT_BGCOLOR); + } + menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); + if (menuIconSelected[index]) { + menuIconSelected[index]->clear(HEADER_BGCOLOR); + menuIconSelected[index]->drawMask(0, 0, mask, MENU_TITLE_COLOR); + } + menuIconTitle[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); + if (menuIconTitle[index]) { + menuIconTitle[index]->clear(HEADER_ICON_BGCOLOR); + menuIconTitle[index]->drawMask(0, 0, mask, MENU_TITLE_COLOR); + } + } + } + + void loadMenusIcons() const + { + if (isAsteriskDisplayed()) + loadMenuIcon(ICON_OPENTX, "mask_opentx_testmode.png"); + else + loadMenuIcon(ICON_OPENTX, "mask_opentx.png"); + loadMenuIcon(ICON_RADIO, "mask_menu_radio.png"); + loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png"); + loadMenuIcon(ICON_RADIO_SD_MANAGER, "mask_radio_sd_browser.png"); + loadMenuIcon(ICON_RADIO_TOOLS, "mask_radio_tools.png"); + loadMenuIcon(ICON_RADIO_SPECTRUM_ANALYSER, "/mask_radio_spectrum_analyser.png"); + loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, "mask_radio_global_functions.png"); + loadMenuIcon(ICON_RADIO_TRAINER, "mask_radio_trainer.png"); + loadMenuIcon(ICON_RADIO_HARDWARE, "mask_radio_hardware.png"); + loadMenuIcon(ICON_RADIO_VERSION, "mask_radio_version.png"); + loadMenuIcon(ICON_MODEL, "mask_menu_model.png"); + loadMenuIcon(ICON_MODEL_SETUP, "mask_model_setup.png"); + loadMenuIcon(ICON_MODEL_HELI, "mask_model_heli.png"); + loadMenuIcon(ICON_MODEL_FLIGHT_MODES, "mask_model_flight_modes.png"); + loadMenuIcon(ICON_MODEL_INPUTS, "mask_model_inputs.png"); + loadMenuIcon(ICON_MODEL_MIXER, "mask_model_mixer.png"); + loadMenuIcon(ICON_MODEL_OUTPUTS, "mask_model_outputs.png"); + loadMenuIcon(ICON_MODEL_CURVES, "mask_model_curves.png"); + loadMenuIcon(ICON_MODEL_GVARS, "mask_model_gvars.png"); + loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, "mask_model_logical_switches.png"); + loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, "mask_model_special_functions.png"); + loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, "mask_model_lua_scripts.png"); + loadMenuIcon(ICON_MODEL_TELEMETRY, "mask_model_telemetry.png"); + loadMenuIcon(ICON_STATS, "mask_menu_stats.png"); + loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, "mask_stats_throttle_graph.png"); + loadMenuIcon(ICON_STATS_TIMERS, "mask_stats_timers.png"); + loadMenuIcon(ICON_STATS_ANALOGS, "mask_stats_analogs.png"); + loadMenuIcon(ICON_STATS_DEBUG, "mask_stats_debug.png"); + loadMenuIcon(ICON_THEME, "mask_menu_theme.png"); + loadMenuIcon(ICON_THEME_SETUP, "mask_theme_setup.png"); + loadMenuIcon(ICON_THEME_VIEW1, "mask_theme_view1.png"); + loadMenuIcon(ICON_THEME_VIEW2, "mask_theme_view2.png"); + loadMenuIcon(ICON_THEME_VIEW3, "mask_theme_view3.png"); + loadMenuIcon(ICON_THEME_VIEW4, "mask_theme_view4.png"); + loadMenuIcon(ICON_THEME_VIEW5, "mask_theme_view5.png"); + loadMenuIcon(ICON_THEME_ADD_VIEW, "mask_theme_add_view.png"); + loadMenuIcon(ICON_MONITOR, "mask_monitor.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS1, "mask_monitor_channels1.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS2, "mask_monitor_channels2.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS3, "mask_monitor_channels3.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS4, "mask_monitor_channels4.png"); + loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, "/mask_monitor_logsw.png"); + } + + void loadThemeBitmaps() const + { + // Calibration screen + delete calibStick; + calibStick = BitmapBuffer::load(getThemePath("stick_pointer.png")); + + delete calibStickBackground; + calibStickBackground = BitmapBuffer::load(getThemePath("stick_background.png")); + + delete calibTrackpBackground; + calibTrackpBackground = BitmapBuffer::load(getThemePath("trackp_background.png")); + + delete calibHorus; +#if defined(RADIO_T16) + calibHorus = BitmapBuffer::load(getThemePath("t16.bmp")); +#elif defined(RADIO_T18) + calibHorus = BitmapBuffer::load(getThemePath("t18.bmp")); +#elif defined(RADIO_TX16S) + calibHorus = BitmapBuffer::load(getThemePath("tx16s.bmp")); +#elif defined(PCBX10) + if(STICKS_PWM_ENABLED()) { + calibHorus = BitmapBuffer::load(getThemePath("X10S.bmp")); + } + else { + calibHorus = BitmapBuffer::load(getThemePath("X10.bmp")); + } +#else + calibHorus = BitmapBuffer::load(getThemePath("horus.bmp")); +#endif + + // Channels monitor screen + delete chanMonLockedBitmap; + chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_lockch.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete chanMonInvertedBitmap; + chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_inver.png", TEXT_COLOR, TEXT_BGCOLOR); + + // Model Selection screen + delete modelselIconBitmap; + modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, TEXT_BGCOLOR); + if (modelselIconBitmap) { + BitmapBuffer * bitmap = BitmapBuffer::load(getThemePath("modelsel/icon_default.png")); + modelselIconBitmap->drawBitmap(20, 8, bitmap); + delete bitmap; + } + + delete modelselSdFreeBitmap; + modelselSdFreeBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_sdfree.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete modelselModelQtyBitmap; + modelselModelQtyBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelqty.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete modelselModelNameBitmap; + modelselModelNameBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelname.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete modelselModelMoveBackground; + modelselModelMoveBackground = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveback.png")); + + delete modelselModelMoveIcon; + modelselModelMoveIcon = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveico.png")); + + delete modelselWizardBackground; + modelselWizardBackground = BitmapBuffer::load(getThemePath("wizard/background.png")); + + + // Mixer setup screen + delete mixerSetupMixerBitmap; + mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); + + delete mixerSetupToBitmap; + mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", TEXT_BGCOLOR, HEADER_BGCOLOR); + + delete mixerSetupOutputBitmap; + mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); + + delete mixerSetupAddBitmap; + mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_add.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupMultiBitmap; + mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_multi.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupReplaceBitmap; + mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_replace.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupLabelBitmap; + mixerSetupLabelBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_label.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupCurveBitmap; + mixerSetupCurveBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_curve.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupSwitchBitmap; + mixerSetupSwitchBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_switch.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupSlowBitmap; + mixerSetupSlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_slow.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupDelayBitmap; + mixerSetupDelayBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delay.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupDelaySlowBitmap; + mixerSetupDelaySlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delayslow.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupFlightmodeBitmap; + mixerSetupFlightmodeBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_fm.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete topleftBitmap; + topleftBitmap = BitmapBuffer::loadMaskOnBackground("topleft.png", HEADER_ICON_BGCOLOR, TITLE_BGCOLOR); + } + + virtual void load() const + { + Theme::load(); + loadColors(); + loadMenusIcons(); + loadThemeBitmaps(); + if (!backgroundBitmap) { + backgroundBitmap = BitmapBuffer::load(getThemePath("background.png")); + } + update(); + } + + virtual void update() const + { +#define DARKER(x) ((x * 70) / 100) + + uint32_t color = g_eeGeneral.themeData.options[1].unsignedValue; + uint32_t bgColor = globalData.unexpectedShutdown ? WHITE : g_eeGeneral.themeData.options[0].unsignedValue; + uint32_t textcolor = g_eeGeneral.themeData.options[2].unsignedValue; + uint32_t barcolor = g_eeGeneral.themeData.options[3].unsignedValue; + uint32_t bar2color = g_eeGeneral.themeData.options[4].unsignedValue; + + lcdColorTable[TEXT_COLOR_INDEX] = textcolor; + lcdColorTable[TEXT_BGCOLOR_INDEX] = bgColor; + lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = bgColor; + lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = color; + lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = bgColor; + lcdColorTable[LINE_COLOR_INDEX] = GREY; + lcdColorTable[SCROLLBOX_COLOR_INDEX] = color; + lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = bgColor; + lcdColorTable[MENU_TITLE_COLOR_INDEX] = bgColor; + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = RGB(DARKER(GET_RED(bgColor)), DARKER(GET_GREEN(bgColor)), DARKER(GET_BLUE(bgColor))); + lcdColorTable[HEADER_COLOR_INDEX] = LIGHTGREY; + lcdColorTable[ALARM_COLOR_INDEX] = RED; + lcdColorTable[WARNING_COLOR_INDEX] = YELLOW; + lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY; + lcdColorTable[CURVE_AXIS_COLOR_INDEX] = LIGHTGREY; + lcdColorTable[CURVE_COLOR_INDEX] = RED; + lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = color; + lcdColorTable[TITLE_BGCOLOR_INDEX] = color; + lcdColorTable[TRIM_BGCOLOR_INDEX] = color; + lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = textcolor; + lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = bgColor; + lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = color; + lcdColorTable[HEADER_BGCOLOR_INDEX] = color; // Top bar color + lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = color; // background OPENTX icon + lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RGB(DARKER(GET_RED(color)), DARKER(GET_GREEN(color)), DARKER(GET_BLUE(color))); // icons color + lcdColorTable[OVERLAY_COLOR_INDEX] = bgColor; + lcdColorTable[BARGRAPH1_COLOR_INDEX] = barcolor; + lcdColorTable[BARGRAPH2_COLOR_INDEX] = bar2color; + lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(DARKER(GET_RED(bgColor)), DARKER(GET_GREEN(bgColor)), DARKER(GET_BLUE(bgColor))); + + loadMenusIcons(); + loadThemeBitmaps(); + loadFontCache(); + } + + virtual void drawBackground() const + { + if (backgroundBitmap) { + lcd->drawBitmap(0, 0, backgroundBitmap); + } + else { + lcdSetColor(g_eeGeneral.themeData.options[0].unsignedValue); + lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR); + } + } + void drawTopbarBackground(uint8_t icon) const + { + if (topleftBitmap) { + lcd->drawBitmap(0, 0, topleftBitmap); + uint16_t width = topleftBitmap->getWidth(); + lcd->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + } + else { + lcd->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + } + + + if (icon == ICON_OPENTX) + lcd->drawBitmap(4, 10, menuIconTitle[ICON_OPENTX]); + else + lcd->drawBitmap(5, 7, menuIconTitle[icon]); + + drawTopbarDatetime(); + } + + virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const + { + lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, selected ? menuIconSelected[index] : menuIconNormal[index]); + } + + protected: + static const BitmapBuffer * backgroundBitmap; + static BitmapBuffer * topleftBitmap; + static BitmapBuffer * menuIconNormal[MENUS_ICONS_COUNT]; + static BitmapBuffer * menuIconSelected[MENUS_ICONS_COUNT]; + static BitmapBuffer * menuIconTitle[MENUS_ICONS_COUNT]; +}; + +const BitmapBuffer * FlexiTheme::backgroundBitmap = NULL; +BitmapBuffer * FlexiTheme::topleftBitmap = NULL; +BitmapBuffer * FlexiTheme::menuIconNormal[MENUS_ICONS_COUNT] = { NULL }; +BitmapBuffer * FlexiTheme::menuIconSelected[MENUS_ICONS_COUNT] = { NULL }; +BitmapBuffer * FlexiTheme::menuIconTitle[MENUS_ICONS_COUNT] = { NULL }; + +FlexiTheme FlexiTheme; +#if defined(DEFAULT_THEME_FLEXI) + Theme * theme = &FlexiTheme; +#endif \ No newline at end of file diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 4d65e6840..5d928f2e7 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -531,6 +531,9 @@ const char STR_LAYOUT[] = TR_LAYOUT; const char STR_ADDMAINVIEW[] = TR_ADDMAINVIEW; const char STR_BACKGROUND_COLOR[] = TR_BACKGROUND_COLOR; const char STR_MAIN_COLOR[] = TR_MAIN_COLOR; +const char STR_BAR2_COLOR[] = TR_BAR2_COLOR; +const char STR_BAR1_COLOR[] = TR_BAR1_COLOR; +const char STR_TEXT_COLOR[] = TR_TEXT_COLOR; const char STR_TEXT_VIEWER[] = TR_TEXT_VIEWER; const char STR_RFPOWER[] = TR_RFPOWER; const char STR_BYTES[] = TR_BYTES; diff --git a/radio/src/translations.h b/radio/src/translations.h index f99520cd7..e324be5e5 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -836,6 +836,9 @@ extern const char STR_LAYOUT[]; extern const char STR_ADDMAINVIEW[]; extern const char STR_BACKGROUND_COLOR[]; extern const char STR_MAIN_COLOR[]; +extern const char STR_BAR2_COLOR[]; +extern const char STR_BAR1_COLOR[]; +extern const char STR_TEXT_COLOR[]; extern const char STR_TEXT_VIEWER[]; extern const char STR_RFPOWER[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index adaa7d30e..b8fd77849 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -1180,6 +1180,9 @@ #define TR_ADDMAINVIEW "Přidat hlavní panel" #define TR_BACKGROUND_COLOR "Barva pozadí" #define TR_MAIN_COLOR "Hlavní barva" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Prohlížeč textu" #define TR_MENU_INPUTS "\314Vstupy" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index e6c6eb431..4b445d02e 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -1186,6 +1186,9 @@ #define TR_ADDMAINVIEW "Ansicht hinzufügen" #define TR_BACKGROUND_COLOR "Hintergrundfarbe" #define TR_MAIN_COLOR "Hauptfarbe" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" // ----------------------------- Symbole für Auswahlliste---------- #define TR_MENU_INPUTS "\314Inputs" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index a5a809ec5..5275f9074 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -1184,6 +1184,9 @@ #define TR_ADDMAINVIEW "Add main view" #define TR_BACKGROUND_COLOR "Background color" #define TR_MAIN_COLOR "Main color" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" #define TR_MENU_INPUTS "\314Inputs" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 714fcc217..0335e11c1 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -1183,6 +1183,9 @@ #define TR_ADDMAINVIEW "Añadir vista pral." #define TR_BACKGROUND_COLOR "Color de fondo" #define TR_MAIN_COLOR "Color principal" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Visor de texto" #define TR_MENU_INPUTS "\314Entradas" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 90ce833cb..c560254f6 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -1192,6 +1192,9 @@ #define TR_SETUP_WIDGETS "Setup widgets" #define TR_BACKGROUND_COLOR "Background color" #define TR_MAIN_COLOR "Main color" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" #define TR_MENU_INPUTS "\314Inputs" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index d4a1f5f29..d6096e4f2 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -1208,6 +1208,9 @@ #define TR_ADDMAINVIEW "Ajouter vue principale" #define TR_BACKGROUND_COLOR "Couleur de fond" #define TR_MAIN_COLOR "Couleur principale" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Visualisateur de texte" #define TR_MENU_INPUTS "\314Entrées" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 5ee5463c1..daac7eaac 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -1201,6 +1201,9 @@ #define TR_ADDMAINVIEW "Aggiungi vista princ." #define TR_BACKGROUND_COLOR "Colore background" #define TR_MAIN_COLOR "Colore principale" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Visualizzatore testi" #define TR_MENU_INPUTS "\314Ingressi" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 64af03eea..f9801f767 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -1192,6 +1192,9 @@ #define TR_ADDMAINVIEW "Add main view" #define TR_BACKGROUND_COLOR "Background color" #define TR_MAIN_COLOR "Main color" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 5ba5bd258..34fc6a65d 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1200,6 +1200,9 @@ #define TR_ADDMAINVIEW "Add main view" #define TR_BACKGROUND_COLOR "Background color" #define TR_MAIN_COLOR "Main color" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" #define TR_MENU_INPUTS "\314Wejści" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index e07e3ca78..c4c9d15bd 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -1189,6 +1189,9 @@ #define TR_ADDMAINVIEW "Add main view" #define TR_BACKGROUND_COLOR "Background color" #define TR_MAIN_COLOR "Main color" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" #define TR_MENU_INPUTS "\314Inputs" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index c55d64650..78fde6fa5 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -1200,6 +1200,9 @@ #define TR_ADDMAINVIEW "Add main view" #define TR_BACKGROUND_COLOR "Background color" #define TR_MAIN_COLOR "Main color" +#define TR_BAR2_COLOR "Secondary bar color" +#define TR_BAR1_COLOR "Main bar color" +#define TR_TEXT_COLOR "Text color" #define TR_TEXT_VIEWER "Text Viewer" #define TR_MENU_INPUTS "\314Inputs" From 6264e8524ed233c7210a31d926e388d77cbf6f17 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Sat, 1 Aug 2020 00:23:29 +1000 Subject: [PATCH 109/267] Add timer name to source lists and remove unused code (#7838) --- companion/src/firmwares/modeldata.cpp | 5 +++++ companion/src/firmwares/modeldata.h | 1 + companion/src/firmwares/rawsource.cpp | 29 ++++++++++++--------------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 3add6de8b..d849468c6 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -43,6 +43,11 @@ bool TimerData::isEmpty() return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWN_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/); } +QString TimerData::nameToString(int index) const +{ + return RadioData::getElementName(tr("TMR", "as in Timer"), index + 1, name); +} + /* * ModelData */ diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index df5c6d33d..b5ba8e45c 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -75,6 +75,7 @@ class TimerData { void convert(RadioDataConversionState & cstate); bool isEmpty(); bool isModeOff() { return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } + QString nameToString(int index) const; }; #define CPN_MAX_SCRIPTS 9 diff --git a/companion/src/firmwares/rawsource.cpp b/companion/src/firmwares/rawsource.cpp index 1cb5bcfbb..ef38dc236 100644 --- a/companion/src/firmwares/rawsource.cpp +++ b/companion/src/firmwares/rawsource.cpp @@ -140,21 +140,6 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con tr("Batt"), tr("Time"), tr("Timer1"), tr("Timer2"), tr("Timer3"), }; - static const QString telemetry[] = { - tr("Batt"), tr("Time"), tr("Timer1"), tr("Timer2"), tr("Timer3"), - tr("RAS"), tr("RSSI Tx"), tr("RSSI Rx"), - tr("A1"), tr("A2"), tr("A3"), tr("A4"), - tr("Alt"), tr("Rpm"), tr("Fuel"), tr("T1"), tr("T2"), - tr("Speed"), tr("Dist"), tr("GPS Alt"), - tr("Cell"), tr("Cells"), tr("Vfas"), tr("Curr"), tr("Cnsp"), tr("Powr"), - tr("AccX"), tr("AccY"), tr("AccZ"), - tr("Hdg "), tr("VSpd"), tr("AirSpeed"), tr("dTE"), - tr("A1-"), tr("A2-"), tr("A3-"), tr("A4-"), - tr("Alt-"), tr("Alt+"), tr("Rpm+"), tr("T1+"), tr("T2+"), tr("Speed+"), tr("Dist+"), tr("AirSpeed+"), - tr("Cell-"), tr("Cells-"), tr("Vfas-"), tr("Curr+"), tr("Powr+"), - tr("ACC"), tr("GPS Time"), - }; - static const QString rotary[] = { tr("REa"), tr("REb") }; if (index<0) { @@ -223,7 +208,19 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return LimitData().nameToString(index); case SOURCE_TYPE_SPECIAL: - return CHECK_IN_ARRAY(special, index); + result = CHECK_IN_ARRAY(special, index); + // TODO refactor timers into own source type + if (result.startsWith("Timer")) { + bool ok; + int n = result.right(1).toInt(&ok); + if (ok) { + if (model) + result = model->timers[n - 1].nameToString(n - 1); + else + result = TimerData().nameToString(n - 1); + } + } + return result; case SOURCE_TYPE_TELEMETRY: { From 06d3110d7c4f0886dccf1c1b7c46006ab5633ba8 Mon Sep 17 00:00:00 2001 From: Ben Lye Date: Fri, 31 Jul 2020 15:24:57 +0100 Subject: [PATCH 110/267] Make radio profile choice synchronous in Companion startup (#7822) * Update mainwindow.cpp Choose profile synchronously, before update check and file load. * Prevent profile choice dialog from being resized --- companion/src/mainwindow.cpp | 21 +++++++++------------ companion/src/profilechooser.cpp | 1 + 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 50574ecd5..9d490da39 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -130,15 +130,9 @@ MainWindow::MainWindow(): else { if (!g.previousVersion().isEmpty()) g.warningId(g.warningId() | AppMessages::MSG_UPGRADED); + if (g.promptProfile()) { - QTimer::singleShot(updateDelay, this, SLOT(chooseProfile())); // add an extra second to give mainwindow time to load - updateDelay += 5000; // give user time to select profile before warnings - } - else { - if (checkProfileRadioExists(g.sessionId())) - QTimer::singleShot(updateDelay, this, SLOT(doAutoUpdates())); - else - g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE); + chooseProfile(); } } QTimer::singleShot(updateDelay, this, SLOT(displayWarnings())); @@ -184,6 +178,11 @@ MainWindow::MainWindow(): if (printing) { QTimer::singleShot(0, this, SLOT(autoClose())); } + + if (checkProfileRadioExists(g.sessionId())) + QTimer::singleShot(updateDelay, this, SLOT(doAutoUpdates())); + else + g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE); } MainWindow::~MainWindow() @@ -1805,10 +1804,8 @@ void MainWindow::chooseProfile() connect(pcd, &ProfileChooserDialog::profileChanged, this, &MainWindow::loadProfileId); pcd->exec(); delete pcd; - // doi here as need to wait until dialog dismissed and current radio type is set - if (checkProfileRadioExists(g.sessionId())) - doAutoUpdates(); - else + + if (!checkProfileRadioExists(g.sessionId())) g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE); } } diff --git a/companion/src/profilechooser.cpp b/companion/src/profilechooser.cpp index 17a9570e5..a8942652e 100644 --- a/companion/src/profilechooser.cpp +++ b/companion/src/profilechooser.cpp @@ -28,6 +28,7 @@ ProfileChooserDialog::ProfileChooserDialog(QWidget * parent): { ui->setupUi(this); setWindowIcon(QIcon(":/icon.png")); + setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); QComboBox *prof = ui->cboProfiles; prof->clear(); From ac9f51000520d83ce9444e438b0f6d08fe9a3c21 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Fri, 31 Jul 2020 15:27:31 +0100 Subject: [PATCH 111/267] Check model is off before changing model - Fixes #7715 (#7719) Check model is off before changing model --- radio/src/gui/128x64/model_select.cpp | 4 ++++ radio/src/gui/212x64/model_select.cpp | 4 ++++ radio/src/gui/480x272/model_select.cpp | 8 ++++++++ radio/src/gui/gui_common.cpp | 19 +++++++++++++++++++ radio/src/gui/gui_common.h | 2 ++ 5 files changed, 37 insertions(+) diff --git a/radio/src/gui/128x64/model_select.cpp b/radio/src/gui/128x64/model_select.cpp index d0184e5ea..bbd21b221 100644 --- a/radio/src/gui/128x64/model_select.cpp +++ b/radio/src/gui/128x64/model_select.cpp @@ -36,6 +36,10 @@ void onModelSelectMenu(const char * result) int8_t sub = menuVerticalPosition; if (result == STR_SELECT_MODEL || result == STR_CREATE_MODEL) { + if (!g_eeGeneral.disableRssiPoweroffAlarm) { + if (!confirmModelChange()) + return; + } selectModel(sub); } else if (result == STR_COPY_MODEL) { diff --git a/radio/src/gui/212x64/model_select.cpp b/radio/src/gui/212x64/model_select.cpp index 8b396e1b4..d4223a3c1 100644 --- a/radio/src/gui/212x64/model_select.cpp +++ b/radio/src/gui/212x64/model_select.cpp @@ -28,6 +28,10 @@ void onModelSelectMenu(const char * result) int8_t sub = menuVerticalPosition; if (result == STR_SELECT_MODEL || result == STR_CREATE_MODEL) { + if (!g_eeGeneral.disableRssiPoweroffAlarm) { + if (!confirmModelChange()) + return; + } selectModel(sub); } else if (result == STR_COPY_MODEL) { diff --git a/radio/src/gui/480x272/model_select.cpp b/radio/src/gui/480x272/model_select.cpp index 36dac8ba8..661edd814 100644 --- a/radio/src/gui/480x272/model_select.cpp +++ b/radio/src/gui/480x272/model_select.cpp @@ -242,6 +242,10 @@ void onDeleteModelConfirm(const char * result) void onModelSelectMenu(const char * result) { if (result == STR_SELECT_MODEL) { + if (!g_eeGeneral.disableRssiPoweroffAlarm) { + if (!confirmModelChange()) + return; + } // we store the latest changes if any storageFlushCurrentModel(); storageCheck(true); @@ -259,6 +263,10 @@ void onModelSelectMenu(const char * result) deleteMode = MODE_DELETE_MODEL; } else if (result == STR_CREATE_MODEL) { + if (!g_eeGeneral.disableRssiPoweroffAlarm) { + if (!confirmModelChange()) + return; + } storageCheck(true); modelslist.addModel(currentCategory, createModel()); selectMode = MODE_SELECT_MODEL; diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index b491d84b4..3b9bb3421 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -805,6 +805,25 @@ bool modelHasNotes() return false; } +bool confirmModelChange() +{ + if (TELEMETRY_STREAMING()) { + RAISE_ALERT(STR_MODEL, STR_MODEL_STILL_POWERED, STR_PRESS_ENTER_TO_CONFIRM, AU_MODEL_STILL_POWERED); + while (TELEMETRY_STREAMING()) { + RTOS_WAIT_MS(20); + if (readKeys() == (1 << KEY_ENTER)) { + killEvents(KEY_ENTER); + return true; + } + else if (readKeys() == (1 << KEY_EXIT)) { + killEvents(KEY_EXIT); + return false; + } + } + } + return true; +} + int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable) { int retval = 0; diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index d1fdb8fa1..f5d7e1f51 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -99,6 +99,8 @@ bool isRssiSensorAvailable(int sensor); bool modelHasNotes(); +bool confirmModelChange(); + #if defined(COLORLCD) bool isSwitch2POSWarningStateAvailable(int state); #endif From 035cc6c0317be5fe7241c9de48dc2bbb258d12ce Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Fri, 31 Jul 2020 16:06:11 +0100 Subject: [PATCH 112/267] Brightness adjust from backlight function (#7680) Adjust brightness from backlight function --- .../src/firmwares/customfunctiondata.cpp | 27 ++++++++++--------- companion/src/firmwares/modeldata.cpp | 2 +- companion/src/modeledit/customfunctions.cpp | 20 +++++--------- radio/src/functions.cpp | 13 ++++++--- radio/src/globals.h | 1 + .../gui/128x64/model_special_functions.cpp | 8 ++++++ .../gui/212x64/model_special_functions.cpp | 12 ++++----- .../gui/480x272/model_special_functions.cpp | 8 ++++++ radio/src/main.cpp | 2 ++ radio/src/mixer.cpp | 1 + radio/src/myeeprom.h | 2 +- radio/src/opentx.cpp | 16 ++++++++--- radio/src/opentx.h | 2 +- radio/src/targets/horus/board.h | 2 +- radio/src/targets/sky9x/board.h | 2 +- radio/src/targets/taranis/board.h | 4 +-- 16 files changed, 76 insertions(+), 46 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 3248f1fa4..1d8545b26 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -159,24 +159,24 @@ QString CustomFunctionData::paramToString(const ModelData * model) const if (func <= FuncInstantTrim) { return QString("%1").arg(param); } - else if (func==FuncLogs) { + else if (func == FuncLogs) { return QString("%1").arg(param/10.0) + tr("s"); } - else if (func==FuncPlaySound) { + else if (func == FuncPlaySound) { CustomFunctionData::populatePlaySoundParams(qs); if (param>=0 && param<(int)qs.count()) return qs.at(param); else return tr("Inconsistent parameter"); } - else if (func==FuncPlayHaptic) { + else if (func == FuncPlayHaptic) { CustomFunctionData::populateHapticParams(qs); if (param>=0 && param<(int)qs.count()) return qs.at(param); else return tr("Inconsistent parameter"); } - else if (func==FuncReset) { + else if (func == FuncReset) { QComboBox cb; CustomFunctionData::populateResetParams(model, &cb); int pos = cb.findData(param); @@ -185,11 +185,11 @@ QString CustomFunctionData::paramToString(const ModelData * model) const else return tr("Inconsistent parameter"); } - else if ((func==FuncVolume)|| (func==FuncPlayValue)) { + else if (func == FuncVolume || func == FuncPlayValue || func == FuncBacklight) { RawSource item(param); return item.toString(model); } - else if ((func==FuncPlayPrompt) || (func==FuncPlayBoth)) { + else if (func == FuncPlayPrompt || func == FuncPlayBoth) { if ( getCurrentFirmware()->getCapability(VoicesAsNumbers)) { return QString("%1").arg(param); } @@ -197,7 +197,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return paramarm; } } - else if ((func>=FuncAdjustGV1) && (func= FuncAdjustGV1 && func < FuncCount) { switch (adjustMode) { case FUNC_ADJUST_GVAR_CONSTANT: return tr("Value ")+QString("%1").arg(param); @@ -231,11 +231,12 @@ QString CustomFunctionData::repeatToString() const QString CustomFunctionData::enabledToString() const { - if ((func>=FuncOverrideCH1 && func<=FuncOverrideCH32) || - (func>=FuncAdjustGV1 && func<=FuncAdjustGVLast) || - (func==FuncReset) || - (func>=FuncSetTimer1 && func<=FuncSetTimer2) || - (func==FuncVolume) || + if ((func >= FuncOverrideCH1 && func <= FuncOverrideCH32) || + (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) || + (func == FuncReset) || + (func >= FuncSetTimer1 && func <= FuncSetTimer2) || + (func == FuncVolume) || + (func == FuncBacklight) || (func <= FuncInstantTrim)) { if (!enabled) { return tr("DISABLED"); @@ -249,7 +250,7 @@ void CustomFunctionData::convert(RadioDataConversionState & cstate) cstate.setComponent(tr("CFN"), 8); cstate.setSubComp(nameToString(cstate.subCompIdx, (cstate.toModel() ? false : true))); swtch.convert(cstate); - if (func == FuncVolume || func == FuncPlayValue || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast && adjustMode == 1)) { + if (func == FuncVolume || func == FuncBacklight || func == FuncPlayValue || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast && adjustMode == 1)) { param = RawSource(param).convert(cstate.withComponentField("PARAM")).toValue(); } } diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index d849468c6..16ba240e0 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -718,7 +718,7 @@ int ModelData::updateReference() updateAssignFunc(cfd); if (!cfd->isEmpty()) { updateSwitchRef(cfd->swtch); - if (cfd->func == FuncVolume || cfd->func == FuncPlayValue || (cfd->func >= FuncAdjustGV1 && cfd->func <= FuncAdjustGVLast && (cfd->adjustMode == FUNC_ADJUST_GVAR_GVAR || cfd->adjustMode == FUNC_ADJUST_GVAR_SOURCE))) { + if (cfd->func == FuncVolume || cfd->func == FuncBacklight || cfd->func == FuncPlayValue || (cfd->func >= FuncAdjustGV1 && cfd->func <= FuncAdjustGVLast && (cfd->adjustMode == FUNC_ADJUST_GVAR_GVAR || cfd->adjustMode == FUNC_ADJUST_GVAR_SOURCE))) { updateSourceIntRef(cfd->param); if (cfd->param == 0) cfd->clear(); diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 6b8456eb2..f5c42443e 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -462,7 +462,7 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) else if (func >= FuncSetFailsafeInternalModule && func <= FuncBindExternalModule) { widgetsMask |= CUSTOM_FUNCTION_ENABLE; } - else if (func == FuncVolume) { + else if (func == FuncVolume || func == FuncBacklight) { if (modified) cfn.param = fswtchParamT[i]->currentData().toInt(); populateFuncParamCB(fswtchParamT[i], func, cfn.param); @@ -554,12 +554,6 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) } Helpers::populateFileComboBox(fswtchParamArmT[i], scriptsSet, cfn.paramarm); } - else if (func == FuncBacklight && IS_TARANIS_PLUS(getCurrentBoard())) { - if (modified) - cfn.param = (uint8_t)fswtchBLcolor[i]->value(); - fswtchBLcolor[i]->setValue(cfn.param); - widgetsMask |= CUSTOM_FUNCTION_BL_COLOR; - } else { if (modified) cfn.param = fswtchParam[i]->value(); @@ -701,28 +695,28 @@ void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsi { QStringList qs; b->setModel(new QStandardItemModel(b)); // clear combo box but not any shared item model - if (function==FuncPlaySound) { + if (function == FuncPlaySound) { CustomFunctionData::populatePlaySoundParams(qs); b->addItems(qs); b->setCurrentIndex(value); } - else if (function==FuncPlayHaptic) { + else if (function == FuncPlayHaptic) { CustomFunctionData::populateHapticParams(qs); b->addItems(qs); b->setCurrentIndex(value); } - else if (function==FuncReset) { + else if (function == FuncReset) { CustomFunctionData::populateResetParams(model, b, value); } - else if (function==FuncVolume) { + else if (function == FuncVolume || function == FuncBacklight) { b->setModel(rawSrcInputsItemModel); b->setCurrentIndex(b->findData(value)); } - else if (function==FuncPlayValue) { + else if (function == FuncPlayValue) { b->setModel(rawSrcAllItemModel); b->setCurrentIndex(b->findData(value)); } - else if (function>=FuncAdjustGV1 && function<=FuncAdjustGVLast) { + else if (function >= FuncAdjustGV1 && function <= FuncAdjustGVLast) { switch (adjustmode) { case 1: b->setModel(rawSrcInputsItemModel); diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index 0d1ebbf38..6b41254c4 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -340,7 +340,7 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext #if defined(VARIO) case FUNC_VARIO: - newActiveFunctions |= (1 << FUNCTION_VARIO); + newActiveFunctions |= (1u << FUNCTION_VARIO); break; #endif @@ -348,15 +348,22 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext #if defined(SDCARD) case FUNC_LOGS: if (CFN_PARAM(cfn)) { - newActiveFunctions |= (1 << FUNCTION_LOGS); + newActiveFunctions |= (1u << FUNCTION_LOGS); logDelay = CFN_PARAM(cfn); } break; #endif case FUNC_BACKLIGHT: - newActiveFunctions |= (1 << FUNCTION_BACKLIGHT); + { + getvalue_t raw = getValue(CFN_PARAM(cfn)); +#if defined(COLORLCD) + requiredBacklightBright = (1024 - raw) * (BACKLIGHT_LEVEL_MAX - BACKLIGHT_LEVEL_MIN) / 2048; +#else + requiredBacklightBright = (1024 - raw) * 100 / 2048; +#endif break; + } case FUNC_SCREENSHOT: if (!(functionsContext.activeSwitches & switch_mask)) { diff --git a/radio/src/globals.h b/radio/src/globals.h index cf67dbc4a..f63dfefe0 100644 --- a/radio/src/globals.h +++ b/radio/src/globals.h @@ -50,6 +50,7 @@ extern uint8_t trimsDisplayMask; extern uint16_t maxMixerDuration; extern uint8_t requiredSpeakerVolume; +extern uint8_t requiredBacklightBright; enum MainRequest { REQUEST_SCREENSHOT, diff --git a/radio/src/gui/128x64/model_special_functions.cpp b/radio/src/gui/128x64/model_special_functions.cpp index 2eaad8f2b..c85cdf2f2 100644 --- a/radio/src/gui/128x64/model_special_functions.cpp +++ b/radio/src/gui/128x64/model_special_functions.cpp @@ -338,6 +338,14 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF INCDEC_ENABLE_CHECK(isSourceAvailable); } } + else if (func == FUNC_BACKLIGHT) { + val_max = MIXSRC_LAST_CH; + drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); + if (active) { + INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } + } #if defined(SDCARD) else if (func == FUNC_LOGS) { if (val_displayed) { diff --git a/radio/src/gui/212x64/model_special_functions.cpp b/radio/src/gui/212x64/model_special_functions.cpp index 7c2d1e139..c8b74a124 100644 --- a/radio/src/gui/212x64/model_special_functions.cpp +++ b/radio/src/gui/212x64/model_special_functions.cpp @@ -338,14 +338,14 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF lcdDrawMMM(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, attr); } } -#if defined(PCBX9E) || defined(PCBX9DP) else if (func == FUNC_BACKLIGHT) { - drawSlider(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, CFN_PARAM(cfn), 100, attr); - if (active) INCDEC_SET_FLAG(eeFlags | NO_INCDEC_MARKS); - val_min = 0; - val_max = 100; + val_max = MIXSRC_LAST_CH; + drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); + if (active) { + INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } } -#endif #if defined(GVARS) else if (func == FUNC_ADJUST_GVAR) { switch (CFN_GVAR_MODE(cfn)) { diff --git a/radio/src/gui/480x272/model_special_functions.cpp b/radio/src/gui/480x272/model_special_functions.cpp index d8876a35d..712ab7ba2 100644 --- a/radio/src/gui/480x272/model_special_functions.cpp +++ b/radio/src/gui/480x272/model_special_functions.cpp @@ -333,6 +333,14 @@ bool menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF INCDEC_ENABLE_CHECK(isSourceAvailable); } } + else if (func == FUNC_BACKLIGHT) { + val_max = MIXSRC_LAST_CH; + drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); + if (active) { + INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE); + INCDEC_ENABLE_CHECK(isSourceAvailable); + } + } else if (func == FUNC_LOGS) { if (val_displayed) { lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|PREC1|LEFT, 0, NULL, "s"); diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 1490fb989..66426fd20 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -22,6 +22,8 @@ uint8_t currentSpeakerVolume = 255; uint8_t requiredSpeakerVolume = 255; +uint8_t currentBacklightBright = 255; +uint8_t requiredBacklightBright = 255; uint8_t mainRequestFlags = 0; #if defined(STM32) diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index 0e75f94d7..ee03c9f47 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -964,6 +964,7 @@ void evalMixes(uint8_t tick10ms) // must be done before limits because of the applyLimit function: it checks for safety switches which would be not initialized otherwise if (tick10ms) { requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF; + requiredBacklightBright = g_eeGeneral.backlightBright; if (!g_model.noGlobalFunctions) { evalFunctions(g_eeGeneral.customFn, globalFunctionsContext); diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index 952e94770..304012a0c 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -57,7 +57,7 @@ #define IS_HAPTIC_FUNC(func) (0) #endif -#define HAS_ENABLE_PARAM(func) ((func) < FUNC_FIRST_WITHOUT_ENABLE) +#define HAS_ENABLE_PARAM(func) ((func) < FUNC_FIRST_WITHOUT_ENABLE || (func == FUNC_BACKLIGHT)) #define HAS_REPEAT_PARAM(func) (IS_PLAY_FUNC(func) || IS_HAPTIC_FUNC(func)) #define CFN_EMPTY(p) (!(p)->swtch) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 77bb1b894..93572e07a 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -689,12 +689,19 @@ void checkBacklight() } } - bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || (g_eeGeneral.backlightMode != e_backlight_mode_off && lightOffCounter) || isFunctionActive(FUNCTION_BACKLIGHT)); - if (flashCounter) backlightOn = !backlightOn; - if (backlightOn) + bool backlightOn = (g_eeGeneral.backlightMode == e_backlight_mode_on || (g_eeGeneral.backlightMode != e_backlight_mode_off && lightOffCounter)); + + if (flashCounter) { + backlightOn = !backlightOn; + } + + if (backlightOn) { + currentBacklightBright = requiredBacklightBright; BACKLIGHT_ENABLE(); - else + } + else { BACKLIGHT_DISABLE(); + } } } @@ -1951,6 +1958,7 @@ void opentxInit() #endif currentSpeakerVolume = requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF; + currentBacklightBright = requiredBacklightBright = g_eeGeneral.backlightBright; #if !defined(SOFTWARE_VOLUME) setScaledVolume(currentSpeakerVolume); #endif diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 0d21d5df5..241b12f0c 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -487,6 +487,7 @@ void doMixerCalculations(); void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms); void checkTrims(); +extern uint8_t currentBacklightBright; void perMain(); void per10ms(); @@ -846,7 +847,6 @@ enum FunctionsActive { FUNCTION_TRAINER_CHANNELS = FUNCTION_TRAINER_STICK1 + NUM_STICKS, FUNCTION_INSTANT_TRIM, FUNCTION_VARIO, - FUNCTION_BACKLIGHT, #if defined(SDCARD) FUNCTION_LOGS, #endif diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index e4e99d40f..46e382d3f 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -552,7 +552,7 @@ void backlightEnable(uint8_t dutyCycle = 0); #else #define BACKLIGHT_LEVEL_MIN 46 #endif -#define BACKLIGHT_ENABLE() backlightEnable(globalData.unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright) +#define BACKLIGHT_ENABLE() backlightEnable(globalData.unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : BACKLIGHT_LEVEL_MAX - currentBacklightBright) #define BACKLIGHT_DISABLE() backlightEnable(globalData.unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : ((g_eeGeneral.blOffBright == BACKLIGHT_LEVEL_MIN) && (g_eeGeneral.backlightMode != e_backlight_mode_off)) ? 0 : g_eeGeneral.blOffBright) #define isBacklightEnabled() true diff --git a/radio/src/targets/sky9x/board.h b/radio/src/targets/sky9x/board.h index 8b1c7275c..fd737ceb3 100644 --- a/radio/src/targets/sky9x/board.h +++ b/radio/src/targets/sky9x/board.h @@ -269,7 +269,7 @@ extern "C" { #endif // Backlight driver -#define backlightEnable() (PWM->PWM_CH_NUM[0].PWM_CDTY = g_eeGeneral.backlightBright) +#define backlightEnable() (PWM->PWM_CH_NUM[0].PWM_CDTY = currentBacklightBright) #define backlightDisable() (PWM->PWM_CH_NUM[0].PWM_CDTY = 100) #define isBacklightEnabled() (PWM->PWM_CH_NUM[0].PWM_CDTY != 100) #define BACKLIGHT_ENABLE() backlightEnable() diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 62ad32a5f..5a0b2fbdf 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -665,10 +665,10 @@ uint8_t isBacklightEnabled(); #define backlightEnable(...) #elif defined(PCBX9E) || defined(PCBX9DP) void backlightEnable(uint8_t level = 0, uint8_t color = 0); - #define BACKLIGHT_ENABLE() backlightEnable(g_eeGeneral.backlightBright, g_eeGeneral.backlightColor) + #define BACKLIGHT_ENABLE() backlightEnable(currentBacklightBright, g_eeGeneral.backlightColor) #else void backlightEnable(uint8_t level = 0); - #define BACKLIGHT_ENABLE() backlightEnable(g_eeGeneral.backlightBright) + #define BACKLIGHT_ENABLE() backlightEnable(currentBacklightBright) #endif #if !defined(SIMU) From bb772afabb13717d1607b501ad1b9813b76cbe33 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Fri, 31 Jul 2020 16:07:59 +0100 Subject: [PATCH 113/267] Bars inverse - Closes #2495 (#7709) Enable inversed telemetry bars --- radio/src/gui/128x64/model_display.cpp | 4 ++-- radio/src/gui/128x64/view_telemetry.cpp | 11 ++--------- radio/src/gui/212x64/model_display.cpp | 4 ++-- radio/src/gui/212x64/view_telemetry.cpp | 4 ++-- radio/src/gui/480x272/widgets/gauge.cpp | 16 +++++++++------- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/radio/src/gui/128x64/model_display.cpp b/radio/src/gui/128x64/model_display.cpp index f138593e0..695be1777 100644 --- a/radio/src/gui/128x64/model_display.cpp +++ b/radio/src/gui/128x64/model_display.cpp @@ -242,10 +242,10 @@ void menuModelDisplay(event_t event) } break; case 1: - bar.barMin = checkIncDec(event, bar.barMin, barMin, bar.barMax, EE_MODEL|NO_INCDEC_MARKS); + bar.barMin = checkIncDec(event, bar.barMin, barMin, barMax, EE_MODEL|NO_INCDEC_MARKS); break; case 2: - bar.barMax = checkIncDec(event, bar.barMax, bar.barMin, barMax, EE_MODEL|NO_INCDEC_MARKS); + bar.barMax = checkIncDec(event, bar.barMax, barMin, barMax, EE_MODEL|NO_INCDEC_MARKS); break; } } diff --git a/radio/src/gui/128x64/view_telemetry.cpp b/radio/src/gui/128x64/view_telemetry.cpp index 86d369377..3b6835f64 100644 --- a/radio/src/gui/128x64/view_telemetry.cpp +++ b/radio/src/gui/128x64/view_telemetry.cpp @@ -68,27 +68,20 @@ bool displayGaugesTelemetryScreen(TelemetryScreenData & screen) barMin = calc100toRESX(barMin); barMax = calc100toRESX(barMax); } - if (source && barMax > barMin) { + if (source) { uint8_t y = barHeight+6+i*(barHeight+6); drawSource(0, y+barHeight/2-3, source, SMLSIZE); lcdDrawRect(BAR_LEFT, y, BAR_WIDTH+1, barHeight+2); getvalue_t value = getValue(source); - uint8_t thresholdX = 0; - - - uint8_t width = barCoord(value, barMin, barMax); - + uint8_t width = (barMin < barMax) ? barCoord(value, barMin, barMax) : 99 - barCoord(value, barMax, barMin); uint8_t barShade = SOLID; - lcdDrawFilledRect(BAR_LEFT+1, y+1, width, barHeight, barShade); - for (uint8_t j=24; j<99; j+=25) { if (j>thresholdX || j>width) { lcdDrawSolidVerticalLine(j*BAR_WIDTH/100+BAR_LEFT+1, y+1, barHeight); } } - if (thresholdX) { lcdDrawVerticalLine(BAR_LEFT+1+thresholdX, y-2, barHeight+3, DOTTED); lcdDrawSolidHorizontalLine(BAR_LEFT+thresholdX, y-2, 3); diff --git a/radio/src/gui/212x64/model_display.cpp b/radio/src/gui/212x64/model_display.cpp index 0e1da16d5..ce47f5306 100644 --- a/radio/src/gui/212x64/model_display.cpp +++ b/radio/src/gui/212x64/model_display.cpp @@ -245,10 +245,10 @@ void menuModelDisplay(event_t event) } break; case 1: - bar.barMin = checkIncDec(event, bar.barMin, barMin, bar.barMax, EE_MODEL|NO_INCDEC_MARKS); + bar.barMin = checkIncDec(event, bar.barMin, barMin, barMax, EE_MODEL|NO_INCDEC_MARKS); break; case 2: - bar.barMax = checkIncDec(event, bar.barMax, bar.barMin, barMax, EE_MODEL|NO_INCDEC_MARKS); + bar.barMax = checkIncDec(event, bar.barMax, barMin, barMax, EE_MODEL|NO_INCDEC_MARKS); break; } } diff --git a/radio/src/gui/212x64/view_telemetry.cpp b/radio/src/gui/212x64/view_telemetry.cpp index fa8740667..e15fc5781 100644 --- a/radio/src/gui/212x64/view_telemetry.cpp +++ b/radio/src/gui/212x64/view_telemetry.cpp @@ -66,14 +66,14 @@ void displayGaugesTelemetryScreen(TelemetryScreenData & screen) barMin = calc100toRESX(barMin); barMax = calc100toRESX(barMax); } - if (source && barMax > barMin) { + if (source) { int y = barHeight+6+i*(barHeight+6); drawSource(0, y+barHeight-5, source, 0); lcdDrawRect(BAR_LEFT, y, BAR_WIDTH+1, barHeight+2); getvalue_t value = getValue(source); drawSourceValue(BAR_LEFT+2+BAR_WIDTH, y+barHeight-5, source, LEFT); uint8_t thresholdX = 0; - int width = barCoord(value, barMin, barMax); + uint8_t width = (barMin < barMax) ? barCoord(value, barMin, barMax) : limit(0, 151 - barCoord(value, barMax, barMin), 151); uint8_t barShade = SOLID; lcdDrawFilledRect(BAR_LEFT+1, y+1, width, barHeight, barShade); for (uint8_t j=24; j<99; j+=25) { diff --git a/radio/src/gui/480x272/widgets/gauge.cpp b/radio/src/gui/480x272/widgets/gauge.cpp index 2558f8319..23957fd20 100644 --- a/radio/src/gui/480x272/widgets/gauge.cpp +++ b/radio/src/gui/480x272/widgets/gauge.cpp @@ -49,14 +49,16 @@ void GaugeWidget::refresh() uint16_t color = persistentData->options[3].unsignedValue; int32_t value = getValue(index); - int32_t value_in_range = value; - if (value < min) - value_in_range = min; - else if (value > max) - value_in_range = max; - int w = divRoundClosest(zone.w * (value_in_range - min), (max - min)); - int percent = divRoundClosest(100 * (value_in_range - min), (max - min)); + if (min > max) { + SWAP(min, max); + value = value - min - max; + } + + value = limit(min, value, max); + + int w = divRoundClosest(zone.w * (value - min), (max - min)); + int percent = divRoundClosest(100 * (value - min), (max - min)); // Gauge label drawSource(zone.x, zone.y, index, SMLSIZE | TEXT_INVERTED_COLOR); From 5118a8a59c8e99828aac5e1b090ac440897d9982 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 31 Jul 2020 17:09:00 +0200 Subject: [PATCH 114/267] add FrSky RB30_RB40.lua (#7660) Co-authored-by: WismyYao --- .../horus/SCRIPTS/TOOLS/FrSky RB30_RB40.lua | 464 ++++++++++++++++++ .../SCRIPTS/TOOLS/FrSky RB30_RB40.lua | 464 ++++++++++++++++++ .../SCRIPTS/TOOLS/FrSky RB30_RB40.lua | 464 ++++++++++++++++++ 3 files changed, 1392 insertions(+) create mode 100644 radio/sdcard/horus/SCRIPTS/TOOLS/FrSky RB30_RB40.lua create mode 100644 radio/sdcard/taranis-x7/SCRIPTS/TOOLS/FrSky RB30_RB40.lua create mode 100644 radio/sdcard/taranis-x9/SCRIPTS/TOOLS/FrSky RB30_RB40.lua diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/FrSky RB30_RB40.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/FrSky RB30_RB40.lua new file mode 100644 index 000000000..213faa6cf --- /dev/null +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/FrSky RB30_RB40.lua @@ -0,0 +1,464 @@ +--- - ######################################################################### +---- # # +---- # 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 version = "v1.01" + +local VALUE = 0 +local COMBO = 1 + +local edit = false +local page = 1 +local current = 1 +local refreshState = 0 +local refreshIndex = 0 +local pageOffset = 0 +local pages = {} +local fields = {} +local modifications = {} +local margin = 1 +local spacing = 8 +local numberPerPage = 7 + +local configFields = { + { "Self_stable mode", COMBO, 0x80, nil, { "disable", "enable"} }, + { "Sbus out", COMBO, 0x81, nil, { "disable", "enable"} }, + { "VOut1(V)", VALUE, 0xB2, nil, 50, 84}, + { "VOut2(V)", VALUE, 0xB3, nil, 50, 84}, +} + + +local failsafeFields = { + { "CH1 failsafe", VALUE, 0x82, 150, 80, 220}, + { "CH2 failsafe", VALUE, 0x82, 150, 80, 220}, + { "CH3 failsafe", VALUE, 0x83, 150, 80, 220}, + { "CH4 failsafe", VALUE, 0x83, 150, 80, 220}, + { "CH5 failsafe", VALUE, 0x84, 150, 80, 220}, + { "CH6 failsafe", VALUE, 0x84, 150, 80, 220}, + { "CH7 failsafe", VALUE, 0x85, 150, 80, 220}, + { "CH8 failsafe", VALUE, 0x85, 150, 80, 220}, + { "CH9 failsafe", VALUE, 0x86, 150, 80, 220}, + { "CH10 failsafe", VALUE, 0x86, 150, 80, 220}, + { "CH11 failsafe", VALUE, 0x87, 150, 80, 220}, + { "CH12 failsafe", VALUE, 0x87, 150, 80, 220}, + { "CH13 failsafe", VALUE, 0x88, 150, 80, 220}, + { "CH14 failsafe", VALUE, 0x88, 150, 80, 220}, + { "CH15 failsafe", VALUE, 0x89, 150, 80, 220}, + { "CH16 failsafe", VALUE, 0x89, 150, 80, 220}, + { "CH17 failsafe", VALUE, 0x8A, 150, 80, 220}, + { "CH18 failsafe", VALUE, 0x8A, 150, 80, 220}, + { "CH19 failsafe", VALUE, 0x8B, 150, 80, 220}, + { "CH20 failsafe", VALUE, 0x8B, 150, 80, 220}, + { "CH21 failsafe", VALUE, 0x8C, 150, 80, 220}, + { "CH22 failsafe", VALUE, 0x8C, 150, 80, 220}, + { "CH23 failsafe", VALUE, 0x8D, 150, 80, 220}, + { "CH24 failsafe", VALUE, 0x8D, 150, 80, 220}, +} + +local rx1PinMapFields = { + { "CH1 RX1 map", VALUE, 0x8E, 1, 0, 16}, + { "CH2 RX1 map", VALUE, 0x8E, 2, 0, 16}, + { "CH3 RX1 map", VALUE, 0x8F, 3, 0, 16}, + { "CH4 RX1 map", VALUE, 0x8F, 4, 0, 16}, + { "CH5 RX1 map", VALUE, 0x90, 5, 0, 16}, + { "CH6 RX1 map", VALUE, 0x90, 6, 0, 16}, + { "CH7 RX1 map", VALUE, 0x91, 7, 0, 16}, + { "CH8 RX1 map", VALUE, 0x91, 8, 0, 16}, + { "CH9 RX1 map", VALUE, 0x92, 9, 0, 16}, + { "CH10 RX1 map", VALUE, 0x92, 10, 0, 16}, + { "CH11 RX1 map", VALUE, 0x93, 11, 0, 16}, + { "CH12 RX1 map", VALUE, 0x93, 12, 0, 16}, + { "CH13 RX1 map", VALUE, 0x94, 13, 0, 16}, + { "CH14 RX1 map", VALUE, 0x94, 14, 0, 16}, + { "CH15 RX1 map", VALUE, 0x95, 15, 0, 16}, + { "CH16 RX1 map", VALUE, 0x95, 16, 0, 16}, + { "CH17 RX1 map", VALUE, 0x96, 1, 0, 16}, + { "CH18 RX1 map", VALUE, 0x96, 2, 0, 16}, + { "CH19 RX1 map", VALUE, 0x97, 3, 0, 16}, + { "CH20 RX1 map", VALUE, 0x97, 4, 0, 16}, + { "CH21 RX1 map", VALUE, 0x98, 5, 0, 16}, + { "CH22 RX1 map", VALUE, 0x98, 6, 0, 16}, + { "CH23 RX1 map", VALUE, 0x99, 7, 0, 16}, + { "CH24 RX1 map", VALUE, 0x99, 8, 0, 16}, +} + +local rx2PinMapFields = { + { "CH1 RX2 map", VALUE, 0x9A, 1, 0, 16}, + { "CH2 RX2 map", VALUE, 0x9A, 2, 0, 16}, + { "CH3 RX2 map", VALUE, 0x9B, 3, 0, 16}, + { "CH4 RX2 map", VALUE, 0x9B, 4, 0, 16}, + { "CH5 RX2 map", VALUE, 0x9C, 5, 0, 16}, + { "CH6 RX2 map", VALUE, 0x9C, 6, 0, 16}, + { "CH7 RX2 map", VALUE, 0x9D, 7, 0, 16}, + { "CH8 RX2 map", VALUE, 0x9D, 8, 0, 16}, + { "CH9 RX2 map", VALUE, 0x9E, 9, 0, 16}, + { "CH10 RX2 map", VALUE, 0x9E, 10, 0, 16}, + { "CH11 RX2 map", VALUE, 0x9F, 11, 0, 16}, + { "CH12 RX2 map", VALUE, 0x9F, 12, 0, 16}, + { "CH13 RX2 map", VALUE, 0xA0, 13, 0, 16}, + { "CH14 RX2 map", VALUE, 0xA0, 14, 0, 16}, + { "CH15 RX2 map", VALUE, 0xA1, 15, 0, 16}, + { "CH16 RX2 map", VALUE, 0xA0, 16, 0, 16}, + { "CH17 RX2 map", VALUE, 0xA2, 1, 0, 16}, + { "CH18 RX2 map", VALUE, 0xA2, 2, 0, 16}, + { "CH19 RX2 map", VALUE, 0xA3, 3, 0, 16}, + { "CH20 RX2 map", VALUE, 0xA3, 4, 0, 16}, + { "CH21 RX2 map", VALUE, 0xA4, 5, 0, 16}, + { "CH22 RX2 map", VALUE, 0xA4, 6, 0, 16}, + { "CH23 RX2 map", VALUE, 0xA5, 7, 0, 16}, + { "CH24 RX2 map", VALUE, 0xA5, 8, 0, 16}, +} + +local rx3PinMapFields = { + { "CH1 RX3 map", VALUE, 0xA6, 1, 0, 16}, + { "CH2 RX3 map", VALUE, 0xA6, 2, 0, 16}, + { "CH3 RX3 map", VALUE, 0xA7, 3, 0, 16}, + { "CH4 RX3 map", VALUE, 0xA7, 4, 0, 16}, + { "CH5 RX3 map", VALUE, 0xA8, 5, 0, 16}, + { "CH6 RX3 map", VALUE, 0xA8, 6, 0, 16}, + { "CH7 RX3 map", VALUE, 0xA9, 7, 0, 16}, + { "CH8 RX3 map", VALUE, 0xA9, 8, 0, 16}, + { "CH9 RX3 map", VALUE, 0xAA, 9, 0, 16}, + { "CH10 RX3 map", VALUE, 0xAA, 10, 0, 16}, + { "CH11 RX3 map", VALUE, 0xAB, 11, 0, 16}, + { "CH12 RX3 map", VALUE, 0xAB, 12, 0, 16}, + { "CH13 RX3 map", VALUE, 0xAC, 13, 0, 16}, + { "CH14 RX3 map", VALUE, 0xAC, 14, 0, 16}, + { "CH15 RX3 map", VALUE, 0xAD, 15, 0, 16}, + { "CH16 RX3 map", VALUE, 0xAD, 16, 0, 16}, + { "CH17 RX3 map", VALUE, 0xAE, 1, 0, 16}, + { "CH18 RX3 map", VALUE, 0xAE, 2, 0, 16}, + { "CH19 RX3 map", VALUE, 0xAF, 3, 0, 16}, + { "CH20 RX3 map", VALUE, 0xAF, 4, 0, 16}, + { "CH21 RX3 map", VALUE, 0xB0, 5, 0, 16}, + { "CH22 RX3 map", VALUE, 0xB0, 6, 0, 16}, + { "CH23 RX3 map", VALUE, 0xB1, 7, 0, 16}, + { "CH24 RX3 map", VALUE, 0xB1, 8, 0, 16}, +} + + + + +local function drawScreenTitle(title, page, pages) + if math.fmod(math.floor(getTime() / 100), 10) == 0 then + title = version + end + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + lcd.drawText(LCD_W - 40, 5, page .. "/" .. pages, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, page, pages) + end +end + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[2] == VALUE then + min = field[5] + max = field[6] + elseif field[2] == COMBO then + min = 0 + max = #(field[5]) - 1 + end + if page == 2 and step < 0 and field[4] == min then + field[4] = 0 + elseif page == 2 and step > 0 and field[4] == 0 then + field[4] = min + else + if (step < 0 and field[4] > min) or (step > 0 and field[4] < max) then + field[4] = field[4] + step + end + end +end + +-- Select the next or previous page +local function selectPage(step) + local pagesNum = #pages + if configFields[1][4] == 1 then + pagesNum = 1 + elseif configFields[2][4] == 1 then + pagesNum = pagesNum - 1 + end + page = 1 + ((page + step - 1 + pagesNum) % pagesNum) + refreshIndex = 0 + pageOffset = 0 +end + +-- Select the next or previous editable field +local function selectField(step) + current = 1 + ((current + step - 1 + #fields) % #fields) + if current > numberPerPage + pageOffset then + pageOffset = current - numberPerPage + elseif current <= pageOffset then + pageOffset = current - 1 + end +end + +local function drawProgressBar() + if LCD_W == 480 then + local width = (300 * refreshIndex) / #fields + lcd.drawRectangle(100, 10, 300, 6) + lcd.drawFilledRectangle(102, 12, width, 2) + else + local width = (60 * refreshIndex) / #fields + lcd.drawRectangle(45, 1, 60, 6) + lcd.drawFilledRectangle(47, 3, width, 2) + end +end + +-- Redraw the current page +local function redrawFieldPage() + lcd.clear() + + local pagesNum = #pages + if configFields[1][4] == 1 then + pagesNum = 1 + elseif configFields[2][4] == 1 then + pagesNum = pagesNum - 1 + end + drawScreenTitle("RB30/RB40", page, pagesNum) + + if refreshIndex < #fields then + drawProgressBar() + end + + for index = 1, numberPerPage, 1 do + local field = fields[pageOffset + index] + if field == nil then + break + end + + local attr = current == (pageOffset + index) and ((edit == true and BLINK or 0) + INVERS) or 0 + + lcd.drawText(1, margin + spacing * index, field[1], attr) + + if field[4] == nil then + lcd.drawText(LCD_W, margin + spacing * index, "---", RIGHT + attr) + else + if field[2] == VALUE then + if page == 2 then + if field[4] == 0 then + lcd.drawText(LCD_W, margin + spacing * index, "No pulse", RIGHT + attr) + else + lcd.drawNumber(LCD_W, margin + spacing * index, field[4] * 10, RIGHT + attr) + end + else + if field[4] == 0 then + lcd.drawText(LCD_W, margin + spacing * index, "No map", RIGHT + attr) + else + if field[3] == 0xB2 or field[3] == 0xB3 then + lcd.drawNumber(LCD_W, margin + spacing * index, field[4] , attr + RIGHT + PREC1) + else + lcd.drawNumber(LCD_W, margin + spacing * index, field[4], RIGHT + attr) + end + end + end + elseif field[2] == COMBO then + if field[4] >= 0 and field[4] < #(field[5]) then + lcd.drawText(LCD_W, margin + spacing * index, field[5][field[4] + 1], RIGHT + attr) + end + end + end + end +end + +local function telemetryIdle(field) + return sportTelemetryPush(0x1C, 0x21, 0x0b80, field) +end + +local function telemetryUnIdle(field) + return sportTelemetryPush(0x1C, 0x20, 0x0b80, field) +end + +local function telemetryRead(field) + return sportTelemetryPush(0x1C, 0x30, 0x0b80, field) +end + +local function telemetryWrite(field, value) + return sportTelemetryPush(0x1C, 0x31, 0x0b80, field + value * 256) +end + +local telemetryPopTimeout = 0 +local function refreshNext() + if refreshState == 0 then + if #modifications > 0 then + telemetryWrite(modifications[1][1], modifications[1][2]) + modifications[1] = nil + refreshIndex = 0 + refreshState = 0 + elseif refreshIndex < #fields then + local field = fields[refreshIndex + 1] + if page > 1 then + field = fields[refreshIndex + 2] + end + if telemetryRead(field[3]) == true then + refreshState = 1 + telemetryPopTimeout = getTime() + 100 -- normal delay is 500ms + end + end + elseif refreshState == 1 then + local physicalId, primId, dataId, value = sportTelemetryPop() + if primId == 0x32 and (dataId >= 0x0b80 or dataId <= 0x0b8f) then + local fieldId = value % 256 + local field = fields[refreshIndex + 1] + if fieldId == field[3] then + value = math.floor(value / 256) + if field[2] == COMBO then + fields[refreshIndex + 1][4] = value + else + fields[refreshIndex + 1][4] = value % 256 + if page > 1 then + value = math.floor(value / 256) + fields[refreshIndex + 2][4] = value + end + end + if page > 1 then + refreshIndex = refreshIndex + 2 + else + refreshIndex = refreshIndex + 1 + end + refreshState = 0 + end + elseif getTime() > telemetryPopTimeout then + fields[refreshIndex + 1][4] = nil + if page > 1 then + fields[refreshIndex + 2][4] = nil + refreshIndex = refreshIndex + 2 + else + refreshIndex = refreshIndex + 1 + end + refreshState = 0 + end + end +end + +local function updateField(field) + local value = field[4] + if field[2] == COMBO and #field == 6 then + value = field[6][1 + value] + elseif field[2] == VALUE and #field == 8 then + value = value + field[8] - field[5] + end + + if page > 1 then + if current % 2 == 1 then + value = value + fields[current + 1][4] * 256 + elseif current % 2 == 0 then + value = fields[current - 1][4] + value * 256 + end + end + modifications[#modifications + 1] = { field[3], value } +end + +-- Main +local function runFieldsPage(event) + if event == EVT_VIRTUAL_EXIT then -- exit script + telemetryUnIdle(0x80) + return 2 + elseif event == EVT_VIRTUAL_ENTER then -- toggle editing/selecting current field + if fields[current][4] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + addField(1) + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + addField(-1) + end + else + if event == EVT_VIRTUAL_NEXT then + selectField(1) + elseif event == EVT_VIRTUAL_PREV then + selectField(-1) + end + end + redrawFieldPage() + return 0 +end + +local function runConfigPage(event) + fields = configFields + return runFieldsPage(event) +end + +local function runFailsafePage(event) + fields = failsafeFields + return runFieldsPage(event) +end + +local function runRx1PinMapPage(event) + fields = rx1PinMapFields + return runFieldsPage(event) +end + +local function runRx2PinMapPage(event) + fields = rx2PinMapFields + return runFieldsPage(event) +end + +local function runRx3PinMapPage(event) + fields = rx3PinMapFields + return runFieldsPage(event) +end + +-- Init +local function init() + current, edit, refreshState, refreshIndex = 1, false, 0, 0 + if LCD_W == 480 then + margin = 10 + spacing = 20 + numberPerPage = 12 + end + pages = { + runConfigPage, + runFailsafePage, + runRx1PinMapPage, + runRx2PinMapPage, + runRx3PinMapPage + } + telemetryIdle(0x80) +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_NEXT_PAGE then + if edit == false then + selectPage(1) + end + elseif event == EVT_VIRTUAL_PREV_PAGE then + if edit == false then + killEvents(event) + selectPage(-1) + end + end + + local result = pages[page](event) + refreshNext() + + return result +end + +return { init = init, run = run } + diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/FrSky RB30_RB40.lua b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/FrSky RB30_RB40.lua new file mode 100644 index 000000000..213faa6cf --- /dev/null +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/FrSky RB30_RB40.lua @@ -0,0 +1,464 @@ +--- - ######################################################################### +---- # # +---- # 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 version = "v1.01" + +local VALUE = 0 +local COMBO = 1 + +local edit = false +local page = 1 +local current = 1 +local refreshState = 0 +local refreshIndex = 0 +local pageOffset = 0 +local pages = {} +local fields = {} +local modifications = {} +local margin = 1 +local spacing = 8 +local numberPerPage = 7 + +local configFields = { + { "Self_stable mode", COMBO, 0x80, nil, { "disable", "enable"} }, + { "Sbus out", COMBO, 0x81, nil, { "disable", "enable"} }, + { "VOut1(V)", VALUE, 0xB2, nil, 50, 84}, + { "VOut2(V)", VALUE, 0xB3, nil, 50, 84}, +} + + +local failsafeFields = { + { "CH1 failsafe", VALUE, 0x82, 150, 80, 220}, + { "CH2 failsafe", VALUE, 0x82, 150, 80, 220}, + { "CH3 failsafe", VALUE, 0x83, 150, 80, 220}, + { "CH4 failsafe", VALUE, 0x83, 150, 80, 220}, + { "CH5 failsafe", VALUE, 0x84, 150, 80, 220}, + { "CH6 failsafe", VALUE, 0x84, 150, 80, 220}, + { "CH7 failsafe", VALUE, 0x85, 150, 80, 220}, + { "CH8 failsafe", VALUE, 0x85, 150, 80, 220}, + { "CH9 failsafe", VALUE, 0x86, 150, 80, 220}, + { "CH10 failsafe", VALUE, 0x86, 150, 80, 220}, + { "CH11 failsafe", VALUE, 0x87, 150, 80, 220}, + { "CH12 failsafe", VALUE, 0x87, 150, 80, 220}, + { "CH13 failsafe", VALUE, 0x88, 150, 80, 220}, + { "CH14 failsafe", VALUE, 0x88, 150, 80, 220}, + { "CH15 failsafe", VALUE, 0x89, 150, 80, 220}, + { "CH16 failsafe", VALUE, 0x89, 150, 80, 220}, + { "CH17 failsafe", VALUE, 0x8A, 150, 80, 220}, + { "CH18 failsafe", VALUE, 0x8A, 150, 80, 220}, + { "CH19 failsafe", VALUE, 0x8B, 150, 80, 220}, + { "CH20 failsafe", VALUE, 0x8B, 150, 80, 220}, + { "CH21 failsafe", VALUE, 0x8C, 150, 80, 220}, + { "CH22 failsafe", VALUE, 0x8C, 150, 80, 220}, + { "CH23 failsafe", VALUE, 0x8D, 150, 80, 220}, + { "CH24 failsafe", VALUE, 0x8D, 150, 80, 220}, +} + +local rx1PinMapFields = { + { "CH1 RX1 map", VALUE, 0x8E, 1, 0, 16}, + { "CH2 RX1 map", VALUE, 0x8E, 2, 0, 16}, + { "CH3 RX1 map", VALUE, 0x8F, 3, 0, 16}, + { "CH4 RX1 map", VALUE, 0x8F, 4, 0, 16}, + { "CH5 RX1 map", VALUE, 0x90, 5, 0, 16}, + { "CH6 RX1 map", VALUE, 0x90, 6, 0, 16}, + { "CH7 RX1 map", VALUE, 0x91, 7, 0, 16}, + { "CH8 RX1 map", VALUE, 0x91, 8, 0, 16}, + { "CH9 RX1 map", VALUE, 0x92, 9, 0, 16}, + { "CH10 RX1 map", VALUE, 0x92, 10, 0, 16}, + { "CH11 RX1 map", VALUE, 0x93, 11, 0, 16}, + { "CH12 RX1 map", VALUE, 0x93, 12, 0, 16}, + { "CH13 RX1 map", VALUE, 0x94, 13, 0, 16}, + { "CH14 RX1 map", VALUE, 0x94, 14, 0, 16}, + { "CH15 RX1 map", VALUE, 0x95, 15, 0, 16}, + { "CH16 RX1 map", VALUE, 0x95, 16, 0, 16}, + { "CH17 RX1 map", VALUE, 0x96, 1, 0, 16}, + { "CH18 RX1 map", VALUE, 0x96, 2, 0, 16}, + { "CH19 RX1 map", VALUE, 0x97, 3, 0, 16}, + { "CH20 RX1 map", VALUE, 0x97, 4, 0, 16}, + { "CH21 RX1 map", VALUE, 0x98, 5, 0, 16}, + { "CH22 RX1 map", VALUE, 0x98, 6, 0, 16}, + { "CH23 RX1 map", VALUE, 0x99, 7, 0, 16}, + { "CH24 RX1 map", VALUE, 0x99, 8, 0, 16}, +} + +local rx2PinMapFields = { + { "CH1 RX2 map", VALUE, 0x9A, 1, 0, 16}, + { "CH2 RX2 map", VALUE, 0x9A, 2, 0, 16}, + { "CH3 RX2 map", VALUE, 0x9B, 3, 0, 16}, + { "CH4 RX2 map", VALUE, 0x9B, 4, 0, 16}, + { "CH5 RX2 map", VALUE, 0x9C, 5, 0, 16}, + { "CH6 RX2 map", VALUE, 0x9C, 6, 0, 16}, + { "CH7 RX2 map", VALUE, 0x9D, 7, 0, 16}, + { "CH8 RX2 map", VALUE, 0x9D, 8, 0, 16}, + { "CH9 RX2 map", VALUE, 0x9E, 9, 0, 16}, + { "CH10 RX2 map", VALUE, 0x9E, 10, 0, 16}, + { "CH11 RX2 map", VALUE, 0x9F, 11, 0, 16}, + { "CH12 RX2 map", VALUE, 0x9F, 12, 0, 16}, + { "CH13 RX2 map", VALUE, 0xA0, 13, 0, 16}, + { "CH14 RX2 map", VALUE, 0xA0, 14, 0, 16}, + { "CH15 RX2 map", VALUE, 0xA1, 15, 0, 16}, + { "CH16 RX2 map", VALUE, 0xA0, 16, 0, 16}, + { "CH17 RX2 map", VALUE, 0xA2, 1, 0, 16}, + { "CH18 RX2 map", VALUE, 0xA2, 2, 0, 16}, + { "CH19 RX2 map", VALUE, 0xA3, 3, 0, 16}, + { "CH20 RX2 map", VALUE, 0xA3, 4, 0, 16}, + { "CH21 RX2 map", VALUE, 0xA4, 5, 0, 16}, + { "CH22 RX2 map", VALUE, 0xA4, 6, 0, 16}, + { "CH23 RX2 map", VALUE, 0xA5, 7, 0, 16}, + { "CH24 RX2 map", VALUE, 0xA5, 8, 0, 16}, +} + +local rx3PinMapFields = { + { "CH1 RX3 map", VALUE, 0xA6, 1, 0, 16}, + { "CH2 RX3 map", VALUE, 0xA6, 2, 0, 16}, + { "CH3 RX3 map", VALUE, 0xA7, 3, 0, 16}, + { "CH4 RX3 map", VALUE, 0xA7, 4, 0, 16}, + { "CH5 RX3 map", VALUE, 0xA8, 5, 0, 16}, + { "CH6 RX3 map", VALUE, 0xA8, 6, 0, 16}, + { "CH7 RX3 map", VALUE, 0xA9, 7, 0, 16}, + { "CH8 RX3 map", VALUE, 0xA9, 8, 0, 16}, + { "CH9 RX3 map", VALUE, 0xAA, 9, 0, 16}, + { "CH10 RX3 map", VALUE, 0xAA, 10, 0, 16}, + { "CH11 RX3 map", VALUE, 0xAB, 11, 0, 16}, + { "CH12 RX3 map", VALUE, 0xAB, 12, 0, 16}, + { "CH13 RX3 map", VALUE, 0xAC, 13, 0, 16}, + { "CH14 RX3 map", VALUE, 0xAC, 14, 0, 16}, + { "CH15 RX3 map", VALUE, 0xAD, 15, 0, 16}, + { "CH16 RX3 map", VALUE, 0xAD, 16, 0, 16}, + { "CH17 RX3 map", VALUE, 0xAE, 1, 0, 16}, + { "CH18 RX3 map", VALUE, 0xAE, 2, 0, 16}, + { "CH19 RX3 map", VALUE, 0xAF, 3, 0, 16}, + { "CH20 RX3 map", VALUE, 0xAF, 4, 0, 16}, + { "CH21 RX3 map", VALUE, 0xB0, 5, 0, 16}, + { "CH22 RX3 map", VALUE, 0xB0, 6, 0, 16}, + { "CH23 RX3 map", VALUE, 0xB1, 7, 0, 16}, + { "CH24 RX3 map", VALUE, 0xB1, 8, 0, 16}, +} + + + + +local function drawScreenTitle(title, page, pages) + if math.fmod(math.floor(getTime() / 100), 10) == 0 then + title = version + end + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + lcd.drawText(LCD_W - 40, 5, page .. "/" .. pages, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, page, pages) + end +end + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[2] == VALUE then + min = field[5] + max = field[6] + elseif field[2] == COMBO then + min = 0 + max = #(field[5]) - 1 + end + if page == 2 and step < 0 and field[4] == min then + field[4] = 0 + elseif page == 2 and step > 0 and field[4] == 0 then + field[4] = min + else + if (step < 0 and field[4] > min) or (step > 0 and field[4] < max) then + field[4] = field[4] + step + end + end +end + +-- Select the next or previous page +local function selectPage(step) + local pagesNum = #pages + if configFields[1][4] == 1 then + pagesNum = 1 + elseif configFields[2][4] == 1 then + pagesNum = pagesNum - 1 + end + page = 1 + ((page + step - 1 + pagesNum) % pagesNum) + refreshIndex = 0 + pageOffset = 0 +end + +-- Select the next or previous editable field +local function selectField(step) + current = 1 + ((current + step - 1 + #fields) % #fields) + if current > numberPerPage + pageOffset then + pageOffset = current - numberPerPage + elseif current <= pageOffset then + pageOffset = current - 1 + end +end + +local function drawProgressBar() + if LCD_W == 480 then + local width = (300 * refreshIndex) / #fields + lcd.drawRectangle(100, 10, 300, 6) + lcd.drawFilledRectangle(102, 12, width, 2) + else + local width = (60 * refreshIndex) / #fields + lcd.drawRectangle(45, 1, 60, 6) + lcd.drawFilledRectangle(47, 3, width, 2) + end +end + +-- Redraw the current page +local function redrawFieldPage() + lcd.clear() + + local pagesNum = #pages + if configFields[1][4] == 1 then + pagesNum = 1 + elseif configFields[2][4] == 1 then + pagesNum = pagesNum - 1 + end + drawScreenTitle("RB30/RB40", page, pagesNum) + + if refreshIndex < #fields then + drawProgressBar() + end + + for index = 1, numberPerPage, 1 do + local field = fields[pageOffset + index] + if field == nil then + break + end + + local attr = current == (pageOffset + index) and ((edit == true and BLINK or 0) + INVERS) or 0 + + lcd.drawText(1, margin + spacing * index, field[1], attr) + + if field[4] == nil then + lcd.drawText(LCD_W, margin + spacing * index, "---", RIGHT + attr) + else + if field[2] == VALUE then + if page == 2 then + if field[4] == 0 then + lcd.drawText(LCD_W, margin + spacing * index, "No pulse", RIGHT + attr) + else + lcd.drawNumber(LCD_W, margin + spacing * index, field[4] * 10, RIGHT + attr) + end + else + if field[4] == 0 then + lcd.drawText(LCD_W, margin + spacing * index, "No map", RIGHT + attr) + else + if field[3] == 0xB2 or field[3] == 0xB3 then + lcd.drawNumber(LCD_W, margin + spacing * index, field[4] , attr + RIGHT + PREC1) + else + lcd.drawNumber(LCD_W, margin + spacing * index, field[4], RIGHT + attr) + end + end + end + elseif field[2] == COMBO then + if field[4] >= 0 and field[4] < #(field[5]) then + lcd.drawText(LCD_W, margin + spacing * index, field[5][field[4] + 1], RIGHT + attr) + end + end + end + end +end + +local function telemetryIdle(field) + return sportTelemetryPush(0x1C, 0x21, 0x0b80, field) +end + +local function telemetryUnIdle(field) + return sportTelemetryPush(0x1C, 0x20, 0x0b80, field) +end + +local function telemetryRead(field) + return sportTelemetryPush(0x1C, 0x30, 0x0b80, field) +end + +local function telemetryWrite(field, value) + return sportTelemetryPush(0x1C, 0x31, 0x0b80, field + value * 256) +end + +local telemetryPopTimeout = 0 +local function refreshNext() + if refreshState == 0 then + if #modifications > 0 then + telemetryWrite(modifications[1][1], modifications[1][2]) + modifications[1] = nil + refreshIndex = 0 + refreshState = 0 + elseif refreshIndex < #fields then + local field = fields[refreshIndex + 1] + if page > 1 then + field = fields[refreshIndex + 2] + end + if telemetryRead(field[3]) == true then + refreshState = 1 + telemetryPopTimeout = getTime() + 100 -- normal delay is 500ms + end + end + elseif refreshState == 1 then + local physicalId, primId, dataId, value = sportTelemetryPop() + if primId == 0x32 and (dataId >= 0x0b80 or dataId <= 0x0b8f) then + local fieldId = value % 256 + local field = fields[refreshIndex + 1] + if fieldId == field[3] then + value = math.floor(value / 256) + if field[2] == COMBO then + fields[refreshIndex + 1][4] = value + else + fields[refreshIndex + 1][4] = value % 256 + if page > 1 then + value = math.floor(value / 256) + fields[refreshIndex + 2][4] = value + end + end + if page > 1 then + refreshIndex = refreshIndex + 2 + else + refreshIndex = refreshIndex + 1 + end + refreshState = 0 + end + elseif getTime() > telemetryPopTimeout then + fields[refreshIndex + 1][4] = nil + if page > 1 then + fields[refreshIndex + 2][4] = nil + refreshIndex = refreshIndex + 2 + else + refreshIndex = refreshIndex + 1 + end + refreshState = 0 + end + end +end + +local function updateField(field) + local value = field[4] + if field[2] == COMBO and #field == 6 then + value = field[6][1 + value] + elseif field[2] == VALUE and #field == 8 then + value = value + field[8] - field[5] + end + + if page > 1 then + if current % 2 == 1 then + value = value + fields[current + 1][4] * 256 + elseif current % 2 == 0 then + value = fields[current - 1][4] + value * 256 + end + end + modifications[#modifications + 1] = { field[3], value } +end + +-- Main +local function runFieldsPage(event) + if event == EVT_VIRTUAL_EXIT then -- exit script + telemetryUnIdle(0x80) + return 2 + elseif event == EVT_VIRTUAL_ENTER then -- toggle editing/selecting current field + if fields[current][4] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + addField(1) + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + addField(-1) + end + else + if event == EVT_VIRTUAL_NEXT then + selectField(1) + elseif event == EVT_VIRTUAL_PREV then + selectField(-1) + end + end + redrawFieldPage() + return 0 +end + +local function runConfigPage(event) + fields = configFields + return runFieldsPage(event) +end + +local function runFailsafePage(event) + fields = failsafeFields + return runFieldsPage(event) +end + +local function runRx1PinMapPage(event) + fields = rx1PinMapFields + return runFieldsPage(event) +end + +local function runRx2PinMapPage(event) + fields = rx2PinMapFields + return runFieldsPage(event) +end + +local function runRx3PinMapPage(event) + fields = rx3PinMapFields + return runFieldsPage(event) +end + +-- Init +local function init() + current, edit, refreshState, refreshIndex = 1, false, 0, 0 + if LCD_W == 480 then + margin = 10 + spacing = 20 + numberPerPage = 12 + end + pages = { + runConfigPage, + runFailsafePage, + runRx1PinMapPage, + runRx2PinMapPage, + runRx3PinMapPage + } + telemetryIdle(0x80) +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_NEXT_PAGE then + if edit == false then + selectPage(1) + end + elseif event == EVT_VIRTUAL_PREV_PAGE then + if edit == false then + killEvents(event) + selectPage(-1) + end + end + + local result = pages[page](event) + refreshNext() + + return result +end + +return { init = init, run = run } + diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/FrSky RB30_RB40.lua b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/FrSky RB30_RB40.lua new file mode 100644 index 000000000..213faa6cf --- /dev/null +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/FrSky RB30_RB40.lua @@ -0,0 +1,464 @@ +--- - ######################################################################### +---- # # +---- # 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 version = "v1.01" + +local VALUE = 0 +local COMBO = 1 + +local edit = false +local page = 1 +local current = 1 +local refreshState = 0 +local refreshIndex = 0 +local pageOffset = 0 +local pages = {} +local fields = {} +local modifications = {} +local margin = 1 +local spacing = 8 +local numberPerPage = 7 + +local configFields = { + { "Self_stable mode", COMBO, 0x80, nil, { "disable", "enable"} }, + { "Sbus out", COMBO, 0x81, nil, { "disable", "enable"} }, + { "VOut1(V)", VALUE, 0xB2, nil, 50, 84}, + { "VOut2(V)", VALUE, 0xB3, nil, 50, 84}, +} + + +local failsafeFields = { + { "CH1 failsafe", VALUE, 0x82, 150, 80, 220}, + { "CH2 failsafe", VALUE, 0x82, 150, 80, 220}, + { "CH3 failsafe", VALUE, 0x83, 150, 80, 220}, + { "CH4 failsafe", VALUE, 0x83, 150, 80, 220}, + { "CH5 failsafe", VALUE, 0x84, 150, 80, 220}, + { "CH6 failsafe", VALUE, 0x84, 150, 80, 220}, + { "CH7 failsafe", VALUE, 0x85, 150, 80, 220}, + { "CH8 failsafe", VALUE, 0x85, 150, 80, 220}, + { "CH9 failsafe", VALUE, 0x86, 150, 80, 220}, + { "CH10 failsafe", VALUE, 0x86, 150, 80, 220}, + { "CH11 failsafe", VALUE, 0x87, 150, 80, 220}, + { "CH12 failsafe", VALUE, 0x87, 150, 80, 220}, + { "CH13 failsafe", VALUE, 0x88, 150, 80, 220}, + { "CH14 failsafe", VALUE, 0x88, 150, 80, 220}, + { "CH15 failsafe", VALUE, 0x89, 150, 80, 220}, + { "CH16 failsafe", VALUE, 0x89, 150, 80, 220}, + { "CH17 failsafe", VALUE, 0x8A, 150, 80, 220}, + { "CH18 failsafe", VALUE, 0x8A, 150, 80, 220}, + { "CH19 failsafe", VALUE, 0x8B, 150, 80, 220}, + { "CH20 failsafe", VALUE, 0x8B, 150, 80, 220}, + { "CH21 failsafe", VALUE, 0x8C, 150, 80, 220}, + { "CH22 failsafe", VALUE, 0x8C, 150, 80, 220}, + { "CH23 failsafe", VALUE, 0x8D, 150, 80, 220}, + { "CH24 failsafe", VALUE, 0x8D, 150, 80, 220}, +} + +local rx1PinMapFields = { + { "CH1 RX1 map", VALUE, 0x8E, 1, 0, 16}, + { "CH2 RX1 map", VALUE, 0x8E, 2, 0, 16}, + { "CH3 RX1 map", VALUE, 0x8F, 3, 0, 16}, + { "CH4 RX1 map", VALUE, 0x8F, 4, 0, 16}, + { "CH5 RX1 map", VALUE, 0x90, 5, 0, 16}, + { "CH6 RX1 map", VALUE, 0x90, 6, 0, 16}, + { "CH7 RX1 map", VALUE, 0x91, 7, 0, 16}, + { "CH8 RX1 map", VALUE, 0x91, 8, 0, 16}, + { "CH9 RX1 map", VALUE, 0x92, 9, 0, 16}, + { "CH10 RX1 map", VALUE, 0x92, 10, 0, 16}, + { "CH11 RX1 map", VALUE, 0x93, 11, 0, 16}, + { "CH12 RX1 map", VALUE, 0x93, 12, 0, 16}, + { "CH13 RX1 map", VALUE, 0x94, 13, 0, 16}, + { "CH14 RX1 map", VALUE, 0x94, 14, 0, 16}, + { "CH15 RX1 map", VALUE, 0x95, 15, 0, 16}, + { "CH16 RX1 map", VALUE, 0x95, 16, 0, 16}, + { "CH17 RX1 map", VALUE, 0x96, 1, 0, 16}, + { "CH18 RX1 map", VALUE, 0x96, 2, 0, 16}, + { "CH19 RX1 map", VALUE, 0x97, 3, 0, 16}, + { "CH20 RX1 map", VALUE, 0x97, 4, 0, 16}, + { "CH21 RX1 map", VALUE, 0x98, 5, 0, 16}, + { "CH22 RX1 map", VALUE, 0x98, 6, 0, 16}, + { "CH23 RX1 map", VALUE, 0x99, 7, 0, 16}, + { "CH24 RX1 map", VALUE, 0x99, 8, 0, 16}, +} + +local rx2PinMapFields = { + { "CH1 RX2 map", VALUE, 0x9A, 1, 0, 16}, + { "CH2 RX2 map", VALUE, 0x9A, 2, 0, 16}, + { "CH3 RX2 map", VALUE, 0x9B, 3, 0, 16}, + { "CH4 RX2 map", VALUE, 0x9B, 4, 0, 16}, + { "CH5 RX2 map", VALUE, 0x9C, 5, 0, 16}, + { "CH6 RX2 map", VALUE, 0x9C, 6, 0, 16}, + { "CH7 RX2 map", VALUE, 0x9D, 7, 0, 16}, + { "CH8 RX2 map", VALUE, 0x9D, 8, 0, 16}, + { "CH9 RX2 map", VALUE, 0x9E, 9, 0, 16}, + { "CH10 RX2 map", VALUE, 0x9E, 10, 0, 16}, + { "CH11 RX2 map", VALUE, 0x9F, 11, 0, 16}, + { "CH12 RX2 map", VALUE, 0x9F, 12, 0, 16}, + { "CH13 RX2 map", VALUE, 0xA0, 13, 0, 16}, + { "CH14 RX2 map", VALUE, 0xA0, 14, 0, 16}, + { "CH15 RX2 map", VALUE, 0xA1, 15, 0, 16}, + { "CH16 RX2 map", VALUE, 0xA0, 16, 0, 16}, + { "CH17 RX2 map", VALUE, 0xA2, 1, 0, 16}, + { "CH18 RX2 map", VALUE, 0xA2, 2, 0, 16}, + { "CH19 RX2 map", VALUE, 0xA3, 3, 0, 16}, + { "CH20 RX2 map", VALUE, 0xA3, 4, 0, 16}, + { "CH21 RX2 map", VALUE, 0xA4, 5, 0, 16}, + { "CH22 RX2 map", VALUE, 0xA4, 6, 0, 16}, + { "CH23 RX2 map", VALUE, 0xA5, 7, 0, 16}, + { "CH24 RX2 map", VALUE, 0xA5, 8, 0, 16}, +} + +local rx3PinMapFields = { + { "CH1 RX3 map", VALUE, 0xA6, 1, 0, 16}, + { "CH2 RX3 map", VALUE, 0xA6, 2, 0, 16}, + { "CH3 RX3 map", VALUE, 0xA7, 3, 0, 16}, + { "CH4 RX3 map", VALUE, 0xA7, 4, 0, 16}, + { "CH5 RX3 map", VALUE, 0xA8, 5, 0, 16}, + { "CH6 RX3 map", VALUE, 0xA8, 6, 0, 16}, + { "CH7 RX3 map", VALUE, 0xA9, 7, 0, 16}, + { "CH8 RX3 map", VALUE, 0xA9, 8, 0, 16}, + { "CH9 RX3 map", VALUE, 0xAA, 9, 0, 16}, + { "CH10 RX3 map", VALUE, 0xAA, 10, 0, 16}, + { "CH11 RX3 map", VALUE, 0xAB, 11, 0, 16}, + { "CH12 RX3 map", VALUE, 0xAB, 12, 0, 16}, + { "CH13 RX3 map", VALUE, 0xAC, 13, 0, 16}, + { "CH14 RX3 map", VALUE, 0xAC, 14, 0, 16}, + { "CH15 RX3 map", VALUE, 0xAD, 15, 0, 16}, + { "CH16 RX3 map", VALUE, 0xAD, 16, 0, 16}, + { "CH17 RX3 map", VALUE, 0xAE, 1, 0, 16}, + { "CH18 RX3 map", VALUE, 0xAE, 2, 0, 16}, + { "CH19 RX3 map", VALUE, 0xAF, 3, 0, 16}, + { "CH20 RX3 map", VALUE, 0xAF, 4, 0, 16}, + { "CH21 RX3 map", VALUE, 0xB0, 5, 0, 16}, + { "CH22 RX3 map", VALUE, 0xB0, 6, 0, 16}, + { "CH23 RX3 map", VALUE, 0xB1, 7, 0, 16}, + { "CH24 RX3 map", VALUE, 0xB1, 8, 0, 16}, +} + + + + +local function drawScreenTitle(title, page, pages) + if math.fmod(math.floor(getTime() / 100), 10) == 0 then + title = version + end + if LCD_W == 480 then + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + lcd.drawText(LCD_W - 40, 5, page .. "/" .. pages, MENU_TITLE_COLOR) + else + lcd.drawScreenTitle(title, page, pages) + end +end + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[2] == VALUE then + min = field[5] + max = field[6] + elseif field[2] == COMBO then + min = 0 + max = #(field[5]) - 1 + end + if page == 2 and step < 0 and field[4] == min then + field[4] = 0 + elseif page == 2 and step > 0 and field[4] == 0 then + field[4] = min + else + if (step < 0 and field[4] > min) or (step > 0 and field[4] < max) then + field[4] = field[4] + step + end + end +end + +-- Select the next or previous page +local function selectPage(step) + local pagesNum = #pages + if configFields[1][4] == 1 then + pagesNum = 1 + elseif configFields[2][4] == 1 then + pagesNum = pagesNum - 1 + end + page = 1 + ((page + step - 1 + pagesNum) % pagesNum) + refreshIndex = 0 + pageOffset = 0 +end + +-- Select the next or previous editable field +local function selectField(step) + current = 1 + ((current + step - 1 + #fields) % #fields) + if current > numberPerPage + pageOffset then + pageOffset = current - numberPerPage + elseif current <= pageOffset then + pageOffset = current - 1 + end +end + +local function drawProgressBar() + if LCD_W == 480 then + local width = (300 * refreshIndex) / #fields + lcd.drawRectangle(100, 10, 300, 6) + lcd.drawFilledRectangle(102, 12, width, 2) + else + local width = (60 * refreshIndex) / #fields + lcd.drawRectangle(45, 1, 60, 6) + lcd.drawFilledRectangle(47, 3, width, 2) + end +end + +-- Redraw the current page +local function redrawFieldPage() + lcd.clear() + + local pagesNum = #pages + if configFields[1][4] == 1 then + pagesNum = 1 + elseif configFields[2][4] == 1 then + pagesNum = pagesNum - 1 + end + drawScreenTitle("RB30/RB40", page, pagesNum) + + if refreshIndex < #fields then + drawProgressBar() + end + + for index = 1, numberPerPage, 1 do + local field = fields[pageOffset + index] + if field == nil then + break + end + + local attr = current == (pageOffset + index) and ((edit == true and BLINK or 0) + INVERS) or 0 + + lcd.drawText(1, margin + spacing * index, field[1], attr) + + if field[4] == nil then + lcd.drawText(LCD_W, margin + spacing * index, "---", RIGHT + attr) + else + if field[2] == VALUE then + if page == 2 then + if field[4] == 0 then + lcd.drawText(LCD_W, margin + spacing * index, "No pulse", RIGHT + attr) + else + lcd.drawNumber(LCD_W, margin + spacing * index, field[4] * 10, RIGHT + attr) + end + else + if field[4] == 0 then + lcd.drawText(LCD_W, margin + spacing * index, "No map", RIGHT + attr) + else + if field[3] == 0xB2 or field[3] == 0xB3 then + lcd.drawNumber(LCD_W, margin + spacing * index, field[4] , attr + RIGHT + PREC1) + else + lcd.drawNumber(LCD_W, margin + spacing * index, field[4], RIGHT + attr) + end + end + end + elseif field[2] == COMBO then + if field[4] >= 0 and field[4] < #(field[5]) then + lcd.drawText(LCD_W, margin + spacing * index, field[5][field[4] + 1], RIGHT + attr) + end + end + end + end +end + +local function telemetryIdle(field) + return sportTelemetryPush(0x1C, 0x21, 0x0b80, field) +end + +local function telemetryUnIdle(field) + return sportTelemetryPush(0x1C, 0x20, 0x0b80, field) +end + +local function telemetryRead(field) + return sportTelemetryPush(0x1C, 0x30, 0x0b80, field) +end + +local function telemetryWrite(field, value) + return sportTelemetryPush(0x1C, 0x31, 0x0b80, field + value * 256) +end + +local telemetryPopTimeout = 0 +local function refreshNext() + if refreshState == 0 then + if #modifications > 0 then + telemetryWrite(modifications[1][1], modifications[1][2]) + modifications[1] = nil + refreshIndex = 0 + refreshState = 0 + elseif refreshIndex < #fields then + local field = fields[refreshIndex + 1] + if page > 1 then + field = fields[refreshIndex + 2] + end + if telemetryRead(field[3]) == true then + refreshState = 1 + telemetryPopTimeout = getTime() + 100 -- normal delay is 500ms + end + end + elseif refreshState == 1 then + local physicalId, primId, dataId, value = sportTelemetryPop() + if primId == 0x32 and (dataId >= 0x0b80 or dataId <= 0x0b8f) then + local fieldId = value % 256 + local field = fields[refreshIndex + 1] + if fieldId == field[3] then + value = math.floor(value / 256) + if field[2] == COMBO then + fields[refreshIndex + 1][4] = value + else + fields[refreshIndex + 1][4] = value % 256 + if page > 1 then + value = math.floor(value / 256) + fields[refreshIndex + 2][4] = value + end + end + if page > 1 then + refreshIndex = refreshIndex + 2 + else + refreshIndex = refreshIndex + 1 + end + refreshState = 0 + end + elseif getTime() > telemetryPopTimeout then + fields[refreshIndex + 1][4] = nil + if page > 1 then + fields[refreshIndex + 2][4] = nil + refreshIndex = refreshIndex + 2 + else + refreshIndex = refreshIndex + 1 + end + refreshState = 0 + end + end +end + +local function updateField(field) + local value = field[4] + if field[2] == COMBO and #field == 6 then + value = field[6][1 + value] + elseif field[2] == VALUE and #field == 8 then + value = value + field[8] - field[5] + end + + if page > 1 then + if current % 2 == 1 then + value = value + fields[current + 1][4] * 256 + elseif current % 2 == 0 then + value = fields[current - 1][4] + value * 256 + end + end + modifications[#modifications + 1] = { field[3], value } +end + +-- Main +local function runFieldsPage(event) + if event == EVT_VIRTUAL_EXIT then -- exit script + telemetryUnIdle(0x80) + return 2 + elseif event == EVT_VIRTUAL_ENTER then -- toggle editing/selecting current field + if fields[current][4] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + addField(1) + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + addField(-1) + end + else + if event == EVT_VIRTUAL_NEXT then + selectField(1) + elseif event == EVT_VIRTUAL_PREV then + selectField(-1) + end + end + redrawFieldPage() + return 0 +end + +local function runConfigPage(event) + fields = configFields + return runFieldsPage(event) +end + +local function runFailsafePage(event) + fields = failsafeFields + return runFieldsPage(event) +end + +local function runRx1PinMapPage(event) + fields = rx1PinMapFields + return runFieldsPage(event) +end + +local function runRx2PinMapPage(event) + fields = rx2PinMapFields + return runFieldsPage(event) +end + +local function runRx3PinMapPage(event) + fields = rx3PinMapFields + return runFieldsPage(event) +end + +-- Init +local function init() + current, edit, refreshState, refreshIndex = 1, false, 0, 0 + if LCD_W == 480 then + margin = 10 + spacing = 20 + numberPerPage = 12 + end + pages = { + runConfigPage, + runFailsafePage, + runRx1PinMapPage, + runRx2PinMapPage, + runRx3PinMapPage + } + telemetryIdle(0x80) +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_NEXT_PAGE then + if edit == false then + selectPage(1) + end + elseif event == EVT_VIRTUAL_PREV_PAGE then + if edit == false then + killEvents(event) + selectPage(-1) + end + end + + local result = pages[page](event) + refreshNext() + + return result +end + +return { init = init, run = run } + From 6b50cbab152d4755e177520b92aad8d20c740282 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Fri, 31 Jul 2020 16:10:06 +0100 Subject: [PATCH 115/267] Show 9 gvars on 9x radios - Fixes 7678 (#7679) Show 9 gvars on 9x radios --- radio/src/gui/128x64/model_flightmodes.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/radio/src/gui/128x64/model_flightmodes.cpp b/radio/src/gui/128x64/model_flightmodes.cpp index b1b408b4a..c8eac3b2b 100644 --- a/radio/src/gui/128x64/model_flightmodes.cpp +++ b/radio/src/gui/128x64/model_flightmodes.cpp @@ -64,13 +64,8 @@ void menuModelFlightModeOne(event_t event) drawFlightMode(13*FW, 0, s_currIdx+1, (getFlightMode()==s_currIdx ? BOLD : 0)); #if defined(GVARS) && !defined(GVARS_IN_CURVES_SCREEN) -#if defined(PCBTARANIS) #define VERTICAL_SHIFT (ITEM_MODEL_FLIGHT_MODE_FADE_IN-ITEM_MODEL_FLIGHT_MODE_TRIMS) static const uint8_t mstate_tab_fm1[] = {0, 3, 0, 0, (uint8_t)-1, 1, 1, 1, 1, 1, 1}; -#else - #define VERTICAL_SHIFT (ITEM_MODEL_FLIGHT_MODE_FADE_IN-ITEM_MODEL_FLIGHT_MODE_SWITCH) - static const uint8_t mstate_tab_fm1[] = {0, 0, 0, (uint8_t)-1, 1, 1, 1, 1, 1}; -#endif static const uint8_t mstate_tab_others[] = {0, 0, 3, 0, 0, (uint8_t)-1, 2, 2, 2, 2, 2}; check(event, 0, NULL, 0, (s_currIdx == 0) ? mstate_tab_fm1 : mstate_tab_others, DIM(mstate_tab_others)-1, ITEM_MODEL_FLIGHT_MODE_MAX - HEADER_LINE - (s_currIdx==0 ? (ITEM_MODEL_FLIGHT_MODE_FADE_IN-ITEM_MODEL_FLIGHT_MODE_SWITCH-1) : 0)); From f73aa19a51bba2c10d4b09f71125e750be25a989 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Fri, 31 Jul 2020 16:11:54 +0100 Subject: [PATCH 116/267] Fix scale and telemetry fields (#7704) --- radio/src/gui/128x64/model_input_edit.cpp | 2 +- radio/src/gui/212x64/model_input_edit.cpp | 10 +++++++++- radio/src/gui/480x272/model_inputs.cpp | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/model_input_edit.cpp b/radio/src/gui/128x64/model_input_edit.cpp index a2423189b..036b640a5 100644 --- a/radio/src/gui/128x64/model_input_edit.cpp +++ b/radio/src/gui/128x64/model_input_edit.cpp @@ -118,7 +118,7 @@ void menuModelExpoOne(event_t event) case EXPO_FIELD_SCALE: lcdDrawTextAlignedLeft(y, STR_SCALE); - drawSensorCustomValue(EXPO_ONE_2ND_COLUMN, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), attr); + drawSensorCustomValue(EXPO_ONE_2ND_COLUMN, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), RIGHT | attr); if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); break; diff --git a/radio/src/gui/212x64/model_input_edit.cpp b/radio/src/gui/212x64/model_input_edit.cpp index 64b7b81db..4d2ab97da 100644 --- a/radio/src/gui/212x64/model_input_edit.cpp +++ b/radio/src/gui/212x64/model_input_edit.cpp @@ -109,7 +109,15 @@ void menuModelExpoOne(event_t event) case EXPO_FIELD_SOURCE: lcdDrawTextAlignedLeft(y, STR_SOURCE); drawSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, STREXPANDED|attr); - if (attr) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailableInInputs); + if (attr && menuHorizontalPosition==0) + ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailableInInputs); + if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { + drawSensorCustomValue(EXPO_ONE_2ND_COLUMN+30, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, getValue(ed->srcRaw), LEFT|(menuHorizontalPosition==1?attr:0)); + if (attr && menuHorizontalPosition == 1) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); + } + else if (attr) { + menuHorizontalPosition = 0; + } break; case EXPO_FIELD_SCALE: diff --git a/radio/src/gui/480x272/model_inputs.cpp b/radio/src/gui/480x272/model_inputs.cpp index c6a868df6..4e1aa7dec 100644 --- a/radio/src/gui/480x272/model_inputs.cpp +++ b/radio/src/gui/480x272/model_inputs.cpp @@ -243,7 +243,7 @@ bool menuModelExpoOne(event_t event) if (attr && menuHorizontalPosition==0) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailableInInputs); if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - drawSensorCustomValue(EXPO_ONE_2ND_COLUMN+75, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|(menuHorizontalPosition==1?attr:0)); + drawSensorCustomValue(EXPO_ONE_2ND_COLUMN+75, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, getValue(ed->srcRaw), LEFT|(menuHorizontalPosition==1?attr:0)); if (attr && menuHorizontalPosition == 1) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); } else if (attr) { From 326f2622cffb8a93af9480648e0d233ba748467b Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 1 Aug 2020 08:59:10 +0200 Subject: [PATCH 117/267] Add missing files --- radio/sdcard/horus/THEMES/Flexi/X10.bmp | Bin 0 -> 164938 bytes radio/sdcard/horus/THEMES/Flexi/X10S.bmp | Bin 0 -> 160138 bytes radio/sdcard/horus/THEMES/Flexi/asterisk.bmp | Bin 0 -> 44238 bytes radio/sdcard/horus/THEMES/Flexi/busy.bmp | Bin 0 -> 44238 bytes radio/sdcard/horus/THEMES/Flexi/horus.bmp | Bin 0 -> 164938 bytes .../horus/THEMES/Flexi/mask_currentmenu_bg.png | Bin 0 -> 335 bytes .../THEMES/Flexi/mask_currentmenu_dot.png | Bin 0 -> 293 bytes .../THEMES/Flexi/mask_currentmenu_shadow.png | Bin 0 -> 491 bytes .../sdcard/horus/THEMES/Flexi/mask_library.png | Bin 0 -> 1052 bytes .../horus/THEMES/Flexi/mask_menu_model.png | Bin 0 -> 873 bytes .../horus/THEMES/Flexi/mask_menu_radio.png | Bin 0 -> 1194 bytes .../horus/THEMES/Flexi/mask_menu_stats.png | Bin 0 -> 978 bytes .../horus/THEMES/Flexi/mask_menu_theme.png | Bin 0 -> 674 bytes .../horus/THEMES/Flexi/mask_model_curves.png | Bin 0 -> 814 bytes .../THEMES/Flexi/mask_model_flight_modes.png | Bin 0 -> 1081 bytes .../horus/THEMES/Flexi/mask_model_gvars.png | Bin 0 -> 807 bytes .../horus/THEMES/Flexi/mask_model_heli.png | Bin 0 -> 671 bytes .../horus/THEMES/Flexi/mask_model_inputs.png | Bin 0 -> 796 bytes .../Flexi/mask_model_logical_switches.png | Bin 0 -> 1222 bytes .../THEMES/Flexi/mask_model_lua_scripts.png | Bin 0 -> 980 bytes .../horus/THEMES/Flexi/mask_model_mixer.png | Bin 0 -> 1090 bytes .../horus/THEMES/Flexi/mask_model_outputs.png | Bin 0 -> 1063 bytes .../horus/THEMES/Flexi/mask_model_setup.png | Bin 0 -> 604 bytes .../Flexi/mask_model_special_functions.png | Bin 0 -> 811 bytes .../THEMES/Flexi/mask_model_telemetry.png | Bin 0 -> 1095 bytes .../sdcard/horus/THEMES/Flexi/mask_monitor.png | Bin 0 -> 1200 bytes .../THEMES/Flexi/mask_monitor_channels1.png | Bin 0 -> 1083 bytes .../THEMES/Flexi/mask_monitor_channels2.png | Bin 0 -> 1076 bytes .../THEMES/Flexi/mask_monitor_channels3.png | Bin 0 -> 1076 bytes .../THEMES/Flexi/mask_monitor_channels4.png | Bin 0 -> 1079 bytes .../horus/THEMES/Flexi/mask_monitor_gvars.png | Bin 0 -> 698 bytes .../horus/THEMES/Flexi/mask_monitor_inver.png | Bin 0 -> 543 bytes .../horus/THEMES/Flexi/mask_monitor_lockch.png | Bin 0 -> 530 bytes .../horus/THEMES/Flexi/mask_monitor_logsw.png | Bin 0 -> 1078 bytes .../horus/THEMES/Flexi/mask_monitor_mixes1.png | Bin 0 -> 1090 bytes .../horus/THEMES/Flexi/mask_monitor_mixes2.png | Bin 0 -> 1091 bytes .../horus/THEMES/Flexi/mask_mplex_add.png | Bin 0 -> 954 bytes .../horus/THEMES/Flexi/mask_mplex_multi.png | Bin 0 -> 1013 bytes .../horus/THEMES/Flexi/mask_mplex_replace.png | Bin 0 -> 841 bytes .../sdcard/horus/THEMES/Flexi/mask_opentx.png | Bin 0 -> 1110 bytes .../THEMES/Flexi/mask_opentx_testmode.png | Bin 0 -> 1157 bytes .../THEMES/Flexi/mask_radio_calibration.png | Bin 0 -> 575 bytes .../Flexi/mask_radio_global_functions.png | Bin 0 -> 1049 bytes .../horus/THEMES/Flexi/mask_radio_hardware.png | Bin 0 -> 686 bytes .../THEMES/Flexi/mask_radio_sd_browser.png | Bin 0 -> 1091 bytes .../horus/THEMES/Flexi/mask_radio_setup.png | Bin 0 -> 1326 bytes .../Flexi/mask_radio_spectrum_analyser.png | Bin 0 -> 921 bytes .../horus/THEMES/Flexi/mask_radio_tools.png | Bin 0 -> 1195 bytes .../horus/THEMES/Flexi/mask_radio_trainer.png | Bin 0 -> 1032 bytes .../horus/THEMES/Flexi/mask_radio_version.png | Bin 0 -> 544 bytes .../horus/THEMES/Flexi/mask_sbar_mixer.png | Bin 0 -> 751 bytes .../horus/THEMES/Flexi/mask_sbar_output.png | Bin 0 -> 791 bytes .../sdcard/horus/THEMES/Flexi/mask_sbar_to.png | Bin 0 -> 631 bytes .../horus/THEMES/Flexi/mask_stats_analogs.png | Bin 0 -> 800 bytes .../horus/THEMES/Flexi/mask_stats_debug.png | Bin 0 -> 641 bytes .../horus/THEMES/Flexi/mask_stats_lua.png | Bin 0 -> 1226 bytes .../THEMES/Flexi/mask_stats_throttle_graph.png | Bin 0 -> 860 bytes .../horus/THEMES/Flexi/mask_stats_timers.png | Bin 0 -> 1052 bytes .../horus/THEMES/Flexi/mask_stats_value.png | Bin 0 -> 1292 bytes .../horus/THEMES/Flexi/mask_switches.png | Bin 0 -> 536 bytes .../horus/THEMES/Flexi/mask_textline_curve.png | Bin 0 -> 572 bytes .../horus/THEMES/Flexi/mask_textline_delay.png | Bin 0 -> 630 bytes .../THEMES/Flexi/mask_textline_delayslow.png | Bin 0 -> 891 bytes .../horus/THEMES/Flexi/mask_textline_fm.png | Bin 0 -> 755 bytes .../horus/THEMES/Flexi/mask_textline_label.png | Bin 0 -> 492 bytes .../horus/THEMES/Flexi/mask_textline_slow.png | Bin 0 -> 815 bytes .../THEMES/Flexi/mask_textline_switch.png | Bin 0 -> 666 bytes .../horus/THEMES/Flexi/mask_theme_add_view.png | Bin 0 -> 545 bytes .../horus/THEMES/Flexi/mask_theme_setup.png | Bin 0 -> 425 bytes .../horus/THEMES/Flexi/mask_theme_view1.png | Bin 0 -> 400 bytes .../horus/THEMES/Flexi/mask_theme_view2.png | Bin 0 -> 525 bytes .../horus/THEMES/Flexi/mask_theme_view3.png | Bin 0 -> 597 bytes .../horus/THEMES/Flexi/mask_theme_view4.png | Bin 0 -> 506 bytes .../horus/THEMES/Flexi/mask_theme_view5.png | Bin 0 -> 549 bytes .../horus/THEMES/Flexi/mask_theme_view6.png | Bin 0 -> 625 bytes .../horus/THEMES/Flexi/mask_theme_view7.png | Bin 0 -> 486 bytes .../horus/THEMES/Flexi/mask_theme_view8.png | Bin 0 -> 659 bytes .../horus/THEMES/Flexi/modelsel/icon_boat1.png | Bin 0 -> 2570 bytes .../horus/THEMES/Flexi/modelsel/icon_boat2.png | Bin 0 -> 3310 bytes .../THEMES/Flexi/modelsel/icon_default.png | Bin 0 -> 2119 bytes .../THEMES/Flexi/modelsel/icon_favorites.png | Bin 0 -> 2286 bytes .../THEMES/Flexi/modelsel/icon_plane1.png | Bin 0 -> 3065 bytes .../THEMES/Flexi/modelsel/icon_plane2.png | Bin 0 -> 2487 bytes .../THEMES/Flexi/modelsel/icon_plane3.png | Bin 0 -> 2689 bytes .../THEMES/Flexi/modelsel/mask_iconback.png | Bin 0 -> 1498 bytes .../THEMES/Flexi/modelsel/mask_modelname.png | Bin 0 -> 661 bytes .../THEMES/Flexi/modelsel/mask_modelqty.png | Bin 0 -> 773 bytes .../THEMES/Flexi/modelsel/mask_moveback.png | Bin 0 -> 653 bytes .../THEMES/Flexi/modelsel/mask_moveico.png | Bin 0 -> 756 bytes .../THEMES/Flexi/modelsel/mask_sdfree.png | Bin 0 -> 534 bytes radio/sdcard/horus/THEMES/Flexi/question.bmp | Bin 0 -> 44238 bytes radio/sdcard/horus/THEMES/Flexi/shutdown.bmp | Bin 0 -> 48538 bytes .../horus/THEMES/Flexi/stick_background.png | Bin 0 -> 4997 bytes .../horus/THEMES/Flexi/stick_pointer.png | Bin 0 -> 902 bytes radio/sdcard/horus/THEMES/Flexi/t16.bmp | Bin 0 -> 164938 bytes radio/sdcard/horus/THEMES/Flexi/t18.bmp | Bin 0 -> 164938 bytes radio/sdcard/horus/THEMES/Flexi/thumb.bmp | Bin 0 -> 6462 bytes radio/sdcard/horus/THEMES/Flexi/topleft.png | Bin 0 -> 526 bytes .../horus/THEMES/Flexi/topmenu_opentx.bmp | Bin 0 -> 3858 bytes .../horus/THEMES/Flexi/trackp_background.png | Bin 0 -> 5110 bytes radio/sdcard/horus/THEMES/Flexi/tx16s.bmp | Bin 0 -> 164938 bytes .../horus/THEMES/Flexi/wizard/background.png | Bin 0 -> 86040 bytes .../horus/THEMES/Flexi/wizard/itembg.png | Bin 0 -> 403 bytes .../sdcard/horus/THEMES/Flexi/wizard/pgdn.png | Bin 0 -> 1863 bytes .../sdcard/horus/THEMES/Flexi/wizard/pgup.png | Bin 0 -> 1897 bytes 105 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 radio/sdcard/horus/THEMES/Flexi/X10.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/X10S.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/asterisk.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/busy.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/horus.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_currentmenu_bg.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_currentmenu_dot.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_currentmenu_shadow.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_library.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_menu_model.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_menu_radio.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_menu_stats.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_menu_theme.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_curves.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_flight_modes.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_gvars.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_heli.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_inputs.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_logical_switches.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_lua_scripts.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_mixer.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_outputs.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_setup.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_special_functions.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_model_telemetry.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_channels1.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_channels2.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_channels3.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_channels4.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_gvars.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_inver.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_lockch.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_logsw.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_mixes1.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_monitor_mixes2.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_mplex_add.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_mplex_multi.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_mplex_replace.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_opentx.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_opentx_testmode.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_calibration.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_global_functions.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_hardware.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_sd_browser.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_setup.png create mode 100755 radio/sdcard/horus/THEMES/Flexi/mask_radio_spectrum_analyser.png create mode 100755 radio/sdcard/horus/THEMES/Flexi/mask_radio_tools.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_trainer.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_radio_version.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_sbar_mixer.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_sbar_output.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_sbar_to.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_stats_analogs.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_stats_debug.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_stats_lua.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_stats_throttle_graph.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_stats_timers.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_stats_value.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_switches.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_curve.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_delay.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_delayslow.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_fm.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_label.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_slow.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_textline_switch.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_add_view.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_setup.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view1.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view2.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view3.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view4.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view5.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view6.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view7.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/mask_theme_view8.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_boat1.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_boat2.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_default.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_favorites.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane1.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane2.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane3.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/mask_iconback.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/mask_modelname.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/mask_modelqty.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/mask_moveback.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/mask_moveico.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/modelsel/mask_sdfree.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/question.bmp create mode 100755 radio/sdcard/horus/THEMES/Flexi/shutdown.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/stick_background.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/stick_pointer.png create mode 100755 radio/sdcard/horus/THEMES/Flexi/t16.bmp create mode 100755 radio/sdcard/horus/THEMES/Flexi/t18.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/thumb.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/topleft.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/topmenu_opentx.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/trackp_background.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/tx16s.bmp create mode 100644 radio/sdcard/horus/THEMES/Flexi/wizard/background.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/wizard/itembg.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/wizard/pgdn.png create mode 100644 radio/sdcard/horus/THEMES/Flexi/wizard/pgup.png diff --git a/radio/sdcard/horus/THEMES/Flexi/X10.bmp b/radio/sdcard/horus/THEMES/Flexi/X10.bmp new file mode 100755 index 0000000000000000000000000000000000000000..32a730cde7d179c4715d60e1495f86c4e0044c2e GIT binary patch literal 164938 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 U8sHk>8sHk>8sHlE;c4K10Y7TM`~Uy| literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/asterisk.bmp b/radio/sdcard/horus/THEMES/Flexi/asterisk.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3dd3cfef4c8eb25f6beacd45a8f36f22b8a00f7b GIT binary patch literal 44238 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 F{tf;*Tz&um literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/horus.bmp b/radio/sdcard/horus/THEMES/Flexi/horus.bmp new file mode 100644 index 0000000000000000000000000000000000000000..98bc631cd288bc7c16a1dd6e287b6255d98dd6f2 GIT binary patch literal 164938 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

N;_gChAda*qG&?Dy8tU15Bo8AF;QV;#Q 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_currentmenu_dot.png b/radio/sdcard/horus/THEMES/Flexi/mask_currentmenu_dot.png new file mode 100644 index 0000000000000000000000000000000000000000..6f76965a93d563832f44c81f759f475026b442ce GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@KN#5=*KpF^sI`6IrQk(@Ik;On& z*Fl)kNn>^eP>{XE)7O>#J{KpeC}+U3^IAZmO`a}}AsXlBPTI@cV!+cXuWsZWvn-K= z?Sxm(WHsJ550rGfcW_`w%QXwR=M={tls&go&1LP^rw{g~dv&HAn<>&pIsg^iz^aq5+4aSRNM6`n4RArXh)-n8$26t4 zhK_oHBlpfZaUXQc{>c1;^X(hmw@p*Ut~E_n=vva@DBx3+xA8$y{>IMT)rv=del31B z^RVSr2|+pT*@LO&o)qCbp_t7R3-(u$`g&)!-yYJq3TQ+mS z#f&MOUXxnhmU&N7iO4oQ_QYxDy5EdO$1db;pLsTI=K573S~FYymtW>Kp6RpV`s`Xs&=ddw literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_library.png b/radio/sdcard/horus/THEMES/Flexi/mask_library.png new file mode 100644 index 0000000000000000000000000000000000000000..237883fc5ac1488cab0953fbc7ee6190bfeb505a GIT binary patch literal 1052 zcmV+%1mpXOP)a zgUe`9K~akdgK8oD7r`{Nmp0*|1RN-VViDpDr2L_E(EfNuxS z^CU?U>AF5>TN?YyW5Y0Tc6NrNqa)PobpU|N(Baukh_E8kYN-{)5V}(M2g25nME*ErNhuiJ$ zJL%=+C9bcphw|3c^71lPS6BON&1MtR)6*~v1BF5XztZV603aHT+Mu;s4UdnHn3|dz z3HZr@yks1e!t(g!OF@CCMG5@KR-VbIF(A_{QTS& zFD66Nw0E$Xdrft_U1TyD6pKZ8z21?)l}ZJHKmgrt7Y>KRvYQM|(=h(kVu!;4pU;PK zxjYgS0LtYud_G?v^rOaEmIctOG9MKVhmp(WMh53{IfTPuo84rvEc*^Fl}fgWp->3< ze12qbKA%S@6tdY(2FtP-7X$%7FCd!DX5Yj=>2b5!L@Jd600@GB@jxH|p66j029n9- ze+Et_lQ0YeJkR%9BqAb7lB}xqyHr&rr_)KDPG^W()KjO^A*a)6D}!d0k|gOpSJ!Ga z1cO1;>vhPojJ35j>!g#Dli1ta8#;-+yu2VDkE79OShkCc3#_lNgJBpH3I%vP9tFFte0cDZs=No~)-};#PE6tv%pJ;FYUk&~5`teVg&-5oh+WrB1 WXAvi_r33c>0000rfm$iV(L51(gJmbP|^?h6GePn8BgYrJ#e*kI*`ZkTHW=(4k95#|oDC z6&y0TRV91e$HDi{ujbX}LGX^M5at}_UNPwi90zVB02RRW-@s_b++{9SZ9oez8XzCW8d z2m-OdG))c$1O6!ngMm=5s%lEPAf8Mn;(ATfucWK-7yTqxyqAUY&04liK8f@EXxFt$*0H1$KnS%KR?gpa{!cOS%lg$0JK^y z#BmJMG*PKkvc$glet&L(Z}ar@ln)ONsW_J; zi2&H`c8PYoO#rkkD|O0Rt(KYa*HJ7MQ~Oz#MF4EK+gwhPy{2hcG!l+53?Yb<+`C95 z9AOw@WtO33Sy)|NP0c?(KBC|60|55+_HcA`l(LyuB3Bee5mn|_lu9L(N~P4i@B4yy ze}5l0H#eCSCmf2RU^yX9Hd-VS4ne&CSh}pLTb5Q7jfe_2JIW&ZqOQudk@r>i_`Twvo%_ zKK5MS)(CuC>+`M6-oI>b|F4GrTc0qCezg4ngD)E+P`YqO00000NkvXXu0mjfHSK=i literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_menu_radio.png b/radio/sdcard/horus/THEMES/Flexi/mask_menu_radio.png new file mode 100644 index 0000000000000000000000000000000000000000..6d93fbcccd4f6691ea4281d71e62e75e03364563 GIT binary patch literal 1194 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_model_gvars.png b/radio/sdcard/horus/THEMES/Flexi/mask_model_gvars.png new file mode 100644 index 0000000000000000000000000000000000000000..0b5015dac420e9d7e705ac87cfa48a3958f7dd4f GIT binary patch literal 807 zcmV+?1K9kDP)TDN`4N+m2VE&>2XM@JD3hka@6>FEi*UJn3JtyYmp zB*czaR#pgrdwYAdZJU0@{rx?+x3>v^Yinz+?t%k>02M`Hr_-U|;W!S1!60>AcQw~s zudlCARTZI7$af!&kB>u^W%T>~;k)|#fq#59vC(Lt+wBUPs;U?p8}sCLIvq5dO=z0- zzGqx}1B}ID?>cjOdYS-)v$3(^$!oP*oSmIzDwSfp-F9_FQK%@2_c{FwTr3v3xw$ET zM@B|?adF}1yu7?{d3l-fc$|$!L#Y2Um?Ul6rlx5EJe$puB>Vk7v)L>S!(hE$_tXvq zCdq??0|6{a606lJbGaNRCMIZE)?e!e2DjU7Mx#*y9*ssBi9~pOeEe?Rz+jR*JUsLm ztxza@So0_NOYeAUYRaQsUS57}9)Gsua=FZKI84JZgpO5J{cx%cbk&w+QP*{vrpfE; zYX*Zs0iMt2{Rh|UbsC1j)zww@dOeb4E|(L)kw}EMx3~U*ZQJI|%nX;7mV|kIb8|yk zmIb(JnzSs7j^hjletv#tI-Ta++#Fl2mgoEK?ykq-0C;qC^dZ>Yxg<$KEEdDn)snC4Cy*N5Clq~=N002ovPDHLkV1hJsbF}~f literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_model_heli.png b/radio/sdcard/horus/THEMES/Flexi/mask_model_heli.png new file mode 100644 index 0000000000000000000000000000000000000000..ecd93d2ff31a96f1a374f88861ba48f6b212557e GIT binary patch literal 671 zcmV;Q0$}}#P)Oc_2KMz+el0s4iUn3&+RsjpGT(AoG5_tu|7YKQPbP@6Zwn36kA+5s3 zN`-)he_(F4;q1|?(JME_9oS|kJ3qec?#yh3hzKRv7fP~UML*o{_lV;daU3IxBE)eF z05A*#tyT+$VZbm9Xqr|mAU(0&ZbytU0#Il$#%Qo^rA0KM$M) z7N^%VP3fb_WJ2|No$B>E4Tr-Uz-Tne{nP1G@`*iXj7jrf-|wmCc~BGuuIoxy?s~oA zbUNX5I^ldi-(1Pd<&yia*Q?}nT^EX?!1Fxp_xsz-B@#lX(?PS@{8)x+Hk;^lIuhe9 zxZQ4lJUxn--@$pxjH0Lzc)Q(VHk(Ovoby6p!H~d36^CJn!C>&$ic70bIoL1^0Kj6g zc(|a~>)~)XNTc8HKU^bX3Wfx3wOWP1)oK-uMnf94TCEURFy2!9jy+T?Cc`j9nx-Fv zrfG^W4F3$)G!2X~9FIqg$K#KI$Kx@M$0HbHa(7asNUYaun$PDnpU-KzT;AmB)oPXd zH=E7tMWVE_SZX6sYU@*4v-dCB+e*ar%76EE`VDb~kk7--2ebeH002ovPDHLk FV1mzjG+_V$ literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_model_inputs.png b/radio/sdcard/horus/THEMES/Flexi/mask_model_inputs.png new file mode 100644 index 0000000000000000000000000000000000000000..720099c470641dcfce53ac95a3cf6162fdad27ca GIT binary patch literal 796 zcmV+%1LOROP)q0;x$v zK~zY`)tA3Z>rfQOPea&qLPb>Q;OHPID)=whT^w|Aa&gfvBDx7KB7&0; z6hzeGAUKLp5via<28o2Cv9tU8ZT8S4Z98i;D{sMX4KF6`afE zeuU=pdAho~0zs>S`}+DwrBaxtSrxp#zV53i7K>pR2Ji3hHJRf$mEhjqUPJ_4*I8X% z<>TX{0-Q`Hec+9a4T7oyT(+K`9u5x=J;$e~Cttp<>lBN{#)Hc?Ha5oX?JWT3=jRL! z4Iv^-O-=C|Re%$T1PcobL?RInuBs|?b8}dhh3mTP?Ch|+yW3cBGMNP6?(U9>i3wlt z+uGXL+}z~z^ON7ID!44eFe;{@zrUZ;)6?dH0Sbiz>2#V{Eaq##%*+fA4-d@)mt~qJ zsZ`3hbG5Xzu(Y&9KA*1(T-WIojYcINkBg>h-Z#f_B$vyHZQJ#oGPU-2Z*PxyJnkFn z;o)H}FE5)j)YsS7jEs!OC%DUoSXn~b92M!=qMt>%nnRsXD1@RYsH#dh97fYLFTkif*gHBp3LJBZ zh_@~Rg|~)b(An7;2wW11P1@N acc(w_7+y_}g%8mH0000w1XD>w zK~zY`<(ExJR9zUy|99?nNSq8pCmjooplB==U5F!!E-X+RlQ1Z979xdTAVC{ljEf=! zM={!n;9@qxAVP~GWxy6i5)osI5(Pz^D@=Z%jpOKi-2c1s@(y>#8S8}>?*j+UJp=$JqM% zI;~bK0jN!{*Qe*?a8;|2k+x3`y}P$=%4wY4>x&1M2%e}6yib~~G!n>jErz?qqulY?Kq zdPM-NsHk8h5;=B(m6a8yr>DoH7Emy6})o<_!PT zc6WD~o105XlDM_C730$g$hU9b@axwv?Ci~fH`T4})zP>)JuCAi9vlCriT`9x9fB%lr z(NQ!uHsb8rvv9dws&9FD831tM!iD(C<@I{0*XwCC8o9r}&t!_`bUJCX*=RDER28#W zEVSG0{PE+5I=8sEn4&0hdwVQjI=1IAC2}9RaYZsVP4A_U+pQz=eec)ftUOdFRd@4h;>_Zz><;@0^o-aAGF}o(ozCQnqQK1xm@ZO*xA`hI)7?viU9cV;X^GrJ3E_MSy@RJ z2?m2KC@4_#98Z$#vKj^0N~cGTk#KjilVT( zx|#rZ^XAQjr-zf6+wG=auV-Ff9)rPP;(F}$dRbXnNdU~r$>GbFFF8Lyf8vwm*47qZ zym-M=r%q9))6wN}#rTDIH;G6j0=wOg=g*(x$bI_sXlbUL;F?{+k9Fc{F()C8N& khO1YvYStG1cXy|M0C0!SbQRzAX#fBK07*qoM6N<$f+2%R-v9sr literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_model_lua_scripts.png b/radio/sdcard/horus/THEMES/Flexi/mask_model_lua_scripts.png new file mode 100644 index 0000000000000000000000000000000000000000..a20c1ccbdd8f005fe649c33c1f95f4ea84d1454a GIT binary patch literal 980 zcmV;_11tQAP)Qk0G4G@US5u!ogDyx+wBI!Fu1(Dl&v925*{8Nkjv=vgtD?S5D~)R zFaUt(c|1Qqt4`kB+{FC+{QGx0oyO?sC@dBWhzM@C8(Ujj+F$_K-Q7hf6oM#<_^$bU zJ`j=28HNGR^V;B_+Wq~#0{Z4XJw1x?d3tp4`ua+-Sd5O3kI8Pglh^Adm&+v^b8&I; z<@{tasV%OLw!XfO%F0UQ<>e`&C@d^QcX#)@u%4ctke{Eg8nMsk)3?<(H#g|$==gH$ zKDy0j!|LiP0AOlrO3`mNn{j@At_>Ch0d~7RC(LfQqqVgaB_$;=7z`jH^!4>2o6TZz zaSGMRMU zZ0{9_*X#WwB~4XT6&a02S=-Uk5t&RT^85YD(xj?vZ zPEJlJ5{XbW8kLnkH8nL|hZ900006XrTkls|N#MdoyEeJWO)c9L2+>k#A5@Dj z*jCbw6fKG%vxONtUJ6pEaYmH?xA~gy_~~@4bKoq_1GAa&d44nRW#(}R0LUK*8TmsY z!Zb~jXf#UF=``tdI=>BQx7#F{Op-_>LTH-)?*je}LWp>1G#W&sQE(iG@5cN4J62a$ zp;oKKx)eo;*ZaaB2Tjw+WHMhy)9Ey(r>A@B4Ga@PAeYOrwzdXA5W0$IG8yD@xt^zt zL?V#MWPNM(g)^B9mY0_?K0e-)pw(*e^z;M(C>Dz_o6S%vl~5=Y2!%puHk$x|LZJYy zR@;-W(P$8fL~wn5-3x!j+uIvfR#q@HG$f+=d>#N$DwUwq=@1A6&}cM}&1S(c3|uak zi0AWp5tqy5l52%X%~L9sg3V?VQI=()*X!Z;`@4p{Ua!MqvB2qccHv&H7nhfplIwsB zvRKOHGH9BH&*$rz>mPB4!!htq_$geeRA4X|B+>ulcDsEb{8PAEt-@$D!t3>d=Xvbx z>|lR?AFWpFQ`}~={S=pkYqc6oCKEg!4|tvz=Xq>w43m?SSYKa9p-_;-tyb%&xFpOl z4BT$FhpZPEAc=b8{2V z&(8pWhldBq<#OEK-uA80_v=Ja6lu5H?Hk(DG(Lf*&fKI1FsZ_%0=_!VXhr4pa;V@X1m4*|E z1Qr$+pin6Kb`POY2msJ*HW3Pipin5FR4QRMn^7zl0RW4Oi;`VTAP~UG$;sfYk4mLN zJRa}4<6JJ+w;zkg;}{(s9lZ7F_rtllIo#jhf4SMaySu~8%*@x}+1XjdVlm0j-yk6f z0=Blcx^AE^JU>5=SS%*CI=?d>A0M&3y$y%MA)Xuh>;60a0|WN%>?UFLXaE2J07*qo IM6N<$f)uLwXaE2J literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_model_outputs.png b/radio/sdcard/horus/THEMES/Flexi/mask_model_outputs.png new file mode 100644 index 0000000000000000000000000000000000000000..a209ae8d38769e51a5d7ed9fdb0ce268808cbbb4 GIT binary patch literal 1063 zcmV+?1laqDP)|A+S}TOK}lV%P57Y7HtfJB8a3#xCo30 z`UADJ5~_u!65%2QBf>BQ7bQhSrCopD;=Rs$7t7D=!MAt=v$%6+<~MWBoHKJp06>3$ zIQm1xfJGuvQLI!d$!s>$?(Qz-a=G6EL?RJpYqQz@S-{^!MBiqePKS$&3lyT$(^IBD zIy%balAml>Fc_rH&Q7|#yd;G}@u`knE~oD9ZXzOTYHIp#GX7wj&4%xi=%cD*u~<=A zW-=L?oSY=D*UMyOWo2}Jeoh*Vrugkb3H0*v0*yu^$l7ADe5!kMbA##WX*iuu9334o zc}c(k;PH3_z)Gd^Q@ubSz;q@iCYZdadz{PVC?1bfHk)NKlgUI2w-iuNPjgS8(+T z0uK!heObvyqY?3V9En5%eSLi(A}lN{6n#`*0&i_?F%XX9U@#ca*48Efm&@f?TwDat z^Vr_r#`yU7&w%gm@1amA7{KP{CR;l=IDkr}5(LoE(Sc|*3ZCcld>tPjLnf0!CX-=i zW~MN(&*x)KtyXK^`qR@B>~=dij$?XKsT2-}1DQvoP@7+#FM0Utj-nyF#H5 zv|6nod#zTBa5yZ`X>V^wYilc3R#plF_xJZRM<*vI1o4h|MoEYWBbI-O3Cy-KBm&*v*1IF(94EEY2dJv}{`pPxrC7|c7ve-qF1 zaJgKN%jJRq1_lO@PN$2zPHwkb0RF+{<>fFK449gl!rtB<9v&X@Y_G4c(c9ZAu&b@D z#o5_eaSzDO&JL=ps|&V7j^ohM(t_dPVQg${Ael@u`__mUA0=B-xdpWFN(!t z6bJ;!@As46@29J)tAcO4N~I#bUQc?xp44hJnayT;czB?ini@)_Qh$wFrA(QrREh-> z2m~k+iO~D|`)}>)>gp(&OcD{1kpMJl-zS7#-8r9d=Q&(3Pjg5_w zLZP5=ILv|x27~nY_$UCCN~Pp-xrE*6?d|PHqmSKg&#Uf8B!Z=-C0MOi7>!2k@9*bV zbvBzV8C0oM;_mM5+k*ssL|6)_#bUwj?QKaDjD(1Y<2b6UtSqXqBoYZVH#gJB$OxHC hrjkcv(f{t>=@(0QrFXj4)8+sG002ovPDHLkV1hv&=12el literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_model_setup.png b/radio/sdcard/horus/THEMES/Flexi/mask_model_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..afe526c8fa383787697c0140914d71e47230181a GIT binary patch literal 604 zcmV-i0;BzjP)lNei7{lQZi^YPg zNuf|ctyV*&Qb9Bt{c(}rG#CsBz#l#O{l05Q%IzY89anpDqv)Mxzlr zoeqk{B4)E00N`%S7y|$t4hM*$$TjNQ?S@Dsa(|Ie;R=Vt+(G)R=Ig2cO3UREs;XkQ z+xaReNfIWL3G(^8XY=W;&jf%R$Dv=P>v~{%bzP^Aa__nF9I>J($Ye5sMyx1`=W`EK zJC5^nl5><>|4XX>8n^(!vMd~rM_&a+QADX!^0l)37B15?i81CIEXEj_rs)ZnXVkWB zBgmJNh+y0H^HHm+iurtw{eJJuIGIdBRn`Ag2ao!EKBL)e23kP6-7aFW*mG1Hh7owl zGz{Z;s%2S*APBxA6a)dXEc1bDwOZkHI{7k=$K%N5a-MMgdsN?zv)L>FU^<<0H*ssG z)9J?^x7%&BS}g!TB9XX9!$a<>r)e5`z25ujwApN^(P&UAmEyjimL!Sl^*XKB>&Jby qtIOqr?RLvGYDtnFfMu`xY5NW}J>Hr(I)DrS0000^1>BK(4A{E}Ed z4ibq3fk1%s^Yd?S+}_?!Bod*oudlFy{E=ibDVnAUP_<9jb-B8_l0u?9HVlJYE(gH! z@-l60ZU5};^Yb&Co13K5X?AvY*x1-8?(xdXiU8tpIHVdfF)<;4ba!_b)T`urdwT<* zrKP1R$+NJq0KoP2HK(Viwaei-EMc$aQ`T9b8|DJqoV-q?d|cgJAWJ4G)*oqFN^9u9uEL_cX!0&@#+DuuC6jL zFhDF8D;n;GhKA4K{#(#KpHBek?d`3&No;R#tM=H@(IGD{F9q$~Znv_@<#PEA$O-)S-Xn1&7{W}~ENhXseK~t%e z=(;X|`2GGoUD>QQ3`1--TiyjA2&u^NfMMbQ-LjfUF?NCL_6AP8yAdj7B5rbUG3Wg~d5R2~8%GK_C!-h&Wv; zm11gY3Qni2yM`*RudL8VycQPb&jVMBwxJXlZGIPN###V!_$j87CN*%Z1_LVWzvXvVwFv zT^_JfsRWP5%iH6orY5YfujBsyp6TxI@1w4+4n&0h{(d|^KbN=1i9`Z!w;MY!0a9FFobetmr{IPZ45q0{MD0BW@wE|;rvhpVcp z3OzkNn4h1A&1Qqo=i|toPA9s$x-9*bQk4M~i^X~SJ2El?uh+|Tve_&S4-X51 zmJFOur{VYe;cz&xy1ELLN(CYUkH>?_WI`|)tSo`Nyu9G%<_72I=LiG>czk?hf!OVK zw70iIDwX2s=%{?)tE($4FE2x*(SV3hUtf>T&Q6F#A`lT&DiszN7vcB&k;!Bbi^YoL zg0}>zR0?LZ8A72DM1;}NQJkEdFp@|nlkj*vFdB^@B8bIeY;0`2Ns&bXd%a$EDv1bd zYir2ma&J1Z*=$UAaBvWzQ0Tt{UtV5v$bMpCBG2gV?k;a`Zf=g*s8lKrd%X)BjYiSh z+RC{mKSuxe_wV>kr-Oh9eZ9T->)SUbi^t<^--!rgV`D#Eb`J3P_&5htsZ?^N? zxx`{I>GgU}brK4N*jTMr7p!2~+S=GS91e420|Nual|C027aTg|^ZAS<0HFWlM^3h# zA3m_tp3CJRl}eedLZLuB9xwh>Z*FdKmdj$XaGt{LFJJKcr%(9f^JhFfJTTer?k*?b zLRH64fpfVWW@cuX6Ol;7A^$?{`uZBRwY4B32m}IHt=7_+Yj1B4H8nLLB1j|>SgqEA z*TZhNvn6hBZpQKPae0&p0Ks4oeSLi_d%0YW$;nBW&1RTPCMcCk&a)VeMvH!MiYn*> zfdCyG9FWiFqevt|u~>{68X8EUP>@!urRnKu>geeB|9=v{?C(^G_75ye%v{nsS=Rsn N002ovPDHLkV1k4%@{0ff literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_monitor.png b/radio/sdcard/horus/THEMES/Flexi/mask_monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..68ddd4516cbe5049f0f87997cfca728b1d41f8ed GIT binary patch literal 1200 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_monitor_gvars.png b/radio/sdcard/horus/THEMES/Flexi/mask_monitor_gvars.png new file mode 100644 index 0000000000000000000000000000000000000000..55b488d1e93bc3778770a1fe7a2822ebab22f870 GIT binary patch literal 698 zcmV;r0!96aP)nVj)`iGm?M^0mXc`*xTGa{Rclyw#oH7Z8jU4OeTyM2n0xyBwOrav7q^UPO(_* zcCzbs(sljQwy0DpwxJLXhjF=Fn7HY53ZKts4Gdk^ZSC||;QfA&;cy6^=NWjX)3IXK z>op`vLN=SVynhCNNKq6991I3=KA&+o91w{_kW40VKA)WjFPBTW-EIcnXfzOy#}SQ2 zaXcQGc*np1P$(1_FvoF_Wf`if+F~6A?{+)9zP>W>Znt|g_7?czKJN8;WEckHwOXzF z6FgFJzuyPX^T_A(w)PqhhhKBm^GlXJA|gQ$i0ApA6$8&-Z@+D!=K%KY g^sFb04g4&<0sb;)cxw)i(*OVf07*qoM6N<$f`H90A^-pY literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_monitor_inver.png b/radio/sdcard/horus/THEMES/Flexi/mask_monitor_inver.png new file mode 100644 index 0000000000000000000000000000000000000000..64f251238afafc4d6e9b4d328bd635b935d2c0fb GIT binary patch literal 543 zcmV+)0^t3LP)HrkRpZB^i-Ntkihe0gX2_}=(X0wUis{REQ zqsb&#Od=M$!6GbJ#8nxBh6yr33~G4ZAou+c?>%tBIsDGyIUgS+dAr>(nM^PmjSz+* z9*+kej|U2c0ve45N~ID20JU0;wr#UqE^|7aaDGZQK6^0njjvXXkdiO;uIjLb5E=G)>Ou z^KZ?s1%_czQ504x6`oF~=ed7wqt$9L8jVsA1dc`{9*@Ugr~d_$WV6|%s;Y1QM8J#n hdOcW{^_m6%d;*MCblAkqugd@c002ovPDHLkV1gj`-nRe% literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_monitor_lockch.png b/radio/sdcard/horus/THEMES/Flexi/mask_monitor_lockch.png new file mode 100644 index 0000000000000000000000000000000000000000..360923a093714e84a218ab12295eb959b077a9d4 GIT binary patch literal 530 zcmV+t0`2{YP)Gn#C}AlStJpjj476Prz$?7Z-vd+*~N_{zEG-gD381Ap**KIytHo6ROwRi$Z~ zoX_WcyjXg0^Y|C5)rtVvXf#NYDFArC->_{PymYLx(JS(dPKr_&(-8iv6*j+vGu3CraY!{HE@%LSHY zCDymw4Vg>^i^T$xBmti(yWNh8q6pTCqHw$2iofoIAPDF<4ojsH$Kx@BAQ0C20)>oI UpoSO5S307*qoM6N<$g7bCY@c;k- literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_monitor_logsw.png b/radio/sdcard/horus/THEMES/Flexi/mask_monitor_logsw.png new file mode 100644 index 0000000000000000000000000000000000000000..dc63551434eeaa96992ac1b060ff5f5a6a7b9e3a GIT binary patch literal 1078 zcmV-61j+k}P)d4On@{G)o4vg~f4Jd% zT=(_6@BiUCT(h<(*Qdb(0N{C^Se7Nf-%pE+i!?DYk!{Tv#q&IwOeQiI45Uyf z=;7hvH8%jnVzKn9-rMm}kw^q5Cns>ZT;Mn^Z`?i6JqIGi;x8jV7y z(?K8*q~jQd!N|x60)fE4g2Ul(@!(`KiPqLu$Ye6Cudn0o?hYQ02P-QpsHv$zdwVv(uCK4N5?)&egPWR~Fg-n; z*G9YD4xLU1tJR8)jSVQ3N+^{|JUu;q9h^)iArgtOyStkg{85jOkEpDy#LUc0L2$vE z2?m3dN~MY_pNfhK(&=<$v)Ky96$DRBO_55aBCS?iFiA3*B&X9!x3{-MM5I!w$m{hI z$8q^5<+Iviv4Dthbaa%{(wmzbEH5vkr>6%}sT8%fwXj$$0D#%qSu{2_e(f8p)oKh6 z52q&w0s-vp?O}9u6jfDKkjv#58XCgZ))u^8Z^oipT3XQ8*Y`EBTrS7z>M8&r9*;vN zlcB4t3rkB&aJ${u-`|JN=gaBF_VzXe0s)*(XK~=?=VuTRY&KguSu&YSU%$M(pt`ym zD=5LhzLTV5SN#iIb+ObGo(^!abS+)AdyI5 zGMTax-rwItB9WxyJ3Bjbf{%}nK}7KRe4nr5l9CcqtJUOiII`Xu3Xn4$nW>l+S=Ox((DltDHIA4 wi^YGe27bQ1{c8=q0LVN${nQaA3;a|14Km00!+}?S)Bpeg07*qoM6N<$f+4Buq5uE@ literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_monitor_mixes1.png b/radio/sdcard/horus/THEMES/Flexi/mask_monitor_mixes1.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0c71f609acc5917933db281df10d0a429043ec GIT binary patch literal 1090 zcmV-I1ikx-P)>1HZM(%1Z3*?R^SNrBbk3t&m70qHl`^*VfjG1S%>jz_Ki|*(_F8R!~=0 zhlz;^ghC+zfXCwzji)FI!^6WkI5;Q{9F0cd@pw>HRtAMa0fu3a&1Nw+HU^W)1cqU- zwY3G6N`;Gy3jn~<(h>+E=;`S}Fc{2Pm4d+l5DJCBFbsH}huLh#$jAsXnT#Oj^?IRJ ztHCgg5M!R_^M<1+@Y^n(oScNwXvFL5YmT7T>qSjX4FZ9{r~9C2u+?hC;NT$Q@i-P2 z7jb@mo)@msXmELX`8D8$g#`=@4B+ME1pu(Uy$wPLy1Tovv$KPAI-LW*zrX)Ga3Ya_ zTrNi{l@bZ)^?G5PLZJXn)3~|00RYf64X4vtFgR~sjKyMvq9{U9l*q5uYK7;!yF0SG zyGxFbj|m}!=yW;~2n6z`O_pVIogXc=*=)wl%#85Y!C(*+ML{Z+!eB7q@bFORi$o&O zYPIlsy*aaee0)S>V`Jel<~R=h{r#Aoodp0`EEY^pPvh$9Do41fsR?ekI|s_LESj5} zMG2;$UCnVE`uh4{v)S_IZI)%x($a#Bjg7ojtzZ61I6p_p2(nkC;mSsWHG}LM}2q9lu)M_uh%OXPf-*G2M4jYxA!q{EEa>;>qT*KF;prQ7=}SMo5je;2+U?P7>2>d z#s(@YD{*#q1^`%CSO6geU0q!Wg+e*IQZN_*!r?F&hJhdmuvjb@8X7_-lM%&yJ|8q1 z4H$+IeJltxcpUTd z^Ef>{%?sCRwKzXN{~GYz+#LG)`tbDh1OV9F+yo&6ot>T7+S)=ooz8*Z-roKlIFU#| zsZ=7BN=XC^27@?GrBZ>WX!2k@9&F!(P$Jp zoen;qFK4y)_xGr;uP@xkJkO)Iw-?jX(*OXg)r!f|4 z3R0;Q%FD|oV}JhqDb{}_lH#}EYmuTTxZQ3^48FX)L^7F_oc8wi_Pp~y1+J~F#qsfR zK}i6|aj2-M_&c!GYK7nLm)u0}Su&YKM@PqpSN!88?^P%iBoc`btyW9)dOb-b691XV z@jOq=W-~cBI4CIbQz#U_qv3a@x8Ih~%U-{o{;wyD?7JRq{{WV&UL`9gV;ukh002ov JPDHLkV1f)D?KJ=Z literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_mplex_add.png b/radio/sdcard/horus/THEMES/Flexi/mask_mplex_add.png new file mode 100644 index 0000000000000000000000000000000000000000..14f4ecc231e2cee43227a6dc006c771c7596246d GIT binary patch literal 954 zcmV;r14aCaP)P000pP1^@s6D7ps@00004b3#c}2nYxW zd*{y;?&8jJ=A1Ln zIh+}RB=HULcpNU53)|bg(&F*XvPHQGrAvfs>OHoSvRS6h&mS zS!lId=yW2yw9Sy>?f zPESwgxcB#WS}YaWIysZyz!NF*q_TrNC5K4NZe4#maA_!Eeth=qj( zWHK2z9F9D_q@)D%^YaLY!&qHig}Ak~1&v07=H})U2Syh^3Q)YKI8dOc=lX7Kv@TJRP0 zdOh~`_D~d!Mj?~Q5DJAbIXPKysz4xsKp^mgGZ+kz%jF=6SS$vGLXo#mE|=r^`58r0 zsT7e&1ZuS!ZnqoJXf(HIcXt;aj|Uwc9T*uI$?>J7rD$twgU{!KD2k9ur3HtJL?V#M zWI%g+J2e_j-Vgb=-}MEf(MYvgT~H&*y1F`cb#+l37#P6izzh^d^CHdK1ot>QoK&#c7XYjl3%4=b()rxR9{GVI+=;#Q6Kmb)$Rd6^Q zgP000pP1^@s6D7ps@00004b3#c}2nYxW zdRfr=ncDQ~rTPl+y(Z&0&q#??U!9hhN^e&->}s^E{vT`w_nA z=H>>M%SCBvDT#@RsMTtclasOA?MzNi^6>Bw@V>sjGB-CzK|uj(wHmcrjnQbt<#O@- z{2cJfXTZ|Z5-~9`hzMr08K=|9z`y_whXcJ{kBFet>DbuV;Qs!eii!$EguJ{w`uh6l z@9(FqtPBysXf$$iauN&~9v((Suv)Dg9UXl=AOHsk2ju4FqEINv$;lxsER5CF)o;H2 z{e5C$VlbIZ_`yW@l%S zySqCy8Vw~SCHQ5uvTE4S?OgVM6lUxc)ecWeX?{q9W5;_zd|}XI^Or{bUM1b zyMq=#gOp0;H^7G)8yhhijXwhXem|+HsZ>{2qqx1jmAJS#`K;*ZXqlazmC(>o@%#Ov zQmJHqeqK~6Rgf<>HdbzKZsaFTPfyF~>8Yfoq{xpaal74tD~gDSKt#yQ%;fz1JjmD3 z(11px2@2@T%L^`-3#C#?WMm}8#l_#>#O-#IoSck^P+nfn>FFssIXQ?3Sy@>D(Ch0f z8jS`KAv`=Bo6UyZZV!aHzP=u((@9iR)c5K7NUX(T!Q=4&aCv!2c6K%*LUVI7@Nc?? zhK6uDomj0_ii(Qp?CfN3Zx4Xp-d@7P!@uWbbaa%~*46;vLl+kpG&MD`u(0swoD>!o zVzF3)6W`w6{-?y()zt+W;y3N=>>whHkB|TBu91I2@SG zW<&&o!NA$s8Stg*0UjP6n32s( literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_mplex_replace.png b/radio/sdcard/horus/THEMES/Flexi/mask_mplex_replace.png new file mode 100644 index 0000000000000000000000000000000000000000..faccba0ea634d98ca250a4c58146fbfd5d8c239e GIT binary patch literal 841 zcmV-P1GfB$P)P000pP1^@s6D7ps@00004b3#c}2nYxW zd(*1K~y-6t&}@RBT*QI&kPm;1LBJe(b6Jg;j0LWj*6N}u!w~g zHa>%;4k!^s!Dbq@5)n-r5e2&h71UBpu(MDRtSw{|9T=IQ%-JGj*JQ@cvbzUvasPAg z{r>y-FIb@0*H^BtuCk}6hqbk}EH5u-O-&6IMWLo?)ODRC8Hq$VI5TwGk_AP87lS;6JyCGwP`X_`hs5Eu*wd3AN=n$h+3H9I>y34qaP)Zu@AelidUaD03` zFAkDCJUpZz2pk$3qGegGfSf))J~%NkK>&=!Vh;c9?Tw~sGM!HI?CdNHF${w~pO0;A zZOmjcc@vJ0j~S20d2w;!$hfbsk7Z?Le0q9vfNa}lQ&SV$+uO5{_4RcEU@Dbz3|16{ z05~=_=J0NAZYYW(mzI`XcYJ$$OT#col42r}Kp+r6YilbCRZ>#&vlXhoz8=9~5Q#*> z;Q@dw%Sa}Zn46meBuV1v=%{Np#WXWBLyyPv?b96@8DUpf7fW7VUQk(C>6%S3RaI5N zvMiXU39r}d@OO82vnNtfQGw^@=f8sJx(<)WgVNGcS3eeu;r{*}qAbfurBbeg!r?Id zem_)Ib@kKfG-O$ZAPBDh`T05S?(Xofwodl;_NZwZPfkwq;>GI3vMhFV zbWl~*EQI{O6`zC5&dxHKO#Xt9fBtvnj?nGzdU$wXb8|CKPfzpW_}bau-=`!=1i*%d z21X(g&d<+tdU~2IEiDuTfnKkdJ3Bj66os3co3w44;c%Goc%1L=@9gdE<E=8W?uKhsI_S0f>5s6ipWNB$;dLyAdB zDaJ-kvyjNbN@0tLrG!G3SYRR46tY0$H#6CYg`dW7y$j9rI^WFu^}c=6ch_C#xvuMe zuG4*=^PB^cJUBRDeSJMsQc{?ak-?Uh7M`D<(;vOPy)>K6G@H#VC@7%A;o$c6w&3&f z@&xbCjg1YGWMg9^&1N%)hlho}zP_Gjvzgf4-KAcyrzA;iYHDI-WhDVHDk_TC*ViP; z=H_Mspv7XL)oNu_R1_siqRZtXNlKDLoleK*=4O$5dwV2FlgUH?jEIP!+wCSvrlzJ6 z08v#{MF1Qb84-78v)KrMot>Q;^Ocnql4O5>KLK!Xa8Q`bvdm!NH;;~vW>;62##}l& zIsyRH)z!ft{_DfTLwpaX(}{(J1+1>F0szX&$^`fR{tiV^M0z|PO|8S>z|71H&d$yR z7A8p&0Kn(-3DAG7*XuPK3=Itd08&y?(9_d{>gsC2Ns?h z4EzFXYikLBLqkKNRqgHV1i#rlxpzcNfswKWY2N#|Ov9#|ePp;o)C1cRHQ? z@#6;pu)4aMd3kvRz?hgAVf4?L2Q$~{bhKD3BG=Z|0?fT$FVoZ0zl;G&xw*MGJUm2e zYb)GtH%OwZs|&}+$4E*_0sxpyCa9{4*w|PEi;9X0l$V#|?d?sZe{=-}1yEHL03gdU z#>U2=swz}fg^nbH?)X1M7y#Vf-s0rskKnVFeHH)4L&_Rt?3GazB}JuFMqzizWz>1pU=mVk`kdc7z~`7n`2m5 zn7~?ES_0}qVos7bH#dxli3vKQ)YMcyJv{}~NPqQ+|4SH+M$FF62H>(RV}5=f27@7h z3z<0p6c-mmQ4|f9oScm8?ChZWkeQQ2TU#3*A0IWi%jJUIZvSmjNM=4dI;zp<=jUtI zi;If^H6bzI-`}S!%R<}G&_J)(%fiA!fhmf@tE;Qfnm;`~F)b}k=!`}qA08e^ew`nR zq6n_2sEF_H@1ZldTCGB(*Xy~nv!mgcmX+2-RpOvB|o8zjq@&Et;07*qoM6N<$f@nk$N&o-= literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_opentx_testmode.png b/radio/sdcard/horus/THEMES/Flexi/mask_opentx_testmode.png new file mode 100644 index 0000000000000000000000000000000000000000..b535cf8ee5b06f00d8e5e64036cdccf46ab42374 GIT binary patch literal 1157 zcmeAS@N?(olHy`uVBq!ia0vp^@<1%k!3HEh|D6&7q}Y|gW!U_%O?XxI14-? zi-9_>gD|6$#_S4^U|E4~Z76c!d%l#$Wlw>)^$8)aqXIWuM? z2sH4Jg?g?&(^G%`^;)CUvzYIhl>(J!{f)vZ{EF2n`h~% zr>7^x@U+Ns`m|{tlj`=z8+|@|)_2m9!w&^=#JW{;4=+`(_qppyP^=RbNuSSM6QYvupS6;If$TaP>nkzWx$9^Wwz{ zj>jJ>f(&lfe?Rc_=~9pU++5$6!pkpr?%Zj4B+2l})29m;FIHwvHj#4O^~6@8RLt7i z+GO@w&GetkS(+XFe0@Lu`4e-gzM{f|K}S<_;pwN3e*gY`Ui#h3mz@kd_U)VZ==JN@ zi(~ZE8BV1bZQH(mdBU}8*EkvuCVV*E_IpxIW#vo;3rkDQP51BL4_+9sq~!O>lbsyb zZ{PN2*m5(+M{Tmb`uv$Q7y76vGlYePcGmry^LPLC8#iWXxJ5?JeDM0!t4S);_dLwY z%RBI2?ZWG?hn^O3?pr_a+_Y(84{tv#P!QnYsL~Pp{gYRmgC)a6DsyX8=EjJKkPw&o z=hc6(v$4&XJlUDy%&AjJ>FLWK+`e}&t*p$%@$Z67QC?7pzle9n}RkdOoKzcVvrWo2Es zaigQHjZH*E0;mNaSMLU2=&|^|{b+wMJ?$Wbq$v~g| ze==XF)5WE#xY(F6U|~Q<$G%xFJ%=P)r%Dh-G#IfGF|k5bU3!!5_)D%>HWaGeVv+|z{=E}eo4{M)Rp zuuFp3jTY&RL<9DG{4=BR_zeJP1^Y)U*}Ln)+wDeDN|I7i+qNX7BqAcl7{zf+j4@)2 z(Pp!8bBGz!T7z>AB64~-=g?Zuxy4hk5CUNsf{5UG9?~>LS(d2l8cHcBrBK&3%Cbb7 zrtmxuM1(L5A%s{C9)SZLREk3;pT>XCI_XW>&}r>})%a{5TAE7&MEIsaO4qn)ODvn-ph zN(V3in{&?jG+1kGtJkHFT)|2yTkZF%ZRau#AxRR7qUdEEDvBaXl4QgfbuROAzuzx% zyxndee(U?*`aGY{B&DRo;V=fbZTlne^?IFMV|*IMuM}S{7w4ZdJ|{!H)SiWS(don( zqrUH{X&O2nkIv3b9-mp$G?wuV9LF)8PNxyrC4Vkvz;PT;E52Vem&e_1w-|h{c(n$y ze#>0-kXY?lTrvm*L4Y6#UN;D=I{K_O?ES@f`{sIudG$c=4E}HKP9H>ibq2RO*Tet- N002ovPDHLkV1kLx`3(R7 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_radio_global_functions.png b/radio/sdcard/horus/THEMES/Flexi/mask_radio_global_functions.png new file mode 100644 index 0000000000000000000000000000000000000000..3b3efefb57dbcce2d55613102b71aca3f5ab8a1f GIT binary patch literal 1049 zcmV+!1m^pRP)E&T)Pw)0oYRSU3x1A!Xq&Nm6zb%7U0EYe`a8vXSza#grrqG7GAw@t z&(APT^T!*z-EPRTZ12E-EE0*ZyStl`BoV-mM{aH|4-O7!XTnFS2L=Yv-`@{W6yfoB zeiW@~nux_>`19uv?(XiOD2jc=<>loBz?GF1CNs;jcz%9vyKWc;3knJdfJ;kDU;4y> zGcz*@fDaE3Nr5*uHVA+>H#fFEfk1!&*wE1MbKseo8RqBbQxF6Kpe)NQE-vQm?CdAs z`}=!}qDYs^#b`A8XpHi#wg`Y%S69?^ou+AinW2e^2?Ah!eZ8|( zkB^U2)3mrWL{VfxK>_FI=lS;b#*U5-0^soQa6&GG!(ob|NL5uCiA0hEx3;!YRaMHe zOaN3>m8z<;udk1WVbJUK((CmyCnqN<5v!}K34p<1&^fs_H#Z4@x~}uPHaR&-0IaR8 zbq1{KI{kh>0dRD5)OO>OlaquW_W1b7l#~=Iio&O-r{utqNQ9cEQPVV5R8-iWVRm*l zOG-+3cXwwCUQ<&;0RNKRw+hGy!>g*Q;sosJ>9O^lnwla2`g}fTI!00aM0E_7K;G@Qd3i%>6j!>PftHZ@S$%2%*e>#?d@&C?0nn<%gf8oQoXdaL`jl3 zH#ZlbJ32a|+wCR*N|MB#ot=cB*Voq+1c7e1o3U6dIq=!p85Kn#0H&m*aCLQ+kw}Dj zd3o_@1A#zNE{u(h5dfQ-nw;ynv$Hb}q9_W>%F5!;ZEbCiNLX50N&sA4U3Cup{{GJP z_V!QRMNv_aqoQ(oc}W2Dcs!r{lakA_EH*Ya#zE85(|LV;?I_xjkr4u5OH0f5?&H_j zSNeQDN|MCw?QO^9vbeaI02m5|zTXJwy3Vz=HOHVA7Z(J;w6ruDhGE|b2!a3ruq^A# zBH(hlP*+#yxWFtfE&>4R>gu4Xs(oLbpP!F#IE+vz1i#<^qb@TVjbeIw8UWDT+?=?t z{?Ob1IW$e9Wm#VyMLgoJt}e*3jG>{Scy0ZO6&4mE7z{!bzrGHH-}-kF@k{m>1qFt{ Twl+i;00000NkvXXu0mjfp9$*6 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_radio_hardware.png b/radio/sdcard/horus/THEMES/Flexi/mask_radio_hardware.png new file mode 100644 index 0000000000000000000000000000000000000000..f51a1aee1288e069044aaa27ad48617304c48da2 GIT binary patch literal 686 zcmV;f0#W^mP)QE3}TdyYyN+6BcpDNHR;y4|6_O+c24?T-YTtteIn;BYwbcez~dHq}4>R@ZfM9Ova|uj)7s>AKFJ1wr5u^X5_% zh5zk!Wc*|>7@*l~;(R`%*=)kHtUDW7mbq_15d53VFXCh}NsGmT+n-J+DwRri5&Je* zm{dn15duKzbefjSC3j|;X5dtZrp#Wi2h%jc7{hM2!)P?(XYqJExTCPCJ|2%mL^K|c zsoU+|%=pU=Nu<6^PMqkaTlsZ@Mx z{1Sl_3I#M84a8zGB$G+hYBjzT0D$LtsMqV5OeWZFx6cXWH}pTA{Bqfv;Wh*T;C01!oypQTbMh@yySH2V2l=G#*J4K#1v U{-V2`#{d8T07*qoM6N<$f^cjzx&QzG literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_radio_sd_browser.png b/radio/sdcard/horus/THEMES/Flexi/mask_radio_sd_browser.png new file mode 100644 index 0000000000000000000000000000000000000000..75b6a9296d65f5448d59ca22247ff3dc52e721de GIT binary patch literal 1091 zcmV-J1ibr+P)1JFrC zK~zY`<(ExJYFiY?{~lAb@`h*%7g;qGItPIcp4IUkC&-CWKmv`a41q;sE=YRI^?7i38Yaba& z;t!aBKV%u7HO68wxLhu*tgIjsi9i&^4<3)l6WT;k1WB~Ew!&_=qqMX%HJSggcsx#v z#XfF1_rRc zz7DtB4VTOH^)=qy+ynsh_V&VRwZiA~VR3O0CX)#>Gc%Z+oP@z(fYrnc+6aKl%gemExgh}N=H`+lRVoz$FcOJSuh$a*Pfkvx zDJIF?-CY7;LqkK#sZJQNOeTB(pno@={`26BRA8-Ei}m$&WMup;3j_iXMG+c}21`pz zh{a+s8jUzOIQVt2AP5pzsZ^r5xmkKvUtcd>o6TlyZEeBfa6q9@z+f<-yu2KRg@v%& z?IC+cFIywk|)6>%l z&#tepSyWU+g+jq#F!(iaXJ;n?u(Y(4;c)m?fj*X2a)jU5xQ>|9hYPF`fb7{5Oq=ESI@&W+J$;nBo$;`}zQmKR} ziofq5U@#Z}0Q>vx0?}yn z$9ep|#>dCUDc8p9^%4MUYHE0XelE3(qDYU&^C7S6>gpH@g}xSCKLV5F;NTzuP%f9V zrKN>kU0p0IDu{uG!b*Vfi(wOT2c%L#xwosRwe{ajsLm1_07=jG*b zb93`^a8*@RTHx<38jUg@k4yI#7Z z-;dSRRdjcE!|(S40Q7o&VpLyVUQ(mc{L-=Z-HVEfcz1Uv9hc|k=IC%Zcz=H{O||U% zUzpR=Qw$Fe} z7Y>MvY}_a+Zbgws&(+IUEi{(=_;ezSuW22HD=;W>HZQ?RGmY77GE0wY9Ysiz{Aw;$SccP1D5l z{QNvtR#vdJwS{0Xh`6{o#KryNVs&*@*exzDA`l4NEYxRbXQ;2QM^RA`mY0_S0CRJ5 z0DzvJ9%z~dUDwgk(IJ3!T}NMEA4*G0QBzX`zuzBQsKenf>+9=97|mugTUuI}l9ECI zTwh-ow&UaD1i*Xu?(yNnhlcP!di03De*KCX>Zrh7U0npg)YMcqHa0RoK3+I0EiEbg#c!y)u`yPa;gn|JTtB>FMEZ<->;$ z1iG60(cmYt7BznExh_Cm0y_6)007Tdi4i1=`n;Q|C z)6-L9s53J&34qU^Kj+7fAGx}^Djd69E&`yUC>Ih*Q4|88%jJqFvr|)3Oh`x|0Fhb( z_?-IDYOXBH1VB|)F9cRql>jKqa#VeCaxwvkf+FtNVzB@KPEJn#lQx)`m?*epsMY6$ z@_0NrJv}vSDl9An0DS)Z`9jC%x^r6q2!%qJot?$|_wPls(RJNOuG-pK1F?#Wi@CYE zd1G=N9UY15`+M!~?i%lSX=#Za9UUwzETq|NCICKp@!fCnhEgA04$x3=ap5dc$CQX(#)oUcDj!R#sNtx=GB;%n$%gCKKo9=Pw4fRmGx;`#gc?*zca#6)IiXVYf0 z(Q3640AIX#VHg(o_Vz9%9%5x`Mn*=|Jbw4?T}*yVvHm;l?(QNK3c+kPBOxIHNl8h# kbLWm2?5`^^1OHp{2kZ&tw|FTU$p8QV07*qoM6N<$f?`2npa1{> literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_radio_spectrum_analyser.png b/radio/sdcard/horus/THEMES/Flexi/mask_radio_spectrum_analyser.png new file mode 100755 index 0000000000000000000000000000000000000000..2908833ec991a72c14c091b8c877aef986f7c3fe GIT binary patch literal 921 zcmV;K17`e*P)})9(D@n9cn$*L`0+ zbIqfOi135Z{vRk53gmLRro7y4x6{_vRvDBpq*5svA0HP@(*$VBqiLELh9Q@imn9le zN|L(WZj#9)x~|jS-d=NR%d)W9Y;|pz&*yn~c);y;bAEpQ?}pdc*8+&w>lMqgtn7I;yIEJ9Q)y;q2_Jt_@XHrLV6KfR~q- z&sbfZUr41=TwPrei^Upgs~n<{;-jM@0HV?8&k~-Vp7QqgR*`#pdSZQjy?KO|WtFA; z`1l|mk5}X)kqE1+tEAKE<`9~u$@B9wEiElTwXC5~h+Hm5Fc@r{@Z#cvU@%A^5a9Uu z7=XjWLjV>Q762*^PN7gJ=MN4J*xTEy%_t(0NF)RhzuzzEbXqz(I?8ULD2nXt?8w;I zm;hp$rVI`ail%9j&1OYJBpeP4Aa1u?Y&M%X91hvs+>~fEDt^CT07)bgA|g^nxVE-d z)2f_Kr&yLHfj~e2357x;BC@!+D1hwm@5{={O1XZ1e!hyZ%DgqLDnw6D55vR5jE;_? zsw#$Iu)DiUEEdD>_Y)3>X>Dy~YHEt<>1lLbM?`SBTx@J?d@pGxlM$!WSz*8@Cnpt_ zb$54HmM|WV3m}t|lfSgL-)^YuI;Lr|wY5b)pJ#4v4zJf+QPbbw&(P2i$z+o4?QH;N zXJ>04lF2#N+W`n&xM)^u44~ vDe?JyO}{2iOicXxnpnc$z6e(l{%!mNKrg9in^*dl00000NkvXXu0mjf?}4Qv literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_radio_tools.png b/radio/sdcard/horus/THEMES/Flexi/mask_radio_tools.png new file mode 100755 index 0000000000000000000000000000000000000000..711d499deafa2283a1ff2a4577c1de5edabddc42 GIT binary patch literal 1195 zcmV;c1XTNpP)(hI=4z~+!amKXtZ(xQKEv;oUI4_wP-s8Owrn&c= z`Q3Z&SvUZo|51v6%ka<)9FNSZ!da!dZ5v0AP53xXJ_LB zpP!$h)oR6)DwRrE=D?v0G4H#osUtb3Rn9XKnWo1e1YHMpHZkw^rcot+>e?Ck6S z0OA(D*=(kjl@)TiTx7LcX=-YU4i670KR;h$S65d@85tS$`T0ptPfuht8mX_Zk2sE_ za5zjvL@6mL(S;vR6B85Qc^-Pb9w#R!3HgLVA(WJqfQS+c>9@wm$HDVF3)US4o}dkeqc55M1!}Ng31-jjC@H~&q%uIN_ z-k3I9TU$`8)lo?k3kCoj5PLki-EL}XY9hT}PwVUJWHOneY__+z$!4=rC={al`g&rF zQCeCWDHIA)tJT!j)<%LL$WM!J4G#~4F%~yL_V@Rp)oP(sD&g^Xu(q~_hK2?>91gs` zzRE@|3YZA&;NSo{oeoN+5=%=<*xK5HN~ID*X|-Ar3M{pd6s;a6e*wxh)a&mIO7{kcONX!F; z!(p_vw8#RJlas}I6yLD8xEO*UU}a@R{L#(L4RUjH!Eqc02M1%qz7;RL?d|Q-8vAo=}%3WY*cSXdaV^ZjGV_R{}Ke*qfvkvN2ZqrLzD002ov JPDHLkV1kEi41E9q literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_radio_trainer.png b/radio/sdcard/horus/THEMES/Flexi/mask_radio_trainer.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0b56970779259ac9771f98f58ab7cfcefc5822 GIT binary patch literal 1032 zcmV+j1o!)iP)cc zK~zY`<(JJ%a$6L}m&bFO2x($g5Pr-`L<$NDO2~qWoM_afNwWx?)F3EvA_fvc;y(~U zMU+I;ARQm*&t+qth@-oah4250ZJ`R%n?>+EMs0D%60 zG4zLwEj0*$Ha0dW7z|P<6rz`x7iw#3qmGUanwXfNnwpx*$@qswqfzws_JUy;bar;a z;c&p^a$#s_2nvM)4Gj(0-Q7i{Rs>Ealc=w+hhDG8@$s=B`0?=(Hk%DZgg_whbKud@ zQ7Dy4+~42-a*@quLv3v>5{X3R;ERh35D`{aSHE49NF<DzH7)(t~;qC3MbjC87jMC|}=x#`-(?S7=kp;FiHL#{5v*2g>2`9v-H=M9I668i3w?ckh0$n4OG^u~*(}!A z*F}TXYISLFE|)`hcQ-hWgU91RCX*@5@jQ=UFbJ(y3%Oj5^Ye26!1D64Xs|+|C=JfT z-EKFks;a=UEKDX7%w{v>ayf_y0|Nty#bSl{%gamA;J&`T!aBk2#Ih{8TrRr0x+1^d zPg<>(o}QjasZ^3iqalezLJJEEbaHY+L`2Qa&D7M?B&rkac6<3tB5!`bA1up)h@jKy zFg`vGr_+hCv9UtGYBU-Q4i2Kdz5Uy4&1N%ro-cpYJkMi#dKyH8+1Xj#-rfo}wYazl zsZ=WZh;25T;J+>akBp3f<2bCWtO$UaOa>;CNi>j%(Ae0Bo12?o-%f{zhae&x92^J& zQ>heGD%HP&=Dn`24#{M)JlJZr!eX(Y)bZ=}`rm@)-Rt$121gn%*CQYliYR0X9`sr~&SAo<_Do&EyVSk`yw2eMEA0000RhWWLNgj2Z1n|yx*J2WZp<5i3jKj50dQN2qDnzcG2l{uv)DGg(MOQ z6bc2Hrio}Y`eNj(TCElVeCgF_G&~$7?;B?_8BC{B)a!MarU^-s0*koU>!DOCA(>2K zHk$zePT)u+0wDx8n+;;ISfHss9*>Y^*~{?t;tGXAffv%XH6K>hzQK;;z_Kjl^LY%1 z!`olLPki#p&ETkEL#{t8TkaiD)edwulcP6isp6*H9_0 zt8d^d2r4e5DCkCF{~&EqN;L?j6|}lkH$Hzxe zN=c*95YO{OL?jlA$>!#!+}+)Y48J%S46rPVLZN_Cird>;48x$&XaI0}dWvb9^m;v% zQWOdW^7%Y3FE9Gk8a9-ml%ig*V;BaVPAA}#ZQCT1Ngf^^LW?D%3qw$~TIKESZ7ktC z@UO6;v}Uu3>$+qz8P?a=xxBm#B%Pn1W11%Yem@-Mx-K&_Gc=n`{j$|+F*!M@ug%`x z9v>ec0Gyqjk;!DjVS~YdY&JV~^p%yBK=}>B&=MCH7kPbs4VRftr?G8Y&)e;`zE2Aa z3+(Uj^ZEG+!2SI_hGF2kZaB}#)heY@2@%1vES{gA zzhK>NmtwIL8K_U=JO5F^}BlO`wpDpX-_7T!QS2;78VwO-rimU_%)qQCoe88Xck9D zM+Cr?l@I(+uL}2d;|c@%*>!r zD46yF08F)a{M_H)uPe;ua)!Dr%k=yG+}+)!<>zm{VsvyA!C+8-%d%{!tE!62%gZnI zok~)v6elJoI5;@S@$qqFJ4whliY-oBMGkI;>u=b7*LY02qx%`TqXScsx#r!$AOaI2??}<5X4E07$3P^!xpc zMx$-m@$oSM(ChVfjUh>ru5x~Uo(~TXdVPO?p8yyNh1#%4Btih(+S)Q5;r#rZZns;% zx%KsRzP-J%TCGx16qZV*HtgWwfB@+8`S|hiVG3+C8eCgjqs3w&0D3$gUSD5#@9g>c znIj`31VER|#igYs#$qwv-QBU-Y#N}qx3{{xf*^2pb(M`q!|3My{XJ)AXS>|A+wBYl z0^Ho(&5W!Fg7+eV6)j^3i}RC(~wT5;dZ-GtJU-m^7%YoUtbNqk|Y70BmI<4r<01JkR&xt zW1&!BEEePP@-oBWFl)6M@$JmNG8hc<^YhcxW!+7y)%q)BY;25)M1rkW>sJs-{s9)` V3w_XAKwba<002ovPDHLkV1gETXnFtu literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_sbar_to.png b/radio/sdcard/horus/THEMES/Flexi/mask_sbar_to.png new file mode 100644 index 0000000000000000000000000000000000000000..813610307097c64a9d83ae1853373c73551164a8 GIT binary patch literal 631 zcmV--0*L*IP)JbYXrJ0Sjl5EIMUV&mOvA348 zQr>_!fR!R88!tdI8}=yF6eIsi(%A63n>+Vj&zLdXp66S$`JLZ6pU#<@IgliYDwPVk z-EIQ# zR--5irBW%=JKk=$ca8=oA`%4QDXhMlLZLtm!x*AG&ufgkqgLbfdQBZ4!!Rh7N@%rO zu-R<7eS#opY7b$L$72ds6OYF+7z}jhIh{^(u*2ba2?hYa-w#m~5ekJgV;sksgFik# zUWNgH=XsRNWmqhhySm+OHwP<<@)lgL*Ab0IalKye>ZjAm9DF<;Ujm5;`FtM9WD=Ll zMKgXl95l6`j}}@i7N$`}QIJlj(QGz#drZTV$;1@8+wG7@B+%>i4EtnR*3>jdJ)KUm zTCJXdBuV>|`1Tvfe!u_rJC`iW^b5FPK8_5e RZ6W{w002ovPDHLkV1k)n6pH`= literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_stats_analogs.png b/radio/sdcard/horus/THEMES/Flexi/mask_stats_analogs.png new file mode 100644 index 0000000000000000000000000000000000000000..96fc0c8a20b4e9a62be69f815748e0ef6516d99a GIT binary patch literal 800 zcmV+*1K<3KP)rfQO|EbR=C^n^P5k4~oEMz(f# zb_jrjgM$=8)Pq%3B>>*t-7#ttLNFeW698S;B}q1jOOm9>btFkr!?}^E`ta}&i;Ige z3wOTFk>FDUd!@~o*y1IgAilV^tJh-k4MNwLhSO~#wXllH*wM77& zot@?F?QPwj=XqRTUk{?yb^YCpy^Xjj*l`@nvdp2Op-|z?&(9M8kB*L70yk}^)6-LU zo`==d)lku`t*rq7j*pL{`8rva1pvrqv!QJ(URWzz5$;-=2_Vo0yv$K=B zuJii(nk0FDf6sEc%)Y)p0${0B`cU;>0+Zy)$q6SWCW3xVrBak6i2#^LBv>pKL+5#0 zv)c1Klu9L-rU}ckFgiMl>FH^#tgK*UWF)ktU!K+N+NTG|KfB(3*ERd?xdGU((eCap zUSD4$1(wTYJU>5UU|<08c)Y%k^ZER@UvK~Zas}YW$472&Z!?q0L=GGt9_Gfz2CLO- e0G9r{f2ZFC#|1?{IGv#Y0000QEGgS3*Dq#b5;S2TUp(3$1+yJ9{e;EQK^$ z`wC7G&=*LVS__}RK5#4)MT`hqC_&Z~bCo;yj$Q_$7Izi|!bx(zv(Dbx*^x*RFOd8S zC;%vzlihBIe!maPvann(1BIm1X*3!Qv|25ws`@d=UouUT03Ldnrg_0pBFW?Nh+HlQ z&+}ke7HYLxppI?ZMx)VyrfHbZ=Lm#t1fq?({I<41h_`ZJ$3-*O$>)a z06;dIh2uCVl}Z4B!{Gqeb#IzcRTa9ff8TMt-M+Rf<#L&e#p1g1&1SbAx7V!hbUIK~_2CWuMH2a+ b{X4w_j&6cL2?Ub#00000NkvXXu0mjf6tN66 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_stats_lua.png b/radio/sdcard/horus/THEMES/Flexi/mask_stats_lua.png new file mode 100644 index 0000000000000000000000000000000000000000..d1b54a4d6c7a408875a71c065f6ca85fb806e743 GIT binary patch literal 1226 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 KMNUMnLSTYxbnqqs literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_textline_delay.png b/radio/sdcard/horus/THEMES/Flexi/mask_textline_delay.png new file mode 100644 index 0000000000000000000000000000000000000000..9b771a94a91ca98231f47029388fbcd943a2e9ad GIT binary patch literal 630 zcmV-+0*U>JP)jK~yM_jgmi0>Twvw&;4N~1iyyVpNlkTgCSvoxTKR)FF>MK z&?c?Eg4U)8S^}ZP^@|8DHkr6cBB<4nLqP@x{)tpR4RO0!m&-lyEYCTIhv#s#wY7z$ z|C;UXZCv4SSdB(Qwr$JpcI!I;jYcC)CKJVCF}b4As5~Bzj*pLjh`(ZIXGiIDTGntl zlx^E{WWV3n+uPgR&bDn0hePT5`WnY^Xt&$c>va|v7mG#b((CoUO0d2@77m9M3t>2x}HyFMdb;o#taR4PTk z-{(6zoerT;h_kb^4}&p=NF>5wFaV%XD6qc1PBNL~>FEi8o0}WFUN7Bl7iF_qj*gB1 zxV*f??RI~bEgFsT{QL|+I-Mq;&*QRfTaS;AlB8O#rc5R?7fPj4(eCc99v&XFva+II zdwYB2^LZ^VFKc08;jhJeeSOtrGSUA2zFe(VOVjC8FE20uY^!BiYBrmC2XdN|ds>Ws Q*#H0l07*qoM6N<$g0L1E*Z=?k literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_textline_delayslow.png b/radio/sdcard/horus/THEMES/Flexi/mask_textline_delayslow.png new file mode 100644 index 0000000000000000000000000000000000000000..de2f7d1f74e2c2f88c1ab8831352f35c30abd7db GIT binary patch literal 891 zcmV->1BCpEP)Mp^L__3M zYY>zWQL}`Ev?#QMLIoK_LsJkDAry#2IBB25ttCiP9J&_X|9OX(r}u%Ehvx@pJcq;K zpr@yY0RE^%B0-PG!?Uw9{z6q%_V@Qw_WS*qot?$V$O!7{>hOy~p@2vvf|;2aWV2bM zQYrX+J^;YS#|IQefvnT%Fh4&JyWNhbrzdEd2D{x3olb|t!$auxdbnIJ#N+Xw;9{|e zrluw!o6T}!VuG1Wh7Ao31i+1r4aVbfuCA_l}fR@yPLAp=|nIX z#QOR=_V)Jh`1puov5337JA^_ZXqpDA)e580h+HlQhr@wlu?W3h4+#LgUN2&?7%nd_ z;rIIi0Et8b+uPf4xmV{{Oz8*ySv=q-)D1k zGjq8dNpf*j$w^?KRZ*vND`O_CHs&}Oso;o*U}xw+x>^))9aCuuU7I6gkk z>FH@Yole%**VAAy@Z#d)C-C_An1h3ZBuTWkw$f}i(`K{LYPHg0v9PPFi*C1@kw}E) za+%-X-+X?4W;h&XXJ;ofnascDv9U3lOs4)l-d9Q literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_textline_fm.png b/radio/sdcard/horus/THEMES/Flexi/mask_textline_fm.png new file mode 100644 index 0000000000000000000000000000000000000000..b9a2c4a32f57a93c19850fe669bdee4dfef3390c GIT binary patch literal 755 zcmV+g z0HsnXeSLi?91g2mtxA$cMn-gYcBY-39RUJ#d3mX=tu3D`m5KtEWdTqsm2h2`bUIC; zQ1Cl)90%LB{mmW6;b-QDeTnCJ6pKYH%km5Tk@WWV5{*Xvp`oE6dU|?%=KcL$p-@PG zmY0{cx3{N>i3v&4?d`3i(WoS8etuqn;_M*#fJR406$phwzO_=RkVquBxVQkI zudk2i=VxADU+M4f2jK4R4oN=W+}seTR;yfJUjvXzr8qx7Cz(tFaCLP>XJ;p^t*sm# z9RZL|r)g+t@Hvyo5I8(ML=pg|Y5q}MKA*?3EIvLyC>D$UUvgcSlamvl13xd((a}LN znWRuCV49{cF-?=x(^KBw-u$8C<6}NQKYhlwZ2_8|o|fl%G7Lix4-b;0cs#Chxvcf| zbpdK@Y}Cuki&j@x1t=H{%5`1+m(6CeEDOuBD3{B0b#>9*-Oc^|J&%u%3=a?U^z=k7 zm!qYn1;a3yoSY<=%V8J>&CSj5`x-YkHngy?AW7QZ-qy^_%)ejp-%713zm0=~gKBGQ lQzQ~mQ&W?6cX#XBNdb5;NV002ovPDHLkV1jsAPW}J@ literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_textline_label.png b/radio/sdcard/horus/THEMES/Flexi/mask_textline_label.png new file mode 100644 index 0000000000000000000000000000000000000000..8be12e39bcba3dbd933f9f148f56063f5a9afd6a GIT binary patch literal 492 zcmV$_90eMM8 zK~y-)t&^{-!eA7J-^u<3Wr#&grWFwkrp+Q2%WNhUF^WI_1<`1<8AU;GAjm+lDOgQ{ zFoUq5ScDDXH5ukE*NuB0IPit}5;gCUiOGE%jCX?7~Hc(X+ zj^m)&YzEqT@qLKL<5;iPP*oL{Wue#W`IZQuzxaGUBbUp;vaGL9kh@0Jd%8 i@p$-aMHGLQ1my*Dy0TEzHs2or00008e_57)8oa@!KbmMDNJBz*h@inCC`_Uj zmxds6hz1)QgNPObhn5O*i3lQ+NQi_62OFv(sG&3poC-2AtjN62JA8Z>Uf!hxXE^X2 z&civ#FAxfaH`uh6B#4q6U`N(FohJJf{OOKVzF3cZf;K8Znun%j!L85ZeOxmtt>Auv$wa$ z?(QxolL?Q4l#PuIEEWsVXq45} zRim(lg$2&f&(U=qfJ&u;q9~Z|@9*j8=-~A9ltd!I)6)}2M@Ou!tx+zQ0SE*F6pKYv zRW;gH6a~}Q*B7s^uheQa^7%ZOOoq$LOL}{IDHI9-)M_kKma(xhQ4~c&p^!{YPKu^!Vm6z_;c!SMlQB|Lsg(5h z_lt-~LwkF>w6wI))YL?+R^#XAhsMT6y1Tmxhr`Uy&SEy3sZ=WL?Cg+8Bse}kW?*0d t!r`zeit-nMf2gXeL?RKny}dQQ_yZIrto%OH6deEn002ovPDHLkV1nDddg=fG literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_textline_switch.png b/radio/sdcard/horus/THEMES/Flexi/mask_textline_switch.png new file mode 100644 index 0000000000000000000000000000000000000000..7c7d646e417cb40d27b0b2b4102aaf75bee02527 GIT binary patch literal 666 zcmV;L0%iS)P)2&CDIJmR3!&0fVOahXu)oQfc?Nn8jxm<2pEEbDv?Ob19Q&m-Jn#N|c`DOC) z@j+eJDIz>SKezOn&*wMJB%MxEMCkYXIUbLRQmI4{VK5lvY&NsN*=%;B$$UO%C={ZI z@bdDK%H!iB03eY_z-F^q=A|eKHc3$w#N%-QfMFO=+U+&~ARG>Fgn!dF-|O|DOePZm zfY<9?1Aiu4mCNM<0GOr;#p!ec0EWZi8o0l||EtN{+ZzDD?RG;sI5+?Rv|6n-@Z{tK zjYb2(U~sM0>2v@9fj|JbxVWH*@c8(6>uUeh;o%`ggoQ$Zm`07*qoM6N<$g5;w$ ABLDyZ literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_theme_add_view.png b/radio/sdcard/horus/THEMES/Flexi/mask_theme_add_view.png new file mode 100644 index 0000000000000000000000000000000000000000..7e240372f638aaa932db3a48f381dfae319ddc8e GIT binary patch literal 545 zcmV++0^a?JP)2w$l zhbW2?N!rKb!E`!hJRVc8*D*~qm^how3Lv&^OVqmEZURUqlkvS!ajB{rNz&hyPNzd& zY*qaY{s#Zs;MHnHtJQiv*tTsBhr{c^pHcj6@fq#+`@s5qKI6J>$k#NDLZR^YP5x{W zj^n)h!*^Zx!)*MTrLOJ z+wGQQG8yvU)Bji1k$^Q#qgJau_mEd*xm->Fxm+&M%{;$!yhM(9UZviaJVm6y`I-Md(>#0~Q((QJs jR4Tp~0KZbPeNI0CQvBVqgcfMy00000NkvXXu0mjfv;*xn literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_theme_setup.png b/radio/sdcard/horus/THEMES/Flexi/mask_theme_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..2a39d1e4028f9e4b89da1e026743bf5685b7b41b GIT binary patch literal 425 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4f>32+({X{1V#nN?%nb~$I0z~W+ctMTVBFKV_v9CD_}Gq|3;PUG6es-$#Bp^ROG!EW31Wtjr5g1gpPizG>~*_>y7{^#}*{i&&* z41Ttmv-B*4rrB@Hm5!XI$m49$SDbOYa^B(ZwFTz_uYcW_>>9|y;CtD{J$sfKlfxvH zwfCgfX&>5~`lmmHxnbSwgf};;o>l&nwds4j@WT8rY}ZWQPU-u-@BVD>Qz?;lwpSNv z>|6dT%k}~Lp9c94%>S$_W_qOOD4m{td*$}Nqt{=zs(1>pFzTGP5{VIEWjJ7RHrCX| z<;pf)!()EYxvY*0R;{|WamTvt3mdKI;Vst0Hsc(*Z=?k literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_theme_view1.png b/radio/sdcard/horus/THEMES/Flexi/mask_theme_view1.png new file mode 100644 index 0000000000000000000000000000000000000000..9e67515ece26a02963687415d5f7b4386feadca5 GIT binary patch literal 400 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4N`#-?&$UE56xSeQ>?kPChtww%{abmF|t2C9t=#Db``Y!UbSS(qW-VHikghS z^xl(E;e57Pa$6MhZrOdWe!Z#Ee!sZy@eH$6&&4{6zrXzQ%jJI8_hciv`|BS?Uv0Gh z@VIer?Vn5UlLJK}L|E-sbKO6-)xk7FYXj=wVEmZ@Acs`Yn{JK s^37S6`TBuI$6{n28JGkr>mdKI;Vst07WgRHUIzs literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_theme_view2.png b/radio/sdcard/horus/THEMES/Flexi/mask_theme_view2.png new file mode 100644 index 0000000000000000000000000000000000000000..45cec234d5fe578d996c3129ec5645d3247dab6d GIT binary patch literal 525 zcmV+o0`mQdP)!cY{3PqZKeoPt9ahr3HRi+usW5eK1A zgpN|Ngi5%56o(3}yGyq^Ra}Zg<1^&yCANQ;Ce)f(sW%2ne>h9dNk0C2&J9Nc0E$70 zIG{vCgii?}z&QtF3`C?f7-Qg^LkQvF;Db5m%7Nd5bG{0W!fB9mPntJenx<{jv5n=; za%{u(5B~e$Jry6xWHNM+%aLIiWKXB`G#rxay6a!^FZ3_(=0mTCg%E(0a2*Gb5-;O1 z4)giIdQV#)wXW-QR;$r|HcM&4pp$Z$(&;qab-SeNdUTG1qSb5yQsSskh|Y14UTvF* zXnmhmJ5DB(cpMC1PbNsEQlXBOgFB`PNQvWOaqVy^1z((>15)C$(FmCXz^!F%RIh9ia2IqP)blS-S z89}%V!;1lIG|*3+&36X} zq}HU?96UUbTJzi89pCKjP4!RwK%NTTD3$1}t}>ZS(lkx3xh_AoTKw+&e3sA02cA}& z0HupWAJE z-7cv$PZ|woEi2w|J+4F|!55Ch`r#q_o=1CmdA{Mw**1s$K5NzLe8bhfzsF!OfXrq? zJu`!!udIwco2H4k`}+_=;A68H+Bfqe(DyuC-`wEh@)AM_ymVcpQYpMYKE~GeHipCD zm_JwqW(JpvMXVPJ$XXVJ5ct~Z;6tN&=Sctfzm&m<#NvbaqjuxDnvv` zKs!l50RZ$N&+|wKL7Z~}Ak{eMB!nQ(^L!iyU?GHb;CB;3`~+_!(^Z-KQvBvp6eUW< zLd);v#G30L{P)2r*%$7m)6jJtFRqK(Y_{Gp7-$J|Y8VE&jzhFu(nY<#(#Mz7r@rs^ z@$5LEe5HZSav8^^63!cq_`tfZ!x{{5*=%AuoyG^g?RH_?Hin~7=$Ob|eN-&stXjok zp%CohFp8A1rfC>j7H&EnJlXa}v5_)9tyI7=88F6h*XscQb~OzEaAKOs<#M=cwXj$$ zvkKDlgY1s$$xz~ w=Uaz!cY{3|CDZSrYRP*!`-y%(m@xyN(U)+6r`&! zAaoLhPWL5>gF}np(7_>tFJJ}hQl~1l4uVoB#srF|OA~5iq&A6x(jU%{aFUO6?td;v zAR>YX+VKJk0H9APib9-oVvG?0M}sj&oO4nXMaPi==A1hY{2rWhGuVwvS5>Y{(9I?6ir!PnXk)s z#k#`rxT)fGat!>~>ur?pn7IxD0mNf5_o^RB*5=#3t54Hu zh@yyxZWnu^i1Ta~`CJY!qY;jiNzCW-rF>)WutCuc}rKja)De|Dgb~; zIBc2Y3OE*x0syAdsdbXwRv$$oOV5%dQB#&_p{kTjBrIEP8~i#RQ>WFUn`)KhMuXlJ zg%+wxSLO2Bo6EMV&oUXDq*4e4g8+c1ejm5B8t&U|s}2KjV4OCUY4u@@xeooxFu;XE n!FBKw!>fhfIOA>0v(@JV5Ml0uEmmrS00000NkvXXu0mjf{)*~X literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/mask_theme_view6.png b/radio/sdcard/horus/THEMES/Flexi/mask_theme_view6.png new file mode 100644 index 0000000000000000000000000000000000000000..2aa43514b9cd21e5473db3f89ddab3af1b3ae442 GIT binary patch literal 625 zcmV-%0*?KOP){z}lW0ooMYPGK5{N>nh~m=1x;i%c?WMbz#XIke__0zZCm%+JG~_xIyvI*nGVg`M49d_O$w9_&775AQRX zO!7meLMp{yXJ@>=xFD5cHk0Xy?f5|M3ysBMtZi@epwVD%e4O!koVu=aB9~)dZ*Oqm zg}FIWDW*q9?-cJFc&l3Fr;QB)pkWvs8XROa8vQTWb61Oj0W|mbv05s@JUYVHdL2KG zkMVAC@!uQ0WUTAD+fX0Z*ZF30lCQE^u9ixqQp`_J2UBWI(|BoFq*5Fi9&WpkWt#lF zxfz_s-tlKIK00@8d5kdqGy~%LFg+d{4a2vxB`=JMVSL27a&_@P)_ghQ9&Qe2B;_7*KT z7v!K2@FPeEDIKJPi|ydj#j!)B2-2ZiaLCe>L*(z`1vRM_lUzXj!<+8C`|;gLI`lqK}3lL=Nv)^_`a`j2*5&!#DQN!2oVOS;WWy*5>vy)7>m=f zwWVe`v*G#&|9!9#_kwNC$u!N#GtcwrX+DoG3?B3^-{!+)0!SGdXRaH{SBmX;uT%;- zKCD*hxL&7=PG@ytT*pyyI~?L}G>SY6eVIu?cZ)?pN*vW{dckh1g_p$wo4K4muw_|z zp3QLC?XJv81$hP)>GAtqcd z#u&UR77-4GkXc_x?f4i^G8rhP2JU;m|4B;?fDe|JdD&`_T9aDybEU$&sg%FsdY;RX zS~It~$vbm%ytlBx7h792oem$atoR46SF3z~bTr@#27_!jn|!^u=a0B-+eR{xK)ulz z@N2E{Gb&ENi%NLYP@W<7>PtY-~FW}Qfn4=cl|5%qt#WKPKVv|bH3T%=i7q= ze(iSIZZ;_#XDoOs8%Q#dz|)Nl2*-gj2Gv>(FL!p(ZnsBO7{Cj7F4I_qTm(F*Fcx&cMlJ`Aa0K38 zAD(^YyzH~~e)sK#{xk1z*za1;^I!kH_S(-{>shatr(f==yG(GxElk{0h zqa+QI^eIUnOX`wzF#7i$NjoLAOWGi5o0-u}&SwO`cwlMt_fwR|eqar72QaLe+^uSZ zfJvGq>4>ELlD0}}lSK6Qrz>)GOww9OPfB|E|F$OqEx;|nThZOUHR>?{Pef%FR?urg z*aR#BTF$l(rvbPg*cu~ybDc0XU}jWyWkTZ)0*6Wpv>mwWZ0jLl9Ppb2k_+pE0r(qW zz;Iv*a2;?VK0wFq4gn?r*8+C~D}cQ@1X=@(JsX+`_!fSblWuQofT0!fn1T0RXZIEw5K>- z4h#y^3kd`8T%`ni{q+YPA-VVL1V&cnY&gk-T_KqmR^vxf}hL<3;}?kY?!8~^bn4*|=H z8chn{J7~~TV#LyL0H|v2l*C~i@P5E|4Y(<VNK z75fnmlC}_tQ!)_ADh$Aa!Y$6uX8_^QV;gBp7Qo^{iYD93Bg`%eh+lxy*9bpS3zI4s z;okB>2@-jP0eGNri}UeX74ZEgzWUgw@O6R0CxFQ%2;>!}3pl@2tF!THBRngg6Dk~f z6FpgyKwe=0R+RCXgWH;b?^W?aBY@+9f|r$aUPPE0@a0mi&c-&Bqy_m2mh!p{&;uJqBPO zP2N-kE(-YV08GHNz?Zu!a$H6jfS*^vqXEbN1pH2Ek$2lTUT-#`j93CpY_gB_foqqw zNV+ue#ik~cX@pbqjr|WK-PvSc8w1zcS|wc&_+o1l2>edcA0!=@?`t2G??3naeOywf zq@!kbq6z+!urqM2tyNMe(tN)O1YR|>$C_ZbEZPItCbY`)Q{vd$1Ok=DCHTDNTuCFM zfA^W$?gFOv1+EQimGsHj#cne@Ucf;kBnktZBWZZbXSAd-v1^}^)Eb$nDLhtYL0442 zp5KsGNuP)nJXX+wq|+KXTBnnp1B_GOMvs+LHePww%nFT*W>y2bBn_#%Jj8%*N%dO^ zoo1Fw6u4Z{67_cDbL9y^O%2ErP?I#x%(fLZ0CehfVY*x8Z-fzb7Y75@AVXQJ$t(_| z`g`RuS3RcmY@6&4tVx8rSR+R_lAiJv$9urzcmhG6?9G_%0eIHbPk^<+ZhRLbj&5Kx zp6NLc7?@&na-e<(J=-XRSIqCrD@z)yib zAqen5p!~}E0(S%o6bhQ~rP9~y?CheqR}9Hm|9bq6A1)|H2)l`8Y7ovTyiJPE65wmA z9g@z8@tkR9|0p2NXi1ACy=Z2k^^Q=TOXLgS-%&WWNWu|GTO_?LZ^M5j9gx&rS7x9* zXx(gPZ$$oPN_rz^>mx~{%&a%)1y%>tTb+YoZs2)1cjIRPvZ5P!3AhtaWi5121o&0J zcHdnCU{0XmNh)4*#WO&E0Q?y^l~Ih7z{~i-jnetLz@!AYzjZ^4lvyr;qXR!*4Bs~+iBWbpoy^7C7=S%7*f3`#M!bnNuqt8T1 zSE%pUm4?0Y&At_8wmC<=OaQKw^qTsf)%vEHh4YW`Ge@Cq%eRP9Pl3yU=Sk1u&x?QK zIsadxiCeRHEEH%X0uSPo%Mgy@`GAWl80F#vangCs4P=)d{wvR&3%1OpZ;$5>2pqZKck%>$$)BLEHDHZ3LFO<4s;KH1?&Po4S!by ztALfjJYcDrsXg?yQVN$;1$+~@05}Ucxq{}4DDQqu7W&z4F0TN_Dfl5}&5PD3O;70RTc zMp9*|Na-c-%4t}k9EML)2T6~$l9bOS-H?ZVXG&U}Mb2Us+wMr}EopU1g-w!vFR4Q- z0C*Fko0f4 z|MRwdSllLQU;#Q`CO5uGcvpoEE4iUfvnlH&olsB(C4{7EN$XQ|uTdceS?ROS3P}Ts z*p8Aw(mUtH>%@gYBB582s8l)n+_Hrlm!-RG;NxHl(!jhb~Iu&{N zNV-qb+Sqkbk?W{dvd!5hsZVPJCAs-ck|q{Y;r4{2OHxL_)O-b8knpf1{Vq~wpbh~W z6NjY9DQ!D9Ue;m{9tJGMvujZ-0lsQxdqcV30{hJD-Zm(A6mSC25$FtT12&r32d$OW zhPpcf3qt)-ECWWESySdld91)yv?N8qk20hLd%Yx0kb)5vIByCLUO@|8P`J6a*Da273<9j6#fBQ?#Y+e@G zwZI^J?I*4BZotTJ^#S?6)qkuiY>%ddM3&4DQ#=8+%D-Y~`CTT>rN+lgGPrm<3QhWw& zE&iHtZvueHE%9brE`|*?vsGCX{0$&8yC>!QPQYF>+n-B2Nu7anfQ!O)I6h@>!WY@P z0X=|Tz+ik;ayIa&nQhG_KTEyCan~V=%|IUuZ)j2M%k^f~KZnp;fbnK_eOndFBD)VT z0O$?$#D`rSPSSES+g-%EHimkls4mBys5oB9LuM^L42ljj+lUXt_CO5~f4O7IfME%R z-_1i{E%22BmHIMZLBhSEWwZQ}!W(q_$R-;ZtE00_yV=TFaqN%r2lQcCT9*?lZ)ZWRR0k z%OOro$ieK2-R@SFP__|xUY=0Og<1}AV$x6!e2#mS8C2kI`)iUWnAy54&)3pMVkA`n z$K(6)2H^YdwgT@3Kc-y}JpZn-+dIl}uQEzZ8SoIU=p0G6n%RsLxwYg;jC?=d#lR%s zWIUi;j|ZV2;kK?9Fi_HMX7)lpa*Kd2c$=fxVUiZ&(QgzZ&1|uJ+r?ve%<@ZMc68UI zJhN60RGL`}gDt5So|>G5r$S!?=Hj{Dm3VYr7p`u&Ykmc=A9xwx)wvPxNt=KTX11k` z1SBE^qAOyz=a&O(X-UjfNvE3G97!XAZvl@0m6Be-J>%!iY@4J__*>o*{y#1daXD}j z9*6A&jyJO{lCA@$0Ph4cj>Pl6JMmbk8n_0i!~@$AIAKSJdIY?maIFPu+Ym=3<&Fvw>kDi8Trs zjR&$lf#twG#A3D33-8DG46g^~ggR&8#=1U~>x73mrvRq|w*vhn%?(7o3A`0eikVrB zq(Q*f@sV*2u+$_~1B+wCY%sHaVamP{I2GT${SrP5z7O1o+kuyWwPscmX~<~Yqpk!- zo7pcC$j^z8FS#GM6kqPBhlx@?K6VP>{6_Rw%VvsZ=pv~GCH@~CjH0oYD+?qprKwuWm<z&3cb~ z=fks-zAN8ZaigRcgI+())NMh5n}EN>cvxm;C&m=L3HU8Oi|oe3iDGP5Z^U!LUGW~b z1UH=T;>q$gncgL-KRz7B0psymE7L=|lD%i`$9v#bd_t(g4gVaxcijt|VP;D*#8lz4 zOXrxNlg(^yTzXJ$JbrlM9DEoy;6x>b8EwISJcj9jH+Bupv(0>9Nn|a3Aa20N;fFa! z;9mXi(5AikO!#NuaWmTxo;wnEHpk(!>2^gMjwIHREp7`{49`5%H!2N|>UA-;HoN+Rotkrm^@+s~zYzICI*RF8Y;XUs` zytfa)!;z6@)&K_*WB7PGDOT|&e0F%z%;MpBews^10B6ul)Uh~GU1+)Eq7>MT56yD? z#av@%&q%tP<|Ud%(SSL=Kjvy+dQ7Nr!iL~RaUsob@Wv01m0OB8WHfLh9$yT?z17aR^Oup;3AgqYIAL4x zI3{zg2IfV*&j?ZDflKgssXoYxI(3Dh0q-eaMD211b^u^zHa}Xp#Z)WleBgfxf+w5V zPx945QaMggFMQ}^u3`9eyA_`%=itM1MdaB|xLbcVo~k$rpY8VILn6~V4IdsIIB-bT z<6dzY{^ByE%nT;uQQ!uAC<9C|vjf_k=z~a_jt6E3+y*9^**|lUk_(|{4#lU+(|`-{ z2G0!F%kjW3*r}@6)jjgL2kwDy$EWsMe2J$v z)mEWXQUtCnH94XFa`{P>Me+j}7q;^Gfs)pwv|&miiglFlTS>xtm1tNc=us93S|Z=L zcw8ROjLykOJ zv+HB>?PeL<73nA+I@wOG z?w4l;$CXfz@u7}AIY{}V3N^H?RBWQs&mBgwOw!Ng`Qk$AGfAT)-7TM;a-mLAejaTC z3Q9}L4cSAqOmL-P6Q0RmfUha70=7hx(DGQOJMP{O!-LjpJTsT?@!;oxADCIb`+Q0R z`DxgEepVnGwkv&#%@>Q6bROVfLd{NCwnOWs!Xio6>dW>sDN)F6!36p2R9`|ecFUJu z&Ql3~Sf|uK#Z-y!UdXH_6zWHOg6|0b2kqNV>;M1& literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_default.png b/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_default.png new file mode 100644 index 0000000000000000000000000000000000000000..6f3042c67b93897404b608f723480432488abc56 GIT binary patch literal 2119 zcmV-N2)Or&P){oX678hKOD~PoacRRGw<`>o@Y!I0f;0GO8SlfCmB}2KC25VM3neX=bWDdV|0`*uq^Beenc0UO@~aMj z0T%*~03TBxJ_6POD}YjOa7O^90XG0UOLRsSMq=50tu?I>z)Ilt4h*XS`{GY0cnw%l zgiReig1{+~ek19!z$WiXdQ#G6NxLNNl(fgp4y5?bl61VJ)8fxLa&hX!KsiH_ZZfmC z1L=zca1AgPa2Iy~Hv>xwkhv7N1=n?2;Q(-X5w?aZcLeZ<7D`P5e*-QkNe2U#0UL1L zuo>a+wHYT=ycfZ1g!Fdx{}MEDE9xupmX)9<_% zH*F{I&7y_Q1>S7(l-vnSD^zHEdiDc%lij`E0Ok}W>=G+y)$5 zunnaFI1v|He7H6*v$?nlQW~ScqLOSV4Zs=rmmB-Z6O4f;JWM8l0ZNX7-t+>m{YPM;A-F@(|kk zAGkf)Et1l6lLyUgZK&|r^GAl#7k~qK_kn?jBwdxd{EDQrrtktT@jPM^!2Hhi?mYmD z@CV|RKH^6`gzIt$>j&-ycIOQe9bf`CI{mB-=REA;w?0iY8~DUS`0X5mep;UWL0Fd4 zG3R@Dd<4vt8o_ss-1$xt!)Eqk4p}GVQLF^y>hfmxg4}sk64NF9NNPA{YTuInm{YOf z(V-;FXrs-aJl9rAlHtx|_DK3hieQ16?Ff|sER{Q5>=s@&v+a2r0E;CJr!Mc2bb^3E zPhWehFm-zzz`Gv%Pw$iD+m3uTC-a$dM;!B1^099QCgtMAW-}Yl*>SVul2yy&54~Pcb=`r zvLI;iXog5G-z|w5gl8z#6*z^m56)ZTBuVwy80?rh7NvX5L7!uygn(A4D-dbw&R^9? zKDBwCm>HVdYYs+|djL+7QD1%Ap zC^MqQ5V;_SGG(Cdm{Bv^-$uJ6wnaL$JJfM@2*-HW{mmuCLXHFi(x$$h`68_|&!c zByEl2S?)hPjn)J*Pjkug}J3%^d3bPdSPU0*D+Unt~GoC%#4o? z(r9^ng3CXt0rs2rdw8eYJ`~e_O2ri* zz_AHk?+R?Q!DHvkBo@zK7-41rzm)Vyiq9Hgm6<)!L{@!d>X&o{@IjzmMIbp#Z+Wf+ zTrBBQ&ri2z?n9BCIP@w_5{H1RJ#D&y@3nABKmPKe*GVM~`5Ldt5Sp3(37lP$p?Q&> ztflq5-_xcBd?!uo-H-zQBIydxPixKWK@a`ml2$iUsv-G87F|6JTmi;Gk3IXyTU&q|zI{X5C_S7ip9XMd#!a3CR<+CYw;6r;4T>&tDFL`r zBHy_g1P<+4jQfF&8H{%Wg9X}AC~#@D?`P=#44sw{0keQtG6d|!%?o-8G%w593>1@9 z3e0KpNY;Qm=z*GNT3)1%k zzQtk?ig<;=J@T(seHfMWPr3B2ZIZT1+MV$VgE?|5=Y?{4g5Q-C8Y9-ry~5y~K>Bh3 ztRlPiFcd|+^kSHTUV2d&aQ%KYt}wl7JP-V!+dgOt8ZXv)m@n~*b-sEPQwAzaR>qA; xOXULT!uT^^uIOt(jpDB{xtyIbb@An0{|AJ=vklGsD~|vG002ovPDHLkV1lPO_k#cc literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_favorites.png b/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_favorites.png new file mode 100644 index 0000000000000000000000000000000000000000..e4a37dacb8713da5d5a5073d9b39247d57da9387 GIT binary patch literal 2286 zcmVDD!r8CeePoGV+8XH=m zh@lV!tci(_PezCzj4_e;NCZWZgbz1M!MwU4Po0BVx@C0#7(Y)Olg-*-vs zk<^ut9+q@a(p!?YOWKxPPfOZjW^_VdtCBe|N75Qemq=PJ>4Y{}{!7vZNsmbyG_%*+ z23!K%2fRsjcmr4utN4%a&8Cm32Nsmg}Bx#qVos#yL*?|n-Ig(D1^q%CpP|i=C7Aa>? z(oJUeawL6u0Iml{Bi`Z;;2Xe_5@ap~ZpL+;W;g&`T}EAFl{*IbX%nT!fnNh3sz?U| zmH`jqx?vvf0*(z!cG8&0)qAemzE-^L;zO+ z`vW>}0p_(UU}jG9e2EVFlA^%*o>v08KSetZ8Td(n@m63#iS?)iz`GM&eYmq#rfUNX zw*e=WT!+d4oQCr)KHN~0*#g`IDT@)Hx1u^!2H;%$%Z>dMamK(S9wvu@6T!1sWc z18=DeOyH&tUn@|jmAHv#77xTSxH6#3w! zfb%OdKnT(P4mZ#7O+AKyqtdjsOxYVGEzI1Tkn~wII~1!xhcOXLXl8Fq`kbWf_Gq7^ zYmcC<|AE_+-6APFH@Vx)*2W4?EPq@qeF->FbRQVFN7APL{Ve-ba?a!(UG3@mC- z@977i7r!H3=_7u?L%6u@9&Z4%rH1oeBNyLEW5~?@TtL?8MHH(5g}S_%ZIp{wr7=^|RZ_z+llzwR z^MZ;+j}8@KRtsf*;kma`k_;D<*(2%148hrEwj)*ouv9L(I8}JY%(fRP0Q5;3%G}-~ z=~Myzp1$@{Y3fdM0Iz!Le@>Sq-*)5^1)0y53vnz|#bMt7#^wCQCNmo=SayS?>ofN* z>XPJJE&nZ+EAT~0-R0v=o(PtF5_KPXgjgyz|#GfD0$thy>e>o*{?=?wVYsoD451vA6BAKVVVyk|pj{cly=jJ?X z)$#i3__&$9v(WmzhakI05klJ|Qwr(IKPqWJjde_3l2WUNxz2O1VGCe3E_9GZ)9?u{ z|D=w*uFw>~2H>}8xwzoi{XlR(7g(3bKLm6~%JPLz#(99mzHH|9)m&rfN{$_9=ig4o4v*aWW0oQrTOa(sL#3H zF#Qv_prVQvMRu~5)$_-mG84duv$Wm~Dd1Of>z6Fnn%UhR`lBVS&Qq#G7z}j>;4_kb z>3Ou?%;M5o38%A;0=`Apv<01{eNzftTJ1jqx<5|4$B2MAz~+E}y|{V7 zv;z6Zay9|wWR(K*bDHD?a0}Dm%rkDY>o&p%xpo1kmM!#T8fzjSb})s9x`5UAC#Q8h z2^?RQ@Y%o2kby0(TOobKM-cnqmji&m%H5 zBOtGa8);$>r0)kpA(^Hq;}ZtAD;yKbVnos(<Th->=5Se>#n)fR9hL4RS%_!#el!Km1{x z|9^@pfQ}_A<3^;Va*lLyaxIcO^wp!r;jdA-oSjik;m5oF3l)f9tvC8^X8-^I07*qo IM6N<$f^yeEJOBUy literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane1.png b/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane1.png new file mode 100644 index 0000000000000000000000000000000000000000..a49a50271399e529c7f4ee5ac85d6859f9d4dca9 GIT binary patch literal 3065 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 Hu0mjfxjo85 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane2.png b/radio/sdcard/horus/THEMES/Flexi/modelsel/icon_plane2.png new file mode 100644 index 0000000000000000000000000000000000000000..8305b4150c6f7bee9fb4f50ecff8dae08d0d9481 GIT binary patch literal 2487 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/modelsel/mask_iconback.png b/radio/sdcard/horus/THEMES/Flexi/modelsel/mask_iconback.png new file mode 100644 index 0000000000000000000000000000000000000000..966f6b7f23a160c0b17bf2f93134bd50d4f280ce GIT binary patch literal 1498 zcmV<01tt24P)9qR!*xue&0nj1vn4h0lzu$MBL&7&Ak;wOt2%EXSzP2UK5Anj`uq}a! z!dzWl*%If6cnu8=sHmv0B{-qa&CQK1aej#B@puplg=`5<=yQL6kLTxS%W-7A55wUw z5{ZQ6;6!28*Vl7Xlq}0AFE3}hPEiynit=%t-U-ySi0AO|5Y5famSX%Un*fRD1j%I5 zatvMZXwDOl9djNz@0SoJ92v}i&I71vaROoDk@LhOtZdRk zT|C0Xqp~R;D_cV(-Y;WiYly^i0lcNtP&}W{hnkuiuD}F4yd9^ZcmQ>rP9Vg20F_QB z5av7*Pdvh$M{7Otkn>8IR4T=_HALdM1RnQMx9Jji=DnigH8wUvmUaIUjIa?Q&XZ*s z!C;UpA%Yzt;sI1Toj^$01gL3o0urx;;n^Bu5|0+@;^EmEBImgXvrP+$=OWC`d(>^Z z2qDh<_wOI7tE;&ZBG?h)Jb;=OClDqcl}_;pDVqQ_Elyx|YlzCG%P`|Ua-Pc|@k$so zO$#+FE<=d(LZJ{OiTQMb6CutkFE2+R5a3FPU`L4a04kkMAjEk9H7!nHW^0JVb0Mtm zGbJ7^)SW?ycr@oZ18dVlWz&U_^GX;Y&a1Dlhu7=nN{C=bi1Pq)pA)1~DWucsqUVu# zE`_CZnih|m7H427ouFH^vb6;Lwu(h>i zDn9W6OWBmoZmn+Fr7O;KW{2Fk%!57z{lHz-`?JA88i}!pt7=7Ks;WX!6pW3HSsJLdwG~}mUC3lIn3q96MLNb}m&3S}% zik3|P$Y!$uJ{~&v^z@h-!0Yv5WMl*Yu(Y&C;%WHkK49ZB=Q)|dtW9v&3(~m z6n?+IXy8~ZhO)9U92^{Ae}CU%nuH!krSpq;TJs{yGA1V{4TE-ecA~AV4e#&oh{xmh zWJ>&i*?Bb^2}2B@g=ymS_Vywa3gsT3eSW0I0=?2k z42I4F^6q}NizgN z{1a?MEYhT~N*5Aq3k9(ntZkwQ7Pg8YCPmVSh2Usq1V56KV%c%Cg}VnXk9T=L4)>l1 zlH6{$R1}5DWRf0_hbEJWMx&8uXJ?dUnYCJto6UxQ3ghvZk|Ytp!SeZhe0zJ_gRxlb zpV03Lhr=4Ex7lp?U%6ba{bfF%GnGnFuh$=RlVzE+*-UdZRI61eih`Gy7u4%@^m;u^ zCKCXF!{LA=Nw~SWfh0*N7K_N|^S>M|77JxrW~bBPcDv=@TCdk^x7)nBx}w|d-Y=(8 zsqpCNhyZA{S{Vw3czu1%L?S^^6lpe_34lhUk&2@HjAJ+)GMmlP>-GMaRS*OQ0s-c7 zIljKWax$6h!OP1_jz%M`@$vD&_xE=$77MOcE3H2m3>XT9_RDd(T=4t-NF)-7$K$xT zxPZgqz;rr=s;U?a252^$Xfzs#Mx$C9I#pF!C=^gGm$6>20RWjy2BIixj|4%0+wF$e z>xIE!fZc8f?(XjR_4UQYV!?X7&d0|`K0Q4h%(UC>62V}Q(P)&dR*RpXpCrlq`}>37 z^Yb%x{eGXaEMq(#!)moc6h++L-eR}gfuwc{f&iP%hU4R7oSvQnB)MEJSt^y7PN#W( veomcE$CHy2I-O2NA`w13JWy5DA0Oiz#dxpUGm9As00000NkvXXu0mjfg{vY) literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/modelsel/mask_modelqty.png b/radio/sdcard/horus/THEMES/Flexi/modelsel/mask_modelqty.png new file mode 100644 index 0000000000000000000000000000000000000000..9a38e4f13dcaa706050cbc0e4cade094077257f7 GIT binary patch literal 773 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 P00000NkvXXu0mjfqT{cE literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/stick_pointer.png b/radio/sdcard/horus/THEMES/Flexi/stick_pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..0d75765f9e2fa4ce3bd7278ffe4fb1b3c45e74c9 GIT binary patch literal 902 zcmV;119|+3P)ryPZ(7nN*6ba%+#D$jL}HZTA&!mGQ~U7aXCIwB%wtHDo7EEit~ z-bUJ)xg6P{Au@x5XmgF$kzVSlua?$sn2C}M(sz$)Ma`9|zT72=iI&1HN#R_I- zbbjDq0)4+{&jnyV;HfI+-@d*MlxL!j8g{??9^(6KC$CVT>y1-Md^DTu2VMepiHI1V z;0O}+(l3|E^dHz37OUgfvE;jy@w+`Z01N=WG3pJ*hnayxJHi0Gd>+%&13*^9Ka|aO z1A76_AZ|G}x|QW^5SyjeqN}S(Tn)nj=mmVEzPlVB*h&S$U`L?6QbDZ+U?~jiixF@S zAg1@P*;$|p;Oceir%&&2J*7T6nuLGaU*xVCx~2!VMZ6jkMdh+irdf5{3%aqfL1BXpj4h3w(OxV1Ix#5j$yFHlArsz z*~Q7R#;vC+8?DOLp53ib)qUP*j6c6{*f!Jv_Y+P3UZSDxZa04cm;wF(YO1 cKvR^bLIa20ncQpT2=l-@vDD;L|to=^Oa;4Sf0rK79k9zJX8Qz^8BE|IHh) zrAwFEFa6Rl*^C)8Y~8wbmYA4mDJdzIoSf{4<6Vd^@jlAT%(Us0 zot;Brb!TaJF3JVM0_iX-s z(E4{q%lqYf=$qbu@ZiD!H{AEhy^8(Hul$N#b=6gN#~pV#oJ{AJ;CJxL{yX_^=v)6S z`QChM*8R7i-tOA9%OJnquLs+TiVFK6+P!&b#?Efef#!Vkm~E}?c-8ILxXwZnBaMW%UqLdL)=^N zzIhHP_>8;<-Wl)5zgylp%e3H3*Zbsh^#A=kckk{Oe&HAF#v5<64I4JNcLuKqalo?2 zgKvHR=Xu_@ymyxUH(=d=AOCGwhl1bEuLm&@+~&WvZ`luCpq-8f&m(>xerMYUk>@Ml zvY+R-U<*I`b(j~iV;&>4 zZ1Y&~-_U=%%WV(td3wP`)x{c{GN-{XZg_^r?@y1Ke-czD>_+X;34xBqjtM?^$~ zK}ktTPOpuLiE-P#-0+z2>t2R(5E+*Zuj~=J7KfJAR#e<{A7m`Dgdf9sF)A^IX_G*tpoC*h11f{yT!l*^B66 zk&%(^eW1s}^XOr5adGZ`5GS|@18wQ?RLF` z?c1U4p>0~{clPtgaSn*{!BgI667-Sa|KPs7AH(|_xOZnCPWcYJ&g=UmFLQaXydVEQ zd8Zzq9-ChF1aTC^RuEHuUyk({_jvN(EBJj_=6BvN&&NIUO}KyXqviiD__5g7SX;Yx zt^59bFWwjLk#7TT;59E>rt_=!$?)Dh4|rdU_rXl(Tl{SBlIL4^EXcc_XW`Lp+qOB{ zx^=6&Ph=ky9UX0(Hf?eg6%}b4Hg1q!8Q}<7xqkh6=l|omJgr%?#-P=!S6f(E*dS}WdG2y!DWALuH(sHn|r}832~3% z;o*+B=M5VpY-1#S*GA)+qN1YQbMm}=qb*yu81Dh1F9Wv_xb=9w99HQsz$@Ru`zYai zzLWoEd_TS=I0*W6Jbr^Z1{j7fI0w1~IQIP-$8t{L`@b@NoIb|)<9*Wiw6?Z7YHn_J z@}K^VcY|!<-SGZ`eS8oj9v|QgY|y7q#{}Qm2{j-TME z_bAdLqe8p}&qYQ?Ik~xU<3{s@91W5`mTfGD&aXBa5*|g-F``S$QNnBVCCq}`4dKKT#`~0-2X9!}^VRYn z3}$$CXlQ82y%*k55GVfqv0Z*g_kMVX;0t0Kzxj8}{=9d84*v~k`98c;Fn})M^$c3C zdwAO=q{9UCF?22IROnXtFfK>l7wB7WKYZLaXmj{uob93O`F8qsy)Nk5sr!20HtCB& z9ntHC{`rEsoY(#Qy9mA?ugju~dGfmM<@7R-dGuvu3GbfoP2UKC0l(bW*Jsecz<|RA zynvq>0vGVdfddC@Vq(IM9XnC?ccxO9S2|V-RUpE4SgdrP}@G~*l}@8 z>__-qITLpH@L?MtA9wEz9C)7ht;dU(%k(Y0Ux;PMzjwX^x*p`e3%{{Ue@4H^H^)Bq zHnnfPJw3f$EgK$MBRkvM*KRqm99693J;5t}1e*E|)Y@9rK(oUZ~ZRgINv%BuP%kH`79=mw)qTP4jeQx>u z`SZp;hYlTbeJ?!3x5ftYGEdm~B;)3iO@rK~kKq0A{*bxwyq7g##uM9oKLu8N{{zKS`W<{g@HD8h$(-XKi>0ur0efP zJ_5JQx(FV38OA>7#(WdL{k`|z>*(&g z?=}cbKo>4taNEHK7AOH5*0%GC1IHsV54aUEytxbueMM3-!#fUO0OyLb_PN54FUhagTo zK0G%3xqR#Gt6y}Ckp-CZ%2*IA`l zq5Oc=dx;S$zfpE;t@IU7b^GMgz^@^ymksb9DSog1qd_`Hy@<9^>rhiOZS$q|&^Bzk zz_vz?6*Z6tpkwQ0f9u`XW0Q;OxDW29S~(5UX@b7WUGig<>lvUjeE+gf(0RNc1%221 zPjX~?XX|}w{}*_e$m{r>&$|-zpE^+$Rk? zws`Skr{kj2zVpsI?mc1Op-&-CkTvL~5V8h)pY8bCyqw{kAjcng-~lJQ!5+jmaE1QI z_w)TA-v+zX`DOLJGNxj1?8CQ)56a4v%O?J)5TEbftz0Pi4e%Y{b3;eoQ!T1uT~rAc zm*O7c{NpT79t!w)Z$$`Z9?Us}fXURGy zX$Qw9SV|JKL+4GjwB#hq)b+CEx6an{=jgffbYBI+e4*~KSog~PmJ}6RSxKqw)U#CR zxhmxYtJ1ru7UuT~^LwW-|DGSr=R?1jlf!od+t~m04WT{&`KeNC1i@&hH{_DSX#4_9e=#T!$e&=_7$A0vqAK6=P zz2z_j&X7Cs0(uBCr@nrlzG28O4ekpI3O`A>aXd)uns@lrQ%~6se((c(_St8h?T36o z7ehD0Ps#fSLu^A1p@Sh?kSE?AWclpbvzO!*$6GA zKl)MbrBut=;_EbY9CR4*YW#M^?6yj$l3lP_I#*1jY!T^c8)XxPuUTzt!&X?>@+G!% z$s$|6c%dy@bcZcjaGNc<{T5p^KST>}nP+#*n`^h-Jlp2aon^N|b7tBtb8fU-X5V1* zX5C;n&%EB|&b-0q-gLdqfo_DZ6U}h#TrJyLI+V-NPK4KksI{{nlG;!EM~{?YeK>`@#jHg|>9jVq39fiLF|? zOwYI6Rx3AQ?aEcQK5Vr`=-D?)KaG-Z$~)oR@QxDVgzp65HF1aXFOpsChxbW;Q6xN8 zRaOZ@jkZr%+FP^N^+BP$nfG$id^`?35xYU}K<7gCeE<93w;%ub$M$=__j~r|fBxt8 z&;R_-_HY08Z}zYM`mgpc|MD;P5C8BF_LqP8m-f5A`@8n`+iyGBLqCeXhEEQj^8Fu} zEf7ygkNKov$H`;tWaJ3_1MdS}75RZb44usNQ{sP~m3PN;W6L1Jy`Ky`AyfQ2#=ptC z@Hzr`L4RNynDGQ|AY!a#>Q9T*CaRxazh=)L*(|k#%5h75`Vi^+h$#-eXi5KqR?@n zbFqArjt^}Moj0`aRl>&Aj@_L z@OYDOI%}rgJbSjyn?1|XEpyaDtj~gGPSH)mw%)<4n?ktLJDER6{ebqp?Ph4U-q{?x zl||L*7?|M4FkuE5yWzy5XCzrs5l4{qQYXo^2R zF}U$@0dz8C@b|v=J*S(}FL)m@avHh8v!a{vuD}o&0Soxyy}pdzg>3P0ad6{h444U$ z(*qoaBy%KR@^Z3mlX9^Z-!{)~oq3)5Z}IZ=;xEyh8?LdN)nDIyqe#pAHuuJ>wf!o$ zJX`DV@SN*HG*>))iNwcqw9apHwa?AM*W9V|&2#4qo%3eS>CUHPZ`Ad`&|e? zZvCcf-S2bN-aIq3o&6ngZ|*+0Ug#d?=z4Q>4$k9SmP5qxl0A|;^KYIjxg%L9o_B;y zRG)!Nyk+)Hc8hQ_U+sL!##<#9=d1s?eeO)VP3yO5nQgbuy57CJTV`D+c_z{`+@fc= zMfX4NM!nPPB{$Vq@Gdzva{U^oAG-V)@h`mW*NO@cYF>3x}47;09(X0;Te3= z-}%mWoV@vu|M(9lgZ}ET{>py()1Nx*;J0Q!@WM6dm*ETfDn2o|!9K^=g6{c;fB1*4 zzd(=0cSwH%W_TaSBlI&`@B&VJ{v|CqakhzcF>vGw%=p-~mp8m4uR9ky9j(~1GBYfE z^-5bjf3DpwK9~ouAwR_Hvsm_+aXfsj%@fbgn;GKYIbZ=kgkL2a<_afsXMD-goNK>m zv#$QU&8EHf3pRVk7ah&H?u+7U5$oDMOTY8m%xgX`(mGhvb-|*OEov{x7UYV4hurVF zrju>Rm1{T$^aY!#>(0{hZ1xw?a3;abA3Ax1p2oA&C*WrTFDzrDcz=t>3V8V-%v=sP zmoOteyIA^Mq5P4XqBb}^=2r2;(mQT*v`{w5Lg^NZ&@XNe5&Fgww=8=_`o|L4E=yUT zKhKu%8~O;_g}PsebrdG4MTNhCyM4a9zonc< z$1c#lalK`C+-fUii>+L|K(x?SI9lZFG?tevxZS;jWolRP+rm5Cxt42vN-|f)1&ybHQ zS8^*;@-LHR`N?wdQOhT`WmA+TX`D>l=2%Njkgcq^3~i?3D(Q(ymLOkuyeOMz)Aw`d z(DkxX<@3@#uszF>&dql2h2OL-Cr$4qcB>`p{G2aCzCv0kVT*G6V`AMtY){;}&2p6s znyTkX*t#Wj-}0}eD~6JwI8w6wAKChy=jPh9sj}@8^n9t>hxr}s#>*PsoyUyFjbC@Tk^g>$>f?Or*FSGF zuDwov@g=rPc&e|dwGQ<~dnCgv%XeC5OPdY%4_be>a&zkT$quw)c&P~(rfTD7fa8{E41Bu&gKT?Snl1U=dZB_ z>Bu#ER1-nlD|gD*SiZ~hGINBtc=t@HJLE6dv!x{_Te^IYw3jFL5uSz7tR2aglbRu4 zTe59coHHtVlZ)r#x5qyXR;FXbW5MIX|L!q!xt8;wzae|@G5ywW{g(5&{qZ0Fv5Tc* zFQeDtXTaWpC&Xh{JZ^}k;1{4jKo12s^abdj_{?~RJU3V&rtC4}Wesf*GvLN!XF7hS z+cq9Q;3ikNS|$JL)nELQU3<;7ws^^6+pXA5wc@O4vNy>+*BSVK*nNv(dwBBOC0V~5|V6t%vRg6RWYQkiWP0yW=Y!=Cvqg;voHlELhR#~e5&n8NL0SWW|u3MDBl_}ugl@aW5Q!4h#~*m^cMYuZS0FKMvR7hk-z`@zjr#@ zOD}!hl*H8Gfvwmor}0(1M-m zcmXpWKVJ5Lo9WoW&M7MuZgM4WmMybuKL2ZW?U%lwnYIZ(ps!RVP=`&&a#LUYkDt zBPesGlG} zt!SO?$y%-T)mD?W)+$q1+1~7RR$mxl`|?G35mu8OZhN!V>-Tk5tNr)ouD3c7zt`q$ z(EbrtmltWZxf`@E`-hIH&55x3yp7h7x4~*NbzEMIHRW%%o{A*vs!Fw{lC6$f%i^q| zXp62HZLKAnwYmSs5tW0l_CCHppr%pNCy!qx^*51)(*}0j@Z>m-vXNBnr@%Nugn z$%7@24BY(w@BhA|Z+zn$E}xS=0$Jm62Z)?U2MI?L8s zN9j8K7H$I-o2|bh#=6TQ9QEzmXx%02MC+|f=Lykz>n?`Etw-CS?$U5wXPvbdt+#{C z*><|O+|CYE*wLO{cC34+o$0TzlRc$&vb)I6^_SU+_5wT7T3{!8D{P`AL-)7Qx^%v7 zcP^c~WUX}+t+xKkEp}?C($0?TwUdKYcBnnaj&+sTiSAN6(w=X_d$wCg;Tr2Gj<6%0 zCHCxvLw4~*uXXN#bnw@c@$5C*r|#Z!a!#Kq&f z#%^72jqYKst{tK06zQ3|O7+}R)Lq7Xgz06?GrDL*4okTayu?M+?;P?H5slTj28ZvCGKErMA-5F!8 zxnVXa4BT_L)gCx8Xm=kSuzOF9*uBU4?Shu?Inrx`dy}j+ZU8*_-&td=Wt*%ZWxH&oSo!2ODQE8IV_tgzc>&2VwN_{c>zT$gGm2deCZ=t$2_ zJJ4QiCx>h8!PCQb=0K}W_E+2K{u(>hSt;6Orw1x+qNB(rB_E*UgL~|5_1DMxD#X7L z)>$r`f<0RYKiz0OX(0Ra5r&|tpIOO_$yS50=!f`{gjqXde15Mf1w|k3@* z?5=|icC3H59TgpDE3^YG`F47^#*Pow*y-_l$r#C-`g}XyRbi+5v`=G>?Qbct!@}4^ zhx|8v!kqZ6U6^UfUu$iJ;&oA{YsJ&TM|+WUGvtDJ$>}!2PlvWOiU)@!YmWAm+u)vf z>)Ey0j!4Gcd8pM6wdGs0esAOW!nM|^{lSdGjd(TGhA`BwYqb?^kZjDe`;QOVM0c5u zHy7yMc1uphJ50CauCl%g>2u>vcK^8pcHfBsJ2To~_Z}bA`na7x++vfu=78kj5$TE# zpC8lnYi#q*E%xNS$Lwp*K5BQ4*E#&P<*#`H41pVR30{8r8_q9KTURrMn`$=>1%LD>1vl6(adPA%7L0A$FY44G8)L(8G97$P%x+`4$}cW$&Zr8uRDRch~`MUiK8pzg#MRT7hu0 zarFwj^`@(pr+B?>TX%=_t4wDT9BwbPv8Ej9G)dOGGu8&AAB{KXh@VTWy)4SNtx3F&9BG3W;Zb1^j6j#SU(jE%&B>bst+{q~yis;i zv2~WmNMCET2hSa}3H2)-!YA^ptCVL9J)dj!j9rj)vkpBgwp?pTjO8ZE=O}sOaZ^4e zZ@es--g?Zi9SlG|2NZtr*!d{jkl*}3@=nyoxv^g`r~8*ia_7cnpV(3fy?U#9GcG#l6*XT23$o$Z0X(zPqr2BqJO$#xi({xnhtZnnw4 zwAFe==s^ROvC^xyTmRk!VK3eWtK%FERmU4N!ggV+TYLouzzzBh{5~waoRm$!U*~Tv zSnK%wsBA9mt=8O?4ma(>Q?F!Er)=)767lPl?uIV|-LFG7Q(Mtm@ocn{m19k*c6xBH z9h6SlwKH0{k(|?Yv5$IpZJrBa4P0Kcj^Q!M~+j!56@uAKJvI=>_woACl&5^uW zZnsI^+&K7^)B@r`Ve%B|u(@!6hj)(<(nY}a~-zzV;yZ@=`PZuQR{;$Q3^^flzhfNXW_ z{_gV4A-h3X=#{^&SN^(A>1W81&hkxmP}l6QinZ|?`4J>Tx{zH3|LeaI$kuTJ51xORt)>Utx>&1v~RCc4UG3&|(4Pre78o4(;-bC$D> z_DeP$>L}JWVYq0c`~|VXjc}xMd*V8Sx}VT<>RI_s&)h4S(^MRzn3VK1#kS&Nq7)Yn z`32znAa3Au;blwninN(6d^l=c8-EleCVc+oRvNs{dyl8@kO=NdL!n z1~VZa!A5J7O@!?cvb8tpm~d;(U!!)7_LGbdw$OA)W92xN-Q= zJvqBw*KO9lwdg$PdF|2+MzgLi$N`48FCOQ64v5hoc|nT;y>*^USltR;AO^ zs`i9%!`LqPMc2CGal^ASws|^k7>C63dfa%M1$o0ZU)L;HgT*i71E4>m^)-uG=Urnb zIbWKWH~Jn0c{#RW^)kEVhA-JXjbGWm?pDbf@km>a+Em%(Tb(~^v_8%0X#JJZ(pTc_ z0Qy;Nf^|s-jMm6+T%G7_Aas0h2e-=4g>ShN+gNP}x)bt5eDAh9|8xj9_zAp>=ytwj z;yP@T-NLa>7J)66IVZ~-gk$MZm2oz#>vxuJ6ra9#9*-I4U(>y??(Rd!gBz#MiKn}T zztOrh`Ma|vi?-<=qisyG2j3g=5E_%59j-}`ezsk*G$d2|<)`aV?AQ6w)gQFuOV|Ci zsYTA=5APICpf3Cu+NVdcCHw+8!cBa%@bEcas44oVN zP!odf5X;!G*I$3V`{wkGV8rWcU=iHl7a;eZx*p_Tqo-90Ka3rwMi|)Bu~#&1cy{Cl zb$q;i1AdS<_uqfNle-=_UWOo7e61#OLVZoJU>mH@MU5_BCyDKx4_WVZHSz_pZ`Q3? zY_qOX-1Dl>Tf+L=T|W-5wHGMXp#Hc`{c}ssN^8nmD%)b2v&%cOS6R3GS}l3HljMNIgXD$d?xqDEzthRD%n)+y7ztQOC)yp?wr@}^WdgQ-yLhvI?{@hY@Qa0uKUZ0^_^ofZatmY!;b-gKxk>9=g(LcDN}hsYiIo|>@Q?<*UqhW=R~{WuR4cx&fMf!**B_3Aeobe12GDywd3~5`I_ym0CSUq-_^QQr z%k^K>80M>NM}*{!>}_}x-h$8IXJVmeM(b=szUh&DiL!C>)VD{<-rgiVKS%a+s`R8t z*WZVZ-QfCOc>CN?m7N}`v%Wn^c3APAs_-e{oE}vk6-96bRUqH2ZeUsgN zsM#LBPjxU4bt@*7C7(yWi|IUhpK1mjQhf*c%$g-zJ49DkKcR5daBcukE(G^@mpQ)EB4LhVi8A_v(;S@D;`=UUu&WrRDMl& z#g>qrEv&)Uo$6ORrT-7^5zg96gtY>D`1Fuss?9c{n97)9_{Ronl#5WWIDDI8H7U}I zR(&|{>z+b+2RgTOpCQ>-1G;V~mq)nK??IeQU-q~G+l^VvBv0b(ymAE2Nyc<5FYl!M z)KA=hPB}JxR-L>^K9N$pd!o(GjJGJ4pw;COHAr8>W&t-{I_AlXr|jXoj@VNdPwHJH z*kR%1?n5oInUb8Y*IkulPu_Rj9=&VQo`2+wb=PDo=bW*7+mx>zqqt|Hl}oPdk&fZ( zZJ}TN@8Aaf1aX6`@xFzfyLQ^6kE$lr-S?=*SGlFjzS&i=%h@+WgG0W5zT$C%?4<6K z#|_v4H^?CTjvs}a>DU1`o;*H$3wBuc>)4LOJ;BY!)l21vzDl(tz>VY$x|48&J>YaL z=_L4WC%Vd=UmAVw{Qi3RWpm`4T5p4j@0=d0R_r5HID>b^PimciK>GifVlH=%OV5$t z>+IM*?OP+Cm3-IA0XWiI>0*)hPIjs<-{EY7>AW6Xck%{YN^fhCo``P>{i<7bH?qz7 zB_wm$FL)fwV8-JHpI58wwu!D%d+5#sk~ejdJt_A1{dX$2W?Z^en$;%X>ip~X9qUn^ zTAy+UIwViB9p1XJqa=sBWTQQP?`eDP$%pi84SM#CE;oq$wiAPzuR49X-sL9cvYoV- zpLx_yD*vKMIn@Pe8bd1FkTZ}j+^F8Hi_?G?^egxso#W%^X+e2&IovosP5wG?^Uy;N z+xc_nW$VaCr?@A$p(YM>$FK#2xhons=&Z<2{2SoL({KF7Z@BSze$01JPxE#T?+LvN z`Y720jyzVp+;Mp};6}L3mwgivw#06|@k^>%ag|};^hi(ZaW*!-RpkpSUec==#i;Za zaw3WUoshg4RjydOWXh;=Fef?+ot}lQ9r7Vb{}*qyO74tEj-2Q&kgc;(xtR&F{e@I4WP^4r|C>s%>$0k1+k%T@&)X zH%oRZKS#P`w=mi+SxUa=QRNgpEm|(5s-8~^0syQK?++CwwzrFUAhwoC( zPp|72v_yvOYjUU4WUx1g-eeQE^&bjIGhI4s6?f&~8 zPag-m$;a*{M&$Yga#rOx)A~efo_rhI zT|Yp)>(r3)5@hRi=r??C-Rg@DbQIVrjgJ^^D$uwV+Joc^VdGy;-dxVk@wSe~ zO1^9ymT6sX&6J*o%t1#h6qQI%qi$OG$|b7(^hMP&nqf&B7m1%EmFpPe;urggE6U~{ zB1WSew83g}0TgeSzi2?b)-V07SC}E!q+j+9K3?J%_~5$BX&dc5L4(N5mDIEc>SayP}F!Et&vTIOx1qMFD1{cQ}G-0J>oKK zZ9j7hkl=U6@Eu*VEcS%TI?Ifd{Scsc{?39yd&fZEfmz( zJa+iq%N$zX2l(-rak!CRfV$w|CSsNBo0(Uu7T8Ue7QIw_8lfDQL^~$k<(P7Z4=a!O zh;oyU_ms)z)tE8a%Gd?u4U9MD+oWv%qaxy;=v43&Iu-H4NsSX5SKilQ`2-HBPd+4l zj2wjHjPKDov7slFvvNeggFBZyEuM2W7Jfa+0Q5I}b)DE;l7GF58=^}#Xv}3+LcG;f zsP0~g^4O&R(7$&m{z)DUeLVY-|JJ2#u2rAlaxkT1h43RehV0Qbx%WQ#85kqbBj0$J zp(YLl>yd_+F0AJMVM+ivBh zfx$uT-(9J3;t4UTeX~Kh30HlfIN32Jm*mZd;by^t1(*ELKGr=QH~5jz%lvt0y`Fa0 zop&kU{^%t=tz32vdK$T%@QPxDS1N8gxW914VDOnyXAry)^YrqDZR9=h{2n*R7>Hwu zUx5XFM`!cDc`SK6u@5qf+%$B)55~{8`spvhvfMZA;Fk?D>JcirGj;5GNT| z9D_Wq(K^PlBuQ4tR;f%gJeAqeQ6n_?aPmB6|@;*nEBj>KgzB-oJ$7ln`rfUB*`Ss$gEL(Y)efw>&wbq8T zeN?v8xZ)?{jk-_8j7DUq45N!lZXVS5Iu|oSrhpSj@&f(O>6^mZ0p$#wRKE16`~tm_ ziwA}23FYD)Qtk~t!NbBl`Jea~`=qNLQjTNsZoDYs9d~w_s%Bd^EfVD4POd< z!?<*_1L|XXkWae4%NNiXf^CX@rtFAQANJw6nKf(H`{WIL@lm)z-e3y^adZCsd7C&q zaar7;pIpH?(J#W!#4qugG4FzxH{b^QhHYJ4T@Dw-X~2c|MI)=o+e5baZM4WH2%OMz z3@teZ*ptLZku&IQv=Fj8KR4UfEnjGJR4a4N4CM=i-=Q(j_|rD%`-ZzXev4vzt&$<7l+kf-Lx zONxJqleW3Onm#(p#aSoCt0$EIc0zHVbHe*)Y6g?9B!&Uz`j9G12;jxz=z?6?~Z)&xB)w?(^3l-*%I;#WN5qO zjpCjVyro>mD^=e76}r_1<%>~UkoilfW8;0%)E;CTHLbuy5I4vc_?~w_zB2?a$UpP> z&+JcJl?9v?xM@@N=a=EkV$So(EmoRd7QBnD z6v7<#39%gXrK8IGKi<1rc+PDx8yJfd6n*;|}%iOR@c*#0`BSat5q;+@Po66Br-g@9=_OptP*S3sK@bigly?>+n2G@*&_Ix zT)v9KP{`hXzaJU-1EKRvhLPti`O+kNyeUWawp#L@o0R|N`hW3sL)LPK8L-B-cGZR< zuLnQ28w(~IhI?sN9h}T<^5?2vXJHaD4I2i#MmB`>C>O(V=b)~LYUFsIo{pt27!a<9 zlxI0uy4D7YSKB~YxE+=*c&a_iMoLsqK=0sKYqm{D*E=lva-v0eso5f3YpspSH*x^{ z)F*0P{_L(o+g}@Red<4YB#%dwA3Cc1*KYM2-T4|*TChTSY^!XfT<=tQNf~jf*%`IL zwr<2PuwC+|_>z6&?Rnxie7}z?Z+`99e(h3z8hvXJH}DMGnEPdHblmX}`2r=SMNa;B z+^7$}Qss@4Bjl4&o7dw89nIs0ZR87q0WWW`S-=e~I3V8{Vj0;2z927y*am?w_90#b zmb|PfQf<9bjg`vNIoGOY+T3e@UGznZkGRe0X}$QN#aCd!#Xqzh@@Iw1hn65+Ui~+^ zyvEsd%Ma#iVqiBoo7m$dgcb7j$q(ju`gHUgXS>K2p$-Q*%?HG<9g;bHvg66cChp$D zn0{hCjk2E<S0z z5ic#EW>uK&Ne{E?jMcW+(JHG-U78q_s`Eo)nmw&iQRI$gOrMT&JM zXbz!GQJP06QuS`*-P~s%3SM+2F%bhKN)k0;Sb2n z&X|%n2kh{KK#_2IrxDg&g zu@B{GO5eFpbvOEJQe{(QtJXk{mq6#wr^2TK|Dj z8=gF9gNNm(I5e(y%!Ve$9Su(?ckTEIYwm2d(!8yzovr*d)m@B_i??Kbmtx5kKi(`T zZ_ul~9~%26h#S5u*a0{Ap}`Gp5I11O>nJQ!uWxW@z~Lra^H`OY%g=F8^IT3&IN7GL zCs!?P!l*VV|g z$(6hzANuC&zaV+@WrrL50wLTWFGR=?NA%sqIX8>nHEydzb-Ok8{;>Q^jO%nZ4l-&Q zZqU<+=Qk;@g_>xLK^~TW4BVU^uCrsxwV9OP>45xqqpI&Q)>74dYstu@GfQ?#jm?F15pBjNtvLlqQEgyD|@YSt4Z!V`m_t%@d z+)B$+E$Zk!w&L-(ZRI0xTG+#{S=b}r5xrtz54|eV`a`eVD$(jk-mtYNp0H(Y`-P=R z>$>omJ^awa_U+f-vQZ?S6+L|-udBA z?C8aNHFjgG>Hw!X+mm@wnI{r`?89&~-7kQ=@wh<;@$s8ro(+21blfnuv%kO3;Uz0u z*Oh*_|G>CS9+_~u+m(zP_#c^o&y2cH-oEkjCWsrary+0XN6^iX(~L=D8J_@pSdh@w zrt53GXMEW2>1pJrA#XzC2<9nYfVpdsH`8(B$E!HIUph~}a!-z`#?uk$^!TDrD85cD z0_+!;pB9u;Q@HCOXJ0 zN7%=%>6cHTq&&?woV?$bJ^e#l{>1le)#LA2*c0EiuqWTva)?$w{$00y_0vDF)ptH? z3)@C*%g`~~|HRkqG369L{@}xQ_t`sbba2?7fBISb!8`BT58wTseP4C`pzpm6z3b>V zR6GB7f9Gd*=HUmeI9K*efplj1o^$dG+?=Z5#^?QCPTqi<88cLKC@_v7sHb_{1m%s# z4cNiHp=A!`zP{c|xG64C3~v9J^tVapD-ds8vGN8Ur?0?g#{48f+%UHfx*PMVc|FbB z`t&O-d;fEAJF*(W27-JH)VDtFiJs=;ILv)Zep>kQMK+Ilov?4#&zD_6jSFJz_{T)p z3(o!zjR)?M%{|_nCH%;~sfu^JPV4kD$rU%g5u8Bc)i%XZ8T&^4FyezfV@$aQO#%Mxy7>5}gnQ`09*_iOw>p>Pv%;z3*f^p9-i6F(4Urf_qq zealuo@wTnj`s$~@@8r;;i3_%Q@`Cj}^1R)B*FCBOao)}|p8_kDXy zxOr3farE7H?e(|6XRoRK?z`W&-}>3_*$IuUD#_7&>%vWe`uc3uHqTOjRxAv8c>}*g z!MslL1%iHP`awVbCMa*PbKno}7XT}ew{NI12yRNtOAL8)2{%V4wO={sykc>KoS?73 z_GeyC<|xDujb8xVAcL5bFo>HVM!bf$eG|amg$3t-#FZ8zOy;X zPa}Rq-Pm5$wGE9Ykbg>jb*Fql=sd*W$s_4go+sO!pGt8OSLaDO9JU1ZICaZhTwZNE zc7=Rg98V3+HpNg}&YJM!>SHLrgYULQcKyNjJg3J^C>N|eYpX@9Uu}!SB5Y~IHd`5! zY++loMDk^bHts02NYTdld<&1uvvu2ZY>jC3Hr4Kp&9s$KNw#c#tSt=-x0oo^Ws~mK zr`k9DiaiyV?6BzJ!?ynHGZu0BN!xJxQHwYOJ!TuuJYpNqK4crt(mv`a;@qRQ?$`y} ze)yc#pEzrWPaL($v#0FnU1#n5Ll^DdCm*$oPioHdCm)-l$L&6i2f6V0V|JJ5z9*it zC!YVh^&OpX_Dx=nYSydIN~JGU?AGJPV6-s zwAh@vvJKZOuk%*r3*0(;hNVO6<#-=GRkuhOovM=__j%RsXHa)(81H|N|o9&mK zg5E`)Bdqd}`8XFj zD&*-iK7o48`AN}|C6XU88MY?5($=O{+uHP6TO(SVUSk`w_SvSK28+&ZvY5PPi=oYF zvW?jdwobpVP1$2>6L;Ej;b+xa)q>9@|3v?JF2WI(0uKM zR$HBKHG5U}zq&|sl9Xy59@Q)>kS(uzo$>Jrngd7k#Y_M8_Kn94JRI~xdwCP|3wS;X z;>PP~^og7cSrf#~@X)Yo33NHTCJVeuPa7Q@k$p4i=7it6<;wL7&_`gOpo;}@<@`W-h0n;TM2-w>iSiT5zL!Hn+{Y!BRIZlh10sYE-Buv|@#mvO%bI zdrW?SiFW0Rh^NUpJFI1L&5kQhKB+o<_@-T~Cx4|?*X3H=$&0q_?8DYjp|?(ekt9$PbV!+!U)8>?T_s8DlGUl-ufzCR>xb zPgG-Jse3IfrP{)h_gHv(oke8svke*hEHbm+HVHFPIrSEvS?Bg&m)>Y$+ly?i^tFB2 z%ALc%E+1Pr@muL@9hu52ako5L)9v+5JYinQ$3W7k+goP62I%d9?qvDKw6vD(xH)}E>Q z0?MOHkCWdsYLomrQ7&EpZoD7bW9NSdH{LIBIozPD(N}kNb~wCvc{4IPEO~Rp)#$jA z{Q}rF=xShtI)h%`fE(tJ^0@K7XXH*$PxE$+Z#`}q3tPD z4qcJztLb{gX?t|dAyG^5X2m@>Ywnv!i`<|&K4tHg6#wUOZ;u8~k95u>&_h{pn9H**71A8!*KCfxNC3wEw-V0VhHHV8@`3 z(K_6qqlw&hy~kY1>eY+yusO;Xn0fW*EPk#00z2^wkO!}vG!Z!gw4odot*fsN<>ccp zP>zk_Hq2vzKfvYUE0?5G+dDZ9pF+__SNn{dnoj9)j7Pyw5b`zXcX9|s&5BJ^ z#(zzyj$u)9q^;k$*}~!qZJA`t9SM7EaXfOS#v-JT#pX5G=8Rg45|+Z#t1UvZXI-+e zlC(#7sj@X`dxf#Rwk9@TxKW%_`33N z=xM_vLk>60S8yf!1@J-RC&st&&O7h?9B%Lx_;IV=4~@J5GsqTXI_vnQ@tt`88hG-u z$;%?vxgN)Y70AmQ`X6u;wn+JDU;doKO~N|Hd&(~$eTIBAa_n90bA5ZpKf5~SjA5A~ zH=aey?8p9H@}2c)y;pLlt4Os1XmS-L^5Q!dAuYu?6w9wmiGtqKmpMx}e3j$iCSmoo#($ zrF;ZCEiAsmmWWoyXiV!ma6=s&-Q$#>+~bCP2q!D`TaS3VUi+2jCR%ZZ=3kQ?Qm9%0 z6^gf16e_-+mu6)-s8? zbhz=4l@dORRY z7`KCb@!QxB?06h`{D2jXXS@?K4!ro7C(Fb=bA+2UOUO_Ab(?w3ueYTjv$v+fN(=W| zY*~wiRSej&v_@NzSY<1Pn^i?!7FO10t9OdZyKPl`v29qh*6MS~MFls+aT!k?x*ud` zQ;zDQWiL~mvsJnm%>^gfv@d6!#!8D+|Ew`jc*rZ z&VHOU_<;{DXWy`mab3){=5bS4r25v%A2@d6n47b3+tw>L&xV*ju}@-9)ES&^-+T~m zzytCE9|<}dK78Jv&+TO06Wi#A(A(zCo9FV=oNgwa&EW^!=skj)waXT%zJTg-%v2qy z4NFy@LgQ65&Sk&Gd5(z~`!dpi|0`3=jB8=OChAuvE4Pfg${Hu5nj0f^km>~}&!2ji z%qfMx%pKpTaWQTT4S6>5%V~Mk)hOo}jjPc$dnAL#88jnH?CFZbhQ;#cd%FIVZ0u`m3EEsbUK>sxgPl&N0bxITz1su$lejF+G(1< zU3!`uw-&$+{;v9?{n z`JsiIbt{(GZStqhpQ~}wkt80vd$6n4-qsu&-=65QS0JO0;zrw?EnmU*T=~;9=ch10&KmOtVsC(#ZpEI?9Bi@Y zp19YZfASG~@a!RtJBzdW)H~exfQL2z2XpVRFJs>BIn-(Q9qrdxP{o^6yR!qCDp}I0 zeC>0Z`{TJs?zXQ!{gCF*>38GETQ!b>y1{OIt8BI*jZJ7$4g~YxByLk(jxEuy?qI3> z0*oUF+BfJGe5+vHLF#qlQ{o%rhsG}pFYtYXW8rt@FSoHjk|H0SvDlO(<{Agh&lz5O9va$+?Yw`Kw~}|#Bul>xL&YiR5?HesGF4`XIthFB;?XkBGwb@%5L-wZTLVNRYm%S>?yrFpn-#sQe z(dV%Bx^VUF{q6R`P^&%E*Q#*;b zy{GM!mtR!f!IQ4;8FR?J^ulABU#%%rhgbcw_R@<_*@crs?%fP4Z~Uaj<`DZDQ0`E3$tKNJ9hzS{Q8jJSQj(lM zt*RO^ueIB6zuo0Fl5^to5#a^!^vl2e%a_IxTn;z%nGQE? zA$gOND}E|cuGAE6s0Ab3%u#*aD-k=c|A)UPCnsHf&X2+kn1SeHcwgB0-j_mu6D0Z~ zPv8Y&o$;j{@B9M74c~)z8>-8()Xh_P>#P~dPh0GAC?`9SE!oP8kuIjXVvKPnR(Vik zwcMCv>bL6m{f%i(uj?aUMtT0dk~idf4#*chu5o1J!W#L{)UjpWoDpyH(v3``3yVBa)@;*In zV4&a08^(i)U&>YQ@QTC@JWLR6exNgZ{Am zXWl2jgER84xhA-Az5$K3z`n`Np`Oh$yJOaMcB|^yBy7<9H_8_q)i^Ec)i8e=_0q6o zs7*snerlK>AJn*H@%?~o8)^xj8C4xZ&4GbjBHlkPy=$aewhsI2SZY}u7EfIeA5SR% znK{ddosJ1JjNd<_@sj-&TWnb4iFdyIQ|rEPk7XoovLe~fkM}m%k54H^EG)b!UF{9g zYmzH(NY1=@R3xmtc~}^c4C41UCpztI(GQRJ+Haojv%6aq&rx0Bsx0YL8fU)34EUkFjf0O3A1dFFIR|dO`DWJ#dLJr%Avy>BDE%Yf*V{C~794>a=U;cYQJ#eC zn}QjVAaV{=x@FZCG+V zsRi(W=E9uR*yUl3M|O4j?wPRj;?+sz79gj{`E>PY)wkn|qdxyR$%&^g9@E(SUd3n1 zCBKvtt^F^a8nwsnnH2W+*#UB^FFs%=&mXk{jZw*v{(ru4kCP+d=3U8$cTdQ!kzV$u zaPba$+N5+i$(VP9CH8qs*!-^6(ciu!88y16$X2cjv&wAA8{H4SU}O&C-|Ewr$aYz) z@y@&LsfW(kH(&mmJ$Qan_e;ErxozY>7A~9*K)PL<{OQ!YefzanG*4lx)ut_w??813 zE4I42z0}M)uAJ`|zw)SZq#x1vymV`lTykUWN;UQqe;T++iL;ahaI@9nrnscoc1usI z*uC5NkI~hT704Xy4(296Pv<*=BXlapZ=rWl57+NUtrzmDgR+M6Tn;zrX$6J(*4N+T zaDyz0+aC9z&UHoNh8j@x7tG^19XG^1iRlpA0WTgm*e0waGmt^vmqyDra2O=tA7Klz z%)6#9a(bF{H1ss;K;=l@M1(CDZmzdmZo0-&wyc)DU+nDpW2*Z!CR?OkK54LmF5fOY zhI(U<-hIr@s9yS5o$A@hCVBYWemDOnwV?)URGUV)W1KT%4;Wj?xU&22oK)SHX4S(S zb2&Tsq8Nk!l=|<7&Wvk5&0*zP7uXruPv*zY_w-a zTBfMko*r(pr)0}KG1O#F59l{Y`#joPZ)Y2;bbZ-(o1<*iI{A0O7~sgG9d^p8&YjPSjp+c0NM_b$z){J?paSGHd{F%O94o@PL8SGOs=yFn791}*#Zxp-7miZ zes0F>tA>9|mhCOrqOrv5ZQ-(IwshHYiDidBxvwk?*qWwY|Yl;@RSXc=|wvJupG zY|;2*$=Gz+eDTqnq?@g^+ZXHDl`AY=G1T^A)l<~?HHRC;Xc+fE?PhYZJ4!aGkI0rU z>_Pk1i;v4srrLqxcj7PXhc1S!b#nko2A!9jd;85-U2f{GZFAhb({H`;ZF}mG3wHN0 z&0Q_u3fG}-5B-CycPm-bBkVB0XMPHG)ju3J)A^ruVwqsWQz zPx;Z^cIwngmwy9pbe$^_H^>|00CTW2E_%9O0NkLbffM8n`+D544SmbE$PrKA$&<$v zHewK09#c-<$iAV^S-*<9>N6Z};#F6DnE9hs`>a{^tkXf%SE8FYX`C~2w;Yom^2Rrx zl0R8-7}+$>K7P?Io*tLaT61}*PwrRV8FO2<$p_n}T9@t0scBGq_hh?$^~sC&kn|w@ zYK@tTgtd6pF&`F<=-g{wd);#=XF_u`6lv~6>1fmuq1MfS zF#h^CU(&N2va0QK^-ikn+uwRg@^iP--D;9G_mu1$WK6q!C5KxITz}H5dLM1d7syXe zuoT&=NpagOUGj$fw90^<1|Gmkkb+q781eSMUk|py{n?McGi2X1JHG(mJ}*Dlx_i2& zaMR)ZPCD+2#SJ*1mKEa$zzseD#;8Xr&^Ek4pb>>6vaJP9rTjnh%!o zG*PlqD(w9DKKTOD)vqeHsJu_&KK-hDG*YKJ?6Q%X^OidQ)hX5V8{U^C9Zos@vL9T% z0Qs({@7KLs^V~`f4b*Ckf@~yWK_gY0ZH$~y&9||yFvcn~BW+K%NIvDt+!)(k%3Ng$ zwyQ8++c()>`OK=aB4x`&YJb)E)EK;hof_k*yt6LxeYMP&Y zd~7S+9Kzpy{WW`6^vxH)V&D4ebN0yj1Dcy#b8G88-YFmY;NBGJY%4U*C|Y&Zcc?D3 z^lizUOv%bp@ie*?cnMMvH^Jo(!bcD*!DWvd?4stDW|uFJo0sEo)78^yr%s=Aq?M4Ppg3!@83VR|%fx>vp}V((3w^N<|S;~IB!VZ7DO zs0P}kVnwH9=iGg8pWQWHr8uhU4NCS6NNx=6gs8otx!X#@Y)JaopxdS~rK%?|pmPtF z(`tOD#;S$tS}Atp`~tdfwamfMqj7Y{6kEX;{oEsW**)^fQAekvaHZ4F$OG-jUoM|| zl09;6#J>8}{r1eG7woym&e_usp3=54**?N$r$Lk zc>=_MTQcQ~rY@A;Lurf^r3*JZG@r3@Y(nxTl%E#FOi<={40!CY9$fanPj9^r=lufU zrbW0Zksl&keJ1jzv#Z0&o35@d<*{fEtMc+I5;wjk)7i6Uou20NY=ZK}+c(G?Y@wj6 z@j6;i-hi8+-Q%(2^|bffHp0zR{3cuZ0?QZOZZ|3)`uc0WVDXX5t-nt5?d?gi&fTh$ zSCyvu!7?;Qq3Q?hQlFo*PNcDEdGbXSXznHDZSBiiqc~xh)u}$xz8uLA@kO8J6zix+ zw2qz9cPipl_grzuEaix0lk=syE_HlUp=!vb6bSkJ2Z?CQ%}gR`s6`DZRU|3sgi`Si6-wQiwPQA~0^suykS9#fj^7j{1j%i`ZQ*AG+K9?O|rPg#VuX*(q zm3vxVWod0Msq@;TZrck-$80Z}KdaU-&mLCQ7}qYUdo7tiY*@Ci{GvI-nv>_&bIc#s zEbm2PtoW(EJ6m}J_g_`V*VT1vPOkOLOKU#zlw*fAHRiRa9#=MEUXAmp zVe!0Y45!Sk-{-D%K&Kw-ThFuVzK2)d@9=6nXV|oCK|Oojdr|eVVqx9Cp8M2!jee)E zK6%)(w!ZCMzZG@tF*WB>xsCZ{cgArOyaX5QMdaUh%5=xz!+CXzxf^nD)|qFG?9CKz z_-fC2?z3Ay&AU0rA78)yHo^9Wd|!Fxl{?}a^PcRDc*oisYd+-!%v+kzlDC)ZfTI*X zoMP<(jKNpdb*f{hv~jM-4?dx-aqRizClzjLF8cra<>8>3A3SZ#*5S;prwylX+&rAV zY4dQ-_A`dFPd{VWW`FaRVcp8r!*R#V8y1~Vzn`+&J6s!vbIv$(I2TW9ZP;_pduFxY z-rAm3xzp3jZ{M=M-dm|-YnIjf!`07*m1~9*kDWgpSMPhxpEGY%>Yd@^j;U`c&Z>1= zN6j9Nn_a(uIeSk12Ii<7e@xAHw6=5W+sgB%itUv89%J=)^1_qqedPK*=55=Dt@Yl_ z>6^FJ9An{S*{b3EGtaJXr>z~9FIrMDd3_^z*6d-y++&9&C)N9CH8$yKjw2J!e+s`A#@-*j%(YtDbXH#g1z$*LOzI za(k6kOP38BOLouPR&+gW`*32lJ#OwX!@OgT8Wv31oBn;z*c&{f+q(+B|G|gj>xpqdl3fEhRJOoON!K-Di~?ZmV|s*j93V>Vo?I_3XJNhsRfLsbb`*lI;bxuPm(h@OPAw z*@dmt@w)EzGCF>2;cQ90e`#D7od4|Mta`8Ptit^Kx&~gJ|C|f!y|VIc>Kj<67VhU2 zCcUS*u5b)ro^9irbqy~UJ?{k#Uu##?w*jh;bE>Sbb|=rTwSo2g#@WVR<2bIK1NP{H zErlJJS+#U|;i%$}>T_A)bal~wbKQ4SVROOp_07S`r^C&O_5DE@=+||^4L-pV9$tFs zrEQ+v+AQl#l-IuYwXOJ;ptX+bnTqSQe?@&8`ph%WXz#1_xRHmx=%N=kU0t}G$oNKH zfdBNCx4fn00mgCTTZDd-K+X1;8&zORVUy(D6xYgIf9OLWYS+M=V*QTqN%;*bxo$oo z?65Dvjl2LIbnG#+hbKN}_u;XR-fh^w-rrcdsD3Y}$|(!WewECgG=D+cKBa8dip5K6 zp8NRvKI=2;_x0wNJe*qlrGJl}y~9S#FWWeujjXjF$CV8sKa0v9F0VM$?};uh{OWUF zT{HXeBkDU%vsyW(>Q-jW8fI5HM#-^OA6ff5FGrVs7HcoBxNX(4W%VqTw?E~idYAvO z;e@i^t12(={)-D+$JID}XZF~d^I2N?{S`H~d9^*a)?X|*u6)UAYm6tCTNpM@As2@-_q{qJ++r=dezBD=ed$Ys z@~{8;ufuJ(-8OvXD_RU z`rEV4D|^>_6?P>Xd3%$7U+-`?R{w7@vO7L3O|VhXbnYQ{&#V?!gD$ zzijIvb?$(w_ujL{TK7Gm#&hT)2eSwC`Q;R-Vg+$nL$DV^y3V6*Q?*=hb^YW%L}s@%YCLkFK2m;~(?i+I!NgYfbXDdPh0FnYdo8 zY7Nb`*IwIxN9?j%=lY)ayr*r$3O)JW_rAA&=k{Fq*jb^ztRPV zIaap*kSegU@6&2-w624rLu)O9+SokDp=D1Gs4?5Np3q$Nfd|xnjeB3?bbTAIG1nOD zc%FTKbgApm09FsEb0a#!?S8{k3YT!j2Cl5{4sP3Koy?Xy;6}X4H^c9Dzx&;79^5?n zTi^QDW`p?g;$wQz?^pc&-`BSd3m4V|j~Bs?>v_Wo+kSDoK5RW{21*0%Av4?nBz_t(^M=hMIK z_Pq6cY|D1JlX}O-x|ueXI_9{(*c7ahFp~ls@!{gr!3tmQYhU}?4&0zU z*$!?Z`{aJj>b1lAidpe*OW_Ca@UZbzU1ghfQ5C12R{1gY>9y9-w%Ap%W{MnapDJfm zF73=J)+4r3YjxGu>{^$YVt@NoIirrZ^R-^_OhvnFS8W|*ir=cwcIvb1d|z@8wZ8lQ za!uNtRp-y1YO`ZJH7?_6$1w`us7y!UNmKIN-pj-_68n; z8*!ib294N{FMQz(I|_`jB`M?a54`m8&sV?t)j{E>!Jin1E(>Px2yVRlxn3!oKsU!G zG+QvGL+Q<4KidwL@SpF=KlVMa<11G%zvgTgR4gu^u9(ABY|EYRv{XNRPJMh^Ui}WG z{2IQ^NlKkNajM**jbl^oa=hPf*G!#v{Df|MN8gUu*xZL5-d1{}>4wr7w&{<4eX<-G z-P0H6n?7P^rgYNQsW$8zo$B2Iv1;&fXZFPT6xiw8+ops0@bYkCDY!{|BQMR~$jPG1 zy#Y7kzT^dh8-5y_%nm1}W3Q7(b4{*`9|w0S{I&RS!CZ0!zLR7A$9zyPi{=wrDS0sO z(-(8BnP+9IPOhT%`x7VCxnm0>=7{jye6$?+?^e$9cPcNMB0nk@nvz^<@~PfMQshdr zJyY%dixe?Hier~fm9{-~Zo0O)CUx4};kvp$$J6fhuYY}ehG*3pe$N{{aBAryKCpa` zV*Ad@;%d|8sVm3X>M0-EJe|1%c_DFXa%8E=g_8a8I_FdK<6ueN4Ikhp?-5vM)o@eq zt-a_)my}FQE^gwd_4zcip7_Rd@@=jAv=&GFr}*u8c}dqPV#9a7^PMd=6d%g1h#}=! zQr`Ez_YGHFbyX{JFyxeN5WkH4|B-)A&pF=PmmG_~7M;1dbYs>ay2krc#wyPP+iYTD zEVVN3NAf3gOwCRz{dSK1r0R?(V~fw**V@>p>-fD_TyaIMzrUdQVf;yUg8!Iee^*M{ z^BHaHm!F2FfBeUP+~OLs&3M__xwO>>JuG)bZ*D)Me*bW4-V<&{e%hsNZjdf5*_c>A zjZW(4Z}R=L#DVXB|N9$u$h@4G@_`S0pp_4P@Pmy8=OKJ)(^hav53_w8(JkIvfT9X&N&(U;sE-f&QiYq^{?NdeB&G6 z7*hW2-~MfwuE@3D-ID%tkD2O>E930zJVR>dY;%1m*`bjQUa@h#$K z*tgvm$EG_zeoa5mXPv$8a;{#(Pb(Wam51i1nN#;0aFdD~_J$2-KQez4dy_mg`6s{R zRlFlNt?=P`8{P9OJmAlwJ)f3;oWfs5XS$Mqr?7F!J@E6AlM_FU+lkl$b#G5#Br?R- z@C_4(#onmNSaf>&a@}~*-%Q1vRbSGsKbGyBOaJz>J)I5f$JTH6&yelth~UjVbHC~8 zjOotmVAD9=Blipa9J{N6GB zZum;EHSA1pYvL1(+nC@dHfDU@7%pS$gCW>a($<)g`&Jv9!k0|3@0!@BPpH{!Aux!~x;wE_kxFLIV zeeb8i3O{WeH?cd(pRhYUZhHSLb|+Yf?HR|7`@@MotuvK3l0%Yr;R}l+=_tkbMdi4B z7s1@MBF5tPk%1m7;T3rd&mBwY21z*xCwR=>yUf726H>+b$nlHuUbF0 z`|i6BPk6!;+P3f7J?1fwY1?ow?`+;DzflS|(WCS%4B59GUPcbdNH4qOo}Ux>wjbRr zuK)J8zrCY;=R4mSQoj4$@9x;nvA*s2Hs5Ic-p0`%v#pF{B=YCD=drJydzw3C*>FCaK2t)oU9B@d?K8%V)Bb3*7M6F2RG~uyCV(}(~RS0JSQ-o59s5Z9yf_|dVAC3 z#y!~_d28PllCQwmJ!^fW??3v+Djwsv@2uiIxzNU(gZXoLH9A*ciMjZc^r>?&5nigv z8;rn*+Icdq#$U%^?mzzHKZYOu=tsl&pJUbynad4czW2TFwPQd0;SZ<1MjvqI-aTf6 z)r`kEV;nE8qXWc*ct$4qyKp1thz{mPeaFsvcRI%J*!tcbpZw8}e)RD3KmYTMp5Cc4 z=WC9UO`^B-vnv?MxbCcN-_Ew@yWQS&>(gqDx;Zy~2H(P%Ce_}+jro({#`EkdZpb5_ z>h9tuu}yN7WStxs3mjO(=(~k%3SL{!XpNb9Qu}@{!nZ%+$+ooz@)z_S?3zQeHd|ht zZzq4=%S3pnJ#5HD&=sBs_UK(Q>O48mAFQOXBl>ZD^lbW-w{Sjq%QgCevmP_SsQbA$ z9_Y`$qEE%Pe$h()UQVBl;G4ldewd>%Ux_ww!QS}objQqfu{Xc?i@(_P4{X7c?}U3t z3jcc=?FuIPHpbZR+s@ci_qegHbIp{!S-f~r8+(tN;^{>CX~B&-OS)zpH`B#8u{S?1 z+>m$kn${BFE8oJmyy3`PDE`}LhwvMo;LAEiYf$kD7R~<{zkC84=NO*F@4`pea9lfW zaRE~5$h_$Dnsjqhe%8r=B4GrGZ+Z+g>3uX@$1?lNwoD|&p` z?o05Z&iFkmT?RK<5AC;c&pzkuMt|`vdsA!X?=`rQpQ6v`UUGy#^f=kVmmW9r12fqh zyi86&PAz_#e&e6T)+D|m1N=1e)NGE}hM$4YVjOGz_t;~P;ZcuzRMVl4ee7eK-sEet z@y3g1h_G1^Ei_G%p=x*{VK47Da3l8vEe|)cA_Sv7vyYuu-XhEjo1(w{$J@wW9 z!JNL00d~=w%}~%&6`9O$+KH>Y*>rq&DK`=zg| zZ_5{M_?nZ78}DYIv$Zko4gU3hT5z+oeA-O7p)bed8<$LIQ^h~oiwO+tFn`O&u zeOz!;_MXo;$++RCkwL!EbbcD#(8YHZH<5vva1;ANHpq<9&oQt&NSgrxt@#?q4L_})zu8rLgJ1jr#l7%RU)J#OPpp444t|n*(tms>7=s7;l;1-B z&^~g~%TeTxJf}>DA=k16Y@b+yjv=jz9jarDRfZR6{4v%TS_z71Y$ zY!)v*wY`&s7i&|HdA`3%eXX;SKqq zQ_Ry?nIWP}x}X{(Q32B+JO+~%Hi13d$?VhVZ+ue~Gjyz(pK2fMfXd44{zyrpye zU%CXH@D+_cbNVy}_e!1nPd7HldfdG7m9H#6a{I7u{RlUU3O8q*c}CM=a3i-dskjl} zus7qlfuWt@X58NN>ji=v_JoYX4gDp*?7LX}YjXzXT@Ll0{nR`2@;fQ!9n8%rw&hd3 zUxJ_3V0b6N_~3{9!AbA$1vly*JK-z-@kQX*ys7)jD;bkQUf?F!OnlShh8)0+gV@?Za#T$L}_s3O6;@iL^I6!wtEo-`Sh1$JVFyxJe!gUV<6Cf*0-h9pcl- zr+eZDIdKdh>3H@gMJ~JiG5pD*pp-XvHq_Z@*OcAXmAkw)yLE!@m1o3^{f0 z)N}@)6>f~9_theU>J<0!T<)pRS$t4=6ZX_R4P2-_GupE4XwKHe-k`nX^1x^hQ|Kc1 zhDNTV|KKO~$M`aioz-{5&8tSZ*)XycAbtz}nuCBLa&Em1p3>E@i(mM{ z#=uW(PH+?XjeNlkOdFH&!J}H=7uI*ZgB$H_=Z}quoWRue6;t!c{l+}(EG}Q?gB7nC z5BXIai|zcy_tKY`oWDvhn8Q_*QL^eiCpOOgJX=bh)xK>7uGl|1L~KZ}7!SI`3VRI` z@df1~`)@Jy^ta7NMtlB<>s*iCPp)s^puPDu_6M&$o4BeUPsY{P&eI=sook!DDco3} zcIu*2Do=3Q@H@Zzn!?RXo1Zqx_J)69&O}V3(0}|ixfk&<9ook?nZJRZ#5epjn34NZ zi?Qh}{5R*q??8L81YWT}3LhFqQk+Z4F+9{xd*|?x{^YyipE*!GrRQM;U(~_QOt=x> zTwdQmWoYPa{7Z^a(&^|aB zr+4VCMt3=`&>rT{p1(j(7%zU15q@D}KVve!9y2-C<3``>H`H&tR$gF)n{9=gSKn)J zgFk#u1#ZZKIZN~JXc9kdJU0M03jYk|*dp;JexWJbAl9VY!mpk$w!^pan!X!9hS&PR zPjWye=;vSgm0xLcBG+L2ct~zx2X2BL$H*}GgBvj~y>4DmALavhulZH)2nRQ=CExku ztGSk3M2^|mj};w0Q#q>7mCm`YY{*xpd@Hubc=|u~i>$*B+;~p-xBkUErLmZ^G_ThC zY3`R2+ysAoCidN2vBDmZbA5+q$ zdJ;X^7<`LAK+e%yv7I7TRUBvg9JinIsnag!;mR@Bh?T6tvYv#`!?%|&#yfHw+=Q3- z2Mcn}iIK&j{CRN&eB%GOy~$b`z7s6Kr9NSde5=XMw<-oA*Y2wi`QQF;95-Z9?15(D z3S)w2x#-{q&Vm^=nPi9bC0B_K?56RHRYI@SJsq>1S{uHAKGgj+w)=KHKgFeVB)Zd+ z*8ih{;`u}G;HSrp9O*gdoLha@H>;P|H^Ud5T;J)~+P>pS|G`bsVxn=w-mn+J4L{AC zB|GE00cy5J>Ej!|nrrxTXv;sxH-19Ct%Mih3yk7RUmR2LBf6|V-*3ZK_HE~TPwwHK z)-duP^ux~#c7hxBM!{3OWp7-^Zqdv7oKied95#TMNf$wwA~DZ_ps~T)~VwH1f=H?_x0DnS>j80l0bfYhKm-Gl~Sc3k6@Y-Pi1dTq#{oc5+Vp z=}P*PFL2KlHSeG%KVnxoD&xby9yjD1|Hz!VX=Aq5(KYzaKjM?X4cT<9~>K_bkfwQ}K=XekR({7Q}cDmFWni; z^t7LD+c9!(zKa|LU*VPW)i|AKm&(9_Iz&cO-#`#lyldy;dhXSx1I$6~d?KuIZ1tj{-AtJ!*afoh=9M9dI^>u8!ToLs*b|;@j{stQDr~ z=}S77&A@ZKw;lQ4Ssh%c$J_Pi^vB-7g!K;ASc(Z@g|BN2V*5T19a|F};Cps#4EnRR z3e2!M=mCp#5bTVX&@boVL{5R;lGkVNbB#8A>5sdI0<-)JIunNJAU4l?4*XSR95;Mq z7~QgE^WZnwPdWAEvWr`q|7IL;GpX@S{IuYPtx^fxv^V4k0 z7X&x=7C)`W4V&T{P(5zMHs)&7^38m3@)JA#9oZsk?iMuU(a+7 zUSL6u=H}nU2kfI_oBqyv0l7Q4F=lfFYyy1IRrD14u(RY>otzAvzbgg(^Z|=-Xy0{s zv$K->&{uRjB9zxq}83f!>k_>=fX zoi&)&QuMfCYv9KFp?q0$Yv_yDXvbENLw*}O#P882J_tWVajY*nhSs?z=f|&2AGt>R zU;p)AZ~c)WJ_qc;A^yqD!iBk8a+(;G?x`>Qf^ZkMsKEHkwab#~sxUmNA zw_98~>3&*|8@XuDWc~v__yc&C^#WoZ^E7NZ8k;vkBfbuMP3N=GiLuGE7}vgn#@U{( z?%PgxzCV_}+~Z*nd)V;c2S2#^T6_m~4}KNCkG5h6@(&AqZT=3wQ|^cUwkC~TqWiti zK(@)M`9%Dt^A&3VTB%%-d{N4d^L0$$<3&5i$iDltW5#0}e#PYcB(=Pv8ol|G#=-u{ z1<>n!I=UWCV4a`EFLoRa(Pa0^F`$<`gMtRrm7GtTem}=tH-6l{`#kcIj~ssHXMU!| zWb~$Sh%f0ZG1@@~9oT-GEdHvHQ+N9$nap{=RC_>zl;(@x*E!%y2>?=JoJt6q7p z#tqwI?#A~l;6~n=?`960PN9F;8vYIJp_dq&UyFBgROY^YOT}FHZoBPvhteO@=2w69 zS6i{2(qE%}ioO;5=J+4}@P{|vd4HPih_A@*kS{g|e#AM6$zg%)l0|D!`QfSCda}X? zjM?Vjed}A5V@@e~C-Z%QhZM*E^G$3GACCQG7wLF*UK~MAVFm5c#W#P@F?8x_8Tw9Fr(I}0UOS&Y(fj9q?&n&; z1Ad438@7T!lAMd*`1<%KK0cLy|5|%i`I?u!wB{YoIBf@RHf`QGyz;ki$4%MMiJrfa z7vN{(mD;;va?O0S;D+v_pI>yzi|ThO_Zbd5{II51j;`NVWq;^)_MaY-i$q(vQJc%h zQ~F794EMNVsq$W_ou7LB|n?L zmYgmu(d%ly6FId`<_m?HFP5L?n7<#jf9lb8jyOI#di(LwevYee|NG$&e%Ne?`?Do5 zk^Hqh0N+f_M#YY?|MFbo8oEF%==mIDZ}?2HFX)1fYP9O>Qf2^>_d``a^ei2aaK!Pxp+&o-w@Tt#2;+ey8aIa%OJIJdNKZSh;dV%L~BGD_;4E zmLGz>djoE+yY9NC2jGV8WncJcVjuR#Tn)dC-~FT~Sr5yHJ*<3%)kUW>S}sNY6iwOn z@Qbd;Q$DxC7gpFM_)2kIFz1}>lw7Cpl=$J=!4`heeb$-T7T1!e#JT2^+>?!DcdWUK zE~o3^D84uS4l_5^yKpz$aQ*Q0Z+>I={ttgJ{NP75FXLZ9;D@zr=7+V>;{MUm0!5FJ zsdH)5>bmGhKd5$9*B^|+g!>r_{EQd4fhRtVXAslyiP;#1&5;Y>E0g2UH*$@BXomjb z0XjH7PXC_f+Grno_ch&!j@rPM?|jf(umeZ%B2UJDJnE>Uhl9;i?7wgQM#p)>4L9D< z{9n3@-y(O$zhAkk$IZ*{6}aJJB@Ycd*B5Ts8@TbFpL5ow(|4EG`eA(AyMD)Y&fM88 zzB&K=XSUdsJc{AvMy;1slW#t*Ja2NmspY!*ZTmTvZMEz5+vf-HP2c1sI1PSa$Gz!e z@fP`#FW@)exiypWXk=A^n;tv1Zz&t|uV4D|@ZIlyr!Zq4vx)jW_FqF_b;_oQqu^&8 zJNDrQHpM$=D%U3ufM+mcn|_yvK?k}WP0`G=qMtm0qD>0=WIN~Ytj5Ehk9Yu2_+p!$ zWYhJ_Peyxqo;B;pVXwXStl#)LY52%TKho?v+<14&`xk6eaI<0K`euX7N67D$&ppw8 z8k=%`;U&0XZ{(U`=Dkzz{dyNme)f>cEgt%e-x_8ebwv3K^Gml}(C9;kVMk2CPoi(k ziQqRsccvomGF{2{@c8odtbX*zPjMgl2YkdcvO;F?kbj4Vcm^+W1bBmw+WgwDJ-X!s zquYZU_J-Zb-01h4Z#Du9TXX$wHxGaP`F|Mx;`5bdnnoHS<(RWKWWV2zKI|$s@px`C#;rM>EGkEv3vGYYnPIoxLTih6rQO0zWBnQ;)Ad=Y!-hT@5Q_Fd`BOBRN;8f;mBD> z4DWd7JDQ#4r^$tw`|Rya!_CWIHf3*4op?WO95>=)xSHG9Wz)-PVd6TUlUn+)+G z$P>K`J9q=j^e;@k>)r1fzV`KzZ`9iwxbdxmYd(3+aO3Cey9GCVe)!_ew+w&#PuC3R zfAFQloVTqVj(%6^(f6(&=DctHFt;oA=UkPtVVHYWeIqgDe{C3MUsX2m9qWeYzyH<4 zO*h;)+dqa*2e-m$S%p=6+z>ORLAJqF`e739p*Hwe{5PRSGJ>-)_7h zJ?p;7HO1bLGdlS(kAa)t-{BVpH)3?SL5IKlyT2c9DZG5)<}VCi{^FO0Pha>c|O%&6t(*c-X>;D)_{8}#*zbQJp08T1rd-~(KcJ-!^d zR z-}ct}UE_t#=8|dlK>icW;f8I7896KYHM~VPyduYVN(a(Y3Oy7b0N>FZFYw!bFc-b( ze!12*p5Y_@;~oCH2j1ttc%1x>=Y*TbKJGD7xEXy{B7RzMk2qj3Zs>6O1aA19^Z9y-oQ;taAQ3IIj2wL3DJc<_xzr*e{M3b4ZTIS*i7F*tn+|zgb_eVV95p9kJ56MyFgD=6(=!<=FPr5k1O0Wf^k9+*%DrT$rW-9j^ zdlTGTf8FT&ab)^OmGAmN#qHm(ynwv(f8~$A^}`=lsXE*GlaF@qnQ)^`aDxt>nSMeS z`W=So4YrmYKnrmlnWnSkOw6sIzg#g4c^?~{96=x41C9mOu09GXB{S8nb=D{560QV6HYv#;^F!}$btJcK3@H&SGQQsoVt5G>)Fq0xZ$Vy zmYTUxe%ieWH`iZ3%BjJP+_N>Sa05fWLpiISfBysaZ@QjdM_YME_JDte?s%X^Q@o{r zBJ&S<$U_?6*l~FmwnrbnV_@yCcK~6=T(X#o&4WYo!WYci0N3&H@RfWR3;o7c;U}F7 z%kmX+OZM?l{#DLLzGFt*puxXfM_gO3tAyNE74;qe$bZTm&s6K~-1OU{E8vFT0ypeS z{4}+FfAVRbgRH|1yd>vgAFatfI_zAXYx;0Kj5-Iu_|DdZ_v{C{lL=$wCmub8n|br*w(m#Dfxr!$^PCHwQ+=&z z?-8t9SGcL1yZ5c}b5iXMneY8HxM6S1ahmItC#3WL?9VZ_ZbgFifwqX}-{4sZAc>=VquigCo= z$UngjpMt+;9At>hy!$=x-Z4*4UQ_VX`=NBlPwKD7jd`4`Maz1dnQ%jwD~aFe7P#S` z!V@gfA>+7VFW4Qt6~|d~NI$Y0=#KC7mK+{Tx(AyT+`t__9iQ-+f5BFEnsT4H{2}8*6IN&U$aU|8=kX zAC*(AT>QTKG`i94aDt{}lg}i#f)40Uk0cjqK80_5zx&;<@r=!Z8+KoOFaIFFLWkL> zGwDC`$#_R5_!o2rmdNjslvRJ2RCZgs;TX%3-xKf(;uwJr?Go*tCpKKXJ&n;cKCqCu{Vit=p}l? z^Bp?1)|0J)C3K-f#5ZD-2R`tDZ9NRyv)OztG#AV8^T;`z`Q`#$Nhj(bKJ@XkKl`)8 z10L{z=3k?|w)joXT_dK|kMZ!S=`gzLgcBBy^z8u^-^@S0`TuYOn{?$Rmt5TP0dOPV zcFBt_nRwjLMZpcZAun{0Z^4K+#Cv>RvUK?smkq}rH@DW4S-&zX^JC&G(q|xwU=A zpsu-P>YJg}_U4;?$8GdIDt*CeY8VmUxF26t{$Ean&h@@6yTktxuc3|SC7Yhh^Mz(; zNZ+tGaHS3U$)~}Jb$jM~(O#`+$2UMPdQczilxxv4`4&D!a&q{GA8d(s;unr>$MLrhK#!ZceFuTIs`yv^P7$ zjkRfXi8)q)lf85icctDTu=cq>rf+@8XUf08 zulLa9XuVg!-i+gh9`O90)pNo}=4p~EQPXA4_4GqWbf#a_=&m0;LL>CFow=c&2l{bb zJAK0#zk+@h=fDlQ;D5WXG1@=uuxGS<(wa4^TTI2K(g}P-zRAW-8wxl4wAE#AE^fB# z+ZGpUM((!olToIH@Re&lVc&H@Wj@j zvHscm6mp#67#U|9@C3iK$Gg;cq>Z+b1w6B_je9t*4LvCKH#YLYPjNjtTfc6UPc`rP z`|o~d^U=Qbt#9t|k?4H$*zm%CLk}1dJEwf+A3xL9yng1hpJ{WWpZnZr>)qJTRH?e= zNT-(3)+Rv8kS1ErB2Cn_5QO%wy{4n=SE+Ba|xis$v!I-~s+_10Y61~uk zE#f<&1v`W`o*kXpC&v^#NkKET(N15Xad_fd+i2~Y^rKd^cRgPXcEq*%G`A)XZH&v7 zEw8y&>je%fd$Zr*chld5dh8uCTXY>95KW+7zmF-=&i!Z*Y>4AEliM2QA=6gdu zZsb&~OG6L&Eq>acUj3)-H^ufZKh-=IoZuOZSmPmgMd#B&^go(Lm(f`>6}(rR$9MJk z{&d%BOULpTVMH7E7_0F~q?7Hi&YdW4^B!9?< zAg_F<{6-AFUAd@uIVz+5i!XlRaPdVi7%sV}>Ps%FHkS++z2JGn#ieU5s^b@5bm4Hx z#m}qq{5oIdMK5SRh#af(;wp5sycXKi!QOF_gJE;{{`@(yko-J1l)AcYijvMb4$&tuM@*ntWZC&o+ zhqd)aM;$f#c8M5=-_2K}^T;nf#RtGkG!%30tjP7JkayeKryjr7acf4%693&j#Le@a(Z&% zo=u6dq+n`=ZbyEmo{=p>z7tljY8!tJ%g z#`T{oxvF*>-%vJheO+HZXZ6YcEnmK(*)skhJw@-B>+*bP0aNnS=Edj)b{$SU8+oPk z$fxI(+ebI_SED_CIH$kREc6WBcSh@6*Vpi=Pkp1CaoVPZVge276~Z|DFRxxCic@Ll1C55sqp zo8G!*>+qDPK4my&&g`Pe5$$?58IABto|k-PUK%~+R~$>hV{)F7{Epf&*JzXW`s|DT z6m6|}lhcM5`Bu1~bICf{k{h5y^@qQ7pgfLz7N0zFh5w$x+7o;woAK?Ne_y!Sx|wgk zrZ94DH8*ihVds`%(}s`M zbromS_4?af{gEv`8++p$LF}e+$WN--d$~E>UrP(PtuYLuP^-K0W-yjd=|I8J@_^dq%kraZb~Hhm`L7twVO? z2iPV0gkA}6@Xh=h9b}t-l;T+betW(xpdNqM-8HW7i=1e3(&i$}Eo5F*oRxT%e@({d z2jk*X%CTFcPfysV59xV26>jK|9yeR2V&cse3vVvmY~EboiQjxyVd8%eYu4XdSa@0C zrhY?l{d0%YHr-ly`bhbHe^uif>G0u{y9r;3Wy7rNGv_eO8MyH| zRP3vrHy4WbDUQnr7!My1ZrCLHfIYQ_RUQ+4`7mrgJ$2?;XH4N{Wnt!dE$)Mx(iIbn z8#>w8gBy7sxDgk7e)Gy~s+bve_&9RyPkQqI)AXFUg8wgu;PcW&^cec$L9jAijX&ch z{7N0X2SUa{)(CKSA64{*qbdS z<6Ab%MU8N?b#whTNy-1l4eu{}mL1vj;%YOuuyk&#|57@qh13tK)4Zi)vJiyO9{j}Y9DCm4|*loOK+Fh?a`hMPbCi$5Qp|AOb2 zPxjb^L~5_4+@n{WlkuUN@}Xcy?iCLt$&%ux@L`H)Y58Ue@2C zEBnrm^fcUImwsj5UY9q_+RLVMxxC>~5K zZs@CV+{o?FgD^#Zm_wIOgB|{Yb%4ho=NmD5w0GIyhMeLlS>XSLXY`G@XQm8ZLnb#=?9M;iw6$WJ zEgLE(G2i#w<%|90uzKCi!|Ju)EBxG0?cOtNSo`N?TfSZX+n-cC^A}a$Hmu%oQ{@OM zSFrvc3TuB*#qVaWDcRao`0;($+i}Cbvo(n$&`*2~H;TC&--lvz*hSc(7vP(WvN_@= zv5{vYvuqaI)fd+(&h;g<9ls{+`#xe1-2=~H26pKtw3dS-6KF4|X&%@3`Dknm8RGMM z9`nu5x!~FDHwoCA=U#Y0#X`?%_OtZ%MB+w{$@B89*&B1#;v2rFoQB*DUj|Ovv;O5@ zG#~D-Klaycp7%GO^5izBB`&37$bpy$t>6(~)ncESit{iBqx^UJ%6-S%JFhsd=7+g9 z1!nk)u616GuZsC>y3IPU;D&ER=ICoS%C?+=-(X{l^uK;X{Vv?5`aW67`R4MkHf&s1 zIC;;@$+@= zkB9Y}>bGV#Su3}>;aiMCKcTI){QMj?+p~~6+iVTIvys-^usL``zQtvH0yLnX@ya#& z6}RFC-JtG^^NM5Hc2EAT_Bq#I({F2&e${y9-to`S5$<7FyeK}iF2a1Sd1dn&u*8Pa zXD@u=^9S$Xuc+^+J?A;kZhqRbrOQfZZJt=%kTp6iei~iNzQ})&DSWVQi++gB;aBia z(d>y&{Ee26mb2nJkUw?@ej>~6(HF=2g6`T|bKyM%as5o!rXRXE+wMp9 zZkX?fo2&`5Ek9uVd^7s{(28@!XVw*&5A__2tDohSD_*{$=4O|9KfkcMtbQMC$+^Sw zCFfOn)^N&_3x<=H)b|9cEM9!!uxQC?<#W{B&(bZ!(mKCn$#bi|ph~qVSzcQGEUkL^ za`Ut&w7h_IY2rv@ps(p5w#W1JxUtRl(9v+gPO_==6q$95zl@eWeL|=4TAR=reyyu= zy<^Uemt5cXjqd1A&loqEkqcyN%gf$GrwObgjH6UzNXu9_FjrAiBZ2Lv_|rSzpO+!weq64|zmS{LB1ykCX9j z{)M*e0{)SMT!&ZV?d|6{J}a?pWJeA_F2GpiG3YvRP1aM|mm_dr{FN7y%NCo_5%`M+ z-fQ5SUw*~q!xfiZFm5N@5icn6_0u)|iMrGifAI34|REa&CJttIVw7M!HcHK~J9G^cyqpAD4jp}Xi>Hby~H z;}$=|jpv~UKK+lMZtp~TS9Z?5|r$7n3ZWGrwjr)lhRTJphc<%PA* z32wvz!Ht@~#y>D8K(~7ayrQqB=GmL?)YGb`VYZ#;8!K7=12^{jYy0i+1;6+S@IgO` zvEX?3%DJJvHL~dL*~B%`U1WzG`J)Hq)-JvDb>$O}evkc}bI++5=ebRHo^$p&6O9{w z8W~|z`TgGUL_ahkk7y;|!#CrLvpI4E+L|L357O1JBmanwup`bO*XXP6i(@Ht3tsuI zp*b%;`%G=vQOS{0+>@k&tC^9(pLB z`ON6O$ba~UYf7(NGkofrPYu_8_H)Da*Nxr{WuvbtedGA2uKDEf*|KGH5Ny(~pZe6N zTMp^dRbTU|YleUN=YJYLf5YgzTiO^C9*})9$H$Z#M~mQwZVq-FQ`k({XLH~ux&wC5 z9(~X$G@Fj5ImV|_V8pRp)7R;XJm{N^4`%q6Y&Q&hXGKy?OJPxNAFauUAMO3)%W??cDe{1`GfIy*ll@%;D&t(ZeT}&8@0S3-$1NG zW?_cDQ_ztuM=J%5`#Rf>$M5ZrIo@CIoB}`k#^2D6jdy=}L3YOYjo6jG;G>~I zY%c7Gf5dc$mcA74@Oe%=@uWuI=qY+Ed{FlVKZ1*y+N3=k>I?7aSoQ+n*cmp&cwhr& z;%oJ|!AtgleUl5ME8&Lzw9b$`>jU1z;GT)^G@5(et@$Eo!p?|Ga!yQQu9y70?X7QX zm_x(N|L*o{qu*dyFn>YAwehjt-WeuuaAPd&CVgOh)-`F(ySaX+fO7_^1#4~K@ zbhvTeIctdU2VVGl?j?@n!|^}ynq15AnS+!oQLMj$FMg``V%b_c!#jJn**p3LeXQ>j z-&@n;I?rI7*4CgI+KChCF3)Bh;xw`;J|lxlFr!Y1-J!o>(0va+_@G+L@uX(^*&bL3 z?LxoUme4)(9+AW$*ByPm>bW7IR;3M4d8PJK&ioHQ|yvG|f;;+FD+}Or{ zI{K5>d~&$*O;=XFb#bl#tU1M6kK;aa`hN3+JcJL^@z442PpwVbJ7*q23@Qd-W6Z0Q z4{@lu=*08E%s6hu5{kBLtDK1U3HYXRZ+ML7{0Dw343PuAI5|)&{CB!Wo-T1aIVRI& z#c}tcZ=Gj1Vmq9tcU`ZIecST?)|}%9{IX{-fBW^}341(#@QvVO$`ls3`=n8c9jWqL|=;|+H)Slg! zH+fs(=8QAprrsr3v#RDhH??nc)co#5;)YKT8*swk?{OoZA+PcR^b8vEQN$0{tjSk- zSC1cqPjol@DDd>T&wg&W_H)+`e^feDTutvi{t1t7dTNh7exqW5eOg>%-iMzo#$-1X ze8OXXK7RAtt#xBt_yv3fazW3Lb@Yc7>mAsKaqNgw6qrfDZ#E(CCbLs)kisv==e~w9 zg-rDFVH>VuhhlRg=j1xRR^&X#dW_{9c}mH#^rghfjmjkyPT7tdv^o;@@%F4!PX z@=fFee~e2Bc3ew8@ln`Fd9kPlqSi)^ zOpXb5#RhUv>KTI@?a-VrY94rd*_$Qh+pnniX?@qmyPx%& zg%gRJ=z8k`*bFw4-9R6)jF@so#ei^=*xqrzk(gRc%oq0_ulQGt;d(fM8FUjn>l-G~ zNUY3v*WNlS{Lr_&6Wa#I?#F**^Q`^2ynI5q(+0Hw^_>GRDNzEal=pJ58)g8 zK(^)9%vU2*Z(JfWKk_DSSW~u!&aU5Jo+R9~{FE5mdR6`^-9k>;3ObNp6pxb; zG9%{Tv&o&nllGn!pV5jAgA@5EdYLUz^WWrB#3AwoVjnc-6XFXDps|wpBrytXI3FKO zd$fijHUC+0oD9&X+QOf4(c|U~*$#EK&2=itZ+H%RLynle6Fa4t3pW=^rn61Yzz;m7 zprJN>ZQJ>@rGND0m_Ec2`b-~Vte~*tIH?a}1Kfw^J z?dl&K@Xz?Q=nPvhg5G=qyu}Y~(l4yIH@m|I`qK}+M!$*=!~}AE<`K|EOwHHuK0aRr zPilV>@YB{o(S`IQJqiotpMDqf@weo%;D${yr{H-qCuXkEybqs64j1jv5bofQ&5%c7 zm(~hlSbJ&LHI&_5%n27&nJ6H*BV2i#Jw-~#zm@7#hP%h9|$!MQ< zuuZp#-S{SIVT{HCz8;wpLsf9)&uNVe5%F`A(ZU5WQ8_7#03_ZwR3ThRw!Rk7__ zV=*?*p-#~TKa9aR^$YvxNv6pv*@csoU@CR&6Ir%Rp2hZjC-EQJ(oyiizs8^FM>c|< zw9jT}$A-X3ihlj!pK(hghb@U8Wt%^QzH|~CSvN^PTF;`;H;(DU z`~};@XVMoe;1}M+pMss-2~p!*Ij|D5kezQ`lL4t>cy*@q848~T%LIbMallYjR>H+su;?x(%$ zeCt~uu;3W`pH9bS@8V`MAfJbKuejh9T)2lz~P(sghx=SSXML!RC9`m$-x z8#9}y9eqMBVG_>Mmp0n!myd#0cn>pV&vAWdLoVqLxd8Vg@9yV3U9B%|v_%W@56`fq zjlP^yj1dj+%k#l2-2jW8-E+e?+wOVAd@!O9wD;VJC(#~F$uHjF2Y;HL(~kXjPx#VL z;(T~9X3vd}+8V#}!3?b0_S~?{$ADWg5n13{8y~)sA$o#rDR6_Y=77Zn<@ZNsC#7!k zU|ko!vo&NAPvtFp+|UE~0x#OLsq{GA#~=5Ma0Fj$E<46|=a;Z0ct_9R0h-yy3&pl$ zgZw$}y!&|;dYUdmAGo1^9XBrRQlk}`89&)c z8~rH8M9$egej^!!o#4kf>1}_L3E)O|dJPw4M{ zVmQ3U1G3^iFcr+e4cgOPc;}ovgFf8XIlhK9-JTaO_@YJ2Nx{d?#?gF8<0d0`2sgIH z%6v_D32yL&e+M^o0Xabz`a%xM{8;AK%*WUkOOY9IKK)8Q@n4~HqTBh^_{W#vYtvt9 zzB7Emoc3%Dxl~&xV@!!D$qnqJhNp}@{+9EOtI3|Wo-uxw=cd2W#BtB&oOX(HaH7C~ zV`|595Brg6G<4iCF;LpsC*N?(@5+5#r=5MpzBXi&yt>Zu9xrT;`*<$;gs;oSTt5{T zl2vjm?;g1x_pco1+p!7B(T4}l(Qjm29?+Z*zM5N;6Z9=1`*N(oj{eCL9nAjWEg#Xi z@Q&S-Kk{tsFx(h_$vPaIyDEerRhHMP^K|Ak% zd3TAw3QJ-Rvgex*&hh79B)Xrzqfhw)@l%|avtiTWg`L84Jma%#PiL}Ccr5OvOZkp+ zR^&YL%}&@)Y|Vd3j>hviPoCBGja7WXukZ}`7)+2=7}Fp5`kzIrp$Fxt5!MNa<9*YkUJEL8X9rljTgJ$`or`SXECHruYIV`k8 zLpFe{JC{5H?9f@-;)Ub*4KMU2o{L*9uem5Oo_qT)6gl8~xy~OuYfR!;$JjQykKJZB z(GV@frTA2Io)lc{$~fBd;y3>AVd!zZBo~g84RYn#*k|}5Tl@w4=9lRVzBzrz$HKGZ z=FC0I-N7V(!+Aa-?1*9T!P-x=1sSJ${OFVnd7@)`KOo zPPf^QttCU2g_zBWp5W7|10 zP9Jza$H;&Z+}O^udw$RBoW4_XjLo1&w0BOekoRo67dg~dY!STl#c_PmMxV~5w(UH9 zX8d%O7)4@{Sq{GTpj^7>3Q}99r2X?F&2EmAN+D& zj?^(W$(+OG$1C{(I!&BK4&WL7U`L%2{HXP-OotuEaxU0O9qg!c zY$n_UN3lVX`@V)D`>{nmrqH6_&vlOVm#RVvzCagus!@(IuyS6e|Q8tuDA9L z*4Z!it>Vr}!NJ`b#|}M2Has)B(hF=U%*>rTXIOZ`=(kQ!KINqHnJyX*KkV?Px6nfj z&p(D6Jj^@`8d+znFFpf*UA$wh5C5CJAdB!qFTw+iz^L3EKIn_RVFU3Rj@&~of!@Ve zZRsz(P^a`*f~j$v6fE_1a20zLI~3fgQ(|BGI@p=64xUm67pa4(?C-4B)^oxQy(Pw1 z$UJ)^Rz!31FP4M}dOrRen}N3U9D4<4-hbvN^NsbxH>Ts{Rp3iIJ_D@agFFp?z&s_o zxgT8dLCi_fsd$W!)?v|K#*5xH{<|ZqlXgwZna~^fP3NFHd%<_|+os-UJ+G@M30F_;SXQX6~6cy{9|pk zV{hC`8#TRUttDM*oN^S&Z91ort>7ux>FrXml)AS~!4sJXj$(6qJOxwE1yiYeO!eEl zf~(+5Ti52^YWv1a&c&wUKKWkQ5xb)|T*&3~AG|BThQpBK?6z8-pKnM1Sr5YB;#295 zU9ry(gDY6$C-aeAgI|1w@GA4CFsA4ehUjASf*Eu{ z{5x?qyopQoM+Wq*R^SXz_-Aw^92z70f+xX>vBA!`-3cDFC3{NRIF^!j>e!(kL%~uo z)XQ~nGgA!NW-U)Xy7m-vNUr~k||pfNorPK8VTupypVjD#AT0HiMjG8;V)xV#UC zN8(DkQrE*;^04@TM`Vl~uy59yx*yzSSy(oMlcGmM42V0{rx{I%aHfU!XJ?QYv zMbkZS0tfIVmK2xEalwwZ^qDqd67)xVMI6Fjpda0q_@8d#bHku~k-qVi?L%W@;R70r zeLe_$z%#nTiRXtO+i0KXs%N`9GCGOZG@pllAmic&wmt9StXx%ViIy&D-*`Ims3V8N zkMLW$2Mxb<=x-HG7Pj|XeYezmd)oTWJb%u2vzIH&%4b?KvSD~29+e;EFY^=d@pZ4e zw8=90F<*{{d_aBxeMslxk6aB*!8TdouaQIigtO>;vQE#M)6Q><*@q*z;Y*TRIvYR9 zD4w&m_SGr&dkpD&XJtBUks(-s3*+#8Tlx5miJdndI+-1HZ#eT@=-}BsADuvl7>}`& zW%(`f2%lK4pACQ=ITh!`HJ%p+*#*9s*hapMk1Pi&e$*a+E8-!1)`qR2 z2VqE?*w%~_hUjUf$5Sw*?n~^`O!y*ak)wfmUxja zVM}0#?O$uo* z%a+Y0WAj^|;T`_rEBVv6dyq}z0c zY(l?nzsFLxW1DhcK8qZ^@AdE>jYXlC`MG4!aqZoot#Y5-Pfa%I06ZY?bPxQnGi-x6 zBlg01c8HJ5Ua%$X4&RS%LTi0`Ui32t^gu^4fKKj%CV58V(jKNfAH2Z4Yh2Gyme+yD z`u<1cO5OPA{qe$UfBfFI-G0-e(+Q0a=uq+Dk)@N`v!apjpP&uSy*+Li z{azgzqj$*-I-@sziqG%^f97%M+TS|#>1DG<-zZ|g7A%}!`G*tRx3&0seqU(isuk^f zLB1J8Uf=^?>08_MIPBQQGcgRl;58qO-h&^q$Nx76$DV0xJlf@aWPyG4tbOV6q+KvI zUdK)aYj9wEFsr}|EE^|Gu)pM-FNUt33yq8&jbd;5`=Bd&(hYnyzLUaM$i;{^$gVt` z9G&~%$}QQP$C^C-6!>YL{SME>-d5axhhyvxeO-3HKi+7-X7C&6D!w^7i9hh1T;d6S zkiqbN#D|4-&2g>Ft9--JN3%zx_{sMRee-Kp#ZyPmK5D8Q-TJ~idd>PN_i@kMhkkT_ z`>@5%7#BI>w`AT_EG@r4{`nN_so2Usz2F{dGSU}#>PwHS>9##5d4&gdjsGa7A!p(i zW0LnZcPwVNk49{~-{{fSILI*Fo}yoRLVx5v*mB<3qf1~5{n1738Z`GEwS|@M@of5X zPtWcd^x+vDcVD=N4Y({@HqQSh1AGE8DVld{0a|Ru4-{uso?*Ice_D^d*t`llc7EKh zJZqyhe&7{)k_Cm#kmXTaGN*0pAJ6bE{PTT0IdFDp?z}loKe`@H*oQ|w>QU|YZPnKF zz%`qwxSw|9hCb*2n=iF4L9yllhWIkth|$?7V<30N2kT0|onzYA9?9YHO}5GRcp;Z$ z&sf+7HpX*{BaF!y%om$?F(&gc{4D*lN#>8;hkc1mtI4!+#n!V?{3#gHziZqZ2DDYs zHMMQ_ckS9y&WjyqllU3zUgZOJg~#J<+H;|2`GiN-`!`2ceq5X8Czk&*-u|a`e+8ae zlUC~p?uln?J_Ef@kE+RsnqNY$;KDu|a1`Hq-rRWohn;$Pw-YBpOBa1zqxe6%+aW5dMzeHS|034}bW>8(o4GtrAC7b-9^0V$PEn3d@k&k@jaR2+?e_EO1w^ip&{`ouBhenS_M#-jQa;0pU z_I>eQop(IV8=5oMhnQV^bLyF|4-`@mNp!wD8MVI%!tHW*n65U-F zPFo^ZaOGZX1lcB+@R9-({E6xM;scqR7rW;hbJky(dlxT>L97RpE3zKN{GwPXxH0d@ zKbmfg{55@B2gc?kFQ8@%*&2SVYx699?YV-Rih1rT{nEbKv!YM-@2e-50?lXtg9pUSl^Wmqdtwmvb zw5q%H*KA+q z81B}^_g(Z8r9hhlr%Nkly}LJVaR==2{mEC#xJ=azcHm0fB9~ykM~rTJXYJV_xH2!r zKERQDw|K?1aua+-ak_2o?Th2ZO153IvwMi6Tx+d>e24j2&ysPwwl8^3&#ic-qUGJD zqcN9{dUvnC@4BBH1=_p|eOkKqzjD{+o6E16TeTiV4gem>5}A@yi+n|P$z6{1@>v7f zA*w&U{K?~iZmBgfL;Otp6Xm~6|Xv!`?eS}Ec!YYpH_o=^@=j-=)ytLwY< z*K%@|-@98E-*?eZlmbo0$wBGyAIFW`ya%11IZ(EO9#{Bl)(`AzVOJFG`BiKKd&BM| zKkS%o$Mv6iS8F!qv*B58PToph*_;cUo3~|WV*lEjqnaZ!zauvc8<|I{9QTifR`7Wr zZhjo3y~q39;ipxe>mKiXs>3#yLf*+J`HY`f9!{n{V#4p6j0O|Gxh2c?vW+ z=G&BSuJ`}D{P>68h1m8HHGerisux@TYhGL&Z~ch*7=BUKA6vtv^ga^X6D+Yiu{FU` zY)fno%%lWMu#>enS?wwn)YEt1?tjsMQt-*^81e+o3*h!x0F`SxEb*nGEi->#1A6V_^5^W}PWg8ajW z^~_=#F^1T}w~xdciER?2xVJWrYcKxM9?rxeVv=Bvz0$XPkYDR-$TAzo7TD)o!4bKK zp&pAcsPF^hFS<4bj^R1?aj%RcWAGftY5bnYapQsO8q4~k#jes$yY|kX=QpP+-*;a= z?XHmT|7+*oaw7+UC=BmqR-4t#zOsw(S^7sMtDSc8f=2QKq79f!aZXi%p|_PiFa(?Q zxN}!)js3a&w!dS&^JjgF{BU;49x%U!UWZ3(>@YOf>~RAg@Is#bCw9#q8=u5}vEVm9 zQJ(f&_roOqi04`!ZsD8GcV`DrVAvV3?kijS@U{44*)%>OzF7C;Onksib&>(DYqPzJ z`rrK=ee-*UIX3+_PVcEtJN!v~bvDhtXy0DhVs{z%e(b6??PaKU@oyU#o`!Er;8GXQ4W>D=Y=&9d{Q5HgyJd$Y&pv=L@PQdxm5Bh2w0QvC#qf zdik3447->f!AbAn5Ebzy+jUV zWDmfY*)jeuJ8KN~q1U(7+mrVc+$w?NJ2-sv!(jw|=tcSy2I+qG0j67*z;WLj)1fd9 z>#!k)RT!sJm4z3$XUlx!b6>}Kg3b!`P5)i24p?+Yh~9@e{bq0ATOQow|GT=%@9cu} z2>RAKUH{cj2e2dX&sNxXATPYqCho*1XYcHncy?zqPT>|78Q|a%Y&cH}Qr~h3ohvo?M=MmL8{J7_YUjAB}_lH;&EmeDO&+c_>jJyoQPw%1t&0u5AZtijH5HaP(ixid_OV%@B#G+ zZ_f5+@`hcQz9awdyFf4GCu*yp57Tz$FY;yaCwY*GXWTPhZL{O}gu}Ql@C|V;-igW8 zyGt%x!SFQxBMJPQH@Y6)!$39#M)0XKkAA~TK87|f#gl8VY*zWodRO1|m5s#-GGm9xPt3+lzfk(J>Dxi@bl_JKIMy-r zx%oDiIK%dc>CM(7@@Akz=kwL?uru?eg{6MaIG;wW9euM;#on~Ov7hFfB0r6PCf&Y< t^4^160agp==hDwHJMk*uLpH>3fj&8h%GC$4G;23}TYlWHChI?X=?k(gKR5sY literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/t18.bmp b/radio/sdcard/horus/THEMES/Flexi/t18.bmp new file mode 100755 index 0000000000000000000000000000000000000000..c1220ca1b707e5d86b0335ad7d9abc884dccb0ca GIT binary patch literal 164938 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 n6m8xNQvNm!8W)+hiqpw9Pj7NeLk|7pT90}B zHOkzOvB4K>{JhEL%lq|mh)H8C@fc3CHGjl4{H;C-j+P!jHfO$kpU8>$_PV9K8e7(q z3(dzGMi&E|%NRB$R_rItrfc=!{OHJ7KR-1dl4bEB3%9&A}$n%DaJ0xD13V1SoZ%~ zZCZ8ly0x`O&JhzX%17!kCgByQF|lqT~CV z@7gUG_S)1qjk?1J%(~b5SnK0szym%s>IQzzLmKLLxYfnDt^Hrb`#who4{>OQi{4^0 z?{T(bD1Lcou!aY`N!GE}xR`9?mY-|sh^_p3$-C4imOX{e^TZAe@aI9U^=@$~_SeFF ziQiV9YYpyL9hF==*;fuo_KtpEOFN} zLHv!)uo(x#kKT0jW_NUt?mmgYTB9rKykHS83LQ=>BEX z_;R-B`OXY-f2u6~Dh}ZSFB-o^X}zSMWbvW8Cwv^tmGjFxuf`S*wttulmg~Y-b%Fu= zRQ8#!Eq2)3n$=t!%wri#>rrB8&IPaGV$K@tluM}#oP1pp>#mHo<=@_`^qvd0rkio7 Y`G==#@S`R-;4q8XpTHsdncXAKzpjCx!~g&Q literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/topleft.png b/radio/sdcard/horus/THEMES/Flexi/topleft.png new file mode 100644 index 0000000000000000000000000000000000000000..603bfe1cfc8988218a602a6277b323dca5656c56 GIT binary patch literal 526 zcmV+p0`dKcP)1QB~WvxLhv1es(v<*eB@{73J?J%FuUu||5O?cns zi4fi0Pm74h;c)2nu{)_l6vy9tr%chglOk$Hb<@U$F>WG3L6mV34W*E12u-A-BASbgiX0bZi<(Le zt1;1yAcBgxR}j&?ipVTQ6jy$KBBH1BKiBj6yw6-^?zP$U;Qu}!=RD{4oaf{9{>Mpo z94U|a_YvzY)|)J!6LfBI{d2%^p56|LyIRO`+&}032cNX4;dyJn<;c+xF{F%)46;j2 zO^s_;Kj&@s?`CxwDh_^M@ zPwr#=#`+>+C-7W5cdW;VC+&MYX0GuM?pHnQ7ZDqbk64JMv9Xp9S;~Kf+kYE&`2E6q znWa7#b;NAU`<10^)W5T^&#;xVOdHmLeXzLbN5u|uEHihszhb4aa0g8Gm)sA-jhc;E z->^(RcpWU0`xo~EtpA#Qi(}e*Z*tMjznXiayphWT7T%7{ME@t2k99@PH)bR553F$P z+t+h%%*MOHnc8gJgZExAyP4zL_f6M<@%ZccQN1zjb^0}2<$wAD*!Yg%d!h`~$n&u^ z_7C@8XI*jMqRRVyivjj`tQ1y{W&4`@2D`4Vj_T{{MXjx^_PI7mZ7 zL(iqLu`#;)&Rud6dF~gi&kbGV;^FEZyc@>fIQOt~bngGS##`&|?xwc3HcC%V_uQG< z+FEL8XrQ>bIL|otDkdg|IyyS2v$NAxS67$&7I8Px&&dn6l&Jh56 zE-t3NzCP)1Zf?4QN4vYbaxc)HnVF%SoE)*^p3Ke7$sJf+TqFqj>6<7kE2D*l Q1sWe87k{v4XJ;|zUtS99mjD0& literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/trackp_background.png b/radio/sdcard/horus/THEMES/Flexi/trackp_background.png new file mode 100644 index 0000000000000000000000000000000000000000..5beada59e4f6cb2101dc5ef437784422fad16b1c GIT binary patch literal 5110 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 Y|0@ir`kCY@b^rhX07*qoM6N<$f|i`lu>b%7 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/tx16s.bmp b/radio/sdcard/horus/THEMES/Flexi/tx16s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bb44eda4148d3957279826f501f8cb15548e386b GIT binary patch literal 164938 zcmeF41-Ks7wfA$s`+T=Tu>c7nK!}HULfqZ;#NFL-PbooKY7{RN3N6rbZv`z{yjY-k zaR{!#3DN!ie`{t<_8H!j1o9qw?(?1VWM$9HJMX+Rd$0fUy{FgEL96~x75;uAI$rdM z=zh_ER5_^1f2zIXMm7`wCjJNN|o$im0tZz4)YIov`v0BI(y5OEmg{!!sabi z!j@2F(}oRIHgDV%9=iX5Flxle(6L?n(4}L?(4|9%(7jXV(4%t~qn=&5hF;yehrT^} zhJHPJh5o&IhrYdfg}yy|+Oa-8dW7Ek+|KLTE%fQ$!_FJfyH6P0uU{BCa9|i_G)V2B zFkOIbHc`h!Q&ZymoXa(! zsoG}Weow!{Z%!H`qV?ZTw zZQ2@jIHXNz*Q#}B)1qZ)-Mo25Et@tCEgClo%@1yz(!rt0!3i~P*eEor*B~@%P(K{p zu%S_-2JANo4eQr8YE-{LXsYiXbl`!Zf1keLl1nbBvv%#;jE^^O-kfchH*c5M^?8OM zY;NATvC5WBo2zX2^2>0}FV76ETePq~?bx=R^>x<{9fh4vhJ`-edf1qN7q9@aJy3l# zsBb?b-ySkxKo~x7u*P~w7&Cl?@xw&%{Uq`F6!Dnz8$3CE(&R8}>eMiEN0yX*+lJ>@auMtT0csV9wmIVD7xIaL&B22(sWjJ6+E+b$XaSd9sl|JME0g zQ`9HI^!SN}zlp**SWoacHo;hlFeVy1vLuuY8)o=I7SZoU!W3h|7$MidQ7_>LLareT z8Qae7+J#P##=HF?hh%ta)4XM9)4YXYs->{iT$lnA;OF25!c9tG1>7{KS1;7BTUR)1 z92(TE7h352<;#|Zr=EJMN(jLSo^vWMzAmrpZr-xF%BD@5bpO&d>GP-!Z@&Iom?8dc z*`!G-o7#jf?K)WBc9Xnvy+gmFN1V_vj172!*fz{amy`@0CcQDj#&&}A1>=q!hc9As zUV*O?UK9VpgDK6Q1^=cbo}NEXygqlXFfunRnZF<`6*?J2U|*O* zUd)>2GGe;PiP_VphnYH#Jeg`b4ml(JW;!*|rRXjC6g>rY(Ans27x`oeYgSH}%<6B}omDnxT6+ha0)Bfgsl|A}W8ik}Tf;{QePzo;~SCwd5+K?`QjHB8Q(0ZwOR_?$OW zzc1|0o<6-G&6%llXXzY~>ADnC8K$-GSTCzJxgdEkR|Gz03p05FGtdn6i`$a4=xE0a z{h7zj7}?k(Wy`t!Hf+!!vyH)zW2awgSG(Tn*|}@z-nnx?Hi8`}w&$?v+O(EUCu-fY zWoRXP1VRV3Y}VZP1)gc%M3|DyZQ58ip74b&*H8R3Q~i7S?|&cGy!>*NPe1;o6o!m1 zv3F$m;4c_&ieJl1XMCOOqEoW_O7Ie%eEjin#_2ycp2KJ8)1z0IqH*aWd#qiXHldsF z(HZ|n`)p$;_v+d`V5fnVJWhtCcqzeGk&dA)k=z3}=$x@5WWx(P$i@k&t%h%4{>X%h zrsrl#2brxVesO!oTkNyB@Z{V%h86JQ*x`6gbI?&bhCEMftF87Jb{e{6acaxK?+I2C zyvz|F&zO?fO$pB?zE5UT>R!zcQeVNx;2z@K#I{QJCna)#>&=$E2|ngblg(+A9!qUe z?9l{2U}(C@kFVNnll6P}U-(k^Yx18&hM`p7(7&Tooin(9|1hYJbU%Ji?_R;MBA+L= z5wO*qz>QOeAIZ!v@`><^I>?5D+P7^R+M|zzrM7Jj32kN99g^xI^ihju%`$4%w26H_ zLgU|6*B>HzIz_Vb$ z?p?c@-sL%)G&(qRY~MbNkd2H?=_0w)LpbTKeua8s7vt|szYqiH-&^*WbpIg9u%Uwn zhhf6UaN&dRmy8%8KUa3QurXeG1)8LBo-Qm*MYjkW=A($WXGsnjNw&`t&7}2XX*)aG zpDkVBG)r=ShUD}#@#|Fi2M}Wd_90>j+=tUh#Ta7Zo`*;l50VcsKt2F+v0v{b#?e!@ z3(t?NV7!qZPRNm%_}ropV4o9)Ne8M<+malJaAxe8Y*2qikQG~ zk@}N(NlAMDqlq7+{YpzrVa!OLQ>|hOC9;V}3On#B*WvjJ(E!D0?Du>1)^qm>y?gWw zJhv0iACvV1dWhIUN{%DLPODb(ixp$Y5jGpwgs-sSz!r8L*MJXhy5VnCHmqNt@Mnsd z3V|CitiQM3cr%>3YL$(t%Li;z?HXtJYhb_rp?}}LwolB2 z@n)anY{QR(2PG|I$1y(RyL}m_zT#1B!%twcd$$Ci+yi65Jvo^z&|LO_2;Gkl>Qui@ z-BA0WTA_9=(SZpabijcD+P}trVZZA8W>lkE^$=6_z4r}?>^PCd-rFm zxo?e7vqlYjKDK%0ItLw;QQg|L&A;%t7p?gr_#z@~DQqpbyWoA(CFm1T2l+WMb&~Gs zEZlT%-_ed^llb{;^L+?>93l*5BwYmN_&&cC+j{U_C&g=zKJv)$+`pblWqV?)eI0wM z*yoy#(SJ`r`Ba!Ezm$GLztD33UFENU3Gl&lA}ioc^ZjI}x~*#dpL7y>iI^j?l6nW# z33U&uol&g=4hYbJ`|odbz$M3=KsTi$ala@aBdqI9= zV!;?89?l3LOK6kd5%0;}$-fD4X+v z`|dOBY$4{B`aH$P#P+UdF=k=ru}2>@8^+}hx&}J}Ss&|R_hn+a0k)h>e(1Z0v`C&e z)&p$Eb}if9Z{{m1wqh}uMv7rd*7?2AqPvleWl=0c**4vReo4uXF`iJZ{e@rAfi;ES zi2Qh=Ha^d{3!TR?|D7Fe{XXOK{TOZj`O0fQ?q7Hw*%#{|Y!8o-(dNg~#~c2Msg2@p znQn>oOu7yKppVcu*b`vKx7ml^zzuvbOfkSaZoi|-X2p89$KU1nIdStaSFt1d2i$YN z*j3m_=o#)C{NvkzA^aN7DT_)yv(80t^BsuoPKsGWoY%3f`loHO9qW5^JN+2zdHl?> z$n7=SwyhI8z+2amW_L=5wrr8ubA@p1_kp(kNo?5*RPq$l2oV1 zpZPi1|B(3!vTLwy@;<}Y+Q&8$Z^OUzm&w6Hv=2pc(dDI&jko9q$i_6~`8$Rod&~f^`@W$(}hhvXD zHZ;-wG--UW;Q~JcAH^^t%-HkGzNG)a4!lW!#(l^$LOeIe;d^XSsAC6VgI3?QJ|p%I zX43vMEa-f@hTF^Zo%6r0$5x9r_%~M+vd9|~|xEVER zT}I`vw4oipDS(sy5O`` zOJNj}958%3PSrXOcngoX42$vR+r}H>5%&FBv23xiYu+rwD(53liBI)Wd={U9dqUq5 zLwf3oCoCsl-q;BnWfQL7Xfg0lKK?lT`uy|7>urn|dUWe%zB(}!^TUO6>@xV9XY}VY zo=s&byv*@f2YY{b8-M28){p3J>2u^ip&7U`t? zZrJ#1^cDKRYZZ&?(7 zmuG1#D(ij>3%Xw1a;`s*jiH|1_g#<0tzBFFX=7o(m*_tKO=6aO-=Eii%l92;w0_K5 zu2l%9I>xo(A7l=)6xoUn!(YG-8mOETwj1AXt z&oo|zc-cR*@pO#Hrf1yjH_|cVyIhxJg|6c=)vzQPTM9>!eB`^19dxGIiqc=$@!$nN z0zT~|yT6Wf&e*YI!()#%!lzzdm4>@r>NZMCBaGgP{lU!N9lM zX52T=oXHq{$NRgiHtSD~ZGG__I*s^@j~&-y8_W^2fw&f9>AaS2W4>;nxMstM8W8&_ zixdmzv-;}edL9!`V{`bJ7OzrUC`RS+C2FXg>^}5NrCP_GAJ=|%zP{`C+DOmYsE(dh z_io$7tJDVv!@E$rZMaE4^Yh~GaIB%o`_B3b9O(N-x)%Kjo}8B%6L{ap%=?#fni9hl zzsBTa?LA6%H8(O9!>=c+cqzq36%(tnHdu-n_UZeww!0e$*~ z@ngnXo|b#6TenU)_q=nfeD>LAnY~t2n@zaUJOImWVi(en=oRwk)Yy|7C+>`WhHeBe z-Z${M_eq8ceV=U~2XB2m7*pc*@E6Az2l~&)j^o&<#;@XA_{`4bdlBDl<-dp*a~P>d zuE0sxs;k&7R8QMr2A(tQ7=E|Lv+JqP`K_4O{5fdtS&{4MvwEB7A?Jm%TKBQO;=70^ z%(ua>gRuX_m(69@c#Xc>W_jB!A1`pZW_&ITxW4C}eVZ}#XYl7_-}&3en3nM>^qgjQ zr+y0lsQI|5FO&H$_(It2ZnFz7@De=beC6BT=5dsLH?`S__hADOUllBmv1`|1B4V4>?nDHOzA{ET=Irm8T`O*J3wCF#T*x( zqm;Ee=AV1rB(-1Ni2o}t6H^!R8!1^$ZIlnN7(a1-eFvMy;*okrkHs*i_yZop&V2SX zuhzU-lFLqHWui;K2z_q2l5OVxx?jU%Mda&@>^Bq>Z>2br`knLbdO6$_!j9v{Kl8CR z{Ald4fAA^r#j(?-YaYe0p+iG6jq$((VXQSo{){2cH{tnO3A(0=vHUn(zG z%VNbdR4WK>#;WFzIx_4>?u|aCUz0veVlOU>;N=)EwheC3E$9K^rLklceTH9GDlY)v z(ziO!{2hq?ZK-GH_xLO!#W)p*=3a=WbQEuQ?My5`sp;ZA;XCKIj0bg$ZLi^_HIhxr zJO-Ov(!EFMCF-qO`9AXHh;J4oea3mnx89sjYv=Qwqy~>#vc5Y&)JtZh-}5#C)et*1RLt9k$nf@2~okIkV^V`QU>O65JH2<}<}jpASFyApGF1&yo5vWKwIOJkn!y=tt*w|yb;EZfxVI{$h7E-m=* z*02Iz3Q>A4^HK|vcnyBbliv^D$*wE@1d}^N@%OYH-+R2xwc`E!w#fkGg7*>IUDG*d zrJgB1p7y7mn{4+k>pR>heI#Nm(0`r8v&cDJPVa=K~hMSE=s=om@>t&-655O+8`r`=` ztR9WJVC=O%l56B^Ebc7c=N`ej*{Q+=b`qG!p53Y!65Gdc&YCooTjn+Pv_ z2ivK&hC}j5++O{hpGM5c|%feHhOi884^2!1v<)ZEd^0`ns)O6Q0Z8%Qx9~ zOnj5?@*TgQulpR?`Ds6Ue|yVyxju4gfMUS?`t&y4Y_@>-H^vOxdAtTnj+nE^2sN!ef#aVs}vD8b3f3$ zv!#m`nT>`1CLVyk!M8#8p!3no*sLBK#x_IOI99-VzJHh}oySN=^8fT>O8$AQXV}I^ zVcrJz2V;nRk9=pm&^d1B+wllTjwK&=pA+Ky-a0>()%tl~w~ep&O?f^3-Z#Mw_h9}K zK2zi)ecfj@DkVExIM@<_`cUw zv5lVub{sRb9#4WV`7Llm40zGPg<*!~mGlz-V6U|n25-CVHk)I;v;GZ!jVQeR)?4QL z<9}e6Gmiz|hB+w2(y)hlUNC|!#yy($oX|89x?dhVS_CxJ3uC z4<8z4(wac_v5mmZ{P~*qE}esq(^+zpxZ08>ONyp%WbcJrZ@I;4UWkJ*PjJPOWtz7; zEyE457UV~aml!kgcHB?)+&p_3wyp8cvXxp(v%z)pa-iEjU`i6KwiF9CB@o!`41g-Hg_x<-j6j_SVP9<*o&89Y=9%k*Rt?+E_}bdTK^quS&u+iFBZkuhCgi$ z8`eFDsJxylf8F>uZLJ>GCegDl6@Oy4F?W%*8>VT#Icw56U%(sao~=BQ*0)Pv6vBa@ z=WU^L{21r(JMMS$TdWH+RsQ*6`Rj)sdT1D@d+8xNx0UXpk3M_prI(6=8|8z-dFP#H z`AB2|Iio|DFAvkFO-*VA#H+-h!4&!keCPE}j2oUK#*gpk+jg?ez>8DfPQxB!%_4Ba zb`#}1S*K{l;w6TScpNLYOJnKV=nuoOu)I|3W|^NO9~hsQ^&P0=hhN-Q^ET!Ic)o+; zXO01H9WUPc@j|WZF8n3S9f>yw%O+>tfu)NVhog=AOlh?c2A(b_8n+ zW+ndC(uIpcj}$l9N4^f%j^M`atk?$Q7~ge@KP$A&`S{(`;1Pqyw}O9<(i%4>o_Jzd zApZ-$h8#`{*-Q7{d+$!;hPc4zpMM@!ty*R4MZn+S27SXiW5^9+(b#ET>uPzMw4MU& z@O=0^)K3!wB-cX@$k%M5hKqf!0e0A?C1$WK+^}vW>+v~msMBHn!Ak$8;@}9K!#cp^ zc&K|r){?&gJ9*rg9hJtDV!O@pf!+WY^E`#0ZwbGhJVne4(tS8T-%v2d_}t|3>O#|{1t$60U1>;0)ywpvf+NWGSsed;@@ zF+k@~OYn7YL+w0q0oH0J*0UorvGSbq#TaYH$GAbxvJNUT7haD2n{DC7F%#p(w;dba zmW3OxLH#j#$K}FG9ycA8A8#anyz#~xi;o-W1aNcMiWMd^SWlT)Ja!suj)EP>jknYg zFu#fQu~@r~`CL9vAghzs=ir68EX=7P7eo!sws1qPlXYNOhZSE3+@Nn5C*sYOm?&RN z>`4EjZ-_(pP`niz$#H|ef$!lF@PaL07H;5oi1YnC#|*9G#oI!-;d|&QVp#C(ilwsC zq;FQPTp5;0_n>dUO#{{IUw7SgMaK>J0IP1N5swGI*jD6ay)M;T`h}XChINxUgy@s3 z-cxxduek$1)I(6C!`#m1(ofXSI&SiHHq;B0g&XuuC2s!xm;qNYZu$y0;Dt4}zYcD| zgJZ^X>)^n#<9P8l#*O(x!V5MJpHoW-Zs1{XbHWKHSnjiL-@b+$^i8pG!<=Apt=NW# zE?aKDPrU>(#Pkig5x={i1CAhYz+?8)|K?z4qFo=^Nr{jvKIwp5ymR;U=1w z$Fre3AjgT#aYG#%b{cg9mAI)mxS`gaFh5q#GySdfSp!fe)pgM{O6+JhB)*oil>1aVqoYSaD%>c++e%9e?wnj zr{Vk47s!UTnqPx#AP$|c3HM`NIwWgyGJlYB+o&%a3P0ch%s6(?J=E01xN-jm+~D6- z#sw+{c6eXVGUZCTi~qq5JnOiD_wk3}5%i8@0sR6Nc!z=^Si&$j=22oX5NX z;$*A~10TA+ab3iDZl{@lqnOch`RGR+ez>ihiN2vG8huk&{PxE`{&A;qL%jfXHa|Z7 zbhER#$HSE`0yjLr*G1=X!+pBH1CESTzs5Xt#}(LOn>;CfhE8^zwbJ)nHEW@@QX1J@ zf9Cmny&Cg#*e~SYfS*d-R1C~ur|~YJrQoJhN5c(uHebOF>w#-LA@~M4N*)hg!`ck+ zhQ(UN)7T|Vv?d!dSk{t6FOiEU{t6F*HS#o`;{h{{8}P{50?2z}TaKF*ig9_~vre5l z;fgD+C<<<r1}u~0Sp_;_I5d^TZ+m^}PX-2nZs6mMNOxh)6I5)27D z+IHNyk3(LEwL4R5OY%hX7;SoxM9rU1Na+VgPn#w zf?fd^@?;{ zgPY5*xO}JWG{?;13N!NeBPH-&TwSTPKMd+82~E22ZoAkK~J zLct8!@t6QDV}Sn2M1lpyP&h4a&j|^9btHtrIT0^s=Jqo1g#mXO>sNJ|xeIUj=Ts2g`d34{oP9 zW@zpHQ|!=3=w9s8vWWgc=AdtpM_?tUZRs1sjqJ3_y3UG&8|nodHv<$a{@S=vEXr%K z;0|$4=v&m8Ebaz9Zim~SM!JT%Yv>!|0td;ay7baZcN#Z7 zZ|&@}&N5xeyM&KW9Ab&)n4)iqL*w6+Wv781;u;nYk(!Z&=xbb({yPXE#BTvyCrh61O!q&h}W1W2X z1)P8v&zt-Cj+s1e;2oFO=p1;E?_j5u!j1SC%p4&-Flf*qtFr+&zy9^Fi-wzX&OUpG za1+M_T;HH09WRcNLap1djvHby|^7iJOXp8LzW(eKW9MKU;GT?39HYcqPV7 z-cEyW3gLzv3AP&gCdLi#6~hN|-0;pBV!-Gds+?;ccc%)v>F8)R-N+{mu42)NO@(6%=8MHgLU zb^fDAmV{%a+gM|OdI|7DOWhj&yx%h|&xEd_7TPKHYuL7TIMq(x0l z-M}x{Y542#i0d5m2bjQ4BY%Tk;{FZsB;ywyBmRVJ#=fB@65M!P03BwZ<@62y6LB{D z8|<`0mo2lnFmt7`)rQG_E2h55aC6~>_O4>bjpM~z>TfLWC*KP_WA>V!t8@E~$=prp zKpOvEy5Lw_06lqO@)vf z;0B$;xbYr~Z;TsZ1Ki->kgow>9!ucZH^mJ$TDjpy^=TJv4>x`Knw>_>0y#onljnon z)^vN5&;2~Fr6C^!7U*Ae3VAZNiJNiUVg&N}VtqsHGqJR>(t*?ql+rgdEA>r9&~pn_ zF92?scepLwxXvl1Z&ZI4>l^eBddK4ewAgT82R9|dMr8VidI4f-#MIC?tY=e8zV*51 zp1afj4evDI9Ui~^?Qb(X?YN_l(Ypy$FCbZhzM}NHxgQSimO9>l=8a zPVIv%E&wh#552;6TrYrr%Hsz4?KYfakrw`BZSzG77bNx2(l_9S+Rov_hub>|4pbbX zD7e{_;^y)z%!k9jIsVujZsrO%$~QW095bcbHd}E7mYi7Ysbd;%MxS&Rj=%}nh%w{X z;TZY{oUs-(cADdccPdxrZz>Lcm^+y0n|UVZzzy$evbAp0dgwfE&=1I0>YB;p5l?g6 zkO$4<27HvljpVlBM)3l$65|G2$Z(^2IqF>wSN`UR2shLV6bm=Lt~&aL*ayG+b#deR zhI&2X0x@;f+}7@T_a^VLvv<&F4idTse4u|I$5Ez_I(D|>tZzSF>$-y*;sKTUn~H-O z?6$4AnQOQiE;)z3nJYYb92yjp1|wVkk$ABu5V~L*Yyqd z60zV6H}Y>>-+-F~RIhTu1s4>>zrjws@`@`%W5ugVMvVB{xIupKJf@H2aFX|Nzzy@(h^b*qP^(Uz=RE2Kq;J}a z7aA!?$2+{6Dldcm1&=e&8GVzt(@fvUAHuG~UncJX9`d+>&m1e@2IATrcidRLfbx*U z1df&cG(vn$T!44vUVQPzJB=IS(1x2Uue7`;`sM`I0EzvZ?bvC|S>@i5Ez|^f9*dTF z(9Ii3eu$W_Nlc7&INZkpFHT?xodi|lreb5pF=V(=Yykge#NeS}k#I9#c$zER5GQS1 zuYtV}xtVkbzCLjzw@Z8tq|8nuo+keWtdQpbJLsH7iW^woT=Q>`&)8$ )^+{jLI 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 Date: Sat, 1 Aug 2020 18:48:46 +0200 Subject: [PATCH 118/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4bc94504e..d028b69f7 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1854,3 +1854,4 @@ Chris Geiger Photography John F Smith Robert Zecha Fernando Martins +Martin Brungard From 7b09582fadfd69ab19c6dc5ca47e570c033d802e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 1 Aug 2020 20:36:38 +0200 Subject: [PATCH 119/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d028b69f7..7656c6f32 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1855,3 +1855,4 @@ John F Smith Robert Zecha Fernando Martins Martin Brungard +Paul Witko From 2cd0e3cf5620697ba24c2328a9061e5d4828e199 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 2 Aug 2020 00:28:06 +0200 Subject: [PATCH 120/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7656c6f32..7b671dbdb 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1856,3 +1856,4 @@ Robert Zecha Fernando Martins Martin Brungard Paul Witko +Michael Campbell From cb8d2f64d9f5f9c790b0070b5b09d804ff92ae32 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 3 Aug 2020 10:54:09 +0200 Subject: [PATCH 121/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7b671dbdb..5e6953d6b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1857,3 +1857,5 @@ Fernando Martins Martin Brungard Paul Witko Michael Campbell +Stuart Posnett +PK Doddema From 511187fa76ae949c533dc21eab5302b2f62116e5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 4 Aug 2020 22:57:52 +0200 Subject: [PATCH 122/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5e6953d6b..baa002c51 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1859,3 +1859,4 @@ Paul Witko Michael Campbell Stuart Posnett PK Doddema +Leonard MacKey From f068dc15fb8a8f0a6c82f9663e6614c11b73bc10 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 5 Aug 2020 23:08:30 +0200 Subject: [PATCH 123/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index baa002c51..56b383c75 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1860,3 +1860,4 @@ Michael Campbell Stuart Posnett PK Doddema Leonard MacKey +Andrew Peterson From 83851d62431bb9cf27d71bd87bac42fe392931a6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 6 Aug 2020 08:12:43 +0200 Subject: [PATCH 124/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 56b383c75..a9be5dcea 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1861,3 +1861,4 @@ Stuart Posnett PK Doddema Leonard MacKey Andrew Peterson +Dominik Sedlacek From 16b194735dc4bf9a8125d3f7d0c09a1752cc97ee Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 7 Aug 2020 22:23:49 +0200 Subject: [PATCH 125/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index a9be5dcea..bf296c30f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1862,3 +1862,4 @@ PK Doddema Leonard MacKey Andrew Peterson Dominik Sedlacek +Ronald Hellstern From 8e710a45847e8b9a02e4d31dbf8b4ea606376a2f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 8 Aug 2020 00:12:55 +0200 Subject: [PATCH 126/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index bf296c30f..1199c6080 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1863,3 +1863,4 @@ Leonard MacKey Andrew Peterson Dominik Sedlacek Ronald Hellstern +Vicente Suarez From 03cb6ede3c42028c98f36b08c25f29fa315a5191 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Sat, 8 Aug 2020 11:58:04 +0200 Subject: [PATCH 127/267] Flashing frsk for receiver or sensor via module pins was missing on colorlcd radios without sport connector --- radio/src/gui/480x272/radio_sdmanager.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/radio_sdmanager.cpp b/radio/src/gui/480x272/radio_sdmanager.cpp index 708af82a2..d1bb86ce8 100644 --- a/radio/src/gui/480x272/radio_sdmanager.cpp +++ b/radio/src/gui/480x272/radio_sdmanager.cpp @@ -375,8 +375,14 @@ bool menuRadioSdManager(event_t _event) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); - if (HAS_SPORT_UPDATE_CONNECTOR() && (information.productFamily == FIRMWARE_FAMILY_RECEIVER || information.productFamily == FIRMWARE_FAMILY_SENSOR)) - POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE); + if (information.productFamily == FIRMWARE_FAMILY_RECEIVER || information.productFamily == FIRMWARE_FAMILY_SENSOR) { + if (HAS_SPORT_UPDATE_CONNECTOR()) { + POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE); + } + else { + POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); + } + } #if defined(PXX2) if (information.productFamily == FIRMWARE_FAMILY_RECEIVER) { if (isReceiverOTAEnabledFromModule(INTERNAL_MODULE, information.productId)) From 8d65076d6bc16c14e925151eb0f9e8b155549ab7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 8 Aug 2020 22:26:15 +0200 Subject: [PATCH 128/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1199c6080..c8c369b2c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1864,3 +1864,4 @@ Andrew Peterson Dominik Sedlacek Ronald Hellstern Vicente Suarez +Patrick Geerts From 027fc4bfdbe9fdfa37498a62694f34a40e73d224 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 10 Aug 2020 09:06:37 +0200 Subject: [PATCH 129/267] Fix PXX upper channel cycle --- radio/src/pulses/pxx1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/pulses/pxx1.cpp b/radio/src/pulses/pxx1.cpp index 4dfa3be95..ba05d026e 100644 --- a/radio/src/pulses/pxx1.cpp +++ b/radio/src/pulses/pxx1.cpp @@ -208,7 +208,7 @@ void Pxx1Pulses::setupFrame(uint8_t module) add8ChannelsFrame(module, sendUpperChannels, sendFailsafe); if (moduleState[module].counter-- == 0) { - moduleState[module].counter = 1000; + moduleState[module].counter = 999; } } From 6c8e09a4abf4e72ee8d0c38c933e5b1837e438a7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 10 Aug 2020 22:44:07 +0200 Subject: [PATCH 130/267] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index c8c369b2c..617ef8a2f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1865,3 +1865,6 @@ Dominik Sedlacek Ronald Hellstern Vicente Suarez Patrick Geerts +Rossi Giampaolo +Jose Luis Franco Torres +Bart Sillen From f0e55800be9c759f005ad2f25b60bfb9a2c02c99 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 10 Aug 2020 22:44:31 +0200 Subject: [PATCH 131/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 617ef8a2f..11dd568a5 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1868,3 +1868,4 @@ Patrick Geerts Rossi Giampaolo Jose Luis Franco Torres Bart Sillen +Antonio Moreno From 7de84b900b0954eef03067e6d943334a71f5c072 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 11 Aug 2020 09:48:19 +0200 Subject: [PATCH 132/267] CRSF ppm center handling (#7870) * Add ppm center handling to crsf * Improve * Cosmetics * Cosmetics * Fixing scaling * Fixing scaling * Fixing scaling --- radio/src/pulses/crossfire.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/radio/src/pulses/crossfire.cpp b/radio/src/pulses/crossfire.cpp index 5e4ffdb95..2f5427902 100644 --- a/radio/src/pulses/crossfire.cpp +++ b/radio/src/pulses/crossfire.cpp @@ -20,8 +20,13 @@ #include "opentx.h" -#define CROSSFIRE_CH_CENTER 0x3E0 #define CROSSFIRE_CH_BITS 11 +#define CROSSFIRE_CENTER 0x3E0 +#if defined(PPM_CENTER_ADJUSTABLE) + #define CROSSFIRE_CENTER_CH_OFFSET(ch) ((2 * limitAddress(ch)->ppmCenter) + 1) // + 1 is for rouding +#else + #define CROSSFIRE_CENTER_CH_OFFSET(ch) (0) +#endif // Range for pulses (channels output) is [-1024:+1024] uint8_t createCrossfireChannelsFrame(uint8_t * frame, int16_t * pulses) @@ -34,7 +39,7 @@ uint8_t createCrossfireChannelsFrame(uint8_t * frame, int16_t * pulses) uint32_t bits = 0; uint8_t bitsavailable = 0; for (int i=0; i= 8) { From be7086711da3211a195d4dbd0d6203e544497f74 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 11 Aug 2020 13:09:27 +0200 Subject: [PATCH 133/267] fix wrong length on telem mirror uart (#7874) --- radio/src/telemetry/multi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 42f612d57..599845809 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -319,13 +319,13 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) #if defined(AUX_SERIAL) if (g_eeGeneral.auxSerialMode == UART_MODE_TELEMETRY_MIRROR) { - for (uint8_t c=0; c < len; c++) + for (uint8_t c = 0; c < len + 2; c++) auxSerialPutc(packet[c]); } #endif #if defined(AUX2_SERIAL) if (g_eeGeneral.aux2SerialMode == UART_MODE_TELEMETRY_MIRROR) { - for (uint8_t c=0; c < len; c++) + for (uint8_t c = 0; c < len + 2; c++) aux2SerialPutc(packet[c]); } #endif From 6467b7245bf6c118d09d16a3be9c0881ba7d1ee9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 11 Aug 2020 13:45:12 +0200 Subject: [PATCH 134/267] Fix companion "Trainer" to "Trainer Sticks" to match radio (#7876) --- companion/src/firmwares/customfunctiondata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 1d8545b26..42d84d570 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -46,7 +46,7 @@ QString CustomFunctionData::funcToString(const ModelData * model) const if (func >= FuncOverrideCH1 && func <= FuncOverrideCH32) return tr("Override %1").arg(RawSource(SOURCE_TYPE_CH, func).toString(model)); else if (func == FuncTrainer) - return tr("Trainer"); + return tr("Trainer Sticks"); else if (func == FuncTrainerRUD) return tr("Trainer RUD"); else if (func == FuncTrainerELE) From efae01a208c2af58aa2080f89f651e0a016cebce Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 11 Aug 2020 23:01:28 +0200 Subject: [PATCH 135/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 11dd568a5..c20ce0a12 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1869,3 +1869,4 @@ Rossi Giampaolo Jose Luis Franco Torres Bart Sillen Antonio Moreno +Carolina Carcione From dcf7decc80898ac48e5f6c12b143677bbec661ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Charpentier?= Date: Wed, 12 Aug 2020 12:56:59 +0200 Subject: [PATCH 136/267] Updated French translation (#7862) --- companion/src/translations/companion_fr.ts | 2521 ++++++++------------ 1 file changed, 1028 insertions(+), 1493 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index fff65e6d9..7bba065c5 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -743,58 +743,58 @@ Manche Droit: Profondeur, Direction Boards - + Left Horizontal Gauche Horizontal - + Left Vertical Gauche Vertical - + Right Vertical Droit Vertical - + Right Horizontal Droit Horizontal - + Aux. 1 - + Aux. 2 - - + + Unknown Inconnu - + Rud Dir - + Ele Prf - + Thr Gaz - + Ail @@ -1784,79 +1784,79 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Bind module externe - + Timer1 Chrono 1 - + Timer2 Chrono 2 - + Timer3 Chrono 3 - + Flight Vol - + Telemetry Télémétrie - + Rotary Encoder Sélecteur Rotatif - + REa - + REb - + s - - - + + + <font color=red><b>Inconsistent parameter</b></font> <font color=red><b>Paramètre inconsistant</b></font> - + Value Valeur - + played once, not during startup !1x Lu une fois, mais pas à la mise en route - + repeat(%1s) Répéter chaque %1s - + DISABLED DESACTIVE - + CFN FPN @@ -1916,82 +1916,82 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import %1 - + Delete Special Function. Are you sure? Supprimer la Fonction Spéciale. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Value Valeur - + Source Source - + GVAR VG - + Increment Incrément - + Clear Special Function. Are you sure? Effacer la Fonction Spéciale. Êtes-vous sûr? - + Clear all Special Functions. Are you sure? Supprimer toutes les Fonctions Spéciales. Êtes-vous sûr? @@ -2184,23 +2184,23 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import La source %1 n'est pas supportée sur cette plateforme ! - + OpenTX only accepts %1 points in all curves OpenTX n'accepte que %1 points au maximum entre toutes les courbes - + OpenTx only accepts %1 points in all curves OpenTX n'accepte que %1 points au maximum entre toutes les courbes - - + + OpenTX on this board doesn't accept this function OpenTX ne supporte pas cette fonction sur cette carte - + OpenTX doesn't accept this radio protocol OpenTX ne supporte pas ce protocole radio @@ -2327,62 +2327,47 @@ Pour <b>retirer un filtre de l'historique</b>, sélectionnez-le - - Eeprom is not from Th9X - - L'EEPROM n'est pas une EEPROM TX9X - - - - - Eeprom is not from Gruvin9X - - L'EEPROM n'est pas une EEPROM Gruvin9X - - - - Eeprom is not from ErSky9X - L'EEPROM n'est pas une EEPROM ErSky9X - - - Eeprom is not from Er9X - - L'EEPROM n'est pas une EEPROM Er9X - - - + - Eeprom size is invalid - La taille de l'EEPROM est invalide - + - Eeprom file system is invalid - Le système de fichier de l'EEPROM est invalide - + - Eeprom is from a unknown board - L'EEPROM ne correspond à aucune carte supportée - + - Eeprom is from the wrong board - L'EEPROM ne correspond pas au type de radio sélectionné - + - Eeprom backup not supported - Sauvegarde d'EEPROM non supportée - + - Something that couldn't be guessed, sorry - Erreur inconnue - + Warning: Avertissement: - - + + - Your radio probably uses a wrong firmware, eeprom size is 4096 but only the first 2048 are used - Le firmware de la radio est probablement incorrect, @@ -2390,7 +2375,7 @@ la taille de l'EEPROM est de 4096 mais seulement 2048 bytes sont utilisés. Vérifier la sélection (M64/M128) - + - Your eeprom is from an old version of OpenTX, upgrading! To keep your original file as a backup, please choose File -> Save As specifying a different name. - Votre eeprom provient d'une ancienne version d'OpenTX, mettez à niveau! @@ -2553,22 +2538,22 @@ Si vide, la ligne est toujours active. Éditer %1 - + Popup menu available Menu contextuel disponible - + Clear All Effacer Tout - + Set All Définir Tout - + Invert All Inverser Tout @@ -2937,282 +2922,258 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Firmware - + Channel values displayed in us Positions de voies affichées en µs - + No OverrideCH functions available Désactive la fonction spéciale "Remplacer VOIExx" - + Possibility to enable FAI MODE (no telemetry) at field Possibilité d'activer le mode FAI (télémétrie désactivée) sur le terrain - + FAI MODE (no telemetry) always enabled Mode FAI (télémétrie toujours désactivée) - + Removes D8 FrSky protocol support which is not legal for use in the EU on radios sold after Jan 1st, 2015 Masque le protocole D8. Légalement obligatoire sur les radios Européennes importées après le 1er janvier 2015 - - + + Disable HELI menu and cyclic mix support Supprimer le menu HELICO et les mixages cycliques - - + + Disable Global variables Supprimer le support des variables globales - - + + Enable Lua custom scripts screen Activer l'écran des scripts Lua personnalisés - - Support for PPM internal module hack - Support du remplacement du module interne par un module PPM - - - + Use alternative SQT5 font Utiliser la police alternative SQT5 - - + + Disable RAS (SWR) Désactiver le RAS (SWR). Utile uniquement pour les X9D+ avec un RAS/SWR non fonctionnel - + Pots use in menus navigation Support de la navigation dans les menus avec les POTS - + + Enable AFHDS3 support + Activer le support du AFHDS3 + + + FrSky Taranis X9D+ - + FrSky Taranis X9D+ 2019 - + FrSky Taranis X9D - + Haptic module installed Module vibreur installé - + FrSky Taranis X9E - + Confirmation before radio shutdown Confirmation avant l'arrêt de la radio - + Horus gimbals installed (Hall sensors) Manches Horus à effet HALL installés - + FrSky Taranis X9-Lite - + FrSky Taranis X9-Lite S - + FrSky Taranis X7 / X7S - + FrSky Taranis X7 / X7S Access - + FrSky Taranis X-Lite S/PRO - + FrSky Taranis X-Lite - + FrSky Horus X10 / X10S - - + + Support for ACCESS internal module replacement Prise en charge du module de remplacement interne ACCESS - + FrSky Horus X10 Express / X10S Express - + FrSky Horus X12S - + Use ONLY with first DEV pcb version Exclusivement pour les Horus DEV - - + + Support for MULTI internal module Prise en charge du module MULTI interne - - + + + Support for bluetooth module Prise en charge du module Bluetooth - + Radiomaster TX16s / TX16s Hall / TX16s Masterfire - + Support internal GPS Prise en charge du GPS interne - + + Jumper T18 + + + + Turnigy 9XR-PRO - + Enable HELI menu and cyclic mix support Activer le menu hélico et les mixages CCPM - + Global variables Variables globales - + Enable non certified firmwares Activer les firmwares non certifiés - + In model setup menus automatically set source by moving the control Sélectionner les sources des mixeurs en bougeant le contrôle désiré - + In model setup menus automatically set switch by moving the control Sélectionner les interrupteurs en bougeant le contrôle désiré - + No graphical check boxes and sliders Pas de cases à cocher et de curseurs graphiques - + Battery graph Graphique de la batterie - + Don't use bold font for highlighting active items Ne pas mettre les lignes actives en gras - - Turnigy 9XR with m128 chip - Turnigy 9XR avec une puce m128 - - - - Turnigy 9XR - - - - - 9X with stock board - 9X avec carte mère d'origine - - - + Enable resetting values by pressing up and down at the same time Permet la remise à zéro des valeurs en pressant haut-bas en même temps, valeur min avec gauche/bas, valeur max avec haut/droite, inversion avec gauche/droite - - 9X with stock board and m128 chip - Carte 9x avec un chip m128 - - - + 9X with AR9X board 9X avec carte AR9X - + Jumper T12 / T12 Pro - + Jumper T16 / T16+ / T16 Pro - + 9X with Sky9x board 9X avec carte Sky9x - - - 9X with Gruvin9x board - 9X avec carte Gruvin9x - - - - DIY MEGA2560 radio - Radio DIY à base de MEGA2560 - FirmwarePreferencesDialog @@ -3471,103 +3432,103 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Transférer - + Open Firmware File Sélectionner le firmware - + %1 may not be a valid firmware file %1 ne semble pas être un firmware valide - + The firmware file is not valid. Le firmware est invalide. - + There is no start screen image in the firmware file. Aucun écran d'accueil trouvé dans le firmware. - + Profile image %1 is invalid. L'image du profil %1 est invalide. - + Open image file to use as radio start screen Ouvrir l'image à utiliser comme écran d'accueil - + Images (%1) Images (%1) - + Image could not be loaded from %1 L'image %1 n'a pu être chargée - + The library image could not be loaded L'image n'a pu être chargée depuis la bibliothèque - + Splash image not found Ecran d'accueil sélectionné introuvable - + Cannot save customized firmware Impossible d'enregistrer le firmware personnalisé - + Write Firmware to Radio Transférer le firmware à la radio - - + + Firmware check failed La vérification a échoué - + Could not check firmware from radio Impossible de vérifier le firmware de la radio - + New firmware is not compatible with the one currently installed! Le nouveau firmware ne correspond pas à la version installée, vérifiez le type de radio ! - + Conversion failed La conversion a échoué - + Cannot convert Models and Settings for use with this firmware, original data will be used Impossible de convertir les paramètres et modèles pour ce firmware, les données originales vont être utilisées - + Restore failed Echec de la restauration - + Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1 Impossible de restaurer les paramètres et modèles sur la radio. Le fichier peut être récupéré ici: %1 - + Flashing done Programmation terminée @@ -3580,42 +3541,42 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Exécutable %1 introuvable - + Writing... Ecriture... - + Reading... Lecture... - + Verifying... Vérification... - + unknown inconnu - + ie: OpenTX for 9X board or OpenTX for 9XR board ex: OpenTX pour carte d'origine 9x ou OpenTX pour 9XR - + ie: OpenTX for M128 / 9X board or OpenTX for 9XR board with M128 chip ex:. OpenTX pour carte d'origine 9x avec m128 ou OpenTX pour 9XR avec m128 - + ie: OpenTX for Gruvin9X board ex: OpenTX pour carte gruvin9x - + Your radio uses a %1 CPU!!! Please check advanced burn options to set the correct cpu type. @@ -3624,7 +3585,7 @@ Please check advanced burn options to set the correct cpu type. Vérifiez les options de programmateur et choisissez le bon type de processeur. - + Your radio uses a %1 CPU!!! Please select an appropriate firmware type to program it. @@ -3633,7 +3594,7 @@ Please select an appropriate firmware type to program it. Veuillez choisir le firmware approprié. - + You are currently using: %1 @@ -3642,22 +3603,22 @@ Vous utilisez actuellement: %1 - + Your radio does not seem connected to USB or the driver is not initialized!!!. Votre radio ne semble pas être connectée en USB, ou le pilote n'est pas initialisé !!!. - + Flashing done (exit code = %1) Terminé (code de sortie = %1) - + Flashing done with errors Terminé avec des erreurs - + FUSES: Low=%1 High=%2 Ext=%3 Fusibles: Bas=%1 Haut=%2 Ext=%3 @@ -4169,7 +4130,7 @@ These will be relevant for all models in the same EEPROM. Communs à tous les modèles d'une même EEPROM. - + Trainer Écolage @@ -4184,42 +4145,42 @@ Communs à tous les modèles d'une même EEPROM. Lire étalonnage et paramètres hw du profil - + Global Functions Fonctions globales - + Hardware Matériel - + Calibration - + Wrong data in profile, radio calibration was not retrieved Données du profil erronnées, l'étalonnage n'a pas été lu - + Wrong data in profile, Switch/pot config not retrieved Données incorecte, Interrupteurs/Pot non trouvés - + Wrong data in profile, hw related parameters were not retrieved Données du profil erronnées, les paramètres hw n'ont pas été lus - + Do you want to store calibration in %1 profile<br>overwriting existing calibration? Voulez-vous stocker l'étalonnage dans le profil %1<br> et écraser l'étalonnage existant ? - + Calibration and HW parameters saved. Etalonnage et paramètres hw enregistrés avec succès. @@ -4227,7 +4188,7 @@ Communs à tous les modèles d'une même EEPROM. GeneralSettings - + Radio Settings Paramètres de la radio @@ -4285,169 +4246,169 @@ Communs à tous les modèles d'une même EEPROM. - + Timeshift from UTC Décalage horaire (UTC) - + Voice Language Langue des voix - + Country Code Zone géographique RF - + Stick reverse Inversion des manches - + FAI Mode Mode FAI - + Adjust RTC Ajuster l'heure par GPS - + Vario pitch at max Tonalité du vario au max - - + + Hz - + Speaker Volume Volume haut-parleur - + Backlight Switch Inter de rétroéclairage - + Sound Mode Mode Son - + Color 1 Couleur 1 - + Color 2 Couleur 2 - + Speaker Pitch (spkr only) Tonalité (HP uniquement) - + If this value is not 0, any keypress will turn on the backlight and turn it off after the specified number of seconds. Si cette valeur est différente de 0, l'appui sur une touche de navigation déclenche le rétroéclairage qui s'éteint après la durée spécifiée (en secondes). - - - + + + sec - + Backlight color Couleur du rétroéclairage - + Beeper Bipeur - + Speaker Haut-parleur - + BeeperVoice Bipeur/Voix - + SpeakerVoice Haut-parleur/Voix - + Beep volume Volume des bips - + Wav volume Volume des fichiers audio - + Vario volume Volume du variomètre - + Background volume Volume de la musique d'ambiance - - + + ms - + Backlight Auto OFF after Arrêt rétroéclairage après - + Backlight flash on alarm Clignotement rétroécl. alarmes - + Vario pitch at zero Tonalité du vario à 0 - + Vario repeat at zero Répétition du vario à 0 - + This is the switch selectrion for turning on the backlight (if installed). Interrupteur déclenchant le rétroéclairage' (si installé). - - + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -4462,42 +4423,42 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Plage de valeurs: 20...45</span></p></body></html> - + Backlight Brightness Luminosité du rétroéclairage - + RotEnc Navigation Navigation sélecteur rotatif - + Automatically adjust the radio's clock if a GPS is connected to telemetry. Régle automatique l'heure de la radio si un GPS est présent. - + America Amérique - + Japan Japon - + Europe - + Backlight OFF Brightness Luminosité écran éteint - + Mode selection: Mode 1: @@ -4538,332 +4499,337 @@ Mode 4: - + Mode 1 (RUD ELE THR AIL) Mode 1 (DIR PRF GAZ AIL) - + Mode 2 (RUD THR ELE AIL) Mode 2 (DIR GAZ PRF AIL) - + Mode 3 (AIL ELE THR RUD) Mode 3 (AIL PRF GAZ DIR) - + Mode 4 (AIL THR ELE RUD) Mode 4 (AIL GAZ PRF DIR) - + <html><head/><body><p>Channel order</p><p><br/></p><p>Defines the order of the default mixes created on a new model.</p></body></html> <html><head/><body><p>Ordre des voies</p><p><br/></p><p>Détermine l'ordre des mixages par défaut sur un nouveau modèle.</p></body></html> - + If you enable FAI, only RSSI and RxBt sensors will keep working. This function cannot be disabled by the radio. Si vous activez l'option FAI, seuls les capteurs RSSI et BtRx vont fonctionner. Cette fonction ne peut pas être désactivée sur la radio. - + Owner Registration ID Enregistrement ID du propriétaire - + + Keys Backlight + Rétroéclairage des touches + + + R E T A D P G A - + R E A T D P A G - + R T E A D G P A - + R T A E D G A P - + R A E T D A P G - + R A T E D A G P - + E R T A P D G A - + E R A T P D A G - + E T R A P G D A - + E T A R P G A D - + E A R T P A D G - + E A T R P A G D - + T R E A G D P A - + T R A E G D A P - + T E R A G P D A - + T E A R G P A D - + T A R E G A D P - + T A E R G A P D - + A R E T A D P G - + A R T E A D G P - + A E R T A P D G - + A E T R A P G D - + A T R E A G D P - + A T E R A G P D - + Power Off Delay Délai mise hors tension - + USB Mode Mode USB - + Jack Mode Mode Jack - + Audio - + Trainer Écolage - - + + Ask on Connect Demander à la connexion - + Joystick (HID) - + USB Mass Storage Stockage USB (SD) - + USB Serial (CDC) Port série USB (Debug) - + Stick Mode Mode - + Metric Métrique - + Imperial Impérial - + Default Channel Order Ordre des voies par défaut - + GPS Coordinates Coordonnées GPS - + Min - - + + v - + Max - + RSSI Poweroff Warning Vérifier RSSI à l'extinction - + DMS - + Inactivity Timer Alerte d'inactivité - + Show Splash Screen on Startup Afficher l'écran de démarrage - + Contrast Contraste - + Battery Meter Range Plage de l'indicateur de batterie - + Haptic Strength Puissance vibreur - + LCD Display Type Type de LCD - + "No Sound" Warning Alerte "son coupé" - + Battery Warning Alerte batterie - + Haptic Length Durée vibreur - + MAVLink Baud Rate Baudrate Mavlink - - + + Quiet Mode silencieux - + Only Alarms Seulement les alarmes - - + + No Keys Touches silencieuses - - + + All Tous - + Battery warning voltage. This is the threashhold where the battery warning sounds. @@ -4874,126 +4840,126 @@ Seuil auquel se déclenche l'alarme de batterie. Plage de valeurs: 5v...10v - + Standard - + Optrex - + If not zero will sound beeps if the transmitter has been left without inputs for the specified number of minutes. Si différent de 0, émission d'un bip sonore régulier si aucune action n'a été effectuée sur l'émetteur depuis le temps spécifié (en minutes). Réinitialisation en agissant sur n'importe lequel des manches / touches de navigation. - + nnnnnnNN - + min - + Power On Delay Délai mise sous tension - - + + Show splash screen on startup Affiche l'écran d'accueil au démarrage - + --- - + 2s - + 3s - + 4s - + 6s - + 8s - + 10s - + 15s - + 4800 Baud - + 9600 Baud - + 14400 Baud - + 19200 Baud - + 38400 Baud - + 57600 Baud - + 76800 Baud - + 115200 Baud - - - + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -5018,72 +4984,72 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Alerte mode silencieux - Avertissement si les bips sont désactivés</p></body></html> - - + + X-Short Très court - - + + Short Court - - + + Normal - - + + Long - - + + X-Long Très long - + Low EEPROM Warning Avertissement EEPROM faible - + NMEA - + Play Delay (switch mid position) Délai position centrale inters - + Measurement Units Unités de mesure - + Haptic Mode Mode du vibreur - + Beeper Length Durée des bips - + Beeper Mode Paramètre des bips - + Beeper volume 0 - Quiet. No beeps at all. @@ -5100,7 +5066,7 @@ Long : bips plus longs. Extra long : bips extra longs. - + Alarms Only Seulement les Alarmes @@ -5108,127 +5074,127 @@ Extra long : bips extra longs. GeneralSetupPanel - + OFF - + Keys Touches - + Sticks Manches - + Keys + Sticks Touches + manches - + ON - + English Anglais - + Dutch Hollandais - + French Français - + Italian Italien - + German Allemand - + Czech Tchèque - + Slovak Slovaque - + Spanish Espagnol - + Polish Polonais - + Portuguese Portugais - + Swedish Suédois - + Hungarian Hongrois - + Russian Russe - + No Non - + RotEnc A Sel.Rot. A - + Rot Enc B Sel.Rot. B - + Rot Enc C Sel.Rot. C - + Rot Enc D Sel.Rot. D - + Rot Enc E Sel.Rot. E - + If you enable FAI, only RSSI and RxBt sensors will keep working. This function cannot be disabled by the radio. Are you sure ? @@ -6073,57 +6039,57 @@ Cette fonction ne peut pas être désactivée sur la radio. (infini) - + Delete Logical Switch. Are you sure? Supprimer l'Interrupteur Logique. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear all Logical Switches. Are you sure? Effacer tous les Interrupteurs Logiques. Êtes-vous sûr? @@ -6305,79 +6271,74 @@ Les colonnes contenant l'altitude "GAlt" et la vitesse "GSpd MainWindow - - + + Edit Édition - - + + File Fichier - + New release available Nouvelle version disponible - + Error opening file %1: %2. Erreur à l'ouverture du fichier %1: %2. - + Compilation server too busy, try later Serveur de compilation surchargé, veuillez réessayer plus tard - + Unknown server failure, try later Erreur inconnue du serveur de compilation, veuillez réessayer plus tard - + Do you want to write the firmware to the radio now ? Voulez vous transférer le firmware à la radio maintenant ? - - + + Yes Oui - - + + No Non - - + + Release Notes Notes de version - + Compare models Comparer les modèles - - Show fuses dialog - Afficher les paramètres des fusibles - - - + A monochrome black icon theme Thème avec icônes monochromes noires - + A new version of %1 firmware is available: - current is %2 - newer is %3 @@ -6394,511 +6355,501 @@ Voulez vous la télécharger maintenant ? Nous recommandons de lire attentivement les notes de version à l'aide du bouton ci-dessous, celles-ci peuvent contenir des infos importantes concernant les changements effectués depuis la version précédente. - - + + Synchronize SD Synchroniser la carte SD - + New Nouveau - + Open... Ouvrir... - + A monochrome white icon theme Thème avec icônes monochromes blanches - + A monochrome blue icon theme Thème avec icônes monochromes bleues - + Small Petites - + Use small toolbar icons Petites icônes de barre d'outils - + Use normal size toolbar icons Icônes de barre d'outils normales - + Normal Normales - + Use big toolbar icons Grosses icônes de barre d'outils - + Big Grandes - + Use huge toolbar icons Immenses icônes de barre d'outils - + Huge Immenses - - List programmers... - Lister les programmateurs... - - - - Fuses... - Fusibles... - - - - + + Read Firmware from Radio Lire le firmware de la radio - + Read firmware from Radio Lire le firmware de la radio - + Write Firmware to Radio Transférer le firmware à la radio - + Write firmware to Radio Transférer le firmware à la radio - + Add Radio Profile Ajouter un profil de radio - + Manuals and other Documents Manuels et documents - + Open the OpenTX document page in a web browser Ouvre la page de documentation d'OpenTX dans un navigateur - + Write Models and Settings To Radio Transférer les paramètres et modèles vers la radio - + Write Models and Settings to Radio Transférer les paramètres et modèles vers la radio - - + + Read Models and Settings from Radio Lire les paramètres et modèles depuis la radio - + OpenTX Companion %1 - Radio: %2 - Profile: %3 OpenTX Companion %1 - Radio: %2 - Profil: %3 - + List of recently used files Liste des fichiers récemment utilisés - + Radio Profiles Profil radio - + Create or Select Radio Profiles Créer ou Sélectionner un profil radio - + Configure Communications... Configurer les communications... - + Configure software for communicating with the Radio Configurer le programme gérant les communications avec la radio - + Write Backup to Radio Transférer une sauvegarde à la radio - + Write Backup from file to Radio Transférer une sauvegarde à la radio - + Backup Radio to File Sauvegarder la radio - + Save a complete backup file of all settings and model data in the Radio Créer une copie de sauvegarde de tous les paramètres et modèles de la radio - + Contributors... Contributeurs... - + A tribute to those who have contributed to OpenTX and Companion Remerciements à tout ceux qui ont contribué à OpenTX et Companion - + SD card synchronization Synchronisation de la carte SD - + Recent Files Fichiers récents - + Set Icon Theme Choisir le thème d'icônes - + Set Icon Size Choisir la taille des icônes - + Write Ecrire - + A new version of Companion is available (version %1)<br>Would you like to download it? Une nouvelle version de Companion est disponible (version %1)<br>Voulez-vous la télécharger ? - + OpenTX Home Page: <a href='%1'>%1</a> Page d'accueil d'OpenTX: <a href='%1'>%1</a> - + If you've found this program useful, please support by <a href='%1'>donating</a> Si vous trouvez ce programme utile, merci de le supporter par une <a href='%1'>donation</a> - + Ready Prêt - + Show the application's About box Afficher la fenêtre "A propos" de l'application - - + + File loaded Fichier chargé - - + + Open Models and Settings file Ouvrir un fichier de paramètres et modèles - - + + File saved Fichier sauvegardé - + Read Models and Settings From Radio Lire les paramètres et modèles depuis la radio - + Not enough flash available on this board for all the selected options Pas assez de place disponible pour les options sélectionnées - + Compilation server temporary failure, try later Erreur de compilation sur le serveur, veuillez réessayer plus tard - + Compilation error Erreur de compilation - + Invalid firmware Firmware invalide - + Invalid board Carte non valide - + Invalid language Language non valide - + Some text will not be translated until the next time you start Companion. Please note that some translations may not be complete. Certains textes ne seront pas traduits avant le prochain redémarrage de Companion. Veuillez noter que certaines traductions peuvent ne pas être complètes. - + Models and Settings read Lecture des modèles et paramètres - + Companion update check failed, new version information not found. La vérification de mise à jour de Companion a échoué, les infos sur une nouvelle version sont introuvables. - + Firmware update check failed, new version information not found or invalid. La vérification de mise à jour du Firmware a échoué, les infos sur une nouvelle version sont introuvables ou invalides. - + Local Folder Dossier "Local" - + Radio Folder Dossier "Radio" - - + + This function is not yet implemented Cette fonction n'est pas implémentée pour l'instant - + Save Radio Backup to File Sauvegarder la radio - + Read Radio Firmware to File Sauvegarder le firmware de la radio - + The OpenTX Companion project was originally forked from <a href='%1'>eePe</a> Le projet OpenTX Companion a été créé à partir d'<a href='%1'>eePe</a> - + Copyright OpenTX Team - + Create a new Models and Settings file Créer un nouveau fichier de paramètres et modèles - - + + Save Models and Settings file Enregistrer le fichier de paramètres et modèles - + Save Enregistrer - + Save As... Enregistrer sous... - + Close Fermer - + Close Models and Settings file Fermer le fichier de paramètres et modèles - + Exit the application Quitter l'application - + Exit Quitter - + Create a new Radio Settings Profile Créer un nouveau profil de paramètres et modèles - + Copy Current Radio Profile Copier le profil radio actuel - + Duplicate current Radio Settings Profile Dupliquer les paramètres radio du profil actuel - + Delete the current Radio Settings Profile Supprimer les paramètres radio du profil actuel - + Use default system language. Utiliser le langage système par défaut. - + Use %1 language (some translations may not be complete). Utiliser la langue %1 (certaines traductions peuvent être incomplètes). - + Classical Classique - + Diskimage (*.dmg) Image disque (*.dmg) - + Would you like to open the disk image to install the new version? Voulez-vous ouvrir l'image disque afin d'installer la nouvelle version ? - + Executable (*.exe) Exécutable (*.exe) - - + + Would you like to launch the installer? Voulez-vous lancer l'installateur ? - + No Companion release candidates are currently being served for this version, please switch release channel Aucune version "candidate" de Companion n'est actuellement disponible sur ce serveur, veuillez commuter sur le canal "officiel" - + No nightly Companion builds are currently being served for this version, please switch release channel Aucune version "nightly" de Companion n'est actuellement disponible sur ce serveur, veuillez commuter sur le canal "officiel" - + No Companion release builds are currently being served for this version, please switch release channel Aucune version "officielle" de Companion n'est actuellement disponible sur ce serveur, veuillez commuter sur le canal "candidate" ou "nightly" - + A new release of Companion is available, please check the <a href='%1'>OpenTX website!</a> Une nouvelle version de Companion est disponible, vérifiez le site OpenTX <a href='%1'> !</a> - + No firmware release candidates are currently being served for this version, please switch release channel Aucun firmware "candidate" n'est actuellement disponible sur ce serveur, veuillez commuter sur le canal "officiel" - + No firmware nightly builds are currently being served for this version, please switch release channel Aucun firmware "nightly" n'est actuellement disponible sur ce serveur, veuillez commuter sur le canal "officiel" - + No firmware release builds are currently being served for this version, please switch release channel Aucun firmware "officiel" n'est actuellement disponible sur ce serveur, veuillez commuter sur le canal "candidate" ou "nightly" - + Release candidate builds are now available for this version, would you like to switch to using them? Une version "candidate" est maintenant disponible pour cette version, voulez-vous commuter de canal afin de l'utiliser? - + Channel changed to RC, please restart the download process Canal commuté sur "candidate", veuillez redémarrer le processus de téléchargement - + Official release builds are now available for this version, would you like to switch to using them? Une version "officielle" est maintenant disponible pour cette version, voulez-vous commuter de canal afin de l'utiliser? - + Channel changed to Release, please restart the download process Canal commuté sur "officiel", veuillez redémarrer le processus de téléchargement - + This radio (%1) is not currently available in this firmware release channel Le firmware pour cette radio (%1) n'est actuellement pas disponible dans le canal "officiel" - + Firmware %1 does not seem to have ever been downloaded. Version %2 is available. Do you want to download it now? @@ -6911,18 +6862,18 @@ Voulez-vous le télécharger maintenant? Nous vous recommandons de consulter les notes de mise à jour à l'aide du bouton ci-dessous pour en savoir plus sur les modifications susceptibles de vous intéresser. - - + + Do you want to download version %1 now ? Voulez-vous télécharger la version %1 maintenant? - + Ignore this version %1? Ignorer cette version %1? - + There are unsaved file changes which you may lose when switching radio types. Do you wish to continue? @@ -6931,355 +6882,350 @@ Do you wish to continue? Êtes-vous sûr de vouloir continuer ? - + No local SD structure path configured! Aucun chemin de configuré pour la structure SD en local ! - + No Radio or SD card detected! Pas de Radio ou carte SD de détectée ! - + Release notes... Notes de version... - + Show release notes Afficher les notes de version - + Delete Current Radio Profile... Supprimer le profil radio actuel... - + Export Application Settings.. Exporter Préférences Application... - + Save all the current %1 and Simulator settings (including radio profiles) to a file. Enregistrez tous les paramètres actuels %1 et du simulateur (y compris les profils radio) dans un fichier. - + Import Application Settings.. Importer les paramètres de l'application... - + Load %1 and Simulator settings from a prevously exported settings file. Charger les paramètres %1 et les paramètres de Simulation depuis un fichier précédemment exporté. - + Tabbed Windows Fenêtres à onglets - + Use tabs to arrange open windows. Utilisez les onglets pour organiser les fenêtres ouvertes. - + Tile Windows Titre de la fenêtre - + Arrange open windows across all the available space. Disposez les fenêtres ouvertes sur tout l'espace disponible. - + Cascade Windows Fenêtres en cascade - + Arrange all open windows in a stack. Organisez toutes les fenêtres ouvertes dans une pile. - + Close All Windows Fermer toutes les fenêtres - + Closes all open files (prompts to save if necessary. Fermer tous les fichiers ouverts (proposer de sauvegarder si nécessaire). - + Window Fenêtre - + Ctrl+Shift+S - + Ctrl+Alt+L - + Ctrl+Alt+D - + Ctrl+Alt+R - + The classic companion9x icon theme Thème d'icônes classique de Companion - + Yerico - + Yellow round honey sweet icon theme Thème d'icônes rondes jaunes - + Monochrome - + MonoWhite Monochrome Blanc - + Alt+%1 - + - Copy - Copier - + Companion :: Open files warning Companion::Ouvrir les fichiers d'avertissement - + Please save or close modified file(s) before deleting the active profile. Veuillez sauvegarder ou fermer le(s) fichier(s) modifié(s) avant de supprimer le profil actif. - + Not possible to remove profile Impossible de supprimer le profil - + The default profile can not be removed. Le profil par défaut ne peut pas être supprimé. - + Confirm Delete Profile Confirmer la suppression du profil - + Are you sure you wish to delete the "%1" radio profile? There is no way to undo this action! Etes-vous sûr de vouloir supprimer le profil radio %1? Cette action est irréversible! - + Please save or close all modified files before importing settings Veuillez sauvegarder ou fermer tous les fichiers modifiés avant d'importer les paramètres - + <html><p>%1 and Simulator settings can be imported (restored) from a previosly saved export (backup) file. This will replace current settings with any settings found in the file.</p><p>An automatic backup of the current settings will be attempted. But if the current settings are useful then it is recommended that you make a manual backup first.</p><p>For best results when importing settings, <b>close any other %1 windows you may have open, and make sure the standalone Simulator application is not running.</p><p>Do you wish to continue?</p></html> <html><p>%1 et les paramètres du simulateur peuvent être importés (restaurés) à partir d’un fichier d’exportation (sauvegarde) préalablement sauvegardé. Ceci remplacera les paramètres actuels par tous les paramètres trouvés dans le fichier.</p><p>Une sauvegarde automatique des paramètres actuels sera tentée. Mais si les paramètres actuels sont utiles, il est recommandé de commencer par une sauvegarde manuelle.</p><p>Pour obtenir de meilleurs résultats lors de l'importation des paramètres,<b> fermez les autres fenêtres %1 que vous avez éventuellement ouvertes et assurez-vous que l'application autonome Simulator n'est pas en cours d'exécution. </p><p>Souhaitez-vous continuer? </p></html> - + Confirm Settings Import Confirmer les paramètres d'importation - + Select %1: Sélectionnez %1: - + backup sauvegarde - + Press the 'Ignore' button to continue anyway. Appuyez sur le bouton 'Ignorer' pour continuer quand même. - + The settings could not be imported. Les paramètres n'ont pas pu être importés. - + <html><p>New settings have been imported from:<br> %1.</p><p>%2 will now re-initialize.</p><p>Note that you may need to close and restart %2 before some settings like language and icon theme take effect.</p> <html><p>Les nouveaux paramètres ont été importés depuis:<br> %1. </p><p>%2 sera maintenant réinitialisé.</p><p>Notez que vous devrez peut-être fermer et redémarrer %2 avant que certains paramètres tels que la langue et le thème d’icône prennent effet.</p> - + <p>The previous settings were backed up to:<br> %1</p> <p>Les paramètres précédents ont été sauvegardés sous:<br> %1 </p> - + MonoBlue Monochrome Bleu - + System language Langue du système - + About... A propos... - + View Log File... Ouvrir un Log... - + Open and view log file Ouvrir et visualiser un log - + Settings... Préférences... - + Edit Settings Éditer les préférences - + Download... Télécharger... - + Download firmware and voice files Télécharger firmware et fichiers son - + Check for Updates... Vérifier les mises à jour... - + Check OpenTX and Companion updates Vérifier les mises à jour d'OpenTX et Companion - + Compare Models... Comparer 2 modèles... - + Edit Radio Splash Image... Éditer l'écran d'accueil de la radio... - + Edit the splash image of your Radio Éditer l'écran d'accueil de la radio - - List available programmers - Lister les programmateurs supportés - - - - + + Save As Enregistrer Sous - - + + No updates available at this time. Aucune mise à jour disponible. - - + + Help Aide - + Checking for updates Recherche de mises à jour - + The new theme will be loaded the next time you start Companion. Le nouveau thème sera utilisé lors du prochain lancement de Companion. - + About Companion A propos de Companion - + Set Menu Language Choisir la langue des menus - + Settings Paramètres - + Read/Write Transfert - + %2 - + New Radio Nouvelle radio @@ -8068,12 +8014,12 @@ Mixage actif par défaut si non-renseigné. ModelData - + Model: Modèle: - + Throttle Source Source des gaz @@ -8174,27 +8120,27 @@ Mixage actif par défaut si non-renseigné. Inconnu - + Slave/Jack Elève/Jack - + Master/SBUS Module Maître/SBUS module - + Master/CPPM Module Maître/CPPM Module - + Master/SBUS in battery compartment Maître/SBUS compartiment batterie - + Master/Jack Maître/Jack @@ -8235,9 +8181,9 @@ Mixage actif par défaut si non-renseigné. - - - + + + OFF @@ -8248,7 +8194,7 @@ Mixage actif par défaut si non-renseigné. - + Mode @@ -8282,14 +8228,14 @@ Mixage actif par défaut si non-renseigné. - - + + Delay Délai - + Receiver Récepteur @@ -8315,581 +8261,592 @@ Mixage actif par défaut si non-renseigné. + RF Output Power Puissance RF - + + Output Type + Type de sortie + + + + RX Output Frequency + Fréquence de sortie du RX + + + 90 - + 120 - + 120X - + 140 - + MULT! - - + + Offset Décalage - + Slow Ralenti - + Warn Attn - + Flight modes Phases de vol - + Flight mode Phase de vol - + All Tous - + Edge Flanc - + Sticky Bistable - + Timer Chrono - + missing absent - + Duration Durée - + Extended Limits Limites étendues - + Display Checklist Afficher la checklist - + Global Functions Fonctions globales - + Manual Manuel - + Auto - + Failsafe Mode Mode Failsafe - - + + Hold Maintien - + No Pulse Pas d'impulsion - + Not set Non défini - + No pulses Pas d'impulsions - + Silent Aucun - + Beeps Bips - + Voice Voix - + Haptic Vibreur - + Flight Vol - + Manual reset RAZ manuelle - + Step Pas - + Display Affichage - + Extended Étendu - + Never Jamais - + On Change Sur changement - + Always Toujours - - - + + + Source - + Trim idle only Trim ralenti uniquement - + Warning Avertissement - + Reversed Inversé - + Tmr Chrono - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Alti Alt - + Alti+ Alt+ - + VSpeed Vitesse verticale - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells Velm - + Calculated Calculé - + Add Addition - + Average Moyenne - - + + Min - - + + Max - + Multiply Multiplication - + Totalise - + Cell Element LiPo - + Consumption Consommation - + Distance - + Lowest Min - + Cell %1 Elém. %1 - + Highest Max - + Delta - + Formula Formule - - + + Id - + Instance - - - - + + + + Sensor Capteur - - + + Sources - - + + GPS - + Alt. - - + + Blades Pales - + Multi. - + F - + Inst - + Alt Altitude - + Unit Unité - + Prec - + Ratio - + Multi - + A/Offset - + Filter Filtre - + Persist - + Positive - + Log - + Numbers Chiffres - + Bars Barres - + Script - + Filename Nom de fichier - + Error: Unable to open or read file! Erreur: Impossible d'ouvrir ou de lire le fichier! - + Persistent Persistant - + Off - - - - - - + + + + + + None Aucun - + Name Nom - + Countdown Compte à rebours - + Minute call Annonces minutes - - - + + + FM%1 PV%1 - + FM%1%2 PV%1%2 - + FM%1+%2 PV%1+%2 - - + + Weight Ratio - - + + Switch Inter - - + + NoTrim Pas de trim - + No DR/Expo Pas d'expo/DR - + Offset(%1) Décalage(%1) @@ -8904,24 +8861,24 @@ Mixage actif par défaut si non-renseigné. - + Disabled in all flight modes Désactivé pour toutes les phases de vol - + instant immédiat - - - + + + Custom Prédéfini - + Standard @@ -8957,7 +8914,7 @@ Mixage actif par défaut si non-renseigné. Module - + Failsafe Mode Mode Failsafe @@ -8967,241 +8924,251 @@ Mixage actif par défaut si non-renseigné. 1ère voie - + PPM delay Impulsion - + Negative Négative - + Positive - + Polarity Polarité - + Trainer Mode Mode écolage - + PPM Frame Length Longueur de trame PPM - + CH VOIE - + Antenna Antenne - + Option value - + us µs - + Master/Jack Maître/Jack - + Slave/Jack Elève/Jack - + Master/SBUS Module Maître/SBUS module - + Master/CPPM Module Maître/CPPM Module - + Master/SBUS in battery compartment Maître/SBUS dans compartiment batterie - + RF Output Power Puissance RF - + Sub Type Sous-Type - + Master/Bluetooth Maître/Bluetooth - + Slave/Bluetooth Elève/Bluetooth - + Master/Multi Maître/Multi - + Disable Telemetry Désactiver la télémétrie - + Disable Ch. Map Désactiver "Mapping" des VOIES - + Show values in: Afficher les valeurs en: - + % abbreviation for percent - + μs abbreviation for microseconds - + ms - + Receiver 1 Récepteur 1 - - - + + + X - + Receiver 2 Récepteur 2 - + Receiver 3 Récepteur 3 - + WARNING: changing RF Output Power needs RE-BIND ATTENTION: RE-BIND nécessaire pour tout changement de puissance RF - + Channels Nb de voies - + + RX Frequency + Fréquence RX + + + + Hz + + + + Not set Non défini - + Hold Maintien - + Custom Prédéfini - + No Pulses Pas d'impulsions - + Receiver Récepteur - + WARNING: Requires non-certified firmware! ATTENTION: Nécessite un firmware non certifié! - + Registration ID ID d'enregistrement - + Failsafe Positions Positions Failsafe - + Protocol Protocole - + Multi Radio Protocol Protocole Multi - + Receiver No. Récepteur No. - + Output type Type de sortie - + Open Drain - + Push Pull - + Low Power Puissance Réduite @@ -9209,12 +9176,12 @@ Mixage actif par défaut si non-renseigné. ModuleData - + Positive - + Negative Négative @@ -9244,98 +9211,118 @@ Mixage actif par défaut si non-renseigné. 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 + + ModulePanel - + Value Valeur - + Hold Maintien - + No Pulse Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - - Autodetect Format - Format Détection auto - - - + Bind on channel Bind avec VOIE @@ -9425,11 +9412,6 @@ Mixage actif par défaut si non-renseigné. Telemetry Télémétrie - - - Offset - Décalage - @@ -9480,7 +9462,7 @@ Mixage actif par défaut si non-renseigné. - + Name Nom @@ -9551,7 +9533,7 @@ Mixage actif par défaut si non-renseigné. - + Type @@ -9626,165 +9608,118 @@ Mixage actif par défaut si non-renseigné. Fonctions spéciales - + Protocol Protocole - + Low Faible - + Critical Critique - + Telemetry audio Audio télémétrie - + Altimetry Altimétrie - - + + Vario source Source vario - + Vario limits > Limites vario > - + Sink max Chute max - + Sink min Chute min - + Climb min Pompe min - + Climb max Pompe max - + Center silent Silence centrer - + Top Bar Barre de titre de l'écran d'accueil - - + Volts source Source de la tension - + Altitude source Source de l'altitude - - Various - Divers - - - - Serial protocol - Protocole série - - - - FAS offset - Décalage FAS - - - - mAh count - Compteur mAh - - - - Persistent mAh - Mémorisation des mAh - - - - Current source - Source de mesure de courant - - - - Blades - Pales - - - + Parameters Paramètres - + Telemetry Sensors Capteurs Télémétrie - + Telemetry Screens Écrans Télémétrie - + GF%1 FG%1 - + Global Functions Fonctions globales - + Checklist - - - Analogs - Analogiques - - Unit Unité - - Scale - Echelle - - - + RSSI Alarms Alarmes RSSI @@ -9808,13 +9743,13 @@ Mixage actif par défaut si non-renseigné. - Telemetry - Télémetrie + RF power + Puissance HF - Radio output power - Puissance de sortie + Telemetry + Télémetrie @@ -9863,22 +9798,22 @@ Mixage actif par défaut si non-renseigné. OpenTxEepromInterface - + Unknown error Erreur inconnue - + ... plus %1 errors ... plus %1 erreurs - + Cannot write radio settings Impossible d'écrire les paramètres de la radio - + Cannot write model %1 Impossible d'écrire le modèle %1 @@ -10145,25 +10080,20 @@ Mixage actif par défaut si non-renseigné. RadioInterface - + Cannot write file %1: %2. Impossible d'écrire le fichier %1: %2. - - <b><u>WARNING!</u></b><br>This will reset the fuses of %1 to the factory settings.<br>Writing fuses can mess up your radio.<br>Do this only if you are sure they are wrong!<br>Are you sure you want to continue? - <b><u>AVERTISSEMENT !</u></b><br>Ceci réinitialisera les fusibles %1 à leur valeur par défaut.<br>Modifier les fusibles peut entraîner le blocage de votre émetteur.<br>Ne l'effectuer que si vous êtes certain qu'ils sont incorrects !<br>Etes-vous sûr de vouloir continuer ? - - - - + + Could not delete temporary file: %1 Impossible d'effacer le fichier temporaire %1 - + Unable to find radio SD card! Impossible de trouver la carte SD de la radio! @@ -10303,439 +10233,131 @@ E RawSource - - - - + V - - - - + + s - - ft - - - - - - m - - - - - °C - - - - - ° - - - - - % - - - - - - mph - - - - - - km/h - - - - - m/s - - - - - A - - - - - mAh - - - - - W - - - - - g - - - - + TrmR TrmD - + TrmE TrmP - + TrmT TrmG - + TrmA TrmA - + Trm5 - + Trm6 - + TrmH - + TrmV - - + Batt - - + Time Temps - - + Timer1 Chrono 1 - - + Timer2 Chrono 2 - - + Timer3 Chrono 3 - - RSSI Tx - - - - - RSSI Rx - - - - - RAS - - - - - A1 - - - - - A2 - - - - - A3 - - - - - A4 - - - - - Alt - Altitude - - - - Rpm - - - - - Fuel - Carburant - - - - T1 - - - - - T2 - - - - - Speed - Vitesse - - - - Dist - Distance - - - - GPS Alt - Altitude GPS - - - - Cell - Element LiPo - - - - Cells - Velm - - - - Vfas - Vfas - - - - Curr - Courant - - - - Cnsp - Consommation - - - - Powr - Puissance - - - - AccX - - - - - AccY - - - - - AccZ - - - - - Hdg - Cap - - - - VSpd - Vitesse Verticale - - - - AirSpeed - Vitesse Air - - - - dTE - - - - - A1- - - - - - A2- - - - - - A3- - - - - - A4- - - - - - Alt- - - - - - Alt+ - - - - - Rpm+ - - - - - T1+ - - - - - T2+ - - - - - Speed+ - Vit+ - - - - Dist+ - - - - - AirSpeed+ - VitA+ - - - - Cell- - Elem- - - - - Cells- - Velm- - - - - Vfas- - - - - - Curr+ - Cour+ - - - - Powr+ - Puis+ - - - - ACC - - - - - GPS Time - Heure GPS - - - + REa - + REb - - + + ??? - + ---- - + I as in Input E - + LUA%1%2 - + MAX - + CYC%1 - + TR as in Trainer - + SRC @@ -11286,87 +10908,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 - + THR GAZ - + 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? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? @@ -12090,62 +11712,62 @@ La valeur par défaut est configurée dans le profil radio sélectionné.Simulateur Radio (%1) - + Could not determine startup data source. Impossible de déterminer les données source. - + Could not load data, possibly wrong format. Impossible de charger les données, mauvais format. - + Data Load Error Erreur de lecture des données - + Invalid startup data provided. Plese specify a proper file/path. Données de démarrage fournies non valides. Veuillez spécifier un fichier/chemin approprié. - + Simulator Startup Error Erreur de démarrage du simulateur - + Error saving data: could open file for writing: '%1' Erreur de sauvegarde des données: impossible d'écrire le fichier '%1' - + Error saving data: could not get data from simulator interface. Erreur de sauvegarde des données: impossible de récupérer les données depuis l'interface de simulation. - + An unexpected error occurred while attempting to save radio data to file '%1'. Une erreur inattendue s'est produite lors de l'enregistrement des données radio dans le fichier '%1'. - + Data Save Error Erreur de sauvegarde des données - + Cannot open joystick, joystick disabled Impossible d'accéder au Joystick, Joystick désactivé - + Radio firmware error: %1 Erreur firmware radio: %1 - + - Flight Mode %1 (#%2) - Phase de Vol %1 (#%2) @@ -12791,11 +12413,6 @@ Trop d'erreurs, abandon. mAmps (mA) Courant (mA) - - - Range - Plage - TelemetryCustomScreen @@ -12805,22 +12422,22 @@ Trop d'erreurs, abandon. Type d'écran personnalisé - + None Aucun - + Numbers Chiffres - + Bars Barres - + Script @@ -12848,104 +12465,104 @@ Trop d'erreurs, abandon. TelemetryPanel - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Telemetry screen %1 Ecran de télémesure %1 - + Source - + Low Alarm Alarme basse - + Critical Alarm Alarme critique - + Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (non supporté) - + Alti Alt - + Alti+ Alt+ - + VSpeed Vitesse verticale - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells Velm - + --- @@ -13243,67 +12860,67 @@ Trop d'erreurs, abandon. TelemetrySensorPanel - + TELE%1 - + Popup menu available Menu contextuel disponible - + Lowest Min - + Cell %1 Elém. %1 - + Highest Max - + Delta - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Clear All Effacer Tout - + Clear Telemetry Sensor. Are you sure? Effacer le capteur de télémétrie. Êtes-vous sûr? - + Clear all Telemetry Sensors. Are you sure? Effacez tous les capteurs de télémétrie. Êtes-vous sûr? @@ -13695,6 +13312,12 @@ hh:mm:ss Timer %1 Chrono %1 + + + TMR + as in Timer + + TimerEdit @@ -13724,37 +13347,37 @@ Utilisez CTRL + le défilement de la souris ou les touches PAGE HAUT/PAGE BAS po TimerPanel - + Silent Aucun - + Beeps Bips - + Voice Voix - + Haptic Vibreur - + Not persistent Non persistant - + Persistent (flight) Persistant (vol) - + Persistent (manual reset) Persistant (RAZ manuelle) @@ -14309,128 +13932,68 @@ Extra long : bips extra longs. burnConfigDialog - - + Port - - List all available programmers. - Lister tous les programmateurs supportés. - - - - - - + + + The location of the AVRDUDE executable. The location of the AVRDUDE.EXE executable. Chemin d'accès de l'exécutable AVRDUDE. - + DFU-Util Location Chemin de DFU-UTIL - - - + + Use this button to browse and look for the AVRDUDE executable file. Utiliser ce bouton pour parcourir à la recherche du fichier exécutable AVRDUDE.EXE. - - Programmer used for communicating with the controller. -Please consult the programmer's documentation and the AVRDUDE documentation to select the appropriate programmer. - Programmateur utilisé pour la communication avec le contrôleur. -Consulter la documentation du programmateur utilisé et celle d'AVRDUDE pour effectuer le choix approprié. - - - - Communication port to the programmer. - - Port de communication pour le programmateur. - - - - - MCU - Processeur - - - + at91sam3s8-9xr - + Alternate device Dispositif alternatif - + Use advanced controls Utiliser les contrôles avancés - - Extra Arguments - Paramètres supplémentaires - - - - Show Help - Aide - - - - - + + Browse... Parcourir... - + DFU-UTIL Configuration Configuration de DFU-UTIL - + SAM-BA Configuration Configuration de SAM-BA - - - + + Select Location Choisir l'emplacement - - List available programmers - Lister les programmateurs supportés - - - - Avrdude help - Aide d'AVRDUDE - - - - <b><u>WARNING!</u></b><br>Normally CPU type is automatically selected according to the chosen firmware.<br>If you change the CPU type the resulting eeprom could be inconsistent. - <b><u>ATTENTION !</u></b><br>Le type de processeur est normalement choisi automatiquement pour le firmware sélectionné.<br>Si vous le changez, l'EEPROM peut être corrompue lors du transfert. - - - - List Available - Affiche Liste - - - - + Extra arguments used in AVRDUDE. This can be used for providing extra information to AVRDUDE. @@ -14440,16 +14003,6 @@ Permet de fournir des informations supplémentaires à AVRDUDE. A n'utiliser qu'en connaissance de cause: il n'y a pas de contrôle d'erreur et votre programmateur pourrait être endommagé. - - - AVRDUDE Location - Emplacement d'AVRDUDE - - - - Show AVRDUDE help - Affiche l'aide d'AVRDUDE - Programmer Configuration @@ -14457,58 +14010,40 @@ A n'utiliser qu'en connaissance de cause: il n'y a pas de contrô Configuration du programmateur - - + Extra arguments that will be passed to AVRDUDE on every call Paramètres complémentaires passés à chaque appel d'AVRDUDE - - AVRDUDE Configuration - Configuration d'AVRDUDE - - - - Location of AVRDUDE executable - Chemin d'accès de l'exécutable AVRDUDE - - - - Programmer - Programmateur - - - - + CPU of your TX CPU de l'émetteur - - + CPU present on your 9x radio Should be m64 for stock radios m2560 for v4.1 boards CPU de l'émetteur. Devrait être m64 pour les cartes d'origine, m2560 pour les cartes Gruvin9x v4.x - + SAM-BA Location Emplacement de SAM-BA - - + + Location of sam-ba executable Emplacement de l'exécutable SAM-BA - + ARM MCU Processeur ARM - + sam-ba serial port Port série de SAM-BA From e084d7b3fda4f1389749e4cee4bfe6423fe40174 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 14 Aug 2020 22:05:19 +0200 Subject: [PATCH 137/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c20ce0a12..618a2c47e 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1870,3 +1870,4 @@ Jose Luis Franco Torres Bart Sillen Antonio Moreno Carolina Carcione +Lonnie Misner From a91a94fb4edb1308a0fbc539bf07b6902e9ab3cb Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 15 Aug 2020 11:42:53 +0200 Subject: [PATCH 138/267] Fix some colors on value widget (#7875) --- radio/src/gui/480x272/widgets/value.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/widgets/value.cpp b/radio/src/gui/480x272/widgets/value.cpp index c7fe6ce99..9f14f17e4 100644 --- a/radio/src/gui/480x272/widgets/value.cpp +++ b/radio/src/gui/480x272/widgets/value.cpp @@ -97,7 +97,7 @@ void ValueWidget::refresh() if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER) { TimerState & timerState = timersStates[field-MIXSRC_FIRST_TIMER]; if (timerState.val < 0) { - lcdSetColor(ALARM_COLOR_INDEX); + lcdSetColor(lcdColorTable[ALARM_COLOR_INDEX]); } drawSource(x+NUMBERS_PADDING, y+2, field, CUSTOM_COLOR); drawSource(x+NUMBERS_PADDING + 1, y + 3, field, BLACK); @@ -108,7 +108,7 @@ void ValueWidget::refresh() if (field >= MIXSRC_FIRST_TELEM) { TelemetryItem & telemetryItem = telemetryItems[(field-MIXSRC_FIRST_TELEM)/3]; // TODO macro to convert a source to a telemetry index if (!telemetryItem.isAvailable() || telemetryItem.isOld()) { - lcdSetColor(ALARM_COLOR_INDEX); + lcdSetColor(lcdColorTable[TEXT_DISABLE_COLOR_INDEX]); } } From d349bf6faab9be60f204fc421553a5d2934bf176 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 15 Aug 2020 18:33:20 +0200 Subject: [PATCH 139/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 618a2c47e..6c389f7ce 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1871,3 +1871,4 @@ Bart Sillen Antonio Moreno Carolina Carcione Lonnie Misner +John Eppling From 0f9c9a8725c260df7ecc58bd481477c8ecc54071 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 16 Aug 2020 17:20:06 +0200 Subject: [PATCH 140/267] Do not display too many horintals on x7 screen This fixes #7854 --- radio/src/gui/128x64/widgets.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/gui/128x64/widgets.cpp b/radio/src/gui/128x64/widgets.cpp index 36c31d3fd..505ba057f 100644 --- a/radio/src/gui/128x64/widgets.cpp +++ b/radio/src/gui/128x64/widgets.cpp @@ -67,7 +67,11 @@ void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uin void drawGauge(coord_t x, coord_t y, coord_t w, coord_t h, int32_t val, int32_t max) { +#if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines lcdDrawRect(x, y, w+1, h); +#else + h++; +#endif lcdDrawFilledRect(x+1, y+1, w-1, 4, SOLID, ERASE); coord_t len = limit((uint8_t)1, uint8_t((abs(val) * w/2 + max/2) / max), uint8_t(w/2)); coord_t x0 = (val>0) ? x+w/2 : x+1+w/2-len; From f875ed551db3dd103bda258ace3d223de4be6dd5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 17 Aug 2020 08:00:54 +0200 Subject: [PATCH 141/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 6c389f7ce..d5817c12c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1872,3 +1872,4 @@ Antonio Moreno Carolina Carcione Lonnie Misner John Eppling +Phillip Elliott From 13d4ec1eb048b022b9c0e7d2e3695a63ebd2c980 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 17 Aug 2020 09:12:27 +0200 Subject: [PATCH 142/267] Always display 400k/115k baurate choice on all colorlcd radio --- radio/src/gui/480x272/radio_hardware.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp index ff6d36ade..6b395290e 100644 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ b/radio/src/gui/480x272/radio_hardware.cpp @@ -126,7 +126,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #define EXTERNAL_ANTENNA_ROW #endif -#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000 +#if defined(CROSSFIRE) #define MAX_BAUDRATE_ROW 0 #else #define MAX_BAUDRATE_ROW HIDDEN_ROW From 21bf5ba94debe5c4dea5716e6c6a1afaeeeabe27 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 17 Aug 2020 22:14:55 +0200 Subject: [PATCH 143/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d5817c12c..885c3b1aa 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1873,3 +1873,4 @@ Carolina Carcione Lonnie Misner John Eppling Phillip Elliott +Robin Bayer From 6c885e6be6cddddf1f325d48a23317761c0ef25c Mon Sep 17 00:00:00 2001 From: StefanoPerinetti <68896926+StefanoPerinetti@users.noreply.github.com> Date: Tue, 18 Aug 2020 13:58:18 +0200 Subject: [PATCH 144/267] Month name correction (#7884) --- radio/src/translations/it.h.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index daac7eaac..f848f4662 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -841,7 +841,7 @@ #define TR_MENUGLOBALVARS "VARIABILI GLOBALI" #define TR_OWN "Fase" #define TR_DATE "Data" -#define TR_MONTHS { "Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Auo", "Set", "Ott", "Nov", "Dic" } +#define TR_MONTHS { "Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic" } #define TR_ROTARY_ENCODER "R.E." #define TR_CHANNELS_MONITOR "MONITOR CANALI" #define TR_MIXERS_MONITOR "MONITOR MIXER" From a38d76312c4a6d848027958bbb4b2a79cf471804 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 20 Aug 2020 07:38:15 +0200 Subject: [PATCH 145/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 885c3b1aa..38eb394e0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -32,6 +32,7 @@ Michael Byrne FlySky (monthly) JumperRC (monthly) RadioMaster (monthly) +HappyModel (monthly) Aloft Hobbies Jonathan Modellismo WsTech From b9b88ec69cf901604b87af1e2280b6cf39350661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Alfery?= Date: Thu, 20 Aug 2020 11:35:06 +0200 Subject: [PATCH 146/267] Month name correction (#7889) --- radio/src/translations/cz.h.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index b8fd77849..4e819d439 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -820,7 +820,7 @@ #define TR_MENUGLOBALVARS "GLOBÁLNÍ PROMĚNNÉ" #define TR_OWN " \043 " #define TR_DATE "Datum" -#define TR_MONTHS { "Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čec", "Srp", "Zář", "Říj", "Lis", "Pro" } +#define TR_MONTHS { "Led", "Úno", "Bře", "Dub", "Kvě", "Čvn", "Čvc", "Srp", "Zář", "Říj", "Lis", "Pro" } #define TR_ROTARY_ENCODER "R.Enko" #define TR_CHANNELS_MONITOR "MONITOR KANÁLU" #define TR_MIXERS_MONITOR "MONITOR MIXU" From 70d5ca471def812b40bf055d62242c5de08da205 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 23 Aug 2020 12:53:17 +0200 Subject: [PATCH 147/267] Fix german hello.wav --- radio/util/tts_de.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/util/tts_de.py b/radio/util/tts_de.py index 793d3fb85..57b759be6 100755 --- a/radio/util/tts_de.py +++ b/radio/util/tts_de.py @@ -82,7 +82,7 @@ for s, f in [("Inaktivitätsalarm", "inactiv"), ("timer zwei abgelaufen", "timovr2"), ("timer drei abgelaufen", "timovr3"), ("Sensor verloren", "sensorko"), - ("Willkommen bei open te ix", "hello"), + ("Willkommen bei open t x", "hello"), ]: systemSounds.append((s, filename(f))) for i, s in enumerate(["Uhr", "Uhr", "Sender", "Empfang", "A1", "A2", "Hoehe", "Motor", From 9c8fd6e515165e0b55b8a3ee7024c127dcc13eb3 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 23 Aug 2020 14:40:49 +0200 Subject: [PATCH 148/267] No pcbrev on tx16s/t16/t18 --- radio/src/targets/horus/board.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 46e382d3f..5da953e61 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -89,6 +89,8 @@ enum { #elif defined(PCBX10) #if defined(PCBREV_EXPRESS) #define IS_FIRMWARE_COMPATIBLE_WITH_BOARD() (hardwareOptions.pcbrev == PCBREV_X10_EXPRESS) + #elif defined(RADIO_FAMILY_T16) + #define IS_FIRMWARE_COMPATIBLE_WITH_BOARD() (true) #else #define IS_FIRMWARE_COMPATIBLE_WITH_BOARD() (hardwareOptions.pcbrev == PCBREV_X10_STD) #endif From 9f4ab50e1bc139d15e247c3774df2be487f33cd0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 24 Aug 2020 22:05:58 +0200 Subject: [PATCH 149/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 38eb394e0..250308c13 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1875,3 +1875,4 @@ Lonnie Misner John Eppling Phillip Elliott Robin Bayer +Jurij Peternel From db09a52aa16c65abe0adedba4d615853425f5b9b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 26 Aug 2020 09:16:05 +0200 Subject: [PATCH 150/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 250308c13..ef39e1cad 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1876,3 +1876,4 @@ John Eppling Phillip Elliott Robin Bayer Jurij Peternel +George Aandahl From 9c1c8c557393a2722435878030aaf7a8ff514d0e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 26 Aug 2020 11:32:09 +0200 Subject: [PATCH 151/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ef39e1cad..62a1f1386 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1877,3 +1877,4 @@ Phillip Elliott Robin Bayer Jurij Peternel George Aandahl +Guy Dangerfield From 3a695665273288dea1acc5102efab596637fd423 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 26 Aug 2020 14:03:49 +0200 Subject: [PATCH 152/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 62a1f1386..2b42a8458 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1878,3 +1878,4 @@ Robin Bayer Jurij Peternel George Aandahl Guy Dangerfield +Thomas Limpert From d21f45938650f752e3df76d11c3dcfc09efed81f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 27 Aug 2020 10:28:50 +0200 Subject: [PATCH 153/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 2b42a8458..0bc8a8637 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1879,3 +1879,4 @@ Jurij Peternel George Aandahl Guy Dangerfield Thomas Limpert +Ismael G Zapater From ccd6df9758d4d8644795e91ec588ab740aaebf67 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 27 Aug 2020 16:12:51 +0200 Subject: [PATCH 154/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0bc8a8637..5019fded8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1880,3 +1880,4 @@ George Aandahl Guy Dangerfield Thomas Limpert Ismael G Zapater +Alan Hall From ce2c05ebe0b6539976f4833271682a56fd47246c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 28 Aug 2020 09:22:32 +0200 Subject: [PATCH 155/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5019fded8..42fd75e5d 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1881,3 +1881,4 @@ Guy Dangerfield Thomas Limpert Ismael G Zapater Alan Hall +Bradley Goodwin From f85a54425077e60a664b7c756e3e4c02ff5be121 Mon Sep 17 00:00:00 2001 From: tgreer Date: Fri, 28 Aug 2020 12:18:53 +0100 Subject: [PATCH 156/267] [sd-card] X7 delta setup wizard fix scroll changing page rather than field (#7904) --- radio/sdcard/taranis-x7/SCRIPTS/WIZARD/delta.lua | 10 +++++----- radio/sdcard/taranis-x7/SCRIPTS/WIZARD/multi.lua | 4 ++-- radio/sdcard/taranis-x7/SCRIPTS/WIZARD/plane.lua | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/delta.lua b/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/delta.lua index bcd9238f4..9cf7fdefd 100755 --- a/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/delta.lua +++ b/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/delta.lua @@ -52,10 +52,10 @@ end local function fieldIncDec(event, value, max, force) if edit or force==true then - if event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_PREV_REPT then + if event == EVT_VIRTUAL_INC then value = (value + max) dirty = true - elseif event == EVT_VIRTUAL_NEXT or event == EVT_VIRTUAL_NEXT_REPT then + elseif event == EVT_VIRTUAL_DEC then value = (value + max + 2) dirty = true end @@ -93,11 +93,11 @@ local function navigate(event, fieldMax, prevPage, nextPage) dirty = blinkChanged() end else - if event == EVT_VIRTUAL_NEXT then + if event == EVT_VIRTUAL_NEXT_PAGE then page = nextPage field = 0 dirty = true - elseif event == EVT_VIRTUAL_PREV then + elseif event == EVT_VIRTUAL_PREV_PAGE then page = prevPage field = 0 killEvents(event); @@ -335,7 +335,7 @@ local function drawConfirmationMenu() if rudderMode == 1 then drawNextLine(x, y, "Rudder", rudCH1) end - lcd.drawText(48, LCD_H-8, "[Enter Long] to confirm", 0); + lcd.drawText(0, LCD_H-8, "[Enter Long] to confirm", 0); lcd.drawFilledRectangle(0, LCD_H-9, LCD_W, 9, 0) fieldsMax = 0 end diff --git a/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/multi.lua b/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/multi.lua index 321b7324f..923acc43b 100755 --- a/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/multi.lua +++ b/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/multi.lua @@ -99,11 +99,11 @@ local function navigate(event, fieldMax, prevPage, nextPage) dirty = blinkChanged() end else - if event == EVT_PAGE_BREAK or event == EVT_RIGHT_FIRST then + if event == EVT_VIRTUAL_NEXT_PAGE then page = nextPage field = 0 dirty = true - elseif event == EVT_PAGE_LONG or event == EVT_LEFT_FIRST then + elseif event == EVT_VIRTUAL_PREV_PAGE then page = prevPage field = 0 killEvents(event); diff --git a/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/plane.lua b/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/plane.lua index 26282cdf7..d52c1f936 100755 --- a/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/plane.lua +++ b/radio/sdcard/taranis-x7/SCRIPTS/WIZARD/plane.lua @@ -103,11 +103,11 @@ local function navigate(event, fieldMax, prevPage, nextPage) dirty = blinkChanged() end else - if event == EVT_PAGE_BREAK or event==EVT_RIGHT_BREAK then + if event == EVT_VIRTUAL_NEXT_PAGE then page = nextPage field = 0 dirty = true - elseif event == EVT_PAGE_LONG or event==EVT_LEFT_BREAK then + elseif event == EVT_VIRTUAL_PREV_PAGE then page = prevPage field = 0 killEvents(event); From db5b9dc1fcce80fd21bd01dda5fdf759033bdc95 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 28 Aug 2020 14:38:12 +0200 Subject: [PATCH 157/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 42fd75e5d..d3cba487a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1882,3 +1882,4 @@ Thomas Limpert Ismael G Zapater Alan Hall Bradley Goodwin +Paul Dittman From 1cfe0edeb65e977aacf04b86a83bbf98f9ac4d1e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 28 Aug 2020 15:14:14 +0200 Subject: [PATCH 158/267] X7 + XLite - Default contrast is too dark. Change from 20 to 15. Untested. (#7906) --- radio/src/opentx.cpp | 6 ++++-- radio/src/targets/taranis/board.h | 2 +- radio/src/targets/taranis/lcd_driver_spi.cpp | 12 ++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 93572e07a..b4ed1941d 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -266,7 +266,9 @@ void generalDefault() #if defined(PCBHORUS) g_eeGeneral.blOffBright = 20; -#else +#endif + +#if defined(LCD_CONTRAST_DEFAULT) g_eeGeneral.contrast = LCD_CONTRAST_DEFAULT; #endif @@ -2041,7 +2043,7 @@ int main() // important to disable it before commencing with system initialisation (or // we could put a bunch more WDG_RESET()s in. But I don't like that approach // during boot up.) -#if defined(PCBTARANIS) +#if defined(LCD_CONTRAST_DEFAULT) g_eeGeneral.contrast = LCD_CONTRAST_DEFAULT; #endif diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 5a0b2fbdf..2b3f9db75 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -847,7 +847,7 @@ void ledBlue(); #define IS_LCD_RESET_NEEDED() true #define LCD_CONTRAST_MIN 10 #define LCD_CONTRAST_MAX 30 -#define LCD_CONTRAST_DEFAULT 20 +#define LCD_CONTRAST_DEFAULT 15 #endif #if defined(PCBX9D) || defined(PCBX9E) || (defined(PCBX9DP) && PCBREV < 2019) diff --git a/radio/src/targets/taranis/lcd_driver_spi.cpp b/radio/src/targets/taranis/lcd_driver_spi.cpp index ca66dc1a7..6936ae85f 100644 --- a/radio/src/targets/taranis/lcd_driver_spi.cpp +++ b/radio/src/targets/taranis/lcd_driver_spi.cpp @@ -144,9 +144,9 @@ void lcdStart() lcdWriteCommand(0xE9); // Set bias=1/10 lcdWriteCommand(0x81); // Set Vop #if defined(BOOT) - lcdWriteCommand(LCD_CONTRAST_OFFSET+LCD_CONTRAST_DEFAULT); + lcdWriteCommand(LCD_CONTRAST_OFFSET + LCD_CONTRAST_DEFAULT); #else - lcdWriteCommand(LCD_CONTRAST_OFFSET+g_eeGeneral.contrast); + lcdWriteCommand(LCD_CONTRAST_OFFSET + g_eeGeneral.contrast); #endif lcdWriteCommand(0xA2); // Set line rate: 28KLPS lcdWriteCommand(0x28); // Set panel loading @@ -173,11 +173,11 @@ void lcdStart() void lcdWriteAddress(uint8_t x, uint8_t y) { - lcdWriteCommand(x & 0x0F); // Set Column Address LSB CA[3:0] - lcdWriteCommand((x>>4) | 0x10); // Set Column Address MSB CA[7:4] + lcdWriteCommand(x & 0x0Fu); // Set Column Address LSB CA[3:0] + lcdWriteCommand((x >> 4u) | 0x10u); // Set Column Address MSB CA[7:4] - lcdWriteCommand((y&0x0F) | 0x60); // Set Row Address LSB RA [3:0] - lcdWriteCommand(((y>>4) & 0x0F) | 0x70); // Set Row Address MSB RA [7:4] + lcdWriteCommand((y & 0x0Fu) | 0x60u); // Set Row Address LSB RA [3:0] + lcdWriteCommand(((y >> 4u) & 0x0Fu) | 0x70u); // Set Row Address MSB RA [7:4] } #endif From 205ac7a21dfc12ed55a87f42bcfefdd8593d5d70 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 28 Aug 2020 15:54:51 +0200 Subject: [PATCH 159/267] Ghost support (#7900) --- companion/src/firmwares/moduledata.cpp | 4 +- companion/src/firmwares/moduledata.h | 1 + companion/src/firmwares/opentx/opentxeeprom.h | 3 +- .../src/firmwares/opentx/opentxinterface.cpp | 1 + companion/src/firmwares/sensordata.cpp | 6 + companion/src/firmwares/sensordata.h | 8 +- companion/src/modeledit/setup.cpp | 1 + companion/src/modelprinter.cpp | 2 +- .../horus/WIDGETS/Ghost/img/background.png | Bin 0 -> 6257 bytes radio/sdcard/horus/WIDGETS/Ghost/main.lua | 88 +++++++ radio/src/CMakeLists.txt | 5 + radio/src/audio.cpp | 6 +- radio/src/dataconstants.h | 11 +- radio/src/gui/480x272/radio_hardware.cpp | 2 +- .../src/gui/common/stdlcd/radio_hardware.cpp | 6 +- radio/src/gui/gui_common.cpp | 30 ++- radio/src/gui/gui_common.h | 2 +- radio/src/lua/api_general.cpp | 3 + radio/src/options.h | 3 + radio/src/pulses/ghost.cpp | 89 ++++++++ radio/src/pulses/modules_constants.h | 10 +- radio/src/pulses/modules_helpers.h | 21 +- radio/src/pulses/pulses.cpp | 20 +- radio/src/pulses/pulses.h | 12 +- radio/src/stamp.cpp | 2 + radio/src/targets/common/arm/CMakeLists.txt | 13 ++ radio/src/targets/horus/CMakeLists.txt | 1 + radio/src/targets/horus/extmodule_driver.cpp | 6 + .../src/targets/taranis/extmodule_driver.cpp | 6 + radio/src/tasks.cpp | 2 +- radio/src/telemetry/ghost.cpp | 216 ++++++++++++++++++ radio/src/telemetry/ghost.h | 114 +++++++++ radio/src/telemetry/telemetry.cpp | 17 ++ radio/src/telemetry/telemetry.h | 7 + radio/src/telemetry/telemetry_sensors.cpp | 5 + radio/src/translations/cz.h.txt | 8 +- radio/src/translations/de.h.txt | 8 +- radio/src/translations/en.h.txt | 8 +- radio/src/translations/es.h.txt | 10 +- radio/src/translations/fi.h.txt | 10 +- radio/src/translations/fr.h.txt | 10 +- radio/src/translations/it.h.txt | 8 +- radio/src/translations/nl.h.txt | 10 +- radio/src/translations/pl.h.txt | 10 +- radio/src/translations/pt.h.txt | 10 +- radio/src/translations/se.h.txt | 10 +- radio/src/translations/untranslated.h | 2 +- radio/util/tts_en.py | 3 + 48 files changed, 776 insertions(+), 54 deletions(-) create mode 100644 radio/sdcard/horus/WIDGETS/Ghost/img/background.png create mode 100644 radio/sdcard/horus/WIDGETS/Ghost/main.lua create mode 100755 radio/src/pulses/ghost.cpp create mode 100755 radio/src/telemetry/ghost.cpp create mode 100755 radio/src/telemetry/ghost.h diff --git a/companion/src/firmwares/moduledata.cpp b/companion/src/firmwares/moduledata.cpp index c899632af..3ce9836fb 100644 --- a/companion/src/firmwares/moduledata.cpp +++ b/companion/src/firmwares/moduledata.cpp @@ -160,7 +160,8 @@ QString ModuleData::protocolToString(unsigned protocol) "FrSky ACCESS R9M Lite", "FrSky ACCESS R9M Lite Pro", "FrSky XJT lite (D16)", "FrSky XJT lite (D8)", "FrSky XJT lite (LR12)", - "AFHDS3" + "AFHDS3", + "ImmersionRC Ghost" }; return CHECK_IN_ARRAY(strings, protocol); @@ -221,6 +222,7 @@ int ModuleData::getMaxChannelCount() case PULSES_XJT_LITE_X16: case PULSES_PXX_XJT_X16: case PULSES_CROSSFIRE: + case PULSES_GHOST: case PULSES_SBUS: case PULSES_PPM: return 16; diff --git a/companion/src/firmwares/moduledata.h b/companion/src/firmwares/moduledata.h index d3e851879..e22c167e3 100644 --- a/companion/src/firmwares/moduledata.h +++ b/companion/src/firmwares/moduledata.h @@ -59,6 +59,7 @@ enum PulsesProtocol { PULSES_XJT_LITE_D8, PULSES_XJT_LITE_LR12, PULSES_AFHDS3, + PULSES_GHOST, PULSES_PROTOCOL_LAST }; diff --git a/companion/src/firmwares/opentx/opentxeeprom.h b/companion/src/firmwares/opentx/opentxeeprom.h index 29d5b0b93..bb55b5687 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.h +++ b/companion/src/firmwares/opentx/opentxeeprom.h @@ -96,7 +96,7 @@ class ProtocolsConversionTable: public ConversionTable addConversion(PULSES_ACCESS_R9M, val++); addConversion(PULSES_PXX_R9M_LITE, val++); addConversion(PULSES_ACCESS_R9M_LITE, val++); - addConversion(PULSES_PXX_R9M_LITE_PRO, val++); + addConversion(PULSES_GHOST, val++); addConversion(PULSES_ACCESS_R9M_LITE_PRO, val++); addConversion(PULSES_SBUS, val++); @@ -104,6 +104,7 @@ class ProtocolsConversionTable: public ConversionTable addConversion(PULSES_XJT_LITE_X16, val); addConversion(PULSES_XJT_LITE_D8, val); addConversion(PULSES_XJT_LITE_LR12, val++); + if (version >= 219) { addConversion(PULSES_AFHDS3, val++); } diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 75aab7b64..910271eca 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -760,6 +760,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_MULTIMODULE: case PULSES_CROSSFIRE: case PULSES_AFHDS3: + case PULSES_GHOST: return true; case PULSES_ACCESS_R9M: return IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || board == BOARD_TARANIS_X9DP_2019 || board == BOARD_X10_EXPRESS || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess")); diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 0a7e7eada..6048b8641 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -84,6 +84,12 @@ QString SensorData::unitString() const return tr("V"); case UNIT_MILLILITERS_PER_MINUTE: return tr("ml/minute"); + case UNIT_HERZ: + return tr("Hertz"); + case UNIT_MS: + return tr("mS"); + case UNIT_US: + return tr("uS"); default: return ""; } diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 3f995ea3e..7b0a5f368 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -94,10 +94,10 @@ class SensorData { UNIT_MILLILITERS, UNIT_FLOZ, UNIT_MILLILITERS_PER_MINUTE, - UNIT_MAX = UNIT_MILLILITERS_PER_MINUTE, - UNIT_SPARE1, - UNIT_SPARE2, - UNIT_SPARE3, + UNIT_HERZ, + UNIT_MS, + UNIT_US, + UNIT_MAX = UNIT_US, UNIT_SPARE4, UNIT_SPARE5, UNIT_SPARE6, diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 898596a6c..df6792ff8 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -424,6 +424,7 @@ void ModulePanel::update() max_rx_num = 20; break; case PULSES_CROSSFIRE: + case PULSES_GHOST: mask |= MASK_CHANNELS_RANGE; module.channelsCount = 16; break; diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index bd56876c6..3dc945f5d 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -212,7 +212,7 @@ QString ModelPrinter::printModule(int idx) str << printLabelValue(tr("Delay"), QString("%1us").arg(module.ppm.delay)); } else { - if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_SBUS)) { + if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_GHOST || module.protocol == PULSES_SBUS)) { str << printLabelValue(tr("Receiver"), QString::number(module.modelId)); } if (module.protocol == PULSES_MULTIMODULE) { diff --git a/radio/sdcard/horus/WIDGETS/Ghost/img/background.png b/radio/sdcard/horus/WIDGETS/Ghost/img/background.png new file mode 100644 index 0000000000000000000000000000000000000000..887c022fa03dd0436f486629f9ac297745634e44 GIT binary patch literal 6257 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 Iu6xY?07z=94gdfE literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/WIDGETS/Ghost/main.lua b/radio/sdcard/horus/WIDGETS/Ghost/main.lua new file mode 100644 index 000000000..dfaef17c9 --- /dev/null +++ b/radio/sdcard/horus/WIDGETS/Ghost/main.lua @@ -0,0 +1,88 @@ +---- ######################################################################### +---- # # +---- # 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 backgroundBitmap +local offsetX +local offsetY +local sensors = {} + +local options = { + { "VTX", BOOL, 0 }, +} + +local function getValues(wgt) + if wgt.options.VTX == 0 then + sensors[1] = string.format("%s", getValue("RFMD")) + sensors[2] = string.format("%d Hz", getValue("FRat")) + sensors[3] = string.format("%d %%", getValue("RQly")) + if (getValue("TPWR") == 0) then + sensors[4] = "Range" + else + sensors[4] = string.format("%d mW", getValue("TPWR")) + end + else + sensors[1] = string.format("%s", getValue("VBan")) + sensors[2] = string.format("%dMHz", getValue("VFrq")) + sensors[3] = string.format("CH: %d", getValue("VChn")) + sensors[4] = string.format("%d mW", getValue("VPwr")) + end +end + +local function create(zone, options) + local wgt = { zone=zone, options=options} + backgroundBitmap = Bitmap.open("/WIDGETS/Ghost/img/background.png") + offsetX = (wgt.zone.w - 178) / 2 + offsetY = (wgt.zone.h - 148) / 2 + return wgt +end + +local function update(wgt, options) + wgt.options = options +end + +local function background(wgt) +end + +function refresh(wgt) + -- runs onty on large enough zone + if wgt.zone.w < 180 or wgt.zone.h < 145 then + return + end + + if backgroundBitmap ~= nil then + lcd.drawBitmap(backgroundBitmap, wgt.zone.x + offsetX, wgt.zone.y + offsetY) + end + + if getRSSI() ~= 0 then + getValues(wgt) + + -- RF Mode/Band + lcd.drawText(wgt.zone.x + offsetX + 75, wgt.zone.y + offsetY + 2, sensors[1], CENTER + DBLSIZE) + + -- Frame rate / Frequency + lcd.drawText(wgt.zone.x + offsetX + 85, wgt.zone.y + offsetY + 35, sensors[2], CENTER + DBLSIZE) + + -- RSSI / Channel + lcd.drawText(wgt.zone.x + offsetX + 85, wgt.zone.y + offsetY + 70, sensors[3], CENTER + DBLSIZE) + + -- Transmit power + lcd.drawText(wgt.zone.x + offsetX + 85, wgt.zone.y + offsetY + 105, sensors[4], CENTER + DBLSIZE) + end + +end + +return { name="Ghost", options=options, create=create, update=update, refresh=refresh, background=background } \ No newline at end of file diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 989457f92..47a1fed2a 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -54,6 +54,7 @@ option(MODULE_PROTOCOL_FLEX "Add support for non certified FLEX modules" OFF) option(MODULE_PROTOCOL_D8 "Add support for D8 modules" ON) option(FRSKY_RELEASE "Used to build FrSky released firmware" OFF) option(TBS_RELEASE "Used to build TBS released firmware" OFF) +option(IMRC_RELEASE "Used to build IMRC released firmware" OFF) option(ALLOW_TRAINER_MULTI "Allow multi trainer" OFF) # since we reset all default CMAKE compiler flags for firmware builds, provide an alternate way for user to specify additional flags. @@ -348,6 +349,10 @@ if(TBS_RELEASE) add_definitions(-DTBS_RELEASE) endif() +if(IMRC_RELEASE) + add_definitions(-DIMRC_RELEASE) +endif() + if(FRSKY_RELEASE) add_definitions(-DFRSKY_RELEASE) set(POPUP_LEVEL 3) diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index c8417568d..702ec044e 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -158,9 +158,9 @@ const char * const unitsFilenames[] = { "ml", "founce", "mlpm", - "spare1", - "spare2", - "spare3", + "hertz", + "ms", + "us", "spare4", "spare5", "spare6", diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 512ac301f..52ddc5854 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -270,7 +270,8 @@ enum TelemetryProtocol PROTOCOL_TELEMETRY_HOTT, PROTOCOL_TELEMETRY_MULTIMODULE, PROTOCOL_TELEMETRY_AFHDS3, - PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_AFHDS3, + PROTOCOL_TELEMETRY_GHOST, + PROTOCOL_TELEMETRY_LAST=PROTOCOL_TELEMETRY_GHOST, PROTOCOL_TELEMETRY_LUA }; @@ -304,10 +305,10 @@ enum TelemetryUnit { UNIT_MILLILITERS, UNIT_FLOZ, UNIT_MILLILITERS_PER_MINUTE, - UNIT_MAX = UNIT_MILLILITERS_PER_MINUTE, - UNIT_SPARE1, - UNIT_SPARE2, - UNIT_SPARE3, + UNIT_HERTZ, + UNIT_MS, + UNIT_US, + UNIT_MAX = UNIT_US, UNIT_SPARE4, UNIT_SPARE5, UNIT_SPARE6, diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp index 6b395290e..419fedbf5 100644 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ b/radio/src/gui/480x272/radio_hardware.cpp @@ -126,7 +126,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #define EXTERNAL_ANTENNA_ROW #endif -#if defined(CROSSFIRE) +#if (defined(CROSSFIRE) || defined(GHOST)) #define MAX_BAUDRATE_ROW 0 #else #define MAX_BAUDRATE_ROW HIDDEN_ROW diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index a2d486193..8824d6a57 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -162,7 +162,7 @@ enum { ITEM_RADIO_HARDWARE_CAPACITY_CALIB, #endif -#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000 +#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000 ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE, #endif @@ -278,7 +278,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #define TX_CAPACITY_MEASUREMENT_ROWS #endif -#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000 +#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000 #define MAX_BAUD_ROWS 0, #else #define MAX_BAUD_ROWS @@ -544,7 +544,7 @@ void menuRadioHardware(event_t event) break; #endif -#if defined(CROSSFIRE) && SPORT_MAX_BAUDRATE < 400000 +#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000 case ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE: lcdDrawTextAlignedLeft(y, STR_MAXBAUDRATE); lcdDrawNumber(HW_SETTINGS_COLUMN2, y, CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate], attr|LEFT); diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 3b9bb3421..d832bc1c4 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -584,6 +584,14 @@ bool isModuleUsingSport(uint8_t moduleBay, uint8_t moduleType) } } +bool areModulesConflicting(int intModuleType, int extModuleType) +{ + if (intModuleType == MODULE_TYPE_ISRM_PXX2) + return (extModuleType == MODULE_TYPE_GHOST); + + return false; +} + #if defined(HARDWARE_INTERNAL_MODULE) bool isInternalModuleAvailable(int moduleType) { @@ -605,7 +613,7 @@ bool isInternalModuleAvailable(int moduleType) if (moduleType == MODULE_TYPE_ISRM_PXX2) { #if defined(PXX2) && defined(INTERNAL_MODULE_PXX2) - return true; + return !areModulesConflicting(moduleType, g_model.moduleData[EXTERNAL_MODULE].type); #endif } @@ -620,9 +628,6 @@ bool isInternalModuleAvailable(int moduleType) bool isExternalModuleAvailable(int moduleType) { - if (moduleType == MODULE_TYPE_R9M_LITE_PRO_PXX1) - return false; - #if !defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) if (isModuleTypeR9MLite(moduleType) || moduleType == MODULE_TYPE_XJT_LITE_PXX2) return false; @@ -657,6 +662,11 @@ bool isExternalModuleAvailable(int moduleType) return false; #endif +#if !defined(GHOST) + if (moduleType == MODULE_TYPE_GHOST) + return false; +#endif + #if !defined(DSM2) if (moduleType == MODULE_TYPE_DSM2) return false; @@ -673,6 +683,9 @@ bool isExternalModuleAvailable(int moduleType) #endif #if defined(HARDWARE_INTERNAL_MODULE) + if (areModulesConflicting(g_model.moduleData[INTERNAL_MODULE].type, moduleType)) + return false; + if (isTrainerUsingModuleBay() || (isModuleUsingSport(EXTERNAL_MODULE, moduleType) && isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type))) return false; #endif @@ -697,6 +710,11 @@ bool isRfProtocolAvailable(int protocol) return false; } #endif +#if defined(GHOST) + if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST) { + return false; + } +#endif #if !defined(MODULE_PROTOCOL_D8) if (protocol == MODULE_SUBTYPE_PXX1_ACCST_D8) { return false; @@ -726,6 +744,10 @@ bool isTelemetryProtocolAvailable(int protocol) return false; } + if ( protocol== PROTOCOL_TELEMETRY_GHOST) { + return false; + } + #if !defined(MULTIMODULE) if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM || protocol == PROTOCOL_TELEMETRY_FLYSKY_IBUS || protocol == PROTOCOL_TELEMETRY_MULTIMODULE) { return false; diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index f5d7e1f51..2c89a24ed 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -175,7 +175,7 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx) else return 0; } - else if (isModuleDSM2(moduleIdx) || isModuleCrossfire(moduleIdx) || isModuleSBUS(moduleIdx)) { + else if (isModuleDSM2(moduleIdx) || isModuleCrossfire(moduleIdx) || isModuleGhost(moduleIdx) || isModuleSBUS(moduleIdx)) { return 0; } else { diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 3ee1bf1ae..544417dda 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1981,6 +1981,9 @@ const luaR_value_entry opentxConstants[] = { {"UNIT_MILLILITERS", UNIT_MILLILITERS }, {"UNIT_FLOZ", UNIT_FLOZ }, {"UNIT_MILLILITERS_PER_MINUTE", UNIT_MILLILITERS_PER_MINUTE }, + {"UNIT_HERTZ", UNIT_HERTZ }, + {"UNIT_MS", UNIT_MS }, + {"UNIT_US", UNIT_US }, {"UNIT_HOURS", UNIT_HOURS }, {"UNIT_MINUTES", UNIT_MINUTES }, {"UNIT_SECONDS", UNIT_SECONDS }, diff --git a/radio/src/options.h b/radio/src/options.h index 741c38b77..f2b3c8369 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -29,6 +29,9 @@ static const char * const options[] = { #if defined(CROSSFIRE) "crossfire", #endif +#if defined(GHOST) + "ghost", +#endif #if !defined(MODULE_PROTOCOL_D8) "eu", #endif diff --git a/radio/src/pulses/ghost.cpp b/radio/src/pulses/ghost.cpp new file mode 100755 index 000000000..6c5ec243b --- /dev/null +++ b/radio/src/pulses/ghost.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 "opentx.h" + +// Range for pulses (channels output) is [-1024:+1024] +uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses) +{ + static uint8_t lastGhostFrameId = GHST_UL_RC_CHANS_HS4_5TO8; + uint8_t ghostUpper4Offset = 0; + + switch (lastGhostFrameId) { + case GHST_UL_RC_CHANS_HS4_5TO8: + ghostUpper4Offset = 0; + break; + case GHST_UL_RC_CHANS_HS4_9TO12: + ghostUpper4Offset = 4; + break; + case GHST_UL_RC_CHANS_HS4_13TO16: + ghostUpper4Offset = 8; + break; + } + + uint8_t * buf = frame; + *buf++ = GHST_ADDR_MODULE; + *buf++ = GHST_UL_RC_CHANS_SIZE; + uint8_t * crc_start = buf; + *buf++ = lastGhostFrameId; + + // first 4 high speed, 12 bit channels (11 relevant bits with openTx) + uint32_t bits = 0; + uint8_t bitsavailable = 0; + for (int i = 0; i < 4; i++) { + uint32_t value = limit(0, GHST_RC_CTR_VAL_12BIT + (((pulses[i]) << 3) / 5), 2 * GHST_RC_CTR_VAL_12BIT); + bits |= value << bitsavailable; + bitsavailable += GHST_CH_BITS_12; + while (bitsavailable >= 8) { + *buf++ = bits; + bits >>= 8; + bitsavailable -= 8; + } + } + + // second 4 lower speed, 8 bit channels + for (int i = 4; i < 8; ++i) { + *buf++ = limit(0, GHST_RC_CTR_VAL_8BIT + (((pulses[i + ghostUpper4Offset]) >> 1) / 5), 2 * GHST_RC_CTR_VAL_8BIT); + } + + *buf++ = crc8(crc_start, GHST_UL_RC_CHANS_SIZE - 1); + + switch (lastGhostFrameId) { + case GHST_UL_RC_CHANS_HS4_5TO8: + lastGhostFrameId = GHST_UL_RC_CHANS_HS4_9TO12; + break; + case GHST_UL_RC_CHANS_HS4_9TO12: + lastGhostFrameId = GHST_UL_RC_CHANS_HS4_13TO16; + break; + case GHST_UL_RC_CHANS_HS4_13TO16: + lastGhostFrameId = GHST_UL_RC_CHANS_HS4_5TO8; + break; + } + + return buf - frame; +} + +void setupPulsesGhost() +{ + if (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST) { + uint8_t * pulses = extmodulePulsesData.ghost.pulses; + extmodulePulsesData.ghost.length = createGhostChannelsFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]); + } +} diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 4f391243a..0ddf2477a 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -29,11 +29,11 @@ enum ModuleType { MODULE_TYPE_DSM2, MODULE_TYPE_CROSSFIRE, MODULE_TYPE_MULTIMODULE, - MODULE_TYPE_R9M_PXX1, - MODULE_TYPE_R9M_PXX2, - MODULE_TYPE_R9M_LITE_PXX1, - MODULE_TYPE_R9M_LITE_PXX2, - MODULE_TYPE_R9M_LITE_PRO_PXX1, // Doesn't exist + MODULE_TYPE_R9M_PXX1, // R9M + MODULE_TYPE_R9M_PXX2, // R9M ACCESS + MODULE_TYPE_R9M_LITE_PXX1, //R9MLite + MODULE_TYPE_R9M_LITE_PXX2, //R9MLP + MODULE_TYPE_GHOST, // Replaces MODULE_TYPE_R9M_LITE_PRO_PXX1 which doesn't exist MODULE_TYPE_R9M_LITE_PRO_PXX2, MODULE_TYPE_SBUS, MODULE_TYPE_XJT_LITE_PXX2, diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index e13eaf2fb..2d56bf564 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -33,6 +33,7 @@ #endif #define CROSSFIRE_CHANNELS_COUNT 16 +#define GHOST_CHANNELS_COUNT 12 #if defined(MULTIMODULE) // When using packed, the pointer in here end up not being aligned, which clang and gcc complain about @@ -150,6 +151,18 @@ inline bool isModuleCrossfire(uint8_t idx) } #endif +#if defined(GHOST) +inline bool isModuleGhost(uint8_t idx) +{ + return idx == EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST; +} +#else +inline bool isModuleGhost(uint8_t idx) +{ + return false; +} +#endif + #if defined(PCBSKY9X) inline bool isExtraModule(uint8_t idx) { @@ -178,7 +191,7 @@ inline bool isModulePPM(uint8_t moduleIdx) inline bool isModuleTypeR9MNonAccess(uint8_t type) { - return type == MODULE_TYPE_R9M_PXX1 || type == MODULE_TYPE_R9M_LITE_PXX1 || type == MODULE_TYPE_R9M_LITE_PRO_PXX1; + return type == MODULE_TYPE_R9M_PXX1 || type == MODULE_TYPE_R9M_LITE_PXX1; } inline bool isModuleR9MNonAccess(uint8_t idx) @@ -218,7 +231,7 @@ inline bool isModuleR9MLiteNonPro(uint8_t idx) inline bool isModuleTypeR9MLitePro(uint8_t type) { - return type == MODULE_TYPE_R9M_LITE_PRO_PXX1 || type == MODULE_TYPE_R9M_LITE_PRO_PXX2; + return type == MODULE_TYPE_R9M_LITE_PRO_PXX2; } inline bool isModuleTypeR9MLite(uint8_t type) @@ -384,6 +397,8 @@ inline int8_t sentModuleChannels(uint8_t idx) { if (isModuleCrossfire(idx)) return CROSSFIRE_CHANNELS_COUNT; + else if (isModuleGhost(idx)) + return GHOST_CHANNELS_COUNT; else if (isModuleMultimodule(idx) && !isModuleMultimoduleDSM2(idx)) return 16; else if (isModuleSBUS(idx)) @@ -528,7 +543,7 @@ inline bool isTelemAllowedOnBind(uint8_t moduleIndex) return true; } - if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1 || g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_LITE_PRO_PXX1) { + if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1) { if (isModuleR9M_LBT(EXTERNAL_MODULE)) return g_model.moduleData[EXTERNAL_MODULE].pxx.power < R9M_LBT_POWER_200_16CH_NOTELEM; else diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index f669f5fdb..d6833bfe9 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -133,7 +133,6 @@ uint8_t getRequiredProtocol(uint8_t module) #if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) case MODULE_TYPE_R9M_LITE_PXX1: - case MODULE_TYPE_R9M_LITE_PRO_PXX1: protocol = PROTOCOL_CHANNELS_PXX1_SERIAL; break; @@ -193,6 +192,12 @@ uint8_t getRequiredProtocol(uint8_t module) break; #endif +#if defined(GHOST) + case MODULE_TYPE_GHOST: + protocol = PROTOCOL_CHANNELS_GHOST; + break; +#endif + default: protocol = PROTOCOL_CHANNELS_NONE; break; @@ -243,6 +248,12 @@ void enablePulsesExternalModule(uint8_t protocol) break; #endif +#if defined(GHOST) + case PROTOCOL_CHANNELS_GHOST: + EXTERNAL_MODULE_ON(); + break; +#endif + #if defined(PXX2) && defined(EXTMODULE_USART) case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: extmoduleInvertedSerialStart(PXX2_HIGHSPEED_BAUDRATE); @@ -332,6 +343,13 @@ bool setupPulsesExternalModule(uint8_t protocol) return true; #endif +#if defined(GHOST) + case PROTOCOL_CHANNELS_GHOST: + setupPulsesGhost(); + scheduleNextMixerCalculation(EXTERNAL_MODULE, GHOST_PERIOD); + return true; +#endif + #if defined(MULTIMODULE) case PROTOCOL_CHANNELS_MULTIMODULE: setupPulsesMultiExternalModule(); diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index f92629f6e..5bff463f6 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -237,6 +237,12 @@ PACK(struct CrossfirePulsesData { uint8_t length; }); +#define GHOST_FRAME_MAXLEN 16 +PACK(struct GhostPulsesData { + uint8_t pulses[GHOST_FRAME_MAXLEN]; + uint8_t length; +}); + union InternalModulePulsesData { #if defined(PXX1) #if defined(INTMODULE_USART) @@ -286,6 +292,8 @@ union ExternalModulePulsesData { PpmPulsesData ppm; CrossfirePulsesData crossfire; + + GhostPulsesData ghost; } __ALIGNED(4); /* The __ALIGNED keyword is required to align the struct inside the modulePulsesData below, @@ -310,6 +318,7 @@ bool setupPulsesInternalModule(); bool setupPulsesExternalModule(); void setupPulsesDSM2(); void setupPulsesCrossfire(); +void setupPulsesGhost(); void setupPulsesMultiExternalModule(); void setupPulsesMultiInternalModule(); void setupPulsesSbus(); @@ -364,7 +373,8 @@ enum ChannelsProtocols { PROTOCOL_CHANNELS_SBUS, PROTOCOL_CHANNELS_PXX2_LOWSPEED, PROTOCOL_CHANNELS_PXX2_HIGHSPEED, - PROTOCOL_CHANNELS_AFHDS3 + PROTOCOL_CHANNELS_AFHDS3, + PROTOCOL_CHANNELS_GHOST }; inline void stopPulses() diff --git a/radio/src/stamp.cpp b/radio/src/stamp.cpp index ad28e4037..a61d4a29a 100644 --- a/radio/src/stamp.cpp +++ b/radio/src/stamp.cpp @@ -40,6 +40,8 @@ #define DISPLAY_VERSION "-radiomaster" #elif defined(TBS_RELEASE) #define DISPLAY_VERSION "-tbs" +#elif defined(IMRC_RELEASE) +#define DISPLAY_VERSION "-imrc" #else #define DISPLAY_VERSION #endif diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 3a8846dfc..b6c6b639e 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -14,6 +14,7 @@ option(PPM "PPM TX Module" ON) option(DSM2 "DSM2 TX Module" ON) option(SBUS "SBUS TX Module" ON) option(CROSSFIRE "Crossfire TX Module" ON) +option(GHOST "Ghost TX Module" ON) option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) option(DEBUG_INTERRUPTS "Count interrupts" OFF) option(DEBUG_LATENCY "Debug latency" OFF) @@ -175,6 +176,18 @@ if(MULTIMODULE OR AFHDS3) set(SRC ${SRC} telemetry/flysky_ibus.cpp ) endif() +if(GHOST) + add_definitions(-DGHOST) + set(PULSES_SRC + ${PULSES_SRC} + ghost.cpp + ) + set(SRC + ${SRC} + telemetry/ghost.cpp + ) +endif() + add_definitions(-DCPUARM) add_definitions(-DGPS) add_definitions(-DBOLD_FONT -DBATTGRAPH -DTHRTRACE) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 525c8a4ad..68d20e733 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -48,6 +48,7 @@ if (PCB STREQUAL X10) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) set(BLUETOOTH ON) add_definitions(-DHARDWARE_POWER_MANAGEMENT_UNIT) + add_definitions(-DRADIO_X10E) elseif (PCBREV STREQUAL T16) set(FLAVOUR t16) set(LUA_EXPORT lua_export_t16) diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 9c04aeb21..47a40ae02 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -368,6 +368,12 @@ void extmoduleSendNextFrame() break; #endif +#if defined(GHOST) + case PROTOCOL_CHANNELS_GHOST: + sportSendBuffer(extmodulePulsesData.ghost.pulses, extmodulePulsesData.ghost.length); + break; +#endif + default: EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; break; diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 0910f156e..a5b6a4acd 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -321,6 +321,12 @@ void extmoduleSendNextFrame() break; #endif +#if defined(GHOST) + case PROTOCOL_CHANNELS_GHOST: + sportSendBuffer(extmodulePulsesData.ghost.pulses, extmodulePulsesData.ghost.length); + break; +#endif + default: EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; break; diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index e9dcfac45..16e120784 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -76,7 +76,7 @@ bool isForcePowerOffRequested() bool isModuleSynchronous(uint8_t moduleIdx) { uint8_t protocol = moduleState[moduleIdx].protocol; - if (protocol == PROTOCOL_CHANNELS_PXX2_HIGHSPEED || protocol == PROTOCOL_CHANNELS_PXX2_LOWSPEED || protocol == PROTOCOL_CHANNELS_CROSSFIRE || protocol == PROTOCOL_CHANNELS_NONE) + if (protocol == PROTOCOL_CHANNELS_PXX2_HIGHSPEED || protocol == PROTOCOL_CHANNELS_PXX2_LOWSPEED || protocol == PROTOCOL_CHANNELS_CROSSFIRE || protocol == PROTOCOL_CHANNELS_GHOST || protocol == PROTOCOL_CHANNELS_NONE) return true; #if defined(INTMODULE_USART) || defined(EXTMODULE_USART) if (protocol == PROTOCOL_CHANNELS_PXX1_SERIAL) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp new file mode 100755 index 000000000..1146b9555 --- /dev/null +++ b/radio/src/telemetry/ghost.cpp @@ -0,0 +1,216 @@ +/* + * 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 "opentx.h" + +const char * const ghstRfProfileValue[GHST_RF_PROFILE_COUNT] = { "Auto", "Norm", "Race", "Pure", "Long" }; +const char * const ghstVtxBandName[GHST_VTX_BAND_COUNT] = { "- - -" , "IRC", "Race", "BandE", "BandB", "BandA" }; + +struct GhostSensor +{ + const uint16_t id; + const char * name; + const TelemetryUnit unit; + const uint8_t precision; +}; + +// telemetry sensors ID +enum +{ + GHOST_ID_RX_RSSI = 0x0001, // Rx-side RSSI + GHOST_ID_RX_LQ = 0x0002, // Rx-side link quality + GHOST_ID_RX_SNR = 0x0003, // Rx-side signal to noise + GHOST_ID_FRAME_RATE = 0x0004, // Tx-side frame rate + GHOST_ID_TX_POWER = 0x0005, // Tx-side power output + GHOST_ID_RF_MODE = 0x0006, // Tx-side frame rate + GHOST_ID_TOTAL_LATENCY = 0x0007, // Tx-side total latency + GHOST_ID_VTX_FREQ = 0x0008, // Vtx Frequency (in MHz) + GHOST_ID_VTX_POWER = 0x0009, // Vtx Power (in mW) + GHOST_ID_VTX_CHAN = 0x000a, // Vtx Channel + GHOST_ID_VTX_BAND = 0x000b, // Vtx Band + +}; + +const GhostSensor ghostSensors[] = { + {GHOST_ID_RX_RSSI, ZSTR_RSSI, UNIT_DB, 0}, + {GHOST_ID_RX_LQ, ZSTR_RX_QUALITY, UNIT_PERCENT, 0}, + {GHOST_ID_RX_SNR, ZSTR_RX_SNR, UNIT_DB, 0}, + + {GHOST_ID_FRAME_RATE, ZSTR_FRAME_RATE, UNIT_RAW, 0}, + {GHOST_ID_TX_POWER, ZSTR_TX_POWER, UNIT_MILLIWATTS, 0}, + {GHOST_ID_RF_MODE, ZSTR_RF_MODE, UNIT_TEXT, 0}, + {GHOST_ID_TOTAL_LATENCY, ZSTR_TOTAL_LATENCY, UNIT_RAW, 0}, + + {GHOST_ID_VTX_FREQ, ZSTR_VTX_FREQ, UNIT_RAW, 0}, + {GHOST_ID_VTX_POWER, ZSTR_VTX_PWR, UNIT_RAW, 0}, + {GHOST_ID_VTX_CHAN, ZSTR_VTX_CHAN, UNIT_RAW, 0}, + {GHOST_ID_VTX_BAND, ZSTR_VTX_BAND, UNIT_TEXT, 0}, + + {0x00, NULL, UNIT_RAW, 0}, +}; + +const GhostSensor *getGhostSensor(uint8_t id) +{ + for (const GhostSensor * sensor = ghostSensors; sensor->id; sensor++) { + if (id == sensor->id) + return sensor; + } + return nullptr; +} + +void processGhostTelemetryValue(uint8_t index, int32_t value) +{ + if (!TELEMETRY_STREAMING()) + return; + + const GhostSensor * sensor = getGhostSensor(index); + setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, value, sensor->unit, sensor->precision); +} + +void processGhostTelemetryValueString(const GhostSensor * sensor, const char * str) +{ + int i = 0; + if (TELEMETRY_STREAMING()) { + do { + setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, str[i], UNIT_TEXT, i); + } while (str[i++] != 0); + } +} + +bool checkGhostTelemetryFrameCRC() +{ + uint8_t len = telemetryRxBuffer[1]; + uint8_t crc = crc8(&telemetryRxBuffer[2], len - 1); + return (crc == telemetryRxBuffer[len + 1]); +} + +uint16_t getTelemetryValue_u16(uint8_t index) +{ + return (telemetryRxBuffer[index] << 8) | telemetryRxBuffer[index + 1]; +} + +uint32_t getTelemetryValue_s32(uint8_t index) +{ + uint32_t val = 0; + for (int i = 0; i < 4; ++i) + val <<= 8, val |= telemetryRxBuffer[index + i]; + return val; +} + +void processGhostTelemetryFrame() +{ + if (!checkGhostTelemetryFrameCRC()) { + TRACE("[GS] CRC error"); + return; + } + + uint8_t id = telemetryRxBuffer[2]; + switch(id) { + case GHST_DL_LINK_STAT: + { + uint8_t rssiVal = min(telemetryRxBuffer[3], 100); + uint8_t lqVal = min(telemetryRxBuffer[4], 100); + uint8_t snrVal = min(telemetryRxBuffer[5], 100); + + processGhostTelemetryValue(GHOST_ID_RX_RSSI, rssiVal); + processGhostTelemetryValue(GHOST_ID_RX_LQ, lqVal); + processGhostTelemetryValue(GHOST_ID_RX_SNR, snrVal); + + // give OpenTx the LQ value, not RSSI + if (lqVal) { + telemetryData.rssi.set(lqVal); + telemetryStreaming = TELEMETRY_TIMEOUT10ms; + } + else { + telemetryData.rssi.reset(); + telemetryStreaming = 0; + } + + processGhostTelemetryValue(GHOST_ID_TX_POWER, getTelemetryValue_u16(6)); + processGhostTelemetryValue(GHOST_ID_FRAME_RATE, getTelemetryValue_u16(8)); + processGhostTelemetryValue(GHOST_ID_TOTAL_LATENCY, getTelemetryValue_u16(10)); + uint8_t rfModeEnum = min(telemetryRxBuffer[12], GHST_RF_PROFILE_MAX); + + // RF mode string, one char at a time + const GhostSensor * sensor = getGhostSensor(GHOST_ID_RF_MODE); + const char * rfModeString = ghstRfProfileValue[rfModeEnum]; + processGhostTelemetryValueString(sensor, rfModeString); + break; + } + + case GHST_DL_VTX_STAT: + { + uint8_t vtxBandEnum = min(telemetryRxBuffer[8], GHST_VTX_BAND_MAX); + + const GhostSensor * sensor = getGhostSensor(GHOST_ID_VTX_BAND); + const char * vtxBandString = ghstVtxBandName[vtxBandEnum]; + + processGhostTelemetryValue(GHOST_ID_VTX_FREQ, getTelemetryValue_u16(4)); + processGhostTelemetryValue(GHOST_ID_VTX_POWER, getTelemetryValue_u16(6)); + processGhostTelemetryValue(GHOST_ID_VTX_CHAN, min(telemetryRxBuffer[9], 8)); + processGhostTelemetryValueString(sensor, vtxBandString); + break; + } + } +} + +void processGhostTelemetryData(uint8_t data) +{ + if (telemetryRxBufferCount == 0 && data != GHST_ADDR_RADIO) { + TRACE("[GH] address 0x%02X error", data); + return; + } + + if (telemetryRxBufferCount < TELEMETRY_RX_PACKET_SIZE) { + telemetryRxBuffer[telemetryRxBufferCount++] = data; + } + else { + TRACE("[GH] array size %d error", telemetryRxBufferCount); + telemetryRxBufferCount = 0; + } + + if (telemetryRxBufferCount > 4) { + uint8_t length = telemetryRxBuffer[1]; + if (length + 2 == telemetryRxBufferCount) { + processGhostTelemetryFrame(); + telemetryRxBufferCount = 0; + } + } +} + + +void ghostSetDefault(int index, uint8_t id, uint8_t subId) +{ + TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index]; + + telemetrySensor.id = id; + telemetrySensor.instance = subId; + + const GhostSensor * sensor = getGhostSensor(id); + if (sensor) { + TelemetryUnit unit = sensor->unit; + uint8_t prec = min(2, sensor->precision); + telemetrySensor.init(sensor->name, unit, prec); + } + else + telemetrySensor.init(id); + + storageDirty(EE_MODEL); +} diff --git a/radio/src/telemetry/ghost.h b/radio/src/telemetry/ghost.h new file mode 100755 index 000000000..57adb8f96 --- /dev/null +++ b/radio/src/telemetry/ghost.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef _GHOST_H_ +#define _GHOST_H_ + +#include +#include "dataconstants.h" + +// Device (destination) address +#define GHST_ADDR_RADIO 0x80 // phase 1 +#define GHST_ADDR_MODULE 0x81 +#define GHST_ADDR_FC 0x82 +#define GHST_ADDR_GOGGLES 0x83 // phase 2 +#define GHST_ADDR_5G_TXCTRL 0x84 // phase 3 +#define GHST_ADDR_5G_TWRSCAN 0x85 +#define GHST_ADDR_5G_RLY 0x86 + +#define GHST_UL_RC_CHANS_HS4_5TO8 0x10 // High Speed 4 channel (12 bits), plus CH5-8 (8 bits) +#define GHST_UL_RC_CHANS_HS4_9TO12 0x11 // High Speed 4 channel (12 bits), plus CH9-12 (8 bits) +#define GHST_UL_RC_CHANS_HS4_13TO16 0x12 // High Speed 4 channel (12 bits), plus CH13-16 (8 bits) +#define GHST_UL_RC_CHANS_SIZE 12 // 1 (type) + 10 (data) + 1 (crc) + +#define GHST_DL_OPENTX_SYNC 0x20 +#define GHST_DL_LINK_STAT 0x21 +#define GHST_DL_VTX_STAT 0x22 + +#define GHST_RC_CTR_VAL_12BIT 0x7C0 // 0x3e0 << 1 +#define GHST_RC_CTR_VAL_8BIT 0x7C + +#define GHST_CH_BITS_12 12 +#define GHST_CH_BITS_8 8 + +enum GhstPowerMode +{ + GHST_PWR_16UW = 0, + GHST_PWR_100UW = 1, + GHST_PWR_1MW = 2, + GHST_PWR_25MW = 3, + GHST_PWR_100MW = 4, + GHST_PWR_200MW = 5, + GHST_PWR_350MW = 6, + GHST_PWR_500MW = 7, + GHST_PWR_600MW = 8, + GHST_PWR_1W = 9, + GHST_PWR_1_5W = 10, + GHST_PWR_2W = 11, + GHST_PWR_3W = 12, + GHST_PWR_4W = 13, + GHST_PWR_COUNT +}; + +enum GhstRFProfile +{ + GHST_RF_PROFILE_Auto = 0, + GHST_RF_PROFILE_Normal = 1, + GHST_RF_PROFILE_Race = 2, + GHST_RF_PROFILE_PureRace = 3, + GHST_RF_PROFILE_LongRange = 4, + GHST_RF_PROFILE_MAX = GHST_RF_PROFILE_LongRange, + GHST_RF_PROFILE_COUNT +}; + +enum GhstVtxBand +{ + GHST_VTX_BAND_Unknown = 0, + GHST_VTX_BAND_Irc = 1, + GHST_VTX_BAND_Race = 2, + GHST_VTX_BAND_BandE = 3, + GHST_VTX_BAND_BandB = 4, + GHST_VTX_BAND_BandA = 5, + GHST_VTX_BAND_MAX = GHST_VTX_BAND_BandA, + GHST_VTX_BAND_COUNT +}; + +void processGhostTelemetryData(uint8_t data); +void ghostSetDefault(int index, uint8_t id, uint8_t subId); + +#if SPORT_MAX_BAUDRATE < 400000 +// For radios which can't support telemetry at high rates, offer baud rate choices +// (modified vs. unmodified radios) +const uint32_t GHOST_BAUDRATES[] = { + 400000, + 115200, +}; +const uint8_t GHOST_PERIODS[] = { + 4, + 16, +}; +#define GHOST_BAUDRATE GHOST_BAUDRATES[g_eeGeneral.telemetryBaudrate] +#define GHOST_PERIOD GHOST_PERIODS[g_eeGeneral.telemetryBaudrate] +#else +#define GHOST_BAUDRATE 400000 +#define GHOST_PERIOD 4 +#endif + +#endif // _GHOST_H_ diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index a5f1af94f..5c5b3f514 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -45,6 +45,13 @@ void processTelemetryData(uint8_t data) } #endif +#if defined(GHOST) + if (telemetryProtocol == PROTOCOL_TELEMETRY_GHOST) { + processGhostTelemetryData(data); + return; + } +#endif + #if defined(MULTIMODULE) if (telemetryProtocol == PROTOCOL_TELEMETRY_SPEKTRUM) { processSpektrumTelemetryData(EXTERNAL_MODULE, data, telemetryRxBuffer, telemetryRxBufferCount); @@ -295,6 +302,16 @@ void telemetryInit(uint8_t protocol) } #endif +#if defined(GHOST) + else if (protocol == PROTOCOL_TELEMETRY_GHOST) { + telemetryPortInit(GHOST_BAUDRATE, TELEMETRY_SERIAL_DEFAULT); +#if defined(LUA) + outputTelemetryBuffer.reset(); +#endif + telemetryPortSetDirectionOutput(); + } +#endif + #if defined(AUX_SERIAL) || defined(PCBSKY9X) else if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { telemetryPortInit(0, TELEMETRY_SERIAL_DEFAULT); diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index d35198cdc..b03412355 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -23,6 +23,7 @@ #include "frsky.h" #include "crossfire.h" +#include "ghost.h" #include "myeeprom.h" #include "io/frsky_sport.h" @@ -153,6 +154,12 @@ inline uint8_t modelTelemetryProtocol() } #endif +#if defined(GHOST) + if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_GHOST) { + return PROTOCOL_TELEMETRY_GHOST; + } +#endif + if (!sportUsed && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) { return g_model.telemetryProtocol; } diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index d94a8471a..abf531b39 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -526,6 +526,11 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, ui crossfireSetDefault(index, id, instance); break; #endif +#if defined(GHOST) + case PROTOCOL_TELEMETRY_GHOST: + ghostSetDefault(index, id, instance); + break; +#endif #if defined(MULTIMODULE) || defined(AFHDS3) case PROTOCOL_TELEMETRY_FLYSKY_IBUS: flySkySetDefault(index,id, subId, instance); diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 4e819d439..a01ed39d7 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -260,7 +260,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metr.\0""Imper.", "Metrické""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1350,3 +1350,9 @@ #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" #define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 4b445d02e..5d0581c18 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -263,7 +263,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\012") #define TR_VUNITSSYSTEM TR("Metrik""Imper.", "Metrisch\0 ""Imperial\0 ") #define LEN_VTELEMUNIT "\003" - #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" + #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1357,3 +1357,9 @@ #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" #define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 5275f9074..1bd2d5671 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -263,7 +263,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1354,3 +1354,9 @@ #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" #define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 0335e11c1..a9af085b2 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -260,7 +260,7 @@ #define LEN_VUNITSSYSTEM "\010" #define TR_VUNITSSYSTEM "Métrico\0""Imperial" #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1352,4 +1352,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index c560254f6..3a6944d03 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -281,7 +281,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1360,4 +1360,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index d6096e4f2..b4db92ecc 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -283,7 +283,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\012") #define TR_VUNITSSYSTEM TR("Métr.\0""Impér.", "Métriques\0""Impériales") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1377,4 +1377,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index f848f4662..5cd184b65 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -284,7 +284,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\011") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metriche\0""Imperiali") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1371,3 +1371,9 @@ #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" #define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index f9801f767..97f90ce11 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -264,7 +264,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Mtrsch""Engels", "Metrisch\0 ""Engels\0 ") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1362,4 +1362,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 34fc6a65d..b807f86af 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -281,7 +281,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") /*8 decimal*/ #define TR_VUNITSSYSTEM TR("Metr. ""Imper.", "Metryczn""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1369,4 +1369,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index c4c9d15bd..96c61ec56 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -277,7 +277,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial") #define LEN_VTELEMUNIT "\003" - #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" + #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1358,4 +1358,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 78fde6fa5..c4db808f7 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -278,7 +278,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metri.""Imper.", "Metriska""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1369,4 +1369,10 @@ #define ZSTR_CL15 "Cl15" #define ZSTR_CL16 "Cl16" #define ZSTR_CL17 "Cl17" -#define ZSTR_CL18 "Cl18" \ No newline at end of file +#define ZSTR_CL18 "Cl18" +#define ZSTR_FRAME_RATE "FRat" +#define ZSTR_TOTAL_LATENCY "TLat" +#define ZSTR_VTX_FREQ "VFrq" +#define ZSTR_VTX_PWR "VPwr" +#define ZSTR_VTX_CHAN "VChn" +#define ZSTR_VTX_BAND "VBan" \ No newline at end of file diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 32acdcb84..8f13d4cea 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -87,7 +87,7 @@ #endif #define LEN_EXTERNAL_MODULE_PROTOCOLS "\014" -#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""R9MLP\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3" +#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""GHST\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3\0 " #define LEN_INTERNAL_MODULE_PROTOCOLS LEN_EXTERNAL_MODULE_PROTOCOLS #define TR_INTERNAL_MODULE_PROTOCOLS TR_EXTERNAL_MODULE_PROTOCOLS diff --git a/radio/util/tts_en.py b/radio/util/tts_en.py index a914ccf21..7edfa1eec 100755 --- a/radio/util/tts_en.py +++ b/radio/util/tts_en.py @@ -38,6 +38,9 @@ for i, (s, f) in enumerate([("volt", "volt0"), ("volts", "volt1"), ("milliliter", "ml0"), ("milliliters", "ml1"), ("fluid ounce", "founce0"), ("fluid ounces", "founce1"), ("milliliter per minute", "mlpm0"), ("milliliters per minute", "mlpm1"), + ("hertz", "hertz0"), ("hertz", "hertz1"), + ("milisecond", "ms0"), ("miliseconds", "ms1"), + ("microsecond", "us0"), ("microseconds", "us1"), ("hour", "hour0"), ("hours", "hour1"), ("minute", "minute0"), ("minutes", "minute1"), ("second", "second0"), ("seconds", "second1"), From bed9c529d7682925dc5e1179ff82430535d524de Mon Sep 17 00:00:00 2001 From: Ben Lye Date: Fri, 28 Aug 2020 14:56:18 +0100 Subject: [PATCH 160/267] Don't close the FW update log window if the update fails (#7896) --- companion/src/flashfirmwaredialog.cpp | 66 ++++++++++++++------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/companion/src/flashfirmwaredialog.cpp b/companion/src/flashfirmwaredialog.cpp index 4650cd4c0..fc45a89ff 100644 --- a/companion/src/flashfirmwaredialog.cpp +++ b/companion/src/flashfirmwaredialog.cpp @@ -275,11 +275,12 @@ void FlashFirmwareDialog::startFlash(const QString &filename) ProgressDialog progressDialog(this, tr("Write Firmware to Radio"), CompanionIcon("write_flash.png")); // check hardware compatibility if requested + bool checkPassed = true; if (g.checkHardwareCompatibility()) { QString tempFirmware = generateProcessUniqueTempFileName("flash-check.bin"); if (!readFirmware(tempFirmware, progressDialog.progress())) { QMessageBox::warning(this, tr("Firmware check failed"), tr("Could not check firmware from radio")); - return; + checkPassed = false; } FirmwareInterface previousFirmware(tempFirmware); qunlink(tempFirmware); @@ -291,41 +292,44 @@ void FlashFirmwareDialog::startFlash(const QString &filename) qDebug() << "startFlash: removing temporary file" << filename; qunlink(filename); } - return; + checkPassed = false; } } - // backup if requested - bool result = true; - QString backupFilename; - QString backupPath; - if (backup) { - backupPath = g.profile[g.id()].pBackupDir(); - if (backupPath.isEmpty()) { - backupPath=g.backupDir(); + if (checkPassed) { + // backup if requested + bool result = true; + QString backupFilename; + QString backupPath; + if (backup) { + backupPath = g.profile[g.id()].pBackupDir(); + if (backupPath.isEmpty()) { + backupPath=g.backupDir(); + } + backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin"; + result = readEeprom(backupFilename, progressDialog.progress()); + sleep(2); } - backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin"; - result = readEeprom(backupFilename, progressDialog.progress()); - sleep(2); + + // flash + result = (result && writeFirmware(filename, progressDialog.progress())); + + // restore if backup requested + if (backup && result) { + sleep(2); + QString restoreFilename = generateProcessUniqueTempFileName("restore.bin"); + if (!convertEEprom(backupFilename, restoreFilename, filename)) { + QMessageBox::warning(this, tr("Conversion failed"), tr("Cannot convert Models and Settings for use with this firmware, original data will be used")); + restoreFilename = backupFilename; + } + if (!writeEeprom(restoreFilename, progressDialog.progress())) { + QMessageBox::warning(this, tr("Restore failed"), tr("Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1").arg(backupFilename)); + } + } + + progressDialog.progress()->setInfo(tr("Flashing done")); } - - // flash - result = (result && writeFirmware(filename, progressDialog.progress())); - - // restore if backup requested - if (backup && result) { - sleep(2); - QString restoreFilename = generateProcessUniqueTempFileName("restore.bin"); - if (!convertEEprom(backupFilename, restoreFilename, filename)) { - QMessageBox::warning(this, tr("Conversion failed"), tr("Cannot convert Models and Settings for use with this firmware, original data will be used")); - restoreFilename = backupFilename; - } - if (!writeEeprom(restoreFilename, progressDialog.progress())) { - QMessageBox::warning(this, tr("Restore failed"), tr("Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1").arg(backupFilename)); - } - } - - progressDialog.progress()->setInfo(tr("Flashing done")); + progressDialog.exec(); if (isTempFileName(filename)) { From bb19720edf0058f38a1e66f10665a5c63cb539e7 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Fri, 28 Aug 2020 23:57:24 +1000 Subject: [PATCH 161/267] Companion improve switch conversions (#7895) * Companion refactor rawswitch conversions * Consistency --- companion/src/firmwares/rawswitch.cpp | 92 ++++++++++----------------- companion/src/firmwares/rawswitch.h | 1 + 2 files changed, 33 insertions(+), 60 deletions(-) diff --git a/companion/src/firmwares/rawswitch.cpp b/companion/src/firmwares/rawswitch.cpp index a689ceea7..8c1685a25 100644 --- a/companion/src/firmwares/rawswitch.cpp +++ b/companion/src/firmwares/rawswitch.cpp @@ -184,77 +184,39 @@ RawSwitch RawSwitch::convert(RadioDataConversionState & cstate) RadioDataConversionState::EventType evt = RadioDataConversionState::EVT_NONE; RadioDataConversionState::LogField oldData(index, toString(cstate.fromType, cstate.fromGS(), cstate.fromModel())); + int newIdx = 0; + if (type == SWITCH_TYPE_SWITCH) { - int srcIdx = div(abs(index)-1, 3).quot; // raw source index - int delta = 0; - - // SWI to SWR don't exist on !X9E board - if (!IS_TARANIS_X9E(cstate.toType) && IS_TARANIS_X9E(cstate.fromType)) { - if (srcIdx > 7) { - index %= 24; - evt = RadioDataConversionState::EVT_CVRT; - } - } - - // No SE and SG on X7 board - if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { - if (srcIdx == 4 || srcIdx == 5) { - delta = 3; // SE to SD & SF to SF - if (srcIdx == 4) + div_t swtch = div(abs(index) - 1, 3); // rawsource index + QStringList fromSwitchList(getSwitchList(cstate.fromBoard)); + QStringList toSwitchList(getSwitchList(cstate.toBoard)); + // set to -1 if no match found + newIdx = toSwitchList.indexOf(fromSwitchList.at(swtch.quot)); + // perform forced mapping + if (newIdx < 0) { + if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { + // No SE and SG on X7 board + newIdx = toSwitchList.indexOf("SD"); + if (newIdx >= 0) evt = RadioDataConversionState::EVT_CVRT; } - else if (srcIdx == 6) { - delta = 9; // SG to SD - evt = RadioDataConversionState::EVT_CVRT; - } - else if (srcIdx == 7) { - delta = 6; // SH to SH - } - } - else if (IS_JUMPER_T12(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { - if (srcIdx == 4 || srcIdx == 5) { - delta = 3; // SE to SD & SF to SF - if (srcIdx == 4) + else if (IS_JUMPER_T12(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { + // No SE and SG on T12 board + newIdx = toSwitchList.indexOf("SD"); + if (newIdx >= 0) evt = RadioDataConversionState::EVT_CVRT; } - else if (srcIdx == 6) { - delta = 9; // SG to SD - evt = RadioDataConversionState::EVT_CVRT; - } - else if (srcIdx == 7) { - delta = 6; // SH to SH - } - } - // Compensate for SE and SG on X9/Horus board if converting from X7 - else if ((IS_TARANIS_X9(cstate.toType) || IS_FAMILY_HORUS_OR_T16(cstate.toType)) && IS_TARANIS_X7(cstate.fromType)) { - if (srcIdx == 4) { - delta = -3; // SF to SF - } - else if (srcIdx == 5) { - delta = -6; // SH to SH - } - } - else if ((IS_TARANIS_X9(cstate.toType) || IS_FAMILY_HORUS_OR_T16(cstate.toType)) && IS_JUMPER_T12(cstate.fromType)) { - if (srcIdx == 4) { - delta = -3; // SF to SF - } - else if (srcIdx == 5) { - delta = -6; // SH to SH - } } - if (index < 0) { - delta = -delta; // invert for !switch - } - - index -= delta; - + if (newIdx >= 0) + index = (newIdx * 3 + 1 + swtch.rem) * (index < 0 ? -1 : 1); } // SWITCH_TYPE_SWITCH // final validation (we do not pass model to isAvailable() because we don't know what has or hasn't been converted) - if (!isAvailable(NULL, cstate.toGS(), cstate.toType)) { + if (newIdx < 0 || !isAvailable(NULL, cstate.toGS(), cstate.toType)) { cstate.setInvalid(oldData); - type = MAX_SWITCH_TYPE; // TODO: better way to flag invalid switches? + // no switch is safer than an invalid one + clear(); } else if (evt == RadioDataConversionState::EVT_CVRT) { cstate.setConverted(oldData, RadioDataConversionState::LogField(index, toString(cstate.toType, cstate.toGS(), cstate.toModel()))); @@ -266,3 +228,13 @@ RawSwitch RawSwitch::convert(RadioDataConversionState & cstate) return *this; } + +QStringList RawSwitch::getSwitchList(Boards board) const +{ + QStringList ret; + + for (int i = 0; i < board.getCapability(Board::Switches); i++) { + ret.append(board.getSwitchInfo(i).name); + } + return ret; +} diff --git a/companion/src/firmwares/rawswitch.h b/companion/src/firmwares/rawswitch.h index d79ac0aa6..7c0707a6a 100644 --- a/companion/src/firmwares/rawswitch.h +++ b/companion/src/firmwares/rawswitch.h @@ -89,6 +89,7 @@ class RawSwitch { bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN) const; bool isSet() const { return type != SWITCH_TYPE_NONE || index != 0; } void clear() { type = SWITCH_TYPE_NONE; index = 0; } + QStringList getSwitchList(Boards board) const; bool operator== ( const RawSwitch& other) const { return (this->type == other.type) && (this->index == other.index); From 414fba167d123ec830783b5fdef3a03eeb180eed Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 28 Aug 2020 15:58:18 +0200 Subject: [PATCH 162/267] Do not try to flash frsky updates on non frsky internal modules (#7890) --- radio/src/gui/480x272/radio_sdmanager.cpp | 4 ++++ radio/src/gui/common/stdlcd/radio_sdmanager.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/radio/src/gui/480x272/radio_sdmanager.cpp b/radio/src/gui/480x272/radio_sdmanager.cpp index d1bb86ce8..71691c85f 100644 --- a/radio/src/gui/480x272/radio_sdmanager.cpp +++ b/radio/src/gui/480x272/radio_sdmanager.cpp @@ -365,14 +365,18 @@ bool menuRadioSdManager(event_t _event) else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) { if (HAS_SPORT_UPDATE_CONNECTOR()) POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE); +#if defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); +#endif POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); } else if (!READ_ONLY() && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { FrSkyFirmwareInformation information; if (readFrSkyFirmwareInformation(line, information) == nullptr) { +#if defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2) if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); +#endif if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); if (information.productFamily == FIRMWARE_FAMILY_RECEIVER || information.productFamily == FIRMWARE_FAMILY_SENSOR) { diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index ffb4ff0bd..eb20bb283 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -403,14 +403,18 @@ void menuRadioSdManager(event_t _event) else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) { if (HAS_SPORT_UPDATE_CONNECTOR()) POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE); +#if defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); +#endif POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); } else if (!READ_ONLY() && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { FrSkyFirmwareInformation information; if (readFrSkyFirmwareInformation(line, information) == nullptr) { +#if defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2) if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); +#endif if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); if (information.productFamily == FIRMWARE_FAMILY_RECEIVER || information.productFamily == FIRMWARE_FAMILY_SENSOR) { From 1a9a6c1f49bc67e42befd12c1412da3d8f55de3c Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Fri, 28 Aug 2020 23:59:20 +1000 Subject: [PATCH 163/267] Companion fix sources lists for X10 and T16 radios and improve radio conversion of sources (#7888) * Fix Horus T16 rawsource conversions * Companion fix unknown source * Refactor rawsource conversion between radios * Better handling of Mouse Analogs for x10 * Fix unknown sources for X10 and T16 * Revert "Fix unknown sources for X10 and T16" This reverts commit dfe3909ee4f7c985ac5ec27cae3a55cb6c0a15fc. * Companion fix unknown source * Cosmetics * Cosmetics * Clear rawswitch when no match * Check forced conv successful --- companion/src/firmwares/boards.cpp | 2 +- .../src/firmwares/opentx/opentxeeprom.cpp | 12 +- companion/src/firmwares/rawsource.cpp | 146 ++++++------------ companion/src/firmwares/rawsource.h | 2 + 4 files changed, 61 insertions(+), 101 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index a39fe3369..5e0ceeb86 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -289,7 +289,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 0; case MouseAnalogs: - if (IS_FAMILY_HORUS_OR_T16(board)) + if (IS_HORUS_X12S(board)) return 2; else return 0; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index b7e4d8644..63b79d147 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -128,6 +128,14 @@ inline int SWITCHES_CONFIG_SIZE(Board::Type board, int version) return 16; } +inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version) +{ + if (IS_FAMILY_HORUS_OR_T16(board)) + return 2; + else + return 0; +} + #define MAX_ROTARY_ENCODERS(board) 0 #define MAX_FLIGHT_MODES(board, version) 9 #define MAX_TIMERS(board, version) 3 @@ -300,7 +308,7 @@ class SourcesConversionTable: public ConversionTable { } } - for (int i=0; i= CPN_MAX_STICKS + MAX_POTS_STORAGE(board, version)) offset += 2; @@ -2569,7 +2577,7 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type generalData(generalData), board(board), version(version), - inputsCount(CPN_MAX_STICKS + MAX_POTS_STORAGE(board, version) + MAX_SLIDERS_STORAGE(board, version) + Boards::getCapability(board, Board::MouseAnalogs)) + inputsCount(CPN_MAX_STICKS + MAX_POTS_STORAGE(board, version) + MAX_SLIDERS_STORAGE(board, version) + MAX_MOUSE_ANALOG_SOURCES(board, version)) { qCDebug(eepromImport) << QString("OpenTxGeneralData::OpenTxGeneralData(board: %1, version:%2, variant:%3)").arg(board).arg(version).arg(variant); diff --git a/companion/src/firmwares/rawsource.cpp b/companion/src/firmwares/rawsource.cpp index ef38dc236..92377fe0a 100644 --- a/companion/src/firmwares/rawsource.cpp +++ b/companion/src/firmwares/rawsource.cpp @@ -344,111 +344,40 @@ RawSource RawSource::convert(RadioDataConversionState & cstate) RadioDataConversionState::LogField oldData(index, toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType)); if (type == SOURCE_TYPE_STICK) { - if (cstate.toBoard.getCapability(Board::Sliders)) { - if (index >= cstate.fromBoard.getCapability(Board::Sticks) + cstate.fromBoard.getCapability(Board::Pots)) { - // 1st slider alignment - index += cstate.toBoard.getCapability(Board::Pots) - cstate.fromBoard.getCapability(Board::Pots); - } - - if (isSlider(0, cstate.fromType)) { - // LS and RS sliders are after 2 aux sliders on X12 and X9E - if ((IS_HORUS_X12S(cstate.toType) || IS_TARANIS_X9E(cstate.toType)) && !IS_HORUS_X12S(cstate.fromType) && !IS_TARANIS_X9E(cstate.fromType)) { - if (index >= 7) { - index += 2; // LS/RS to LS/RS - } - } - else if (!IS_TARANIS_X9E(cstate.toType) && !IS_HORUS_X12S(cstate.toType) && (IS_HORUS_X12S(cstate.fromType) || IS_TARANIS_X9E(cstate.fromType))) { - if (index >= 7 && index <= 8) { - index += 2; // aux sliders to spare analogs (which may not exist, this is validated later) - evt = RadioDataConversionState::EVT_CVRT; - } - else if (index >= 9 && index <= 10) { - index -= 2; // LS/RS to LS/RS - } - } - } - } - - if (IS_TARANIS(cstate.toType) && IS_FAMILY_HORUS_OR_T16(cstate.fromType)) { - if (index == 6) - index = 5; // pot S2 to S2 - else if (index == 5) - index = -1; // 6P on Horus doesn't exist on Taranis - } - else if (IS_FAMILY_HORUS_OR_T16(cstate.toType) && IS_TARANIS(cstate.fromType) && index == 5) - { - index = 6; // pot S2 to S2 - } - - } // SOURCE_TYPE_STICK + QStringList fromStickList(getStickList(cstate.fromBoard)); + QStringList toStickList(getStickList(cstate.toBoard)); + index = toStickList.indexOf(fromStickList.at(oldData.id)); + // index set to -1 if no match found + // perform forced mapping + } if (type == SOURCE_TYPE_SWITCH) { - // SWI to SWR don't exist on !X9E board - if (!IS_TARANIS_X9E(cstate.toType) && IS_TARANIS_X9E(cstate.fromType)) { - if (index >= 8) { - index = index % 8; - evt = RadioDataConversionState::EVT_CVRT; + QStringList fromSwitchList(getSwitchList(cstate.fromBoard)); + QStringList toSwitchList(getSwitchList(cstate.toBoard)); + index = toSwitchList.indexOf(fromSwitchList.at(oldData.id)); + // index set to -1 if no match found + // perform forced mapping + if (index < 0) { + if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { + // No SE and SG on X7 board + index = toSwitchList.indexOf("SD"); + if (index >= 0) + evt = RadioDataConversionState::EVT_CVRT; + } + else if (IS_JUMPER_T12(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { + // No SE and SG on T12 board + index = toSwitchList.indexOf("SD"); + if (index >= 0) + evt = RadioDataConversionState::EVT_CVRT; } } - - if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { - // No SE and SG on X7 board - if (index == 4 || index == 6) { - index = 3; // SG and SE to SD - evt = RadioDataConversionState::EVT_CVRT; - } - else if (index == 5) { - index = 4; // SF to SF - } - else if (index == 7) { - index = 5; // SH to SH - } - } - else if (IS_JUMPER_T12(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) { - // No SE and SG on T12 board - if (index == 4 || index == 6) { - index = 3; // SG and SE to SD - evt = RadioDataConversionState::EVT_CVRT; - } - else if (index == 5) { - index = 4; // SF to SF - } - else if (index == 7) { - index = 5; // SH to SH - } - } - // Compensate for SE and SG on X9/Horus board if converting from X7 - else if ((IS_TARANIS_X9(cstate.toType) || IS_FAMILY_HORUS_OR_T16(cstate.toType)) && IS_TARANIS_X7(cstate.fromType)) { - if (index == 4) { - index = 5; // SF to SF - } - else if (index == 5) { - index = 7; // SH to SH - } - } - else if ((IS_TARANIS_X9(cstate.toType) || IS_FAMILY_HORUS_OR_T16(cstate.toType)) && IS_JUMPER_T12(cstate.fromType)) { - if (index == 4) { - index = 5; // SF to SF - } - else if (index == 5) { - index = 7; // SH to SH - } - } - else if ((IS_TARANIS_X9(cstate.toType) || IS_FAMILY_HORUS_OR_T16(cstate.toType)) && IS_JUMPER_T12(cstate.fromType)) { - if (index == 4) { - index = 5; // SF to SF - } - else if (index == 5) { - index = 7; // SH to SH - } - } - } // SOURCE_TYPE_SWITCH + } // final validation (we do not pass model to isAvailable() because we don't know what has or hasn't been converted) - if (!isAvailable(NULL, cstate.toGS(), cstate.toType)) { + if (index < 0 || !isAvailable(NULL, cstate.toGS(), cstate.toType)) { cstate.setInvalid(oldData); - index = -1; // TODO: better way to flag invalid sources? - type = MAX_SOURCE_TYPE; + // no source is safer than an invalid one + clear(); } else if (evt == RadioDataConversionState::EVT_CVRT) { cstate.setConverted(oldData, RadioDataConversionState::LogField(index, toString(cstate.toModel(), cstate.toGS(), cstate.toType))); @@ -460,3 +389,24 @@ RawSource RawSource::convert(RadioDataConversionState & cstate) return *this; } + +QStringList RawSource::getStickList(Boards board) const +{ + QStringList ret; + + for (int i = 0; i < board.getCapability(Board::MaxAnalogs); i++) { + ret.append(board.getAnalogInputName(i)); + } + return ret; +} + +QStringList RawSource::getSwitchList(Boards board) const +{ + QStringList ret; + + for (int i = 0; i < board.getCapability(Board::Switches); i++) { + ret.append(board.getSwitchInfo(i).name); + } + return ret; +} + diff --git a/companion/src/firmwares/rawsource.h b/companion/src/firmwares/rawsource.h index b3220dbd3..9d0b19450 100644 --- a/companion/src/firmwares/rawsource.h +++ b/companion/src/firmwares/rawsource.h @@ -252,6 +252,8 @@ class RawSource { bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN) const; bool isSet() const { return type != SOURCE_TYPE_NONE || index != 0; } void clear() { type = SOURCE_TYPE_NONE; index = 0; } + QStringList getStickList(Boards board) const; + QStringList getSwitchList(Boards board) const; bool operator == ( const RawSource & other) const { return (this->type == other.type) && (this->index == other.index); From 94026e5d7f3cca0309fe50554b0690489e020946 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Sat, 29 Aug 2020 00:00:35 +1000 Subject: [PATCH 164/267] Companion fix radio setup console runtime qt warnings (#7886) --- companion/src/generaledit/generalsetup.cpp | 10 +++++----- companion/src/generaledit/generalsetup.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 16ef5b271..680c9c423 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -158,7 +158,7 @@ ui(new Ui::GeneralSetup) ui->usbModeLabel->hide(); ui->usbModeCB->hide(); } - + if (firmware->getCapability(HasSwitchableJack)) { ui->jackModeCB->setCurrentIndex(generalSettings.jackMode); } @@ -266,7 +266,7 @@ ui(new Ui::GeneralSetup) ui->pwrOnDelayLabel->hide(); ui->pwrOnDelay->hide(); } - + setValues(); lock = false; @@ -505,13 +505,13 @@ void GeneralSetupPanel::on_splashScreenDuration_currentIndexChanged(int index) emit modified(); } -void GeneralSetupPanel::on_pwrOnDelay_valueChanged() +void GeneralSetupPanel::on_pwrOnDelay_valueChanged(int i) { generalSettings.pwrOnSpeed = 2 - ui->pwrOnDelay->value(); emit modified(); } -void GeneralSetupPanel::on_pwrOffDelay_valueChanged() +void GeneralSetupPanel::on_pwrOffDelay_valueChanged(int i) { generalSettings.pwrOffSpeed = 2 - ui->pwrOffDelay->value(); emit modified(); @@ -650,7 +650,7 @@ void GeneralSetupPanel::on_backlightautoSB_editingFinished() } } -void GeneralSetupPanel::on_switchesDelay_valueChanged() +void GeneralSetupPanel::on_switchesDelay_valueChanged(int i) { generalSettings.switchesDelay = (ui->switchesDelay->value() / 10) - 15; emit modified(); diff --git a/companion/src/generaledit/generalsetup.h b/companion/src/generaledit/generalsetup.h index 34aeaaa71..9fc2e0bf8 100644 --- a/companion/src/generaledit/generalsetup.h +++ b/companion/src/generaledit/generalsetup.h @@ -65,7 +65,7 @@ class GeneralSetupPanel : public GeneralPanel void on_mavbaud_CB_currentIndexChanged(int index); void on_voiceLang_CB_currentIndexChanged(int index); void stickReverseEdited(); - void on_switchesDelay_valueChanged(); + void on_switchesDelay_valueChanged(int i); void on_blAlarm_ChkB_stateChanged(); void unlockSwitchEdited(); void on_beepVolume_SL_valueChanged(); @@ -89,8 +89,8 @@ class GeneralSetupPanel : public GeneralPanel void on_contrastSB_editingFinished(); void on_registrationId_editingFinished(); - void on_pwrOnDelay_valueChanged(); - void on_pwrOffDelay_valueChanged(); + void on_pwrOnDelay_valueChanged(int i); + void on_pwrOffDelay_valueChanged(int i); private: Ui::GeneralSetup *ui; From 476e1e9d8de9880e93f8bd2e023529bb6bf66b53 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 28 Aug 2020 16:02:11 +0200 Subject: [PATCH 165/267] Cosmetics --- companion/src/generaledit/generalsetup.cpp | 6 +++--- companion/src/generaledit/generalsetup.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/companion/src/generaledit/generalsetup.cpp b/companion/src/generaledit/generalsetup.cpp index 680c9c423..5404a8ccb 100644 --- a/companion/src/generaledit/generalsetup.cpp +++ b/companion/src/generaledit/generalsetup.cpp @@ -505,13 +505,13 @@ void GeneralSetupPanel::on_splashScreenDuration_currentIndexChanged(int index) emit modified(); } -void GeneralSetupPanel::on_pwrOnDelay_valueChanged(int i) +void GeneralSetupPanel::on_pwrOnDelay_valueChanged(int) { generalSettings.pwrOnSpeed = 2 - ui->pwrOnDelay->value(); emit modified(); } -void GeneralSetupPanel::on_pwrOffDelay_valueChanged(int i) +void GeneralSetupPanel::on_pwrOffDelay_valueChanged(int) { generalSettings.pwrOffSpeed = 2 - ui->pwrOffDelay->value(); emit modified(); @@ -650,7 +650,7 @@ void GeneralSetupPanel::on_backlightautoSB_editingFinished() } } -void GeneralSetupPanel::on_switchesDelay_valueChanged(int i) +void GeneralSetupPanel::on_switchesDelay_valueChanged(int) { generalSettings.switchesDelay = (ui->switchesDelay->value() / 10) - 15; emit modified(); diff --git a/companion/src/generaledit/generalsetup.h b/companion/src/generaledit/generalsetup.h index 9fc2e0bf8..43e098aee 100644 --- a/companion/src/generaledit/generalsetup.h +++ b/companion/src/generaledit/generalsetup.h @@ -65,7 +65,7 @@ class GeneralSetupPanel : public GeneralPanel void on_mavbaud_CB_currentIndexChanged(int index); void on_voiceLang_CB_currentIndexChanged(int index); void stickReverseEdited(); - void on_switchesDelay_valueChanged(int i); + void on_switchesDelay_valueChanged(int); void on_blAlarm_ChkB_stateChanged(); void unlockSwitchEdited(); void on_beepVolume_SL_valueChanged(); @@ -89,8 +89,8 @@ class GeneralSetupPanel : public GeneralPanel void on_contrastSB_editingFinished(); void on_registrationId_editingFinished(); - void on_pwrOnDelay_valueChanged(int i); - void on_pwrOffDelay_valueChanged(int i); + void on_pwrOnDelay_valueChanged(int); + void on_pwrOffDelay_valueChanged(int); private: Ui::GeneralSetup *ui; From 3dfb27f22067d3821aa9b0ab9208361a683a78ab Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 28 Aug 2020 17:01:13 +0200 Subject: [PATCH 166/267] Add rx num handling to CRSF (#7881) --- companion/src/firmwares/moduledata.cpp | 1 + companion/src/modeledit/setup.cpp | 1 + radio/src/crc.cpp | 45 ++++++++++++++++++++++++++ radio/src/crc.h | 1 + radio/src/gui/128x64/model_setup.cpp | 12 ++++--- radio/src/gui/212x64/model_setup.cpp | 12 ++++--- radio/src/gui/480x272/model_setup.cpp | 13 +++++--- radio/src/gui/gui_common.h | 11 ++++--- radio/src/pulses/crossfire.cpp | 25 +++++++++++++- radio/src/pulses/modules_helpers.h | 3 ++ radio/src/telemetry/crossfire.cpp | 4 +++ radio/src/telemetry/crossfire.h | 12 +++++++ radio/src/telemetry/telemetry.cpp | 5 +++ 13 files changed, 125 insertions(+), 20 deletions(-) diff --git a/companion/src/firmwares/moduledata.cpp b/companion/src/firmwares/moduledata.cpp index 3ce9836fb..b36d88e04 100644 --- a/companion/src/firmwares/moduledata.cpp +++ b/companion/src/firmwares/moduledata.cpp @@ -66,6 +66,7 @@ bool ModuleData::supportRxNum() const case PULSES_PXX_R9M_LITE: case PULSES_PXX_R9M_LITE_PRO: case PULSES_MULTIMODULE: + case PULSES_CROSSFIRE: case PULSES_ACCESS_ISRM: case PULSES_ACCST_ISRM_D16: case PULSES_ACCESS_R9M: diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index df6792ff8..2bd8cc055 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -424,6 +424,7 @@ void ModulePanel::update() max_rx_num = 20; break; case PULSES_CROSSFIRE: + mask |= MASK_CHANNELS_RANGE | MASK_RX_NUMBER; case PULSES_GHOST: mask |= MASK_CHANNELS_RANGE; module.channelsCount = 16; diff --git a/radio/src/crc.cpp b/radio/src/crc.cpp index 26a1af500..c0be4d7fa 100644 --- a/radio/src/crc.cpp +++ b/radio/src/crc.cpp @@ -79,3 +79,48 @@ uint8_t crc8(const uint8_t * ptr, uint32_t len) } return crc; } + +// CRC8 implementation with polynom = 0xBA +const unsigned char crc8tab_BA[256] = { + 0x00, 0xBA, 0xCE, 0x74, 0x26, 0x9C, 0xE8, 0x52, + 0x4C, 0xF6, 0x82, 0x38, 0x6A, 0xD0, 0xA4, 0x1E, + 0x98, 0x22, 0x56, 0xEC, 0xBE, 0x04, 0x70, 0xCA, + 0xD4, 0x6E, 0x1A, 0xA0, 0xF2, 0x48, 0x3C, 0x86, + 0x8A, 0x30, 0x44, 0xFE, 0xAC, 0x16, 0x62, 0xD8, + 0xC6, 0x7C, 0x08, 0xB2, 0xE0, 0x5A, 0x2E, 0x94, + 0x12, 0xA8, 0xDC, 0x66, 0x34, 0x8E, 0xFA, 0x40, + 0x5E, 0xE4, 0x90, 0x2A, 0x78, 0xC2, 0xB6, 0x0C, + 0xAE, 0x14, 0x60, 0xDA, 0x88, 0x32, 0x46, 0xFC, + 0xE2, 0x58, 0x2C, 0x96, 0xC4, 0x7E, 0x0A, 0xB0, + 0x36, 0x8C, 0xF8, 0x42, 0x10, 0xAA, 0xDE, 0x64, + 0x7A, 0xC0, 0xB4, 0x0E, 0x5C, 0xE6, 0x92, 0x28, + 0x24, 0x9E, 0xEA, 0x50, 0x02, 0xB8, 0xCC, 0x76, + 0x68, 0xD2, 0xA6, 0x1C, 0x4E, 0xF4, 0x80, 0x3A, + 0xBC, 0x06, 0x72, 0xC8, 0x9A, 0x20, 0x54, 0xEE, + 0xF0, 0x4A, 0x3E, 0x84, 0xD6, 0x6C, 0x18, 0xA2, + 0xE6, 0x5C, 0x28, 0x92, 0xC0, 0x7A, 0x0E, 0xB4, + 0xAA, 0x10, 0x64, 0xDE, 0x8C, 0x36, 0x42, 0xF8, + 0x7E, 0xC4, 0xB0, 0x0A, 0x58, 0xE2, 0x96, 0x2C, + 0x32, 0x88, 0xFC, 0x46, 0x14, 0xAE, 0xDA, 0x60, + 0x6C, 0xD6, 0xA2, 0x18, 0x4A, 0xF0, 0x84, 0x3E, + 0x20, 0x9A, 0xEE, 0x54, 0x06, 0xBC, 0xC8, 0x72, + 0xF4, 0x4E, 0x3A, 0x80, 0xD2, 0x68, 0x1C, 0xA6, + 0xB8, 0x02, 0x76, 0xCC, 0x9E, 0x24, 0x50, 0xEA, + 0x48, 0xF2, 0x86, 0x3C, 0x6E, 0xD4, 0xA0, 0x1A, + 0x04, 0xBE, 0xCA, 0x70, 0x22, 0x98, 0xEC, 0x56, + 0xD0, 0x6A, 0x1E, 0xA4, 0xF6, 0x4C, 0x38, 0x82, + 0x9C, 0x26, 0x52, 0xE8, 0xBA, 0x00, 0x74, 0xCE, + 0xC2, 0x78, 0x0C, 0xB6, 0xE4, 0x5E, 0x2A, 0x90, + 0x8E, 0x34, 0x40, 0xFA, 0xA8, 0x12, 0x66, 0xDC, + 0x5A, 0xE0, 0x94, 0x2E, 0x7C, 0xC6, 0xB2, 0x08, + 0x16, 0xAC, 0xD8, 0x62, 0x30, 0x8A, 0xFE, 0x44 +}; + +uint8_t crc8_BA(const uint8_t * ptr, uint32_t len) +{ + uint8_t crc = 0; + for (uint32_t i=0; i 0) { CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx)); if (checkIncDec_Ret) { + if (isModuleCrossfire(moduleIdx)) + moduleState[EXTERNAL_MODULE].counter = CRSF_FRAME_MODELID; modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx]; } else if (event == EVT_KEY_LONG(KEY_ENTER)) { @@ -1366,6 +1363,11 @@ void menuModelSetup(event_t event) } } } + } + else if (attr) { + l_posHorz += 1; + } + if (isModuleBindRangeAvailable(moduleIdx)) { lcdDrawText(bindButtonPos, y, STR_MODULE_BIND, l_posHorz == 1 ? attr : 0); if (isModuleRangeAvailable(moduleIdx)) { lcdDrawText(lcdNextPos + 2, y, STR_MODULE_RANGE, l_posHorz == 2 ? attr : 0); diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 36c7783bc..7503ae92a 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1160,15 +1160,12 @@ void menuModelSetup(event_t event) if (isModuleRxNumAvailable(moduleIdx)) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz == 0 ? attr : 0) | LEADING0 | LEFT, 2); bindButtonPos = lcdNextPos + FW; - } - else if (attr) { - l_posHorz += 1; - } - if (isModuleBindRangeAvailable(moduleIdx)) { if (attr && l_posHorz == 0) { if (s_editMode > 0) { CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx)); if (checkIncDec_Ret) { + if (isModuleCrossfire(moduleIdx)) + moduleState[EXTERNAL_MODULE].counter = CRSF_FRAME_MODELID; modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx]; } else if (event == EVT_KEY_LONG(KEY_ENTER)) { @@ -1181,6 +1178,11 @@ void menuModelSetup(event_t event) } } } + } + else if (attr) { + l_posHorz += 1; + } + if (isModuleBindRangeAvailable(moduleIdx)) { lcdDrawText(bindButtonPos, y, STR_MODULE_BIND, l_posHorz == 1 ? attr : 0); if (isModuleRangeAvailable(moduleIdx)) { lcdDrawText(lcdNextPos + FW, y, STR_MODULE_RANGE, l_posHorz == 2 ? attr : 0); diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 556b1f2bf..315d5a23e 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1543,14 +1543,12 @@ bool menuModelSetup(event_t event) if (isModuleRxNumAvailable(moduleIdx)) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0 | LEFT, 2); bindButtonPos += 40; - } - else if (attr) { - l_posHorz += 1; - } - if (isModuleBindRangeAvailable(moduleIdx)) { if (attr && l_posHorz == 0) { if (s_editMode > 0) { CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx)); + if (checkIncDec_Ret && isModuleCrossfire(moduleIdx)) { + moduleState[EXTERNAL_MODULE].counter = CRSF_FRAME_MODELID; + } if (event == EVT_KEY_LONG(KEY_ENTER)) { killEvents(event); uint8_t newVal = modelslist.findNextUnusedModelId(moduleIdx); @@ -1561,6 +1559,11 @@ bool menuModelSetup(event_t event) } } } + } + else if (attr) { + l_posHorz += 1; + } + if (isModuleBindRangeAvailable(moduleIdx)) { drawButton(bindButtonPos, y, STR_MODULE_BIND, (moduleState[moduleIdx].mode == MODULE_MODE_BIND ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==1 ? attr : 0)); if (isModuleRangeAvailable(moduleIdx)) { drawButton(bindButtonPos + 80, y, STR_MODULE_RANGE, (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==2 ? attr : 0)); diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 2c89a24ed..ab08dad08 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -145,11 +145,14 @@ void runFatalErrorScreen(const char * message); inline uint8_t MODULE_BIND_ROWS(int moduleIdx) { + if (isModuleCrossfire(moduleIdx)) + return 0; + if (isModuleMultimodule(moduleIdx)) { - if (IS_RX_MULTI(moduleIdx)) - return 1; - else - return 2; + if (IS_RX_MULTI(moduleIdx)) + return 1; + else + return 2; } else if (isModuleXJTD8(moduleIdx) || isModuleSBUS(moduleIdx) || isModuleAFHDS3(moduleIdx)) { return 1; diff --git a/radio/src/pulses/crossfire.cpp b/radio/src/pulses/crossfire.cpp index 2f5427902..cbe4373e4 100644 --- a/radio/src/pulses/crossfire.cpp +++ b/radio/src/pulses/crossfire.cpp @@ -28,6 +28,23 @@ #define CROSSFIRE_CENTER_CH_OFFSET(ch) (0) #endif + +uint8_t createCrossfireModelIDFrame(uint8_t * frame) +{ + uint8_t * buf = frame; + *buf++ = UART_SYNC; /* device address */ + *buf++ = 8; /* frame length */ + *buf++ = COMMAND_ID; /* cmd type */ + *buf++ = MODULE_ADDRESS; /* Destination Address */ + *buf++ = RADIO_ADDRESS; /* Origin Address */ + *buf++ = SUBCOMMAND_CRSF; /* sub command */ + *buf++ = COMMAND_MODEL_SELECT_ID; /* command of set model/receiver id */ + *buf++ = g_model.header.modelId[EXTERNAL_MODULE]; /* model ID */ + *buf++ = crc8_BA(frame + 2, 6); + *buf++ = crc8(frame + 2, 7); + return buf - frame; +} + // Range for pulses (channels output) is [-1024:+1024] uint8_t createCrossfireChannelsFrame(uint8_t * frame, int16_t * pulses) { @@ -65,7 +82,13 @@ void setupPulsesCrossfire() else #endif { - extmodulePulsesData.crossfire.length = createCrossfireChannelsFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]); + if (moduleState[EXTERNAL_MODULE].counter == CRSF_FRAME_MODELID) { + extmodulePulsesData.crossfire.length = createCrossfireModelIDFrame(pulses); + moduleState[EXTERNAL_MODULE].counter = CRSF_FRAME_MODELID_SENT; + } + else { + extmodulePulsesData.crossfire.length = createCrossfireChannelsFrame(pulses, &channelOutputs[g_model.moduleData[EXTERNAL_MODULE].channelsStart]); + } } } } diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 2d56bf564..24e45e717 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -429,6 +429,9 @@ inline bool isModuleRxNumAvailable(uint8_t moduleIdx) if (isModuleMultimodule(moduleIdx)) return true; + if (isModuleCrossfire(moduleIdx)) + return true; + return false; } diff --git a/radio/src/telemetry/crossfire.cpp b/radio/src/telemetry/crossfire.cpp index c71f6ce54..6c876b7b7 100644 --- a/radio/src/telemetry/crossfire.cpp +++ b/radio/src/telemetry/crossfire.cpp @@ -106,6 +106,10 @@ void processCrossfireTelemetryFrame() return; } + if (telemetryState == TELEMETRY_INIT && moduleState[EXTERNAL_MODULE].counter != CRSF_FRAME_MODELID_SENT) { + moduleState[EXTERNAL_MODULE].counter = CRSF_FRAME_MODELID; + } + uint8_t id = telemetryRxBuffer[2]; int32_t value; switch(id) { diff --git a/radio/src/telemetry/crossfire.h b/radio/src/telemetry/crossfire.h index 79cc5f025..9855b78f4 100644 --- a/radio/src/telemetry/crossfire.h +++ b/radio/src/telemetry/crossfire.h @@ -40,7 +40,12 @@ #define PING_DEVICES_ID 0x28 #define DEVICE_INFO_ID 0x29 #define REQUEST_SETTINGS_ID 0x2A +#define COMMAND_ID 0x32 +#define RADIO_ID 0x3A +#define UART_SYNC 0xC8 +#define SUBCOMMAND_CRSF 0x10 +#define COMMAND_MODEL_SELECT_ID 0x05 struct CrossfireSensor { const uint8_t id; @@ -79,8 +84,15 @@ enum CrossfireSensorIndexes { UNKNOWN_INDEX, }; +enum CrossfireFrames{ + CRSF_FRAME_CHANNEL, + CRSF_FRAME_MODELID, + CRSF_FRAME_MODELID_SENT +}; + void processCrossfireTelemetryData(uint8_t data); void crossfireSetDefault(int index, uint8_t id, uint8_t subId); +uint8_t createCrossfireModelIDFrame(uint8_t * frame); const uint32_t CROSSFIRE_BAUDRATES[] = { 400000, diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 5c5b3f514..39e2f10e0 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -216,6 +216,11 @@ void telemetryWakeup() if (TELEMETRY_STREAMING()) { if (telemetryState == TELEMETRY_KO) { AUDIO_TELEMETRY_BACK(); +#if defined(CROSSFIRE) + if (isModuleCrossfire(EXTERNAL_MODULE)) { + moduleState[EXTERNAL_MODULE].counter = CRSF_FRAME_MODELID; + } +#endif } telemetryState = TELEMETRY_OK; } From fcedcb9c569725ade04ada47414fa1c47ee3929e Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 28 Aug 2020 17:05:40 +0200 Subject: [PATCH 167/267] LUA improvement (#7878) Add FM handling to LUA --- radio/src/lua/api_model.cpp | 191 +++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 5 deletions(-) diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index 8eea9d5ec..cff1dc668 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -363,6 +363,136 @@ static int luaModelGetInputsCount(lua_State *L) return 1; } +/*luadoc +@function model.deleteFlightModes() + +Clear all flightModes + +@status current Introduced in 2.3.10 +*/ +static int luaModelDeleteFlightModes(lua_State *L) +{ + memset(g_model.flightModeData, 0, sizeof(g_model.flightModeData)); + return 0; +} + +/*luadoc +@function model.getFlightMode(index) + +@param index (unsigned number) flight mode number (use 0 for FM0) + +Return input data for given input and line number + +@retval nil requested input or line does not exist + +@retval table input data: + * `name` (string) input line name + * `switch` (number) input switch index + * `fadeIn` (number) fade in value (in 0.1s) + * `fadeOut` (number) fade out value (in 0.1s) + * `trimsValues` (table) table of trim values: + * `key` is trim number (zero based) + * `value` is trim value + * `trimsModes` (table) table of trim mode: + * `key` is trim number (zero based) + * `value` is trim mode + +@status current Introduced in 2.3.10 +*/ +static int luaModelGetFlightMode(lua_State * L) +{ + unsigned int idx = luaL_checkunsigned(L, 1); + if (idx < MAX_FLIGHT_MODES) { + FlightModeData * fm = flightModeAddress(idx); + lua_newtable(L); + lua_pushtablezstring(L, "name", fm->name); + lua_pushtableinteger(L, "switch", fm->swtch); + lua_pushtableinteger(L, "fadeIn", fm->fadeIn); + lua_pushtableinteger(L, "fadeOut", fm->fadeOut); + lua_pushstring(L, "trimsValues"); + lua_newtable(L); + for (uint8_t i = 0; i < NUM_TRIMS; i++) { + lua_pushinteger(L, i); + lua_pushinteger(L, fm->trim[i].value); + lua_settable(L, -3); + } + lua_settable(L, -3); + lua_pushstring(L, "trimsModes"); + lua_newtable(L); + for (uint8_t i = 0; i < NUM_TRIMS; i++) { + lua_pushinteger(L, i); + lua_pushinteger(L, fm->trim[i].mode); + lua_settable(L, -3); + } + lua_settable(L, -3); + } + else { + lua_pushnil(L); + } + return 1; +} + +/*luadoc +@function model.setFlightMode(index, params) + +Set Flight mode parameters + +@param index (unsigned number) flight mode number (use 0 for FM0) + +@param params see model.getFlightMode return format for table format. + +@status current Introduced in 2.3.10 +*/ +static int luaModelSetFlightMode(lua_State * L) +{ + unsigned int flightMode = luaL_checkunsigned(L, 1); + + if (flightMode >= MAX_FLIGHT_MODES) { + lua_pushinteger(L, 2); + return 1; + } + FlightModeData * fm = flightModeAddress(flightMode); + luaL_checktype(L, -1, LUA_TTABLE); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + luaL_checktype(L, -2, LUA_TSTRING); // key is string + const char * key = luaL_checkstring(L, -2); + if (!strcmp(key, "name")) { + const char * name = luaL_checkstring(L, -1); + str2zchar(fm->name, name, sizeof(fm->name)); + } + else if (!strcmp(key, "switch")) { + fm->swtch = luaL_checkinteger(L, -1); + } + else if (!strcmp(key, "fadeIn")) { + fm->fadeIn = luaL_checkinteger(L, -1); + } + else if (!strcmp(key, "fadeOut")) { + fm->fadeOut = luaL_checkinteger(L, -1); + } + else if (!strcmp(key, "trimsValues")) { + luaL_checktype(L, -1, LUA_TTABLE); + uint8_t idx = 0; + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1), idx++) { + int16_t val = luaL_checkinteger(L, -1); + if (idx < NUM_TRIMS) + fm->trim[idx].value = (val & 0x3FF); + } + } + else if (!strcmp(key, "trimsModes")) { + luaL_checktype(L, -1, LUA_TTABLE); + uint8_t idx = 0; + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1), idx++) { + uint16_t val = luaL_checkinteger(L, -1); + if (idx < NUM_TRIMS) + fm->trim[idx].mode = (val & 0x1F); + } + } + } + storageDirty(EE_MODEL); + lua_pushinteger(L, 0); + return 1; +} + /*luadoc @function model.getInput(input, line) @@ -376,6 +506,7 @@ Return input data for given input and line number @retval table input data: * `name` (string) input line name + * `inputName` (string) input input name * `source` (number) input source index * `weight` (number) input weight * `offset` (number) input offset @@ -383,8 +514,9 @@ Return input data for given input and line number * `curveType` (number) curve type (function, expo, custom curve) * `curveValue` (number) curve index * `carryTrim` (boolean) input trims applied + * 'flightModes' (table) table of enabled flightModes {0,2,7} means that the input is enabled for FM0, FM2 and FM7 -@status current Introduced in 2.0.0, curveType/curveValue/carryTrim added in 2.3 +@status current Introduced in 2.0.0, curveType/curveValue/carryTrim added in 2.3, flightModes, inputName added 2.3.10 */ static int luaModelGetInput(lua_State *L) { @@ -396,6 +528,7 @@ static int luaModelGetInput(lua_State *L) ExpoData * expo = expoAddress(first+idx); lua_newtable(L); lua_pushtablezstring(L, "name", expo->name); + lua_pushtablezstring(L, "inputName", g_model.inputNames[chn]); lua_pushtableinteger(L, "source", expo->srcRaw); lua_pushtableinteger(L, "weight", expo->weight); lua_pushtableinteger(L, "offset", expo->offset); @@ -403,6 +536,16 @@ static int luaModelGetInput(lua_State *L) lua_pushtableinteger(L, "curveType", expo->curve.type); lua_pushtableinteger(L, "curveValue", expo->curve.value); lua_pushtableinteger(L, "carryTrim", expo->carryTrim); + lua_pushstring(L, "flightModes"); + lua_newtable(L); + for (int i = 0, cnt = 0; i < MAX_FLIGHT_MODES; i++) { + if (!(expo->flightModes & (1 << i))) { + lua_pushinteger(L, cnt++); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + } + lua_settable(L, -3); } else { lua_pushnil(L); @@ -421,7 +564,7 @@ Insert an Input at specified line @param value (table) input data, see model.getInput() -@status current Introduced in 2.0.0, curveType/curveValue/carryTrim added in 2.3 +@status current Introduced in 2.0.0, curveType/curveValue/carryTrim added in 2.3, inputName added 2.3.10 */ static int luaModelInsertInput(lua_State *L) { @@ -444,6 +587,10 @@ static int luaModelInsertInput(lua_State *L) const char * name = luaL_checkstring(L, -1); str2zchar(expo->name, name, sizeof(expo->name)); } + else if (!strcmp(key, "inputName")) { + const char * name = luaL_checkstring(L, -1); + str2zchar(g_model.inputNames[chn], name, LEN_INPUT_NAME); + } else if (!strcmp(key, "source")) { expo->srcRaw = luaL_checkinteger(L, -1); } @@ -465,6 +612,16 @@ static int luaModelInsertInput(lua_State *L) else if (!strcmp(key, "carryTrim")) { expo->carryTrim = lua_toboolean(L, -1); } + else if (!strcmp(key, "flightModes")) { + luaL_checktype(L, -1, LUA_TTABLE); + int flighModes = 0x1FF; + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + uint16_t val = luaL_checkinteger(L, -1); + if (val < MAX_FLIGHT_MODES) + flighModes &= ~(1 << val); + } + expo->flightModes = flighModes; + } } } @@ -596,8 +753,9 @@ Get configuration for specified Mix * `delayDown` (number) delay down * `speedUp` (number) speed up * `speedDown` (number) speed down + * 'flightModes' (table) table of enabled flightModes {0,2,7} means that the input is enabled for FM0, FM2 and FM7 -@status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13 +@status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13, flightModes added 2.3.10 */ static int luaModelGetMix(lua_State *L) { @@ -623,6 +781,16 @@ static int luaModelGetMix(lua_State *L) lua_pushtableinteger(L, "delayDown", mix->delayDown); lua_pushtableinteger(L, "speedUp", mix->speedUp); lua_pushtableinteger(L, "speedDown", mix->speedDown); + lua_pushstring(L, "flightModes"); + lua_newtable(L); + for (int i = 0, cnt = 0; i < MAX_FLIGHT_MODES; i++) { + if (!(mix->flightModes & (1 << i))) { + lua_pushinteger(L, cnt++); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + } + lua_settable(L, -3); } else { lua_pushnil(L); @@ -641,7 +809,7 @@ Insert a mixer line into Channel @param value (table) see model.getMix() for table format -@status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13 +@status current Introduced in 2.0.0, parameters below `multiplex` added in 2.0.13, flightModes added 2.3.10 */ static int luaModelInsertMix(lua_State *L) { @@ -706,6 +874,16 @@ static int luaModelInsertMix(lua_State *L) else if (!strcmp(key, "speedDown")) { mix->speedDown = luaL_checkinteger(L, -1); } + else if (!strcmp(key, "flightModes")) { + luaL_checktype(L, -1, LUA_TTABLE); + int flighModes = 0x1FF; + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + uint16_t val = luaL_checkinteger(L, -1); + if (val < MAX_FLIGHT_MODES) + flighModes &= ~(1 << val); + } + mix->flightModes = flighModes; + } } } @@ -997,7 +1175,7 @@ static int luaModelSetCurve(lua_State *L) bool isX = !strcmp(key, "x"); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - int idx = luaL_checkinteger(L, -2)-1; + int idx = luaL_checkinteger(L, -2) - 1; if (idx < 0 || idx > MAX_POINTS_PER_CURVE) { lua_pushinteger(L, 4); return 1; @@ -1418,6 +1596,9 @@ const luaL_Reg modelLib[] = { { "getTimer", luaModelGetTimer }, { "setTimer", luaModelSetTimer }, { "resetTimer", luaModelResetTimer }, + { "deleteFlightModes", luaModelDeleteFlightModes }, + { "getFlightMode", luaModelGetFlightMode }, + { "setFlightMode", luaModelSetFlightMode }, { "getInputsCount", luaModelGetInputsCount }, { "getInput", luaModelGetInput }, { "insertInput", luaModelInsertInput }, From 6d483ad5b961b146f1352f18894cd26d959a2700 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 29 Aug 2020 08:34:04 +0200 Subject: [PATCH 168/267] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index d3cba487a..43fef8b6c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1883,3 +1883,6 @@ Ismael G Zapater Alan Hall Bradley Goodwin Paul Dittman +John Johnson +Liam Mahoney +Joseph Carr From 8fbd0126e7b07bb20a149f3d51bc220747036e80 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 29 Aug 2020 19:23:50 +0200 Subject: [PATCH 169/267] Horus - Trainer driver uses the same interrupt / flags for trainer / trainee. It seems wrong to me. Untested. (#7905) --- radio/src/targets/horus/trainer_driver.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/targets/horus/trainer_driver.cpp b/radio/src/targets/horus/trainer_driver.cpp index 83ec7a542..a4f0c7b6b 100644 --- a/radio/src/targets/horus/trainer_driver.cpp +++ b/radio/src/targets/horus/trainer_driver.cpp @@ -106,7 +106,7 @@ void trainerSendNextFrame() { TRAINER_TIMER->CCR2 = GET_TRAINER_PPM_DELAY() * 2; TRAINER_TIMER->CCER = TIM_CCER_CC2E | (GET_TRAINER_PPM_POLARITY() ? 0 : TIM_CCER_CC2P); - TRAINER_TIMER->CCR1 = *(trainerPulsesData.ppm.ptr - 1) - 4000; // 2mS in advance + TRAINER_TIMER->CCR3 = *(trainerPulsesData.ppm.ptr - 1) - 4000; // 2mS in advance TRAINER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA TRAINER_DMA_STREAM->CR |= TRAINER_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; @@ -123,8 +123,8 @@ extern "C" void TRAINER_DMA_IRQHandler() DMA_ClearITPendingBit(TRAINER_DMA_STREAM, TRAINER_DMA_FLAG_TC); - TRAINER_TIMER->SR &= ~TIM_SR_CC1IF; // Clear flag - TRAINER_TIMER->DIER |= TIM_DIER_CC1IE; // Enable this interrupt + TRAINER_TIMER->SR &= ~TIM_SR_CC3IF; // Clear flag + TRAINER_TIMER->DIER |= TIM_DIER_CC3IE; // Enable this interrupt } extern "C" void TRAINER_TIMER_IRQHandler() @@ -148,10 +148,10 @@ extern "C" void TRAINER_TIMER_IRQHandler() } // PPM out compare interrupt - if ((TRAINER_TIMER->DIER & TIM_DIER_CC1IE) && (TRAINER_TIMER->SR & TIM_SR_CC1IF)) { + if ((TRAINER_TIMER->DIER & TIM_DIER_CC3IE) && (TRAINER_TIMER->SR & TIM_SR_CC3IF)) { // compare interrupt - TRAINER_TIMER->DIER &= ~TIM_DIER_CC1IE; // stop this interrupt - TRAINER_TIMER->SR &= ~TIM_SR_CC1IF; // Clear flag + TRAINER_TIMER->DIER &= ~TIM_DIER_CC3IE; // stop this interrupt + TRAINER_TIMER->SR &= ~TIM_SR_CC3IF; // Clear flag setupPulsesPPMTrainer(); trainerSendNextFrame(); } From 3d363be66943503d7088400bc08e36f82e2a9837 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 29 Aug 2020 22:20:39 +0200 Subject: [PATCH 170/267] Access chan monitor with SHIFT+ENTER instead of just shift on xlite (#7897) --- radio/src/gui/128x64/model_curve_edit.cpp | 8 +++++--- radio/src/gui/128x64/model_input_edit.cpp | 2 +- radio/src/gui/128x64/model_mix_edit.cpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/128x64/model_curve_edit.cpp b/radio/src/gui/128x64/model_curve_edit.cpp index 2dfdaf567..ebf2228d5 100644 --- a/radio/src/gui/128x64/model_curve_edit.cpp +++ b/radio/src/gui/128x64/model_curve_edit.cpp @@ -171,9 +171,11 @@ void menuModelCurveOne(event_t event) killEvents(event); break; #elif defined(NAVIGATION_XLITE) - case EVT_KEY_LONG(KEY_SHIFT): - pushMenu(menuChannelsView); - killEvents(event); + case EVT_KEY_FIRST(KEY_ENTER): + if (IS_SHIFT_PRESSED()) { + pushMenu(menuChannelsView); + killEvents(event); + } break; #endif } diff --git a/radio/src/gui/128x64/model_input_edit.cpp b/radio/src/gui/128x64/model_input_edit.cpp index 036b640a5..419456ac0 100644 --- a/radio/src/gui/128x64/model_input_edit.cpp +++ b/radio/src/gui/128x64/model_input_edit.cpp @@ -78,7 +78,7 @@ void menuModelExpoOne(event_t event) killEvents(event); } #elif defined(NAVIGATION_XLITE) - if (event == EVT_KEY_LONG(KEY_SHIFT)) { + if (event == EVT_KEY_FIRST(KEY_ENTER) && IS_SHIFT_PRESSED()) { pushMenu(menuChannelsView); killEvents(event); } diff --git a/radio/src/gui/128x64/model_mix_edit.cpp b/radio/src/gui/128x64/model_mix_edit.cpp index a2180b6ac..5c1f95e65 100644 --- a/radio/src/gui/128x64/model_mix_edit.cpp +++ b/radio/src/gui/128x64/model_mix_edit.cpp @@ -91,7 +91,7 @@ void menuModelMixOne(event_t event) killEvents(event); } #elif defined(NAVIGATION_XLITE) - if (event == EVT_KEY_LONG(KEY_SHIFT)) { + if (event == EVT_KEY_FIRST(KEY_ENTER) && IS_SHIFT_PRESSED()) { pushMenu(menuChannelsView); killEvents(event); } From ca04a7dfdaebbc0b6d04b01694f24c702f03fd32 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 30 Aug 2020 22:43:51 +0200 Subject: [PATCH 171/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 43fef8b6c..673e25d6f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1886,3 +1886,5 @@ Paul Dittman John Johnson Liam Mahoney Joseph Carr +Fiorenzo Agnelli +Brian Ramsey From fbb26af47f9cd9ca511a827fe177829dea513f13 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Tue, 1 Sep 2020 02:54:24 +1000 Subject: [PATCH 172/267] Companion add external R9M 2019 to X7 ACCESS (#7907) --- companion/src/firmwares/opentx/opentxinterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 910271eca..eb4ed6f8f 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -763,14 +763,14 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_GHOST: return true; case PULSES_ACCESS_R9M: - return IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || board == BOARD_TARANIS_X9DP_2019 || board == BOARD_X10_EXPRESS || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess")); + return IS_ACCESS_RADIO(board, id); case PULSES_PXX_R9M_LITE: case PULSES_ACCESS_R9M_LITE: case PULSES_ACCESS_R9M_LITE_PRO: case PULSES_XJT_LITE_X16: case PULSES_XJT_LITE_D8: case PULSES_XJT_LITE_LR12: - return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board)); + return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board)); default: return false; } From 79189ebfb02beed5a9063879bc7e8df4a41f050f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 1 Sep 2020 10:29:37 +0200 Subject: [PATCH 173/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 673e25d6f..22bfa7bb0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1888,3 +1888,4 @@ Liam Mahoney Joseph Carr Fiorenzo Agnelli Brian Ramsey +Angelo Movio From 200116257c012af084ec433c2585bcc15f91bc1b Mon Sep 17 00:00:00 2001 From: pascallanger Date: Wed, 2 Sep 2020 21:13:58 +0200 Subject: [PATCH 174/267] Changes to allow Lua to access DSM Forward Programming (#7913) --- radio/src/pulses/multi.cpp | 18 ++++++++++++++++++ radio/src/telemetry/spektrum.cpp | 15 +++++++++++++++ radio/src/telemetry/telemetry.h | 2 ++ 3 files changed, 35 insertions(+) diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index 42d57b231..ec901abf1 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 sendDSM(uint8_t moduleIdx); #endif void multiPatchCustom(uint8_t moduleIdx) @@ -194,6 +195,9 @@ void setupPulsesMulti(uint8_t moduleIdx) else if (IS_HOTT_MULTI(moduleIdx)) { sendHott(moduleIdx); //1 byte of additional data } + else if (IS_DSM_MULTI(moduleIdx)) { + sendDSM(moduleIdx); //7 bytes of additional data + } #endif } } @@ -401,4 +405,18 @@ void sendHott(uint8_t moduleIdx) sendMulti(moduleIdx, Multi_Buffer[5]); } } + +void sendDSM(uint8_t moduleIdx) +{ + // Multi_Buffer[0..2]=="DSM" -> Lua script is running + // Multi_Buffer[3]==0x70 + len -> TX to RX data ready to be sent + // Multi_Buffer[4..9]=6 bytes of TX to RX data + // Multi_Buffer[10..25]=16 bytes of RX to TX data + if (Multi_Buffer && memcmp(Multi_Buffer, "DSM", 3) == 0 && (Multi_Buffer[3] & 0xF8) == 0x70) { + for(uint8_t i = 0; i < 7; i++) { + sendMulti(moduleIdx, Multi_Buffer[3+i]); + } + Multi_Buffer[3] = 0x00; // Data sent + } +} #endif diff --git a/radio/src/telemetry/spektrum.cpp b/radio/src/telemetry/spektrum.cpp index aab694db0..1bd1829d5 100644 --- a/radio/src/telemetry/spektrum.cpp +++ b/radio/src/telemetry/spektrum.cpp @@ -47,6 +47,7 @@ #define DSM_BIND_PACKET_LENGTH 12 #define I2C_HIGH_CURRENT 0x03 +#define I2C_FWD_PGM 0x09 #define I2C_TEXTGEN 0x0c #define I2C_GPS 0x17 #define I2C_GPS2 0x17 @@ -349,6 +350,20 @@ void processSpektrumPacket(const uint8_t *packet) // highest bit indicates that TM1100 is in use, ignore it uint8_t i2cAddress = (packet[2] & 0x7f); + if (i2cAddress == I2C_FWD_PGM) { +#if defined(LUA) + // Forward Programming + if (Multi_Buffer && memcmp(Multi_Buffer, "DSM", 3) == 0) { + // Multi_Buffer[0..2]=="DSM" -> Lua script is running + // Multi_Buffer[3]==0x70 -> TX to RX data ready to be sent + // Multi_Buffer[4..9]=6 bytes of TX to RX data + // Multi_Buffer[10..25]=16 bytes of RX to TX data + Multi_Buffer[10] = i2cAddress; + memcpy(&Multi_Buffer[11], &packet[3], 15); // Store the received RX answer in the buffer + } +#endif + return; // Not a sensor + } //SmartBat Hack if (i2cAddress == I2C_SMART_BAT_BASE_ADDRESS) { i2cAddress = i2cAddress + (packet[4] >> 4); // use type to create virtual I2CAddresses diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index b03412355..5680a837f 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -115,6 +115,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_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)) #if defined(HARDWARE_INTERNAL_MODULE) @@ -126,6 +127,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_DSM_MULTI(module) false #define IS_FRSKY_SPORT_PROTOCOL() (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) #define IS_RX_MULTI(module) false #endif From e986454684acbf88cc41180899a51a3181e84d54 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 08:44:01 +0200 Subject: [PATCH 175/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 22bfa7bb0..6ba2a5233 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1889,3 +1889,4 @@ Joseph Carr Fiorenzo Agnelli Brian Ramsey Angelo Movio +Weldon Parks From 9391c7c644a46bfbcc57f96d5cafef6f70712475 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 10:44:16 +0200 Subject: [PATCH 176/267] [XLiteS] No green led --- radio/src/targets/taranis/hal.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 65462c576..aa4601577 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -922,6 +922,14 @@ #define GPIO_LED_GPIO_OFF GPIO_SetBits #define LED_RED_GPIO GPIOA #define LED_RED_GPIO_PIN GPIO_Pin_13 // PA.13 +#elif defined(PCBXLITES) + #define STATUS_LEDS + #define GPIO_LED_GPIO_ON GPIO_ResetBits + #define GPIO_LED_GPIO_OFF GPIO_SetBits + #define LED_BLUE_GPIO GPIOE + #define LED_BLUE_GPIO_PIN GPIO_Pin_6 // PE.06 + #define LED_RED_GPIO GPIOE + #define LED_RED_GPIO_PIN GPIO_Pin_4 // PE.04 #elif defined(PCBXLITE) #define STATUS_LEDS #define GPIO_LED_GPIO_ON GPIO_ResetBits From 6d541dc5895134c2be4baffdcefa9c97d6a93184 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 4 Sep 2020 14:55:09 +0200 Subject: [PATCH 177/267] All radios have 32 curves (fix #7919) --- companion/src/firmwares/opentx/opentxeeprom.cpp | 2 +- companion/src/firmwares/opentx/opentxinterface.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 63b79d147..184447808 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -145,7 +145,7 @@ inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version) #define MAX_EXPOS(board, version) (IS_HORUS_OR_TARANIS(board) ? 64 : 32) #define MAX_LOGICAL_SWITCHES(board, version) (version >= 218 ? 64 : 32) #define MAX_CUSTOM_FUNCTIONS(board, version) 64 -#define MAX_CURVES(board, version) ((version >= 219 || HAS_LARGE_LCD(board)) ? 32 : 16) +#define MAX_CURVES(board, version) (version >= 219 ? 32 : 16) #define MAX_GVARS(board, version) 9 #define MAX_SCRIPTS(board) (IS_FAMILY_HORUS_OR_T16(board) ? 9 : 7) #define MAX_TELEMETRY_SENSORS(board, version) (version <= 218 ? 32 : ((IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board)) ? 60 : 40)) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index eb4ed6f8f..71e059c00 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -540,7 +540,7 @@ int OpenTxFirmware::getCapability(::Capability capability) case Simulation: return 1; case NumCurves: - return (HAS_LARGE_LCD(board) ? 32 : 16); + return 32; case HasMixerNames: return (IS_TARANIS_X9(board) ? 8 : 6); case HasExpoNames: From 6eb38bd048419c778e6fd781972545bff8563c0c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 18:18:36 +0200 Subject: [PATCH 178/267] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 6ba2a5233..8844ebe23 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1890,3 +1890,6 @@ Fiorenzo Agnelli Brian Ramsey Angelo Movio Weldon Parks +Simon Brink +Weldon Parks +Ralf Herzenbruch From f7153ababbb719c6063e8494c215d38dddcbbd33 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 4 Sep 2020 18:24:41 +0200 Subject: [PATCH 179/267] LUA: add ability to read rotenc speed (#7918) Add getRotEncSpeed() --- radio/src/lua/api_general.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 544417dda..e480eaec5 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -375,6 +375,26 @@ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags) return false; // not found } +/*luadoc +@function getRotEncSpeed() + +Return rotary encoder current speed + +@retval number in list: ROTENC_LOWSPEED, ROTENC_MIDSPEED, ROTENC_HIGHSPEED + return 0 on radio without rotary encoder + +@status current Introduced in 2.3.10 +*/ +static int luGetRotEncSpeed(lua_State * L) +{ +#if defined(ROTARY_ENCODER_NAVIGATION) + lua_pushunsigned(L, rotencSpeed); +#else + lua_pushunsigned(L, 0); +#endif + return 1; +} + /*luadoc @function sportTelemetryPop() @@ -1680,6 +1700,7 @@ const luaL_Reg opentxLib[] = { { "getVersion", luaGetVersion }, { "getGeneralSettings", luaGetGeneralSettings }, { "getGlobalTimer", luaGetGlobalTimer }, + { "getRotEncSpeed", luGetRotEncSpeed }, { "getValue", luaGetValue }, { "getRAS", luaGetRAS }, { "getTxGPS", luaGetTxGPS }, @@ -1814,6 +1835,9 @@ const luaR_value_entry opentxConstants[] = { { "EVT_VIRTUAL_NEXT", EVT_ROTARY_RIGHT }, { "EVT_VIRTUAL_DEC", EVT_ROTARY_LEFT }, { "EVT_VIRTUAL_INC", EVT_ROTARY_RIGHT }, + { "ROTENC_LOWSPEED", ROTENC_LOWSPEED }, + { "ROTENC_MIDSPEED", ROTENC_MIDSPEED }, + { "ROTENC_HIGHSPEED", ROTENC_HIGHSPEED }, #elif defined(PCBX9D) || defined(PCBX9DP) // key reverted between field nav and value change { "EVT_VIRTUAL_PREV", EVT_KEY_FIRST(KEY_PLUS) }, { "EVT_VIRTUAL_PREV_REPT", EVT_KEY_REPT(KEY_PLUS) }, From 5a500cc055fcd61b1b32716951b4d74e1fc42c32 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Sat, 5 Sep 2020 02:36:58 +1000 Subject: [PATCH 180/267] Companion enhanced telemetry sensors context menu and other refinements across context menus (#7716) Companion enhanced telemetry sensors context menu with reference updates --- companion/src/firmwares/modeldata.cpp | 72 ++++++-- companion/src/firmwares/modeldata.h | 2 + companion/src/firmwares/sensordata.cpp | 7 +- companion/src/firmwares/sensordata.h | 4 +- companion/src/modeledit/channels.cpp | 13 +- companion/src/modeledit/channels.h | 2 +- companion/src/modeledit/curves.cpp | 12 +- companion/src/modeledit/curves.h | 2 +- companion/src/modeledit/customfunctions.cpp | 12 +- companion/src/modeledit/customfunctions.h | 2 +- companion/src/modeledit/flightmodes.cpp | 107 +++++++++--- companion/src/modeledit/flightmodes.h | 6 +- companion/src/modeledit/inputs.cpp | 28 ++-- companion/src/modeledit/inputs.h | 2 +- companion/src/modeledit/logicalswitches.cpp | 11 +- companion/src/modeledit/logicalswitches.h | 2 +- companion/src/modeledit/mixes.cpp | 25 ++- companion/src/modeledit/mixes.h | 2 +- companion/src/modeledit/setup.cpp | 12 +- companion/src/modeledit/setup.h | 2 +- companion/src/modeledit/telemetry.cpp | 172 ++++++++++++++++---- companion/src/modeledit/telemetry.h | 23 ++- 22 files changed, 381 insertions(+), 139 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 16ba240e0..d38069827 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -532,6 +532,8 @@ int ModelData::updateReference() Firmware *fw = getCurrentFirmware(); + updRefInfo.occurences = 1; + switch (updRefInfo.type) { case REF_UPD_TYPE_CHANNEL: @@ -566,6 +568,7 @@ int ModelData::updateReference() updRefInfo.srcType = SOURCE_TYPE_TELEMETRY; updRefInfo.swtchType = SWITCH_TYPE_SENSOR; updRefInfo.maxindex = fw->getCapability(Sensors); + updRefInfo.occurences = 3; break; case REF_UPD_TYPE_TIMER: updRefInfo.srcType = SOURCE_TYPE_SPECIAL; @@ -767,15 +770,35 @@ int ModelData::updateReference() //s1.report("Telemetry"); } - // TODO maybe less risk to leave it up to the user?? - /* for (int i = 0; i < CPN_MAX_SENSORS; i++) { SensorData *sd = &sensorData[i]; - if (sd->isAvailable() && sd->type == SensorData::TELEM_TYPE_CALCULATED) { + if (!sd->isEmpty() && sd->type == SensorData::TELEM_TYPE_CALCULATED) { + if (sd->formula == SensorData::TELEM_FORMULA_CELL) { + updateTelemetryRef(sd->source); + if (sd->source == 0) { + sd->clear(); + appendUpdateReferenceParams(REF_UPD_TYPE_SENSOR, REF_UPD_ACT_CLEAR, i); + } + } + else if (sd->formula == SensorData::TELEM_FORMULA_DIST) { + updateTelemetryRef(sd->gps); + updateTelemetryRef(sd->alt); + } + else if (sd->formula == SensorData::TELEM_FORMULA_CONSUMPTION || sd->formula == SensorData::TELEM_FORMULA_TOTALIZE) { + updateTelemetryRef(sd->amps); + if (sd->amps == 0) { + sd->clear(); + appendUpdateReferenceParams(REF_UPD_TYPE_SENSOR, REF_UPD_ACT_CLEAR, i); + } + } + else { + for (unsigned int i = 0; i < 4; i++) { + updateTelemetryRef(sd->sources[i]); + } + } } } - s1.report("Telemetry Sensors"); - */ + //s1.report("Telemetry Sensors"); // TODO needs lua incorporated into Companion as script needs to be parsed to determine if input field is source or value /* @@ -811,38 +834,40 @@ void ModelData::updateTypeIndexRef(R & curRef, const T type, const int idxAdj, c newRef.type = curRef.type; newRef.index = abs(curRef.index); + div_t idx = div(newRef.index, updRefInfo.occurences); + switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: - if (newRef.index != (updRefInfo.index1 + idxAdj)) + if (idx.quot != (updRefInfo.index1 + idxAdj)) return; if (defClear) newRef.clear(); else { newRef.type = (T)defType; - newRef.index = defIndex; + newRef.index = defIndex + idxAdj; } break; case REF_UPD_ACT_SHIFT: - if (newRef.index < (updRefInfo.index1 + idxAdj)) + if (idx.quot < (updRefInfo.index1 + idxAdj)) return; newRef.index += updRefInfo.shift; - if (newRef.index < (updRefInfo.index1 + idxAdj) || newRef.index > (updRefInfo.maxindex + idxAdj)) { + if (idx.quot < (updRefInfo.index1 + idxAdj) || idx.quot > (updRefInfo.maxindex + idxAdj)) { if (defClear) newRef.clear(); else { newRef.type = (T)defType; - newRef.index = defIndex; + newRef.index = defIndex + idxAdj; } } break; case REF_UPD_ACT_SWAP: - if (newRef.index == updRefInfo.index1 + idxAdj) - newRef.index = updRefInfo.index2 + idxAdj; - else if (newRef.index == updRefInfo.index2 + idxAdj) - newRef.index = updRefInfo.index1 + idxAdj; + if (idx.quot == updRefInfo.index1 + idxAdj) + newRef.index = ((updRefInfo.index2 + idxAdj) * updRefInfo.occurences) + idx.rem; + else if (idx.quot == updRefInfo.index2 + idxAdj) + newRef.index = ((updRefInfo.index1 + idxAdj) * updRefInfo.occurences) + idx.rem; break; default: qDebug() << "Error - unhandled action:" << updRefInfo.action; @@ -877,7 +902,7 @@ void ModelData::updateTypeValueRef(R & curRef, const T type, const int idxAdj, c newRef.clear(); else { newRef.type = (T)defType; - newRef.value = defValue; + newRef.value = defValue + idxAdj; } break; case REF_UPD_ACT_SHIFT: @@ -891,7 +916,7 @@ void ModelData::updateTypeValueRef(R & curRef, const T type, const int idxAdj, c newRef.clear(); else { newRef.type = (T)defType; - newRef.value = defValue; + newRef.value = defValue + idxAdj; } } break; @@ -1120,13 +1145,14 @@ void ModelData::updateFlightModeFlags(unsigned int & curRef) } } -void ModelData::updateTelemetryRef(unsigned int & curRef) +void ModelData::updateTelemetryRef(int & curRef) { if (updRefInfo.type != REF_UPD_TYPE_SENSOR) return; const int idxAdj = 1; int newRef = curRef; + switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: @@ -1154,13 +1180,23 @@ void ModelData::updateTelemetryRef(unsigned int & curRef) return; } - if (curRef != static_cast(newRef)) { + if (curRef != newRef) { //qDebug() << "Updated reference:" << curRef << " -> " << newRef; curRef = newRef; updRefInfo.updcnt++; } } +void ModelData::updateTelemetryRef(unsigned int & curRef) +{ + int newRef = (int)curRef; + updateTelemetryRef(newRef); + + if (curRef != static_cast(newRef)) { + curRef = (unsigned int)newRef; + } +} + void ModelData::updateModuleFailsafes(ModuleData * md) { if (updRefInfo.type != REF_UPD_TYPE_CHANNEL) diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index b5ba8e45c..560206c72 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -298,6 +298,7 @@ class ModelData { int shift; int updcnt; int maxindex; + int occurences; RawSourceType srcType; RawSwitchType swtchType; }; @@ -315,6 +316,7 @@ class ModelData { void updateDestCh(MixData * md); void updateLimitCurveRef(CurveReference & crv); void updateFlightModeFlags(unsigned int & flags); + void updateTelemetryRef(int & idx); void updateTelemetryRef(unsigned int & idx); void updateModuleFailsafes(ModuleData * md); inline void updateSourceRef(RawSource & src) { updateTypeIndexRef(src, updRefInfo.srcType); } diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 6048b8641..a9b8d7640 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -104,7 +104,7 @@ QString SensorData::getOrigin(const ModelData * model) const { if (type != TELEM_TYPE_CUSTOM || !id) return QString(); - + const ModuleData & module = model->moduleData[moduleIdx]; if (module.isPxx2Module() && rxIdx <= 2 && module.access.receivers & (1 << rxIdx)) { return QString(module.access.receiverName[rxIdx]); @@ -113,3 +113,8 @@ QString SensorData::getOrigin(const ModelData * model) const return QString(); } } + +bool SensorData::isEmpty() const +{ + return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); +} diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 7b0a5f368..a2b993bcc 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -24,6 +24,7 @@ #include constexpr int CPN_MAX_SENSORS = 60; +constexpr int SENSOR_LABEL_LEN = 4; class ModelData; @@ -133,7 +134,7 @@ class SensorData { unsigned int moduleIdx; unsigned int persistentValue; unsigned int formula; - char label[4+1]; + char label[SENSOR_LABEL_LEN + 1]; unsigned int unit; unsigned int prec; bool autoOffset; @@ -166,6 +167,7 @@ class SensorData { QString nameToString(int index) const; QString getOrigin(const ModelData* model) const; void clear() { memset(this, 0, sizeof(SensorData)); } + bool isEmpty() const; }; #endif // SENSORDATA_H diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index 0cde45109..6442cc2fb 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -349,8 +349,11 @@ void Channels::cmCopy() void Channels::cmCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Channel. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + cmCopy(); - cmClear(); + cmClear(false); } void Channels::onCustomContextMenuRequested(QPoint pos) @@ -412,10 +415,12 @@ void Channels::cmMoveDown() swapData(selectedIndex, selectedIndex + 1); } -void Channels::cmClear() +void Channels::cmClear(bool prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Channel. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Channel. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } model->limitData[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index caa0fca38..d20923a35 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -75,7 +75,7 @@ class Channels : public ModelPanel void cmMoveUp(); void cmMoveDown(); void cmInsert(); - void cmClear(); + void cmClear(bool prompt = true); void cmClearAll(); void onCustomContextMenuRequested(QPoint pos); diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 5759e60e6..662dc61c4 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -699,10 +699,12 @@ bool Curves::moveUpAllowed() const return selectedIndex > 0; } -void Curves::cmClear() +void Curves::cmClear(bool prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Curve. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Curve. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } model->curves[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); @@ -734,8 +736,10 @@ void Curves::cmCopy() void Curves::cmCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Curve. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; cmCopy(); - cmClear(); + cmClear(false); } void Curves::cmDelete() diff --git a/companion/src/modeledit/curves.h b/companion/src/modeledit/curves.h index d9a5364a6..34d1a675e 100644 --- a/companion/src/modeledit/curves.h +++ b/companion/src/modeledit/curves.h @@ -81,7 +81,7 @@ class Curves : public ModelPanel void onPointSizeEdited(); void onNodeDelete(); void onCustomContextMenuRequested(QPoint pos); - void cmClear(); + void cmClear(bool prompt = true); void cmClearAll(); void cmCopy(); void cmCut(); diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index f5c42443e..1bf06cfd6 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -628,8 +628,10 @@ void CustomFunctionsPanel::cmCopy() void CustomFunctionsPanel::cmCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; cmCopy(); - cmClear(); + cmClear(false); } void CustomFunctionsPanel::onCustomContextMenuRequested(QPoint pos) @@ -743,10 +745,12 @@ void CustomFunctionsPanel::cmMoveDown() swapData(selectedIndex, selectedIndex + 1); } -void CustomFunctionsPanel::cmClear() +void CustomFunctionsPanel::cmClear(bool prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } functions[selectedIndex].clear(); resetCBsAndRefresh(selectedIndex); diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index 1c93f3e96..54a83ddcf 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -82,7 +82,7 @@ class CustomFunctionsPanel : public GenericPanel void cmMoveUp(); void cmMoveDown(); void cmInsert(); - void cmClear(); + void cmClear(bool prompt = true); void cmClearAll(); private: diff --git a/companion/src/modeledit/flightmodes.cpp b/companion/src/modeledit/flightmodes.cpp index 1634aed3a..12082091d 100644 --- a/companion/src/modeledit/flightmodes.cpp +++ b/companion/src/modeledit/flightmodes.cpp @@ -708,10 +708,12 @@ bool FlightModePanel::moveUpAllowed() const return phaseIdx > 0; } -void FlightModePanel::cmClear() +void FlightModePanel::cmClear(bool prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Flight Mode. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Flight Mode. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } phase.clear(phaseIdx); @@ -780,8 +782,10 @@ void FlightModePanel::cmCopy() void FlightModePanel::cmCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Flight Mode. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; cmCopy(); - cmClear(); + cmClear(false); } void FlightModePanel::cmDelete() @@ -1059,7 +1063,7 @@ void FlightModePanel::gvOnCustomContextMenuRequested(QPoint pos) bool FlightModePanel::gvHasClipboardData() const { - if (gvHasDefnClipboardData() || gvHasValueClipboardData()) + if ((phaseIdx == 0 && gvHasDefnClipboardData() && gvHasAllValuesClipboardData()) || (phaseIdx > 0 && gvHasValueClipboardData())) return true; return false; } @@ -1092,6 +1096,20 @@ bool FlightModePanel::gvHasValueClipboardData(QByteArray * data) const return false; } +bool FlightModePanel::gvHasAllValuesClipboardData(QByteArray * data) const +{ + const QClipboard * clipboard = QApplication::clipboard(); + const QMimeData * mimeData = clipboard->mimeData(); + if (phaseIdx == 0 && mimeData->hasFormat(MIMETYPE_GVAR_ALL_VALUES)) { + if (data) { + data->clear(); + data->append(mimeData->data(MIMETYPE_GVAR_ALL_VALUES)); + } + return true; + } + return false; +} + bool FlightModePanel::gvDeleteAllowed() const { return phaseIdx == 0; @@ -1124,11 +1142,13 @@ bool FlightModePanel::gvMoveUpAllowed() const return (phaseIdx == 0 && gvIdx > 0); } -void FlightModePanel::gvCmClear() +void FlightModePanel::gvCmClear(bool prompt) { if (phaseIdx == 0) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear selected Global Variable across all Flight Modes. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Global Variable across all Flight Modes. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } model->gvarData[gvIdx].clear(); for (int i = 0; i < fmCount; i++) { model->flightModeData[i].gvars[gvIdx] = model->flightModeData[i].linkedGVarFlightModeZero(i); @@ -1136,6 +1156,10 @@ void FlightModePanel::gvCmClear() model->updateAllReferences(ModelData::REF_UPD_TYPE_GLOBAL_VARIABLE, ModelData::REF_UPD_ACT_CLEAR, gvIdx); } else { + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Global Variable. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } phase.gvars[gvIdx] = phase.linkedGVarFlightModeZero(phaseIdx); } emit datachanged(); @@ -1173,38 +1197,58 @@ void FlightModePanel::gvCmCopy() if (phaseIdx == 0) { data.append((char*)&model->gvarData[gvIdx], sizeof(GVarData)); mimeData->setData(MIMETYPE_GVAR_PARAMS, data); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); + + data.clear(); + for (int j = 0; j < fmCount; j++) { + data.append((char*)&model->flightModeData[j].gvars[gvIdx], sizeof(phase.gvars[0])); + } + mimeData->setData(MIMETYPE_GVAR_ALL_VALUES, data); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); + + mimeData->removeFormat(MIMETYPE_GVAR_VALUE); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } else { mimeData->removeFormat(MIMETYPE_GVAR_PARAMS); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); + + mimeData->removeFormat(MIMETYPE_GVAR_ALL_VALUES); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); + + data.clear(); + int val = phase.gvars[gvIdx]; + if (model->isGVarLinked(phaseIdx, gvIdx)) + val = GVAR_MAX_VALUE + 1 + model->getGVarFlightModeIndex(phaseIdx, gvIdx); // store index in case paste is to another flight mode + data.setNum(val); + mimeData->setData(MIMETYPE_GVAR_VALUE, data); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } - QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); - int val = phase.gvars[gvIdx]; - if (model->isGVarLinked(phaseIdx, gvIdx)) - val = GVAR_MAX_VALUE + 1 + model->getGVarFlightModeIndex(phaseIdx, gvIdx); // store index in case paste is to another flight mode - data.setNum(val); - mimeData->setData(MIMETYPE_GVAR_VALUE, data); - QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } void FlightModePanel::gvCmCut() { - gvCmCopy(); - if (phaseIdx > 0) { - gvCmClear(); + if (phaseIdx == 0) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Global Variable across all Flight Modes. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; } else { - model->gvarData[gvIdx].clear(); - phase.gvars[gvIdx] = 0; - model->updateAllReferences(ModelData::REF_UPD_TYPE_GLOBAL_VARIABLE, ModelData::REF_UPD_ACT_CLEAR, gvIdx); - emit datachanged(); - emit modified(); + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Global Variable. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; } + + gvCmCopy(); + gvCmClear(false); } void FlightModePanel::gvCmDelete() { if (phaseIdx > 0) return; + + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Global Variable. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + int maxidx = gvCount - 1; for (int i = gvIdx; i < maxidx; i++) { memcpy(&model->gvarData[i], &model->gvarData[i + 1], sizeof(GVarData)); @@ -1254,11 +1298,20 @@ void FlightModePanel::gvCmMoveUp() void FlightModePanel::gvCmPaste() { QByteArray data; - if (phaseIdx == 0 && gvHasDefnClipboardData(&data)) { - GVarData *gvd = &model->gvarData[gvIdx]; - memcpy(gvd, data.constData(), sizeof(GVarData)); + if (phaseIdx == 0) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Paste to selected Global Variable across all Flight Modes. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + if (gvHasDefnClipboardData(&data)) { + memcpy(&model->gvarData[gvIdx], data.constData(), sizeof(GVarData)); + } + if (gvHasAllValuesClipboardData(&data)) { + for (int j = 0; j < fmCount; j++) { + int *gvar = &model->flightModeData[j].gvars[gvIdx]; + memcpy(gvar, data.mid(j * sizeof(phase.gvars[0]), sizeof(phase.gvars[0])).constData(), sizeof(phase.gvars[0])); + } + } } - if (gvHasValueClipboardData(&data)) { + else if (gvHasValueClipboardData(&data)) { int val = data.toInt(); if (val > GVAR_MAX_VALUE) { if (phaseIdx > 0) diff --git a/companion/src/modeledit/flightmodes.h b/companion/src/modeledit/flightmodes.h index 1986f1e46..61e25c59a 100644 --- a/companion/src/modeledit/flightmodes.h +++ b/companion/src/modeledit/flightmodes.h @@ -29,6 +29,7 @@ class RawSwitchFilterItemModel; constexpr char MIMETYPE_FLIGHTMODE[] = "application/x-companion-flightmode"; constexpr char MIMETYPE_GVAR_PARAMS[] = "application/x-companion-gvar-params"; constexpr char MIMETYPE_GVAR_VALUE[] = "application/x-companion-gvar-value"; +constexpr char MIMETYPE_GVAR_ALL_VALUES[] = "application/x-companion-gvar-all-values"; namespace Ui { class FlightMode; @@ -67,7 +68,7 @@ class FlightModePanel : public ModelPanel void phaseREValue_editingFinished(); void phaseREUse_currentIndexChanged(int index); void onCustomContextMenuRequested(QPoint pos); - void cmClear(); + void cmClear(bool prompt = true); void cmClearAll(); void cmCopy(); void cmCut(); @@ -77,7 +78,7 @@ class FlightModePanel : public ModelPanel void cmMoveDown(); void cmMoveUp(); void gvOnCustomContextMenuRequested(QPoint pos); - void gvCmClear(); + void gvCmClear(bool prompt = true); void gvCmClearAll(); void gvCmCopy(); void gvCmCut(); @@ -127,6 +128,7 @@ class FlightModePanel : public ModelPanel bool gvHasClipboardData() const; bool gvHasDefnClipboardData(QByteArray * data = nullptr) const; bool gvHasValueClipboardData(QByteArray * data = nullptr) const; + bool gvHasAllValuesClipboardData(QByteArray * data = nullptr) const; bool gvDeleteAllowed() const; bool gvInsertAllowed() const; bool gvMoveDownAllowed() const; diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index 806de54fc..2e65c788c 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -227,26 +227,27 @@ void InputsPanel::setSelectedByExpoList(QList list) } } -void InputsPanel::exposDelete(bool ask) +void InputsPanel::exposDelete(bool prompt) { QList list = createExpoListFromSelected(); if (list.isEmpty()) return; - QMessageBox::StandardButton ret = QMessageBox::No; - - if (ask) - ret = QMessageBox::warning(this, CPN_STR_APP_NAME, tr("Delete selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No); - - if ((ret == QMessageBox::Yes) || (!ask)) { - exposDeleteList(list, ask); - emit modified(); - update(); + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete selected Input lines. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; } + + exposDeleteList(list, prompt); + emit modified(); + update(); } void InputsPanel::exposCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut selected Input lines. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + exposCopy(); exposDelete(false); } @@ -317,7 +318,6 @@ void InputsPanel::pasteExpoMimeData(const QMimeData * mimeData, int destIdx) } } - void InputsPanel::exposPaste() { const QClipboard *clipboard = QApplication::clipboard(); @@ -515,7 +515,7 @@ void InputsPanel::exposDeleteList(QList list, bool clearName) void InputsPanel::clearExpos() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Input lines. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { model->clearInputs(); for (int i = 0; i < inputsCount; i++) { model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_CLEAR, i); @@ -559,7 +559,7 @@ bool InputsPanel::cmInputMoveUpAllowed() const void InputsPanel::cmInputClear() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Input. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all lines for the selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; for (int i = CPN_MAX_EXPOS - 1; i >= 0; i--) { @@ -574,7 +574,7 @@ void InputsPanel::cmInputClear() void InputsPanel::cmInputDelete() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Input. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete all lines for the selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; for (int i = 0; i < CPN_MAX_EXPOS; i++) { diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index 884573fef..9922f2873 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -46,7 +46,7 @@ class InputsPanel : public ModelPanel void expolistWidget_customContextMenuRequested(QPoint pos); void expolistWidget_doubleClicked(QModelIndex index); void expolistWidget_KeyPress(QKeyEvent *event); - void exposDelete(bool ask=true); + void exposDelete(bool prompt = true); void exposCut(); void exposCopy(); void exposPaste(); diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index 63a2ae22f..e70d63568 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -533,8 +533,10 @@ void LogicalSwitchesPanel::cmCopy() void LogicalSwitchesPanel::cmCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Logical Switch. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; cmCopy(); - cmClear(); + cmClear(false); } // TODO make something generic here! @@ -597,8 +599,13 @@ void LogicalSwitchesPanel::cmMoveDown() swapData(selectedIndex, selectedIndex + 1); } -void LogicalSwitchesPanel::cmClear() +void LogicalSwitchesPanel::cmClear(bool prompt) { + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Logical Switch. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } + model->logicalSw[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); update(); diff --git a/companion/src/modeledit/logicalswitches.h b/companion/src/modeledit/logicalswitches.h index 2aea300c5..62aa69f30 100644 --- a/companion/src/modeledit/logicalswitches.h +++ b/companion/src/modeledit/logicalswitches.h @@ -59,7 +59,7 @@ class LogicalSwitchesPanel : public ModelPanel void cmMoveUp(); void cmMoveDown(); void cmInsert(); - void cmClear(); + void cmClear(bool prompt = true); void cmClearAll(); private: diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index c92105ec6..4d0f5f4cd 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -253,28 +253,27 @@ void MixesPanel::setSelectedByMixList(QList list) } -void MixesPanel::mixersDelete(bool ask) +void MixesPanel::mixersDelete(bool prompt) { QList list = createMixListFromSelected(); - if(list.isEmpty()) return; + if(list.isEmpty()) + return; - QMessageBox::StandardButton ret = QMessageBox::No; - - if(ask) { - ret = QMessageBox::warning(this, "companion", - tr("Delete Selected Mixes?"), - QMessageBox::Yes | QMessageBox::No); + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete selected Mix lines. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; } - if ((ret == QMessageBox::Yes) || (!ask)) { - mixersDeleteList(list); - emit modified(); - update(); - } + mixersDeleteList(list); + emit modified(); + update(); } void MixesPanel::mixersCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut selected Mix lines. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + mixersCopy(); mixersDelete(false); } diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 46a51a21a..95765465e 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -39,7 +39,7 @@ class MixesPanel : public ModelPanel private slots: // TODO all slots? void clearMixes(); - void mixersDelete(bool ask=true); + void mixersDelete(bool prompt = true); void mixersCut(); void mixersCopy(); void mixersPaste(); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 2bd8cc055..dcbe4f101 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -1592,10 +1592,12 @@ bool SetupPanel::moveTimerUpAllowed() const return selectedTimerIndex > 0; } -void SetupPanel::cmTimerClear() +void SetupPanel::cmTimerClear(bool prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Timer. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Timer. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } model->timers[selectedTimerIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_TIMER, ModelData::REF_UPD_ACT_CLEAR, selectedTimerIndex); @@ -1627,8 +1629,10 @@ void SetupPanel::cmTimerCopy() void SetupPanel::cmTimerCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Timer. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; cmTimerCopy(); - cmTimerClear(); + cmTimerClear(false); } void SetupPanel::cmTimerDelete() diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index fbd56ed86..0aa48ea76 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -154,7 +154,7 @@ class SetupPanel : public ModelPanel void on_potWarningMode_currentIndexChanged(int index); void on_editText_clicked(); void onTimerCustomContextMenuRequested(QPoint pos); - void cmTimerClear(); + void cmTimerClear(bool prompt = true); void cmTimerClearAll(); void cmTimerCopy(); void cmTimerCut(); diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 99915eae5..b34caa89e 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -28,9 +28,6 @@ #include - -/******************************************************/ - TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, RawSourceFilterItemModel * srcModel): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetryCustomScreen), @@ -38,17 +35,17 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, { ui->setupUi(this); - for (int l=0; l<4; l++) { - for (int c=0; cgetCapability(TelemetryCustomScreensFieldsPerLine); c++) { + for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) { + for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) { fieldsCB[l][c] = new QComboBox(this); - fieldsCB[l][c]->setProperty("index", c + (l<<8)); + fieldsCB[l][c]->setProperty("index", c + (l << 8)); fieldsCB[l][c]->setModel(srcModel); ui->screenNumsLayout->addWidget(fieldsCB[l][c], l, c, 1, 1); connect(fieldsCB[l][c], SIGNAL(activated(int)), this, SLOT(customFieldChanged(int))); } } - for (int l=0; l<4; l++) { + for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) { barsCB[l] = new QComboBox(this); barsCB[l]->setProperty("index", l); barsCB[l]->setModel(srcModel); @@ -122,18 +119,18 @@ void TelemetryCustomScreen::update() ui->screenNums->setVisible(screen.type == TELEMETRY_SCREEN_NUMBERS); ui->screenBars->setVisible(screen.type == TELEMETRY_SCREEN_BARS); - for (int l=0; l<4; l++) { - for (int c=0; cgetCapability(TelemetryCustomScreensFieldsPerLine); c++) { + for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) { + for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) { fieldsCB[l][c]->setCurrentIndex(fieldsCB[l][c]->findData(screen.body.lines[l].source[c].toValue())); } } - for (int l=0; l<4; l++) { + for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) { barsCB[l]->setCurrentIndex(barsCB[l]->findData(screen.body.bars[l].source.toValue())); } if (screen.type == TELEMETRY_SCREEN_BARS) { - for (int i=0; i<4; i++) { + for (int i = 0; i < firmware->getCapability(TelemetryCustomScreensBars); i++) { updateBar(i); } } @@ -292,13 +289,14 @@ void TelemetryCustomScreen::barTimeChanged() /******************************************************/ -TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): +TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), sensor(sensor), lock(false), sensorIndex(sensorIndex), - selectedIndex(0) + selectedIndex(0), + sensorCapability(sensorCapability) { ui->setupUi(this); ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1)); @@ -326,7 +324,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->ampsSensor->setField(sensor.amps, this); ui->cellsSensor->setField(sensor.source, this); ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); - for (int i = 1; i <= 6; i++) + for (int i = SensorData::TELEM_CELL_INDEX_LOWEST + 1; i < SensorData::TELEM_CELL_INDEX_HIGHEST; i++) ui->cellsIndex->addItem(tr("Cell %1").arg(i), i); ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); @@ -475,8 +473,8 @@ void populateTelemetrySourcesComboBox(AutoComboBox * cb, const ModelData * model { cb->clear(); if (negative) { - for (int i=-CPN_MAX_SENSORS; i<0; ++i) { - const SensorData& sensor = model->sensorData[-i-1]; + for (int i = -CPN_MAX_SENSORS; i < 0; ++i) { + const SensorData& sensor = model->sensorData[-i - 1]; if (sensor.isAvailable()) { if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) cb->addItem(QString("-%1 (%2)").arg(sensor.label, sensor.getOrigin(model)), i); @@ -486,7 +484,7 @@ void populateTelemetrySourcesComboBox(AutoComboBox * cb, const ModelData * model } } cb->addItem("---", 0); - for (unsigned i=1; i<=CPN_MAX_SENSORS; ++i) { + for (unsigned i = 1; i <= CPN_MAX_SENSORS; ++i) { const SensorData& sensor = model->sensorData[i-1]; if (sensor.isAvailable()) { if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) @@ -572,6 +570,11 @@ void TelemetrySensorPanel::on_customContextMenuRequested(QPoint pos) contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData()); contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear())); contextMenu.addSeparator(); + contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"),this,SLOT(cmInsert()))->setEnabled(insertAllowed()); + contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"),this,SLOT(cmDelete())); + contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(cmMoveUp()))->setEnabled(moveUpAllowed()); + contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(cmMoveDown()))->setEnabled(moveDownAllowed()); + contextMenu.addSeparator(); contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"),this,SLOT(cmClearAll())); contextMenu.exec(globalPos); @@ -589,6 +592,21 @@ bool TelemetrySensorPanel::hasClipboardData(QByteArray * data) const return false; } +bool TelemetrySensorPanel::insertAllowed() const +{ + return ((selectedIndex < sensorCapability - 1) && (model->sensorData[sensorCapability - 1].isEmpty())); +} + +bool TelemetrySensorPanel::moveDownAllowed() const +{ + return selectedIndex < sensorCapability - 1; +} + +bool TelemetrySensorPanel::moveUpAllowed() const +{ + return selectedIndex > 0; +} + void TelemetrySensorPanel::cmCopy() { QByteArray data; @@ -600,8 +618,11 @@ void TelemetrySensorPanel::cmCopy() void TelemetrySensorPanel::cmCut() { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Telemetry Sensor. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + cmCopy(); - cmClear(); + cmClear(false); } void TelemetrySensorPanel::cmPaste() @@ -614,12 +635,15 @@ void TelemetrySensorPanel::cmPaste() } } -void TelemetrySensorPanel::cmClear() +void TelemetrySensorPanel::cmClear(bool prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Telemetry Sensor. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) - return; + if (prompt) { + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Telemetry Sensor. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } sensor.clear(); + model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); emit dataModified(); emit modified(); } @@ -632,6 +656,26 @@ void TelemetrySensorPanel::cmClearAll() emit clearAllSensors(); } +void TelemetrySensorPanel::cmInsert() +{ + emit insertSensor(selectedIndex); +} + +void TelemetrySensorPanel::cmDelete() +{ + emit deleteSensor(selectedIndex); +} + +void TelemetrySensorPanel::cmMoveUp() +{ + emit moveUpSensor(selectedIndex); +} + +void TelemetrySensorPanel::cmMoveDown() +{ + emit moveDownSensor(selectedIndex); +} + /******************************************************/ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): @@ -640,6 +684,10 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin { ui->setupUi(this); + sensorCapability = firmware->getCapability(Sensors); + if (sensorCapability > CPN_MAX_SENSORS) // TODO should be role of getCapability + sensorCapability = CPN_MAX_SENSORS; + if (firmware->getCapability(NoTelemetryProtocol)) { model.frsky.usrProto = 1; } @@ -648,13 +696,17 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->A1GB->hide(); ui->A2GB->hide(); - for (unsigned i= 0; i < CPN_MAX_SENSORS; ++i) { - TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, model, generalSettings, firmware); + for (int i = 0; i < sensorCapability; ++i) { + TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, firmware); ui->sensorsLayout->addWidget(panel); sensorPanels[i] = panel; connect(panel, SIGNAL(dataModified()), this, SLOT(update())); connect(panel, SIGNAL(modified()), this, SLOT(onModified())); connect(panel, SIGNAL(clearAllSensors()), this, SLOT(on_clearAllSensors())); + connect(panel, SIGNAL(insertSensor(int)), this, SLOT(on_insertSensor(int))); + connect(panel, SIGNAL(deleteSensor(int)), this, SLOT(on_deleteSensor(int))); + connect(panel, SIGNAL(moveUpSensor(int)), this, SLOT(on_moveUpSensor(int))); + connect(panel, SIGNAL(moveDownSensor(int)), this, SLOT(on_moveDownSensor(int))); } if (IS_TARANIS_X9(firmware->getBoard())) { @@ -670,7 +722,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, srcModel); - ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i+1)); + ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1)); telemetryCustomScreens[i] = tab; connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified); connect(this, &TelemetryPanel::updated, tab, &TelemetryCustomScreen::update); @@ -703,7 +755,7 @@ void TelemetryPanel::update() populateTelemetrySourcesComboBox(ui->varioSource, model, false); } - for (unsigned i=0; iupdate(); } @@ -761,10 +813,10 @@ void TelemetryPanel::setup() ui->VarioLabel_1->hide(); ui->VarioLabel_2->hide(); } - ui->varioLimitMin_DSB->setValue(model->frsky.varioMin-10); - ui->varioLimitMax_DSB->setValue(model->frsky.varioMax+10); - ui->varioLimitCenterMax_DSB->setValue((model->frsky.varioCenterMax/10.0)+0.5); - ui->varioLimitCenterMin_DSB->setValue((model->frsky.varioCenterMin/10.0)-0.5); + ui->varioLimitMin_DSB->setValue(model->frsky.varioMin - 10); + ui->varioLimitMax_DSB->setValue(model->frsky.varioMax + 10); + ui->varioLimitCenterMax_DSB->setValue((model->frsky.varioCenterMax / 10.0) + 0.5); + ui->varioLimitCenterMin_DSB->setValue((model->frsky.varioCenterMin / 10.0) - 0.5); } ui->altimetryGB->setVisible(firmware->getCapability(HasVario)), @@ -842,7 +894,7 @@ void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index) { if (!lock) { model->frsky.usrProto = index; - for (int i=0; igetCapability(TelemetryCustomScreens); i++) + for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) telemetryCustomScreens[i]->update(); emit modified(); } @@ -862,23 +914,23 @@ void TelemetryPanel::on_rssiAlarmCriticalSB_editingFinished() void TelemetryPanel::on_varioLimitMin_DSB_editingFinished() { - model->frsky.varioMin = round(ui->varioLimitMin_DSB->value()+10); + model->frsky.varioMin = round(ui->varioLimitMin_DSB->value() + 10); emit modified(); } void TelemetryPanel::on_varioLimitMax_DSB_editingFinished() { - model->frsky.varioMax = round(ui->varioLimitMax_DSB->value()-10); + model->frsky.varioMax = round(ui->varioLimitMax_DSB->value() - 10); emit modified(); } void TelemetryPanel::on_varioLimitCenterMin_DSB_editingFinished() { if (!lock) { - if (ui->varioLimitCenterMin_DSB->value()>ui->varioLimitCenterMax_DSB->value()) { + if (ui->varioLimitCenterMin_DSB->value() > ui->varioLimitCenterMax_DSB->value()) { ui->varioLimitCenterMax_DSB->setValue(ui->varioLimitCenterMin_DSB->value()); } - model->frsky.varioCenterMin = round((ui->varioLimitCenterMin_DSB->value()+0.5)*10); + model->frsky.varioCenterMin = round((ui->varioLimitCenterMin_DSB->value() + 0.5) * 10); emit modified(); } } @@ -886,10 +938,10 @@ void TelemetryPanel::on_varioLimitCenterMin_DSB_editingFinished() void TelemetryPanel::on_varioLimitCenterMax_DSB_editingFinished() { if (!lock) { - if (ui->varioLimitCenterMin_DSB->value()>ui->varioLimitCenterMax_DSB->value()) { + if (ui->varioLimitCenterMin_DSB->value() > ui->varioLimitCenterMax_DSB->value()) { ui->varioLimitCenterMax_DSB->setValue(ui->varioLimitCenterMin_DSB->value()); } - model->frsky.varioCenterMax = round((ui->varioLimitCenterMax_DSB->value()-0.5)*10); + model->frsky.varioCenterMax = round((ui->varioLimitCenterMax_DSB->value() - 0.5) * 10); emit modified(); } } @@ -917,8 +969,56 @@ void TelemetryPanel::on_clearAllSensors() { for (int i = 0; i < CPN_MAX_SENSORS; i++) { model->sensorData[i].clear(); + model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_CLEAR, i); } update(); emit modified(); } + +void TelemetryPanel::on_insertSensor(int selectedIndex) +{ + memmove(&model->sensorData[selectedIndex + 1], &model->sensorData[selectedIndex], (CPN_MAX_SENSORS - (selectedIndex + 1)) * sizeof(SensorData)); + model->sensorData[selectedIndex].clear(); + model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1); + + update(); + emit modified(); +} + +void TelemetryPanel::on_deleteSensor(int selectedIndex) +{ + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Sensor. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + + memmove(&model->sensorData[selectedIndex], &model->sensorData[selectedIndex + 1], (CPN_MAX_SENSORS - (selectedIndex + 1)) * sizeof(SensorData)); + model->sensorData[CPN_MAX_SENSORS - 1].clear(); + model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1); + + update(); + emit modified(); +} + +void TelemetryPanel::on_moveUpSensor(int selectedIndex) +{ + swapData(selectedIndex, selectedIndex - 1); +} + +void TelemetryPanel::on_moveDownSensor(int selectedIndex) +{ + swapData(selectedIndex, selectedIndex + 1); +} + +void TelemetryPanel::swapData(int idx1, int idx2) +{ + if ((idx1 != idx2) && (!model->sensorData[idx1].isEmpty() || !model->sensorData[idx2].isEmpty())) { + SensorData sdtmp = model->sensorData[idx2]; + SensorData *sd1 = &model->sensorData[idx1]; + SensorData *sd2 = &model->sensorData[idx2]; + memcpy(sd2, sd1, sizeof(SensorData)); + memcpy(sd1, &sdtmp, sizeof(SensorData)); + model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SWAP, idx1, idx2); + update(); + emit modified(); + } +} diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 28dc7643c..cddade217 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -71,13 +71,18 @@ class TelemetrySensorPanel: public ModelPanel Q_OBJECT public: - TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); + TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); ~TelemetrySensorPanel(); void update(); signals: void dataModified(); void clearAllSensors(); + void insertSensor(int index); + void deleteSensor(int index); + void moveUpSensor(int index); + void moveDownSensor(int index); + protected slots: void on_name_editingFinished(); @@ -90,8 +95,12 @@ class TelemetrySensorPanel: public ModelPanel void cmCopy(); void cmCut(); void cmPaste(); - void cmClear(); + void cmClear(bool prompt = true); void cmClearAll(); + void cmInsert(); + void cmDelete(); + void cmMoveUp(); + void cmMoveDown(); protected: void updateSourcesComboBox(AutoComboBox * cb, bool negative); @@ -102,6 +111,10 @@ class TelemetrySensorPanel: public ModelPanel bool lock = false; int sensorIndex = 0; int selectedIndex = 0; + int sensorCapability; + bool insertAllowed() const; + bool moveDownAllowed() const; + bool moveUpAllowed() const; }; class TelemetryPanel : public ModelPanel @@ -131,17 +144,23 @@ class TelemetryPanel : public ModelPanel void on_mahCount_SB_editingFinished(); void on_mahCount_ChkB_toggled(bool checked); void on_clearAllSensors(); + void on_insertSensor(int index); + void on_deleteSensor(int index); + void on_moveUpSensor(int index); + void on_moveDownSensor(int index); private: Ui::Telemetry *ui; TelemetryCustomScreen * telemetryCustomScreens[4]; TelemetrySensorPanel * sensorPanels[CPN_MAX_SENSORS]; + int sensorCapability; void setup(); void telBarUpdate(); void populateVoltsSource(); void populateCurrentSource(); void populateVarioSource(); + void swapData(int idx1, int idx2); }; #endif // _TELEMETRY_H_ From 74d9c2c73ca4dd48fd6d826d2d9c88ccf376491b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 19:00:12 +0200 Subject: [PATCH 181/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8844ebe23..11e7dc473 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1893,3 +1893,4 @@ Weldon Parks Simon Brink Weldon Parks Ralf Herzenbruch +Ashley Pearce From c7f35e7eb8feae0b2ba3d2dd3654dd3de6398364 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 4 Sep 2020 20:54:24 +0200 Subject: [PATCH 182/267] All radios have 32 curves (fix #7919) --- companion/src/firmwares/opentx/opentxeeprom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 184447808..63b79d147 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -145,7 +145,7 @@ inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version) #define MAX_EXPOS(board, version) (IS_HORUS_OR_TARANIS(board) ? 64 : 32) #define MAX_LOGICAL_SWITCHES(board, version) (version >= 218 ? 64 : 32) #define MAX_CUSTOM_FUNCTIONS(board, version) 64 -#define MAX_CURVES(board, version) (version >= 219 ? 32 : 16) +#define MAX_CURVES(board, version) ((version >= 219 || HAS_LARGE_LCD(board)) ? 32 : 16) #define MAX_GVARS(board, version) 9 #define MAX_SCRIPTS(board) (IS_FAMILY_HORUS_OR_T16(board) ? 9 : 7) #define MAX_TELEMETRY_SENSORS(board, version) (version <= 218 ? 32 : ((IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board)) ? 60 : 40)) From 7f5dc552eff20d53ede4096b558f846c50d1b775 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 23:14:01 +0200 Subject: [PATCH 183/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 11e7dc473..75a0c1f2a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1894,3 +1894,5 @@ Simon Brink Weldon Parks Ralf Herzenbruch Ashley Pearce +SneakIT + From 159a891ab4b33f2dde5306cdc4bff5890da795dd Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 5 Sep 2020 08:24:07 +0200 Subject: [PATCH 184/267] Asym ghost support (#7910) --- radio/src/pulses/ghost.cpp | 6 ++++- radio/src/targets/common/arm/CMakeLists.txt | 1 - radio/src/targets/horus/CMakeLists.txt | 1 + radio/src/targets/horus/telemetry_driver.cpp | 10 ++++++++ radio/src/targets/sky9x/CMakeLists.txt | 1 + radio/src/targets/taranis/CMakeLists.txt | 1 + .../src/targets/taranis/telemetry_driver.cpp | 10 ++++++++ radio/src/telemetry/ghost.cpp | 22 ++++++++++++++++++ radio/src/telemetry/ghost.h | 23 +++++++++---------- radio/src/telemetry/telemetry.h | 5 ++-- 10 files changed, 64 insertions(+), 16 deletions(-) diff --git a/radio/src/pulses/ghost.cpp b/radio/src/pulses/ghost.cpp index 6c5ec243b..478202b57 100755 --- a/radio/src/pulses/ghost.cpp +++ b/radio/src/pulses/ghost.cpp @@ -39,7 +39,11 @@ uint8_t createGhostChannelsFrame(uint8_t * frame, int16_t * pulses) } uint8_t * buf = frame; - *buf++ = GHST_ADDR_MODULE; +#if SPORT_MAX_BAUDRATE < 400000 + *buf++ = g_eeGeneral.telemetryBaudrate == GHST_TELEMETRY_RATE_400K ? GHST_ADDR_MODULE_SYM : GHST_ADDR_MODULE_ASYM; +#else + *buf++ = GHST_ADDR_MODULE_SYM; +#endif *buf++ = GHST_UL_RC_CHANS_SIZE; uint8_t * crc_start = buf; *buf++ = lastGhostFrameId; diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index b6c6b639e..64e998597 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -14,7 +14,6 @@ option(PPM "PPM TX Module" ON) option(DSM2 "DSM2 TX Module" ON) option(SBUS "SBUS TX Module" ON) option(CROSSFIRE "Crossfire TX Module" ON) -option(GHOST "Ghost TX Module" ON) option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) option(DEBUG_INTERRUPTS "Count interrupts" OFF) option(DEBUG_LATENCY "Debug latency" OFF) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 68d20e733..d8778fc78 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -4,6 +4,7 @@ option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) option(AFHDS3 "AFHDS3 TX Module" ON) option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON) +option(GHOST "Ghost TX Module" ON) set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F4) diff --git a/radio/src/targets/horus/telemetry_driver.cpp b/radio/src/targets/horus/telemetry_driver.cpp index 23318dd7e..0288363be 100644 --- a/radio/src/targets/horus/telemetry_driver.cpp +++ b/radio/src/targets/horus/telemetry_driver.cpp @@ -240,6 +240,11 @@ void telemetryPortInvertedRxBit() void telemetryPortSetDirectionOutput() { +#if defined(GHOST) && SPORT_MAX_BAUDRATE < 400000 + if (isModuleGhost(EXTERNAL_MODULE)) { + TELEMETRY_USART->BRR = BRR_400K; + } +#endif TELEMETRY_DIR_GPIO->BSRRL = TELEMETRY_DIR_GPIO_PIN; // output enable TELEMETRY_USART->CR1 &= ~USART_CR1_RE; // turn off receiver } @@ -252,6 +257,11 @@ void sportWaitTransmissionComplete() void telemetryPortSetDirectionInput() { sportWaitTransmissionComplete(); +#if defined(GHOST) && SPORT_MAX_BAUDRATE < 400000 + if (isModuleGhost(EXTERNAL_MODULE) && g_eeGeneral.telemetryBaudrate == GHST_TELEMETRY_RATE_115K) { + TELEMETRY_USART->BRR = BRR_115K; + } +#endif TELEMETRY_DIR_GPIO->BSRRH = TELEMETRY_DIR_GPIO_PIN; // output disable TELEMETRY_USART->CR1 |= USART_CR1_RE; // turn on receiver } diff --git a/radio/src/targets/sky9x/CMakeLists.txt b/radio/src/targets/sky9x/CMakeLists.txt index 4188444fe..181b5ab85 100644 --- a/radio/src/targets/sky9x/CMakeLists.txt +++ b/radio/src/targets/sky9x/CMakeLists.txt @@ -11,6 +11,7 @@ set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} 9x_bitmaps) set(PPM_LIMITS_SYMETRICAL YES) set(PXX1 YES) set(AFHDS3 NO) +set(GHOST NO) add_definitions(-DDISABLE_MULTI_UPDATE) if(PCB STREQUAL 9XRPRO) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index b00c7dfba..95d1b423b 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -3,6 +3,7 @@ option(LCD_DUAL_BUFFER "Dual LCD Buffer" OFF) option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) option(AFHDS3 "AFHDS3 TX Module" OFF) +option(GHOST "Ghost TX Module" ON) option(INTERNAL_MODULE_PPM "Support for PPM internal module" OFF) option(AUTOUPDATE "Auto update internal chips from SD" OFF) diff --git a/radio/src/targets/taranis/telemetry_driver.cpp b/radio/src/targets/taranis/telemetry_driver.cpp index b46eca541..f0068232b 100644 --- a/radio/src/targets/taranis/telemetry_driver.cpp +++ b/radio/src/targets/taranis/telemetry_driver.cpp @@ -203,6 +203,11 @@ void telemetryPortInvertedRxBit() void telemetryPortSetDirectionOutput() { +#if defined(GHOST) && SPORT_MAX_BAUDRATE < 400000 + if (isModuleGhost(EXTERNAL_MODULE)) { + TELEMETRY_USART->BRR = BRR_400K; + } +#endif TELEMETRY_DIR_OUTPUT(); TELEMETRY_USART->CR1 &= ~USART_CR1_RE; // turn off receiver } @@ -215,6 +220,11 @@ void sportWaitTransmissionComplete() void telemetryPortSetDirectionInput() { sportWaitTransmissionComplete(); +#if defined(GHOST) && SPORT_MAX_BAUDRATE < 400000 + if (isModuleGhost(EXTERNAL_MODULE) && g_eeGeneral.telemetryBaudrate == GHST_TELEMETRY_RATE_115K) { + TELEMETRY_USART->BRR = BRR_115K; + } +#endif TELEMETRY_DIR_INPUT(); TELEMETRY_USART->CR1 |= USART_CR1_RE; // turn on receiver } diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 1146b9555..d3ef0ee03 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -125,6 +125,11 @@ void processGhostTelemetryFrame() switch(id) { case GHST_DL_LINK_STAT: { +#if defined(BLUETOOTH) + if (g_eeGeneral.bluetoothMode == BLUETOOTH_TELEMETRY && bluetooth.state == BLUETOOTH_STATE_CONNECTED) { + bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); + } +#endif uint8_t rssiVal = min(telemetryRxBuffer[3], 100); uint8_t lqVal = min(telemetryRxBuffer[4], 100); uint8_t snrVal = min(telemetryRxBuffer[5], 100); @@ -157,6 +162,11 @@ void processGhostTelemetryFrame() case GHST_DL_VTX_STAT: { +#if defined(BLUETOOTH) + if (g_eeGeneral.bluetoothMode == BLUETOOTH_TELEMETRY && bluetooth.state == BLUETOOTH_STATE_CONNECTED) { + bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); + } +#endif uint8_t vtxBandEnum = min(telemetryRxBuffer[8], GHST_VTX_BAND_MAX); const GhostSensor * sensor = getGhostSensor(GHOST_ID_VTX_BAND); @@ -173,6 +183,18 @@ void processGhostTelemetryFrame() void processGhostTelemetryData(uint8_t data) { +#if defined(AUX_SERIAL) + if (g_eeGeneral.auxSerialMode == UART_MODE_TELEMETRY_MIRROR) { + auxSerialPutc(data); + } +#endif + +#if defined(AUX2_SERIAL) + if (g_eeGeneral.aux2SerialMode == UART_MODE_TELEMETRY_MIRROR) { + aux2SerialPutc(data); + } +#endif + if (telemetryRxBufferCount == 0 && data != GHST_ADDR_RADIO) { TRACE("[GH] address 0x%02X error", data); return; diff --git a/radio/src/telemetry/ghost.h b/radio/src/telemetry/ghost.h index 57adb8f96..8a27c7868 100755 --- a/radio/src/telemetry/ghost.h +++ b/radio/src/telemetry/ghost.h @@ -26,7 +26,8 @@ // Device (destination) address #define GHST_ADDR_RADIO 0x80 // phase 1 -#define GHST_ADDR_MODULE 0x81 +#define GHST_ADDR_MODULE_SYM 0x81 // symmetrical, 400k pulses, 400k telemetry +#define GHST_ADDR_MODULE_ASYM 0x88 // asymmetrical, 400k pulses, 115k telemetry #define GHST_ADDR_FC 0x82 #define GHST_ADDR_GOGGLES 0x83 // phase 2 #define GHST_ADDR_5G_TXCTRL 0x84 // phase 3 @@ -96,19 +97,17 @@ void ghostSetDefault(int index, uint8_t id, uint8_t subId); #if SPORT_MAX_BAUDRATE < 400000 // For radios which can't support telemetry at high rates, offer baud rate choices // (modified vs. unmodified radios) -const uint32_t GHOST_BAUDRATES[] = { - 400000, - 115200, + +constexpr uint16_t BRR_400K = PERI1_FREQUENCY / 400000; +constexpr uint16_t BRR_115K = PERI1_FREQUENCY / 115200; + +enum GhostTelemetryBaudrates +{ + GHST_TELEMETRY_RATE_400K, + GHST_TELEMETRY_RATE_115K }; -const uint8_t GHOST_PERIODS[] = { - 4, - 16, -}; -#define GHOST_BAUDRATE GHOST_BAUDRATES[g_eeGeneral.telemetryBaudrate] -#define GHOST_PERIOD GHOST_PERIODS[g_eeGeneral.telemetryBaudrate] -#else +#endif #define GHOST_BAUDRATE 400000 #define GHOST_PERIOD 4 -#endif #endif // _GHOST_H_ diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 5680a837f..e78846845 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -23,10 +23,11 @@ #include "frsky.h" #include "crossfire.h" -#include "ghost.h" #include "myeeprom.h" #include "io/frsky_sport.h" - +#if defined(GHOST) + #include "ghost.h" +#endif #if defined(MULTIMODULE) #include "spektrum.h" #include "hitec.h" From 140ad1d891c8e16f83f3302fa88ab8485cb90e0a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 5 Sep 2020 14:14:43 +0200 Subject: [PATCH 185/267] Update CREDITS.txt --- CREDITS.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CREDITS.txt b/CREDITS.txt index 75a0c1f2a..670e5e6b4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1895,4 +1895,4 @@ Weldon Parks Ralf Herzenbruch Ashley Pearce SneakIT - +Soaring Stuff From cf72dd28bcce2460bf9889e3b265d9d5366ae956 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 5 Sep 2020 23:25:13 +0200 Subject: [PATCH 186/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 670e5e6b4..b815ec716 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1896,3 +1896,4 @@ Ralf Herzenbruch Ashley Pearce SneakIT Soaring Stuff +Jörg Lemnitz From 3323597a8a5c297990c4bfd5e1383bbfabd9aa10 Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Sun, 6 Sep 2020 18:58:33 +1000 Subject: [PATCH 187/267] Companion fix global and special functions context menu (#7885) --- companion/src/modeledit/customfunctions.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 1bf06cfd6..36dba9c97 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -587,7 +587,7 @@ void CustomFunctionsPanel::update() { updateDataModels(); lock = true; - for (int i=0; iclear(); - for (unsigned int i=0; i=FuncOverrideCH1 && i<=FuncOverrideCH32) && (!model || !firmware->getCapability(SafetyChannelCustomFunction))) || ((i==FuncVolume || i==FuncBackgroundMusic || i==FuncBackgroundMusicPause) && !firmware->getCapability(HasVolume)) || ((i==FuncPlayScript && !IS_HORUS_OR_TARANIS(firmware->getBoard()))) || @@ -677,7 +677,7 @@ void CustomFunctionsPanel::populateFuncCB(QComboBox *b, unsigned int value) else { b->addItem(CustomFunctionData(AssignFunc(i)).funcToString(model), i); if (i == value) { - b->setCurrentIndex(b->count()-1); + b->setCurrentIndex(b->count() - 1); } } } @@ -748,7 +748,7 @@ void CustomFunctionsPanel::cmMoveDown() void CustomFunctionsPanel::cmClear(bool prompt) { if (prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; } @@ -759,10 +759,10 @@ void CustomFunctionsPanel::cmClear(bool prompt) void CustomFunctionsPanel::cmClearAll() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Special Functions. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Functions. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; - for (int i=0; icurves[fswCapability - 1].isEmpty())); + return ((selectedIndex < fswCapability - 1) && (functions[fswCapability - 1].isEmpty())); } bool CustomFunctionsPanel::moveDownAllowed() const From 6fa0382608a32c533a0ebbfe5478927148d8d0dc Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Sun, 6 Sep 2020 18:59:37 +1000 Subject: [PATCH 188/267] Fix channel reorder affect on mixes (#7911) --- companion/src/firmwares/modeldata.cpp | 44 +++++++++++++++++++++++++++ companion/src/firmwares/modeldata.h | 1 + 2 files changed, 45 insertions(+) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index d38069827..e38958249 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -659,6 +659,8 @@ int ModelData::updateReference() } } } + if (updRefInfo.type == REF_UPD_TYPE_CHANNEL) + sortMixes(); //s1.report("Mixes"); for (int i = 0; i < CPN_MAX_CHNOUT; i++) { @@ -1330,3 +1332,45 @@ void ModelData::removeMix(const int idx) memmove(&mixData[idx], &mixData[idx + 1], (CPN_MAX_MIXERS - (idx + 1)) * sizeof(MixData)); mixData[CPN_MAX_MIXERS - 1].clear(); } + +void ModelData::sortMixes() +{ + unsigned int lastchn = 0; + bool sortreq = false; + + for (int i = 0; i < CPN_MAX_MIXERS; i++) { + MixData *md = &mixData[i]; + if (!md->isEmpty()) { + if (md->destCh < lastchn) { + sortreq = true; + break; + } + else + lastchn = md->destCh; + } + } + + if (!sortreq) + return; + + // QMap automatically sorts based on key + QMap map; + for (int i = 0; i < CPN_MAX_MIXERS; i++) { + MixData *md = &mixData[i]; + if (!md->isEmpty()) { + // destCh may not be unique so build a compound sort key + map.insert(md->destCh * (CPN_MAX_MIXERS + 1) + i, i); + } + } + + MixData sortedMixData[CPN_MAX_MIXERS]; + int destidx = 0; + + QMap::const_iterator i; + for (i = map.constBegin(); i != map.constEnd(); ++i) { + memcpy(&sortedMixData[destidx], &mixData[i.value()], sizeof(MixData)); + destidx++; + } + + memcpy(&mixData[0], &sortedMixData[0], CPN_MAX_MIXERS * sizeof(MixData)); +} diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 560206c72..558b848df 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -336,6 +336,7 @@ class ModelData { if (value != swtch.toValue()) value = swtch.toValue(); } + void sortMixes(); }; #endif // MODELDATA_H From 6e7e7290d42c7373326fc729cbfc70ddb7d22a14 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 7 Sep 2020 17:35:07 +0200 Subject: [PATCH 189/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index b815ec716..fb3df8197 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1897,3 +1897,4 @@ Ashley Pearce SneakIT Soaring Stuff Jörg Lemnitz +Johann Gross From 7a5f14344317464a1f9479d199c0626e4aaa5627 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 8 Sep 2020 07:36:06 +0200 Subject: [PATCH 190/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index fb3df8197..7d1d1d8a7 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1898,3 +1898,5 @@ SneakIT Soaring Stuff Jörg Lemnitz Johann Gross +Mark Gibson +Rendall Assoc From 15ea27662dfcf9e8e79a57c12afec98f51440ec5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 8 Sep 2020 22:05:43 +0200 Subject: [PATCH 191/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7d1d1d8a7..1788fd47f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1900,3 +1900,4 @@ Jörg Lemnitz Johann Gross Mark Gibson Rendall Assoc +Cozyn Holdings Pty Ltd From 65798595f6e091b70cabb046958db9b8f7717db5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 8 Sep 2020 22:06:10 +0200 Subject: [PATCH 192/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1788fd47f..93624a29a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1901,3 +1901,4 @@ Johann Gross Mark Gibson Rendall Assoc Cozyn Holdings Pty Ltd +Tim Karsten From a6236c181db0a7d77feb4ebb41106c808b6bd63b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 9 Sep 2020 19:33:23 +0200 Subject: [PATCH 193/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 93624a29a..be3927510 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1902,3 +1902,4 @@ Mark Gibson Rendall Assoc Cozyn Holdings Pty Ltd Tim Karsten +Nick Hesson From a5a17abd4121fde891e32f9b20f6925a59ea6206 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 8 Sep 2020 09:08:06 +0200 Subject: [PATCH 194/267] Ghost RSSI is negative (as it should) --- radio/src/telemetry/ghost.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index d3ef0ee03..da4d4164d 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -130,11 +130,11 @@ void processGhostTelemetryFrame() bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); } #endif - uint8_t rssiVal = min(telemetryRxBuffer[3], 100); + uint8_t rssiVal = min(telemetryRxBuffer[3], 120); // RSSI is a negative value, but sent as a positive integer. uint8_t lqVal = min(telemetryRxBuffer[4], 100); uint8_t snrVal = min(telemetryRxBuffer[5], 100); - processGhostTelemetryValue(GHOST_ID_RX_RSSI, rssiVal); + processGhostTelemetryValue(GHOST_ID_RX_RSSI, - rssiVal); processGhostTelemetryValue(GHOST_ID_RX_LQ, lqVal); processGhostTelemetryValue(GHOST_ID_RX_SNR, snrVal); From e47b389c73a8380a240e5b242a3c7fc5f2d98c42 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 11 Sep 2020 17:05:07 +0200 Subject: [PATCH 195/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index be3927510..edbe1fbfa 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1903,3 +1903,4 @@ Rendall Assoc Cozyn Holdings Pty Ltd Tim Karsten Nick Hesson +Laurent Lapassade From 1833dd155e1a9fd0f51aa2ecbb78b14185620402 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 11 Sep 2020 18:27:27 +0200 Subject: [PATCH 196/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index edbe1fbfa..74195ba62 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1904,3 +1904,4 @@ Cozyn Holdings Pty Ltd Tim Karsten Nick Hesson Laurent Lapassade +Peter Deacon From fc25445fa6f1c1d6efa6a45baac2cb608ccc15ea Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 12 Sep 2020 15:48:27 +0200 Subject: [PATCH 197/267] Update CREDITS.txt --- CREDITS.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 74195ba62..85faf1c00 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1905,3 +1905,7 @@ Tim Karsten Nick Hesson Laurent Lapassade Peter Deacon +Gilles Dando +Philippe Desrichard +Jón Haase +Michael Hansen From cc4a5f8de712b4ecf2afbc53eced2a94aa334a23 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 13 Sep 2020 00:20:21 +0200 Subject: [PATCH 198/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 85faf1c00..c48bf10b8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1909,3 +1909,4 @@ Gilles Dando Philippe Desrichard Jón Haase Michael Hansen +Sylvain Vanston From 6931ee67bf78ee3849a629e0b9715f8059e41969 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 13 Sep 2020 11:58:00 +0200 Subject: [PATCH 199/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c48bf10b8..946270ff6 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1910,3 +1910,4 @@ Philippe Desrichard Jón Haase Michael Hansen Sylvain Vanston +Helmut Nemecek From 485de062bb5ffcedcca7ce945b58fa35ef8a5345 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 13 Sep 2020 14:01:30 +0200 Subject: [PATCH 200/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 946270ff6..4d8aeccc1 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1911,3 +1911,4 @@ Jón Haase Michael Hansen Sylvain Vanston Helmut Nemecek +Alexander Mathis From b13ace4de2704515adadf5bf4214a2009769a883 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 20:51:45 +0200 Subject: [PATCH 201/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4d8aeccc1..0b934a22b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1912,3 +1912,4 @@ Michael Hansen Sylvain Vanston Helmut Nemecek Alexander Mathis +Jay Watkins From a39de626a8996791ddd20ae7ad64cc17fcf7c7a4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 22:56:26 +0200 Subject: [PATCH 202/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0b934a22b..1e5b33dfc 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1913,3 +1913,5 @@ Sylvain Vanston Helmut Nemecek Alexander Mathis Jay Watkins +Patrice Oudjaoudi +Didier Hochedez From c519ae9f2856ab6b7fad9fd7470b608ebc469aef Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 15 Sep 2020 07:56:21 +0200 Subject: [PATCH 203/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1e5b33dfc..4b60f05f0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1915,3 +1915,4 @@ Alexander Mathis Jay Watkins Patrice Oudjaoudi Didier Hochedez +Robert Hunter From a643c05e5c3c5b6e9294dc8b8d248f9f63bac5e8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 15 Sep 2020 12:06:50 +0200 Subject: [PATCH 204/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4b60f05f0..c77cce6d3 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1916,3 +1916,4 @@ Jay Watkins Patrice Oudjaoudi Didier Hochedez Robert Hunter +Christophe Gilsoul From 1496fc081b3842606de3a2b704bf79b22ec058f2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 15 Sep 2020 13:03:24 +0200 Subject: [PATCH 205/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c77cce6d3..029ce7c83 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1917,3 +1917,4 @@ Patrice Oudjaoudi Didier Hochedez Robert Hunter Christophe Gilsoul +Lincoln Roberts From 2d8cadb38805d9e1921a4718cb573f3e4eac6694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Charpentier?= Date: Tue, 15 Sep 2020 19:45:08 +0200 Subject: [PATCH 206/267] Updated French Translation for Companion. (#7938) --- companion/src/translations/companion_fr.ts | 641 ++++++++++++--------- 1 file changed, 378 insertions(+), 263 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 7bba065c5..9b18ec30e 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -908,57 +908,62 @@ Manche Droit: Profondeur, Direction Supprimer la Voie. Êtes-vous sûr? - + + Cut Channel. Are you sure? + Couper la Voie. Êtes-vous sûr? + + + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Channel. Are you sure? Effacer la Voie. Êtes-vous sûr? - + Clear all Channels. Are you sure? Effacer toutes les Voies. Êtes-vous sûr? @@ -1596,17 +1601,22 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Effacer Tout - + Clear Curve. Are you sure? Effacer la Courbe. Êtes-vous sûr? - + Clear all Curves. Are you sure? Effacer toutes les Courbes. Êtes-vous sûr? - + + Cut Curve. Are you sure? + Couper la Courbe. Êtes-vous sûr? + + + Delete Curve. Are you sure? Supprimer la Courbe. Êtes-vous sûr? @@ -1640,8 +1650,8 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import - Trainer - Écolage Manches + Trainer Sticks + Manches Écolage @@ -1917,83 +1927,88 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import - Delete Special Function. Are you sure? - Supprimer la Fonction Spéciale. Êtes-vous sûr? + Delete Function. Are you sure? + Supprimer la Fonction. Êtes-vous sûr? - + + Cut Special Function. Are you sure? + Couper la Fonction. Êtes-vous sûr? + + + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Value Valeur - + Source Source - + GVAR VG - + Increment Incrément - - Clear Special Function. Are you sure? - Effacer la Fonction Spéciale. Êtes-vous sûr? + + Clear Function. Are you sure? + Effacer la Fonction. Êtes-vous sûr? - - Clear all Special Functions. Are you sure? - Supprimer toutes les Fonctions Spéciales. Êtes-vous sûr? + + Clear all Functions. Are you sure? + Effacer toutes les Fonctions. Êtes-vous sûr? @@ -2159,48 +2174,48 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Erreur de conversion du champ %1 - + Switch Interrupteur - + Switch L'interrupteur - + cannot be exported on this board! n'est pas supporté sur cette carte ! - + Source - + Source %1 cannot be exported on this board! La source %1 n'est pas supportée sur cette plateforme ! - + OpenTX only accepts %1 points in all curves OpenTX n'accepte que %1 points au maximum entre toutes les courbes - + OpenTx only accepts %1 points in all curves OpenTX n'accepte que %1 points au maximum entre toutes les courbes - - + + OpenTX on this board doesn't accept this function OpenTX ne supporte pas cette fonction sur cette carte - + OpenTX doesn't accept this radio protocol OpenTX ne supporte pas ce protocole radio @@ -2922,255 +2937,255 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Firmware - + Channel values displayed in us Positions de voies affichées en µs - + No OverrideCH functions available Désactive la fonction spéciale "Remplacer VOIExx" - + Possibility to enable FAI MODE (no telemetry) at field Possibilité d'activer le mode FAI (télémétrie désactivée) sur le terrain - + FAI MODE (no telemetry) always enabled Mode FAI (télémétrie toujours désactivée) - + Removes D8 FrSky protocol support which is not legal for use in the EU on radios sold after Jan 1st, 2015 Masque le protocole D8. Légalement obligatoire sur les radios Européennes importées après le 1er janvier 2015 - - + + Disable HELI menu and cyclic mix support Supprimer le menu HELICO et les mixages cycliques - - + + Disable Global variables Supprimer le support des variables globales - - + + Enable Lua custom scripts screen Activer l'écran des scripts Lua personnalisés - + Use alternative SQT5 font Utiliser la police alternative SQT5 - - + + Disable RAS (SWR) Désactiver le RAS (SWR). Utile uniquement pour les X9D+ avec un RAS/SWR non fonctionnel - + Pots use in menus navigation Support de la navigation dans les menus avec les POTS - + Enable AFHDS3 support Activer le support du AFHDS3 - + FrSky Taranis X9D+ - + FrSky Taranis X9D+ 2019 - + FrSky Taranis X9D - + Haptic module installed Module vibreur installé - + FrSky Taranis X9E - + Confirmation before radio shutdown Confirmation avant l'arrêt de la radio - + Horus gimbals installed (Hall sensors) Manches Horus à effet HALL installés - + FrSky Taranis X9-Lite - + FrSky Taranis X9-Lite S - + FrSky Taranis X7 / X7S - + FrSky Taranis X7 / X7S Access - + FrSky Taranis X-Lite S/PRO - + FrSky Taranis X-Lite - + FrSky Horus X10 / X10S - - + + Support for ACCESS internal module replacement Prise en charge du module de remplacement interne ACCESS - + FrSky Horus X10 Express / X10S Express - + FrSky Horus X12S - + Use ONLY with first DEV pcb version Exclusivement pour les Horus DEV - - + + Support for MULTI internal module Prise en charge du module MULTI interne - - - + + + Support for bluetooth module Prise en charge du module Bluetooth - + Radiomaster TX16s / TX16s Hall / TX16s Masterfire - + Support internal GPS Prise en charge du GPS interne - + Jumper T18 - + Turnigy 9XR-PRO - + Enable HELI menu and cyclic mix support Activer le menu hélico et les mixages CCPM - + Global variables Variables globales - + Enable non certified firmwares Activer les firmwares non certifiés - + In model setup menus automatically set source by moving the control Sélectionner les sources des mixeurs en bougeant le contrôle désiré - + In model setup menus automatically set switch by moving the control Sélectionner les interrupteurs en bougeant le contrôle désiré - + No graphical check boxes and sliders Pas de cases à cocher et de curseurs graphiques - + Battery graph Graphique de la batterie - + Don't use bold font for highlighting active items Ne pas mettre les lignes actives en gras - + Enable resetting values by pressing up and down at the same time Permet la remise à zéro des valeurs en pressant haut-bas en même temps, valeur min avec gauche/bas, valeur max avec haut/droite, inversion avec gauche/droite - + 9X with AR9X board 9X avec carte AR9X - + Jumper T12 / T12 Pro - + Jumper T16 / T16+ / T16 Pro - + 9X with Sky9x board 9X avec carte Sky9x @@ -3492,43 +3507,43 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Transférer le firmware à la radio - - + + Firmware check failed La vérification a échoué - + Could not check firmware from radio Impossible de vérifier le firmware de la radio - + New firmware is not compatible with the one currently installed! Le nouveau firmware ne correspond pas à la version installée, vérifiez le type de radio ! - + Conversion failed La conversion a échoué - + Cannot convert Models and Settings for use with this firmware, original data will be used Impossible de convertir les paramètres et modèles pour ce firmware, les données originales vont être utilisées - + Restore failed Echec de la restauration - + Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1 Impossible de restaurer les paramètres et modèles sur la radio. Le fichier peut être récupéré ici: %1 - + Flashing done Programmation terminée @@ -3723,79 +3738,109 @@ Vous utilisez actuellement: - + Copy Copier - + Cut Couper - + Paste Coller - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Flight Mode. Are you sure? Effacer la Phase de Vol. Êtes-vous sûr? - + Clear all Flight Modes. Are you sure? Effacer toutes les Phases de Vol. Êtes-vous sûr? - + + Cut Flight Mode. Are you sure? + Couper la Phase de Vol. Êtes-vous sûr? + + + Delete Flight Mode. Are you sure? Supprimer la Phase de Vol. Êtes-vous sûr? - - Clear selected Global Variable across all Flight Modes. Are you sure? - Effacer les Variables Globales selectionnées à travers toutes les Phases de Vol. Êtes-vous sûr? + + Clear Global Variable across all Flight Modes. Are you sure? + Effacer la Variable Globale pour toutes les Phases de Vol. Êtes-vous sûr? - + + Clear Global Variable. Are you sure? + Effacer la Variable Globale. Êtes-vous sûr? + + + + Cut Global Variable across all Flight Modes. Are you sure? + Couper la Variable Globale pour toutes les Phases de Vol. Êtes-vous sûr? + + + + Cut Global Variable. Are you sure? + Couper la Variable Globale. Êtes-vous sûr? + + + + Delete Global Variable. Are you sure? + Supprimer la Variable Globale. Êtes-vous sûr? + + + + Paste to selected Global Variable across all Flight Modes. Are you sure? + Coller la Variable Globale pour toutes les Phases de Vol. Êtes-vous sûr? + + + Clear all Global Variables for all Flight Modes. Are you sure? Effacer toutes les Variables Globales pour toutes les Phases de Vol. Êtes-vous sûr? - + Clear all Global Variables for this Flight Mode. Are you sure? Effacer toutes les Variables Globales pour cette Phase de Vol. Êtes-vous sûr? @@ -3842,7 +3887,7 @@ Vous utilisez actuellement: - + Clear Effacer @@ -3850,17 +3895,17 @@ Vous utilisez actuellement: FlightModesPanel - + Flight Mode %1 Phase de vol %1 - + (%1) - + (default) (par défaut) @@ -5726,9 +5771,14 @@ Cette fonction ne peut pas être désactivée sur la radio. Pas assez d'Entrées disponibles! - - Delete selected Inputs. Are you sure? - Supprimer les Entrées selectionnés. Êtes-vous sûr? + + Delete selected Input lines. Are you sure? + Supprimer les lignes Entrée sélectionnées. Êtes-vous sûr? + + + + Cut selected Input lines. Are you sure? + Couper les lignes Entrée sélectionnées. Êtes-vous sûr? @@ -5828,18 +5878,18 @@ Cette fonction ne peut pas être désactivée sur la radio. - Clear all Inputs. Are you sure? - Effacer toutes les Entrées. Êtes-vous sûr? + Clear all Input lines. Are you sure? + Effacer toutes les lignes Entrée. Êtes-vous sûr? - Clear Input. Are you sure? - Effacer l'Entrée. Êtes-vous sûr? + Clear all lines for the selected Inputs. Are you sure? + Effacer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? - Delete Input. Are you sure? - Supprimer l'Entrée. Êtes-vous sûr? + Delete all lines for the selected Inputs. Are you sure? + Supprimer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? @@ -6044,52 +6094,62 @@ Cette fonction ne peut pas être désactivée sur la radio. Supprimer l'Interrupteur Logique. Êtes-vous sûr? - + + Cut Logical Switch. Are you sure? + Couper l'Interrupteur Logique. Êtes-vous sûr? + + + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + + Clear Logical Switch. Are you sure? + Effacer l'Interrupteur Logique. Êtes-vous sûr? + + + Clear all Logical Switches. Are you sure? Effacer tous les Interrupteurs Logiques. Êtes-vous sûr? @@ -7883,25 +7943,25 @@ Mixage actif par défaut si non-renseigné. MixesPanel - + Move Up Monter - + Ctrl+Up Ctrl+Haut - + Move Down Descendre - + Ctrl+Down Ctrl+Bas @@ -7916,97 +7976,102 @@ Mixage actif par défaut si non-renseigné. Plus de mixages disponibles ! - - Delete Selected Mixes? - Supprimer les mixages sélectionnés ? + + Delete selected Mix lines. Are you sure? + Supprimer les lignes Mixeur sélectionnées. Êtes-vous sûr? - + + Cut selected Mix lines. Are you sure? + Couper les lignes Mixeur sélectionnées. Êtes-vous sûr? + + + &Add &Ajouter - + Ctrl+A - + &Edit &Édition - + Enter Entrée - + Ctrl+T - + &Toggle highlight &(Dé)sélectionner - + &Delete &Supprimer - + Delete Supprimer - + &Copy &Copier - + Ctrl+C - + Ctrl+X - + C&ut &Couper - + &Paste &Coller - + Ctrl+V - + Du&plicate &Dupliquer - + Ctrl+U - + Clear Mixes? Effacer tous les mixages ? - + Really clear all the mixes? Êtes-vous sûr de vraiment vouloir effacer tous les mixages ? @@ -9176,110 +9241,110 @@ Mixage actif par défaut si non-renseigné. ModuleData - + Positive - + Negative 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 @@ -9302,27 +9367,27 @@ Mixage actif par défaut si non-renseigné. Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - + Bind on channel Bind avec VOIE @@ -10726,7 +10791,22 @@ E - + + Hertz + + + + + mS + + + + + uS + + + + TELE @@ -10908,87 +10988,92 @@ 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 - + THR GAZ - + 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? @@ -12422,22 +12507,22 @@ Trop d'erreurs, abandon. Type d'écran personnalisé - + None Aucun - + Numbers Chiffres - + Bars Barres - + Script @@ -12465,107 +12550,112 @@ Trop d'erreurs, abandon. TelemetryPanel - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Telemetry screen %1 Ecran de télémesure %1 - + Source - + Low Alarm Alarme basse - + Critical Alarm Alarme critique - + Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (non supporté) - + Alti Alt - + Alti+ Alt+ - + VSpeed Vitesse verticale - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells Velm - + --- + + + Delete Sensor. Are you sure? + Supprimer le Capteur. Êtes-vous sûr? + TelemetrySensor @@ -12860,67 +12950,92 @@ Trop d'erreurs, abandon. TelemetrySensorPanel - + TELE%1 - + Popup menu available Menu contextuel disponible - + Lowest Min - + Cell %1 Elém. %1 - + Highest Max - + Delta - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer + + + Insert + Insérer + + + + Delete + Supprimer + + Move Up + Monter + + + + Move Down + Descendre + + + Clear All Effacer Tout - + + Cut Telemetry Sensor. Are you sure? + Couper le capteur de télémétrie. Êtes-vous sûr? + + + Clear Telemetry Sensor. Are you sure? Effacer le capteur de télémétrie. Êtes-vous sûr? - + Clear all Telemetry Sensors. Are you sure? Effacez tous les capteurs de télémétrie. Êtes-vous sûr? From cd876aca81d2276147006d8f3280304922e1670d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 15 Sep 2020 23:00:46 +0200 Subject: [PATCH 207/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 029ce7c83..9c578d091 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1918,3 +1918,4 @@ Didier Hochedez Robert Hunter Christophe Gilsoul Lincoln Roberts +Johan Jurriens From b164d7d4388a07c536684a8de9726c6de835f203 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 16 Sep 2020 12:36:29 +0200 Subject: [PATCH 208/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9c578d091..0a6ad4148 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1919,3 +1919,4 @@ Robert Hunter Christophe Gilsoul Lincoln Roberts Johan Jurriens +Michael Ledward From f328b7f9c0681ffb273a9d4c4583ccb3ba7545c0 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 16 Sep 2020 08:11:47 +0200 Subject: [PATCH 209/267] Support Pure2 and Race2 --- radio/src/telemetry/ghost.cpp | 4 ++-- radio/src/telemetry/ghost.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index da4d4164d..664d7a7a2 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -20,8 +20,8 @@ #include "opentx.h" -const char * const ghstRfProfileValue[GHST_RF_PROFILE_COUNT] = { "Auto", "Norm", "Race", "Pure", "Long" }; -const char * const ghstVtxBandName[GHST_VTX_BAND_COUNT] = { "- - -" , "IRC", "Race", "BandE", "BandB", "BandA" }; +const char *ghstRfProfileValue[GHST_RF_PROFILE_COUNT] = { "Auto", "Norm", "Race", "Pure", "Long", "Unused", "Race2", "Pure2" }; +const char *ghstVtxBandName[GHST_VTX_BAND_COUNT] = { "- - -" , "IRC", "Race", "BandE", "BandB", "BandA" }; struct GhostSensor { diff --git a/radio/src/telemetry/ghost.h b/radio/src/telemetry/ghost.h index 8a27c7868..9fcd54120 100755 --- a/radio/src/telemetry/ghost.h +++ b/radio/src/telemetry/ghost.h @@ -75,7 +75,10 @@ enum GhstRFProfile GHST_RF_PROFILE_Race = 2, GHST_RF_PROFILE_PureRace = 3, GHST_RF_PROFILE_LongRange = 4, - GHST_RF_PROFILE_MAX = GHST_RF_PROFILE_LongRange, + GHST_RF_PROFILE_Reserved = 5, + GHST_RF_PROFILE_Race2 = 6, + GHST_RF_PROFILE_PureRace2 = 7, + GHST_RF_PROFILE_MAX = GHST_RF_PROFILE_PureRace2, GHST_RF_PROFILE_COUNT }; From f3cdf28d4628d45618dc9d072f7d5a945947a65a Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 16 Sep 2020 13:50:00 +0200 Subject: [PATCH 210/267] Bump SD version in prep for 2.3.10 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6908a5419..a483ac504 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "10") 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 "0030") +set(SDCARD_REVISION "0031") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) From 6ad75996e6f7b3d554f738bec2c78979dacbd118 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 16 Sep 2020 20:14:11 +0200 Subject: [PATCH 211/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0a6ad4148..9e8962d12 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1920,3 +1920,4 @@ Christophe Gilsoul Lincoln Roberts Johan Jurriens Michael Ledward +Dave Waldesbuehl From cd08b84b9324267437474ec7e173b7606d3fe024 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 17 Sep 2020 07:27:46 +0200 Subject: [PATCH 212/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9e8962d12..515b38fc4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1921,3 +1921,4 @@ Lincoln Roberts Johan Jurriens Michael Ledward Dave Waldesbuehl +Dale Rhoads From 2889f69654151d174f83ad640a5adda40d993620 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 17 Sep 2020 08:38:21 +0200 Subject: [PATCH 213/267] Update multi scripts --- CMakeLists.txt | 2 +- .../sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua | 557 ++++++++++++++++++ .../sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt | 173 +++--- .../SCRIPTS/TOOLS/MultiChannelsUpdater.lua | 13 +- .../taranis-x7/SCRIPTS/TOOLS/MultiChan.txt | 173 +++--- .../SCRIPTS/TOOLS/MultiChannelsUpdater.lua | 13 +- .../taranis-x9/SCRIPTS/TOOLS/MultiChan.txt | 173 +++--- .../SCRIPTS/TOOLS/MultiChannelsUpdater.lua | 13 +- 8 files changed, 879 insertions(+), 238 deletions(-) create mode 100644 radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index a483ac504..d34763ac4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "10") 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 "0031") +set(SDCARD_REVISION "0032") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua new file mode 100644 index 000000000..8336be507 --- /dev/null +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua @@ -0,0 +1,557 @@ +local toolName = "TNS|DSM Forward Programming|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. # +---- # # +---- ######################################################################### + + +--############################################################################### +-- Multi buffer for DSM description +-- Multi_Buffer[0..2]=="DSM" -> Lua script is running +-- Multi_Buffer[3]==0x70+len -> TX to RX data ready to be sent +-- Multi_Buffer[4..9]=6 bytes of TX to RX data +-- Multi_Buffer[10..25]=16 bytes of RX to TX data +-- +-- To start operation: +-- Write 0x00 at address 3 +-- Write 0x00 at address 10 +-- Write "DSM" at address 0..2 +--############################################################################### + +local RX_VERSION, WAIT_CMD, MENU_TITLE, MENU_LINES, MENU_VALUES, VALUE_CHANGING, VALUE_CHANGING_WAIT, VALUE_CHANGED = 0, 1, 2, 3, 4, 5, 6, 7 +local MENU, LIST_MENU_NOCHANGING, LIST_MENU2, PERCENTAGE_VALUE = 0x1C, 0x6C, 0x4C, 0xC0 +local Phase = RX_VERSION +local Waiting_RX = 0 +local Text = {} +local Retry=100 +local Blink = 0 +local Value_Changed=0 + +local Menu = { Cur=nil, Id=nil, Title="", Prev=nil, PrevId=nil, Next=nil, NextId=nil, Back=nil, BackId=nil, CurLine=nil, SelLine=nil, EditLine=nil } +local Line = {} +local RX = { Name="", Version="" } + +local function conv_int16(number) + if number >= 0x8000 then + return number - 0x10000 + end + return number +end + +local function Get_Text(index) + out = Text[index] + if out == nil then -- unknown... + out = "Unknown_"..string.format("%X",index) + end + return out +end + +local function DSM_Release() + multiBuffer( 0, 0 ) +end + +local function DSM_Send(...) + local arg = {...} + for i = 1 , #arg do + multiBuffer( 3+i, arg[i]) + end + multiBuffer( 3, 0x70+#arg) +end + +local function Value_Add(dir) + local line=Line[Menu.SelLine] + Speed = getRotEncSpeed() + if Speed == ROTENC_MIDSPEED then + line.Val = line.Val + (5 * dir) + elseif Speed == ROTENC_HIGHSPEED then + line.Val = line.Val + (15 * dir) + else + line.Val = line.Val + dir + end + if line.Val > line.Max then + line.Val = line.Max + elseif line.Val < line.Min then + line.Val = line.Min + end + if Line[Menu.SelLine].Type ~= LIST_MENU_NOCHANGING then + Phase = VALUE_CHANGING + Waiting_RX = 0 + end +end + +local function DSM_Menu(event) + local Speed = 0 + if event == EVT_VIRTUAL_NEXT then + if Menu.EditLine == nil then + -- not changing a value + if Menu.SelLine ~= nil then + if Menu.SelLine < 7 then + local num = Menu.SelLine + for i = Menu.SelLine + 1, 6, 1 do + if Line[i].Type ~= nil and Line[i].Next ~= nil then + Menu.SelLine=i + break + end + end + if num == Menu.SelLine then + if Menu.Next ~= 0 then -- Next + Menu.SelLine = 7 + elseif Menu.Prev ~= 0 then -- Prev + Menu.SelLine = 8 + end + end + elseif Menu.Prev ~= 0 then -- Prev + Menu.SelLine = 8 + end + end + else -- need to inc the value + Value_Add(1) + end + elseif event == EVT_VIRTUAL_PREV then + if Menu.EditLine == nil then + if Menu.SelLine ~= nil then + if Menu.SelLine == 8 and Menu.Next ~= 0 then + Menu.SelLine = 7 + elseif Menu.SelLine > 0 then + if Menu.SelLine > 6 then + Menu.SelLine = 7 + end + local num = Menu.SelLine + for i = Menu.SelLine-1, 0, -1 do + if Line[i].Type ~= nil and Line[i].Next ~= nil then + Menu.SelLine=i + break + end + end + if num == Menu.SelLine then -- Back + Menu.SelLine = -1 + end + else + Menu.SelLine = -1 -- Back + end + end + else -- need to dec the value + Value_Add(-1) + end + elseif event == EVT_VIRTUAL_ENTER then + if Menu.SelLine == -1 then -- Back + Menu.Cur = Menu.Back + Menu.Id = Menu.BackId + Menu.SelLine = 0 + Phase = MENU_TITLE + Waiting_RX = 0 + elseif Menu.SelLine == 7 then -- Next + Menu.Cur = Menu.Next + Menu.Id = Menu.NextId + Menu.SelLine = 0 + Phase = MENU_TITLE + Waiting_RX = 0 + elseif Menu.SelLine == 8 then -- Prev + Menu.Cur = Menu.Prev + Menu.Id = Menu.PrevId + Menu.SelLine = 0 + Phase = MENU_TITLE + Waiting_RX = 0 + elseif Menu.SelLine ~= nil and Line[Menu.SelLine].Next ~= nil then + if Line[Menu.SelLine].Type == MENU then -- Next menu exist + Menu.Cur = Line[Menu.SelLine].Next + Menu.Id = Line[Menu.SelLine].NextId + Phase = MENU_TITLE + Waiting_RX = 0 + else + -- value entry + if Menu.EditLine == Menu.SelLine then + Menu.EditLine = nil + Value_Changed = 0 + Phase = VALUE_CHANGED + Waiting_RX = 0 + else + Menu.EditLine = Menu.SelLine + end + end + end + end +end + +local function DSM_Send_Receive() + if Waiting_RX == 0 then + Waiting_RX = 1 + -- Need to send a request + if Phase == RX_VERSION then -- request RX version + DSM_Send(0x11,0x06,0x00,0x14,0x00,0x00) + elseif Phase == WAIT_CMD then -- keep connection open + DSM_Send(0x00,0x04,0x00,0x00) + elseif Phase == MENU_TITLE then -- request menu title + if Menu.Cur == nil then + DSM_Send(0x12,0x06,0x00,0x14,0x00,0x00) -- first menu only + Menu.Cur = 0 + else + DSM_Send(0x16,0x06,Menu.Id,Menu.Cur,0x00,Menu.SelLine) + end + elseif Phase == MENU_LINES then -- request menu lines + if Menu.CurLine == nil then + DSM_Send(0x13,0x04,Menu.Id,Menu.Cur) -- line 0 + elseif Menu.CurLine >= 0x80 then + local last_byte={0x40,0x01,0x02,0x04,0x00,0x00} -- unknown... + DSM_Send(0x20,0x06,Menu.CurLine-0x80,Menu.CurLine-0x80,0x00,last_byte[Menu.CurLine-0x80+1]) -- line X + else + DSM_Send(0x14,0x06,Menu.Id,Menu.Cur,0x00,Menu.CurLine) -- line X + end + elseif Phase == MENU_VALUES then -- request menu values + DSM_Send(0x15,0x06,Menu.Id,Menu.Cur,Line[Menu.CurLine].ValId,Menu.CurLine) -- line X + elseif Phase == VALUE_CHANGING then -- send value + local value=Line[Menu.SelLine].Val + if value < 0 then + value = 0x10000 + value + end + DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value + Phase = VALUE_CHANGING_WAIT + elseif Phase == VALUE_CHANGED then -- send value + if Value_Changed == 0 then + local value=Line[Menu.SelLine].Val + if value < 0 then + value = 0x10000 + value + end + DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value + Value_Changed = Value_Changed + 1 + Waiting_RX = 0 + elseif Value_Changed == 1 then + DSM_Send(0x19,0x06,Line[Menu.SelLine].ValId,Menu.SelLine) -- validate + -- Value_Changed = Value_Changed + 1 + -- Waiting_RX = 0 + --elseif Value_Changed == 2 then + -- DSM_Send(0x1B,0x06,0x10,Menu.SelLine) -- validate again? + -- Value_Changed = Value_Changed + 1 + end + elseif Phase == VALUE_CHANGING_WAIT then + DSM_Send(0x1A,0x06,Line[Menu.SelLine].ValId,Menu.SelLine) + end + multiBuffer(10,0x00); + Retry = 50 + elseif multiBuffer(10) == 0x09 then + -- Answer received + --if multiBuffer(11) == 0x00 then -- waiting for commands? + if multiBuffer(11) == 0x01 then -- read version + --ex: 0x09 0x01 0x00 0x15 0x02 0x22 0x01 0x00 0x14 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + RX.Name = Get_Text(multiBuffer(13)) + RX.Version = multiBuffer(14).."."..multiBuffer(15).."."..multiBuffer(16) + Phase = MENU_TITLE + elseif multiBuffer(11) == 0x02 then -- read menu title + --ex: 0x09 0x02 0x4F 0x10 0xA5 0x00 0x00 0x00 0x50 0x10 0x10 0x10 0x00 0x00 0x00 0x00 + Menu.Cur = multiBuffer(12) + Menu.Id = multiBuffer(13) + Menu.Title = Get_Text(multiBuffer(14)+multiBuffer(15)*256) + Menu.Prev = multiBuffer(16) + Menu.PrevId = multiBuffer(17) + Menu.Next = multiBuffer(18) + Menu.NextId = multiBuffer(19) + Menu.Back = multiBuffer(20) + Menu.BackId = multiBuffer(21) + for i = 0, 6 do -- clear menu + Line[i] = { Menu = nil, Id = nil, Type = nil, Text="", Next = nil, NextId = nil, ValLine = nil, ValId = nil, Min, Max, Def, Val, Unit, Step } + end + Menu.CurLine = nil + if Menu.Next ~= 0 then + Menu.SelLine = 7 -- highlight Next + else + Menu.SelLine = -1 -- highlight Back + end + Blink = 0 + Phase = MENU_LINES + elseif multiBuffer(11) == 0x03 then -- read menu lines + --ex: 0x09 0x03 0x00 0x10 0x00 0x1C 0xF9 0x00 0x10 0x10 0x00 0x00 0x00 0x00 0x03 0x00 + -- Menu Id line Type Text_idx Next V_Id Val_Min Val_Max Val_Def + --ex: 0x09 0x03 0x61 0x10 0x00 0x6C 0x50 0x00 0x00 0x10 0x36 0x00 0x49 0x00 0x36 0x00 + Menu.CurLine = multiBuffer(14) + local line = Line[Menu.CurLine] + line.Menu = multiBuffer(12) + line.Id = multiBuffer(13) -- not quite sure yet + line.Type = multiBuffer(15) -- not quite sure yet: 1C is text menu only, 4C/6C is text followed by text list, C0 is text followed by percentage value + line.Text = Get_Text(multiBuffer(16)+multiBuffer(17)*256) + if multiBuffer(18) == Menu.Cur then + line.Next = nil + else + line.Next = multiBuffer(18) -- not quite sure yet: 1C=text menu=>next menu, others=>line number of the value + end + if Menu.SelLine == -1 and line.Next ~= nil then -- Auto select first line of the menu + Menu.SelLine = Menu.CurLine + end + line.NextId = multiBuffer(19) -- not quite sure yet + line.ValLine = multiBuffer(18) -- not quite sure yet + line.ValId = multiBuffer(19) -- not quite sure yet + line.Min = conv_int16(multiBuffer(20)+multiBuffer(21)*256) + line.Max = conv_int16(multiBuffer(22)+multiBuffer(23)*256) + line.Def = conv_int16(multiBuffer(24)+multiBuffer(25)*256) + if line.Type == MENU then + -- nothing to do on menu entries + elseif line.Type == LIST_MENU_NOCHANGING or line.Type == LIST_MENU2 then + line.Val = nil --line.Def - line.Min -- use default value not sure if needed + line.Def = line.Min -- pointer to the start of the list in Text + line.Max = line.Max - line.Min -- max index + line.Min = 0 -- min index + else -- default to numerical value + line.Val = nil --line.Def -- use default value not sure if needed + end + if line.Type ~= 0x1C then -- value to follow + line.Text = line.Text..":" + end + Phase = MENU_LINES + elseif multiBuffer(11) == 0x04 then -- read menu values + --ex: 0x09 0x04 0x53 0x10 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + -- Menu MeId line VaId Value + --ex: 0x09 0x04 0x61 0x10 0x02 0x10 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + Menu.CurLine = multiBuffer(14) + Line[Menu.CurLine].Val = conv_int16(multiBuffer(16)+multiBuffer(17)*256) + Phase = MENU_VALUES + elseif multiBuffer(11) == 0x05 then -- unknown... need to get through the lines... + Menu.CurLine = 0x80 + multiBuffer(12) + Phase = MENU_LINES + elseif multiBuffer(11) == 0x00 and Phase == VALUE_CHANGING then + Phase = VALUE_CHANGING_WAIT + end + -- Data processed + Waiting_RX = 0 + multiBuffer(10,0x00) + Retry = 50 + else + Retry = Retry - 1 + if Retry <= 0 then + -- Retry the RX request + Retry = 50 + Waiting_RX = 0 + if Phase ~= RX_VERSION and Phase ~= VALUE_CHANGING_WAIT then + Phase = WAIT_CMD + end + end + end +end + +local function DSM_Display() + lcd.clear() + if LCD_W == 480 then + --Draw title + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, "DSM Forward Programming", MENU_TITLE_COLOR) + --Draw RX Menu + if Phase == RX_VERSION then + lcd.drawText(10,50,"No compatible DSM RX...", BLINK) + else + if Menu.Title ~= nil then + local attrib=0; + lcd.drawText(80,32,Menu.Title,MIDSIZE) + for i = 0, 6 do + if i == Menu.SelLine then + attrib = INVERS + else + attrib = 0 + end + if Line[i] ~= nil and Line[i].Type ~= nil then + if Line[i].Type ~= MENU then-- list/value + if Line[i].Val ~= nil then + local text="" + if Line[i].Type == LIST_MENU_NOCHANGING or Line[i].Type == LIST_MENU2 then + text = Get_Text(Line[i].Val+Line[i].Def) + elseif Line[i].Type == PERCENTAGE_VALUE then + text = Line[i].Val.." %" + else + text = Line[i].Val + end + if Menu.EditLine == Menu.SelLine then -- blink edited entry + Blink = Blink + 1 + if Blink > 25 then + attrib = 0 + if Blink > 50 then + Blink = 0 + end + end + end + lcd.drawText(240,32+20*(i+2), text, attrib) -- display value + end + attrib = 0 + end + lcd.drawText(10,32+20*(i+2), Line[i].Text, attrib) -- display text + end + end + if Menu.SelLine == -1 then + lcd.drawText(437,32, "Back", INVERS) + else + lcd.drawText(437,32, "Back", 0) + end + lcd.drawRectangle(437-5, 32-2, 48, 25) + if Menu.Next ~= 0 then + if Menu.SelLine == 7 then + lcd.drawText(437,220, "Next",INVERS) + else + lcd.drawText(437,220, "Next") + end + lcd.drawRectangle(437-5, 220-2, 48, 25) + end + if Menu.Prev ~= 0 then + if Menu.SelLine == 8 then + lcd.drawText(5,220, "Prev",INVERS) + else + lcd.drawText(5,220, "Prev") + end + lcd.drawRectangle(5-5, 220-2, 48, 25) + end + end + lcd.drawText(170,252, "RX "..RX.Name.." v"..RX.Version) -- display RX info + end + else + -- --Draw RX Menu on LCD_W=128 + -- if multiBuffer( 4 ) == 0xFF then + -- lcd.drawText(2,17,"No compatible DSM RX...",SMLSIZE) + -- else + -- if Retry_128 ~= 0 then + -- --Intro page + -- Retry_128 = Retry_128 - 1 + -- lcd.drawScreenTitle("DSM Forward Programming",0,0) + -- lcd.drawText(2,17,"Press Prev Page for previous Menu" ,SMLSIZE) + -- else + -- --Menu page + -- for line = 0, 7, 1 do + -- for i = 0, 21-1, 1 do + -- value=multiBuffer( line*21+6+i ) + -- if value > 0x80 then + -- value = value - 0x80 + -- lcd.drawText(2+i*6,1+8*line,string.char(value).." ",SMLSIZE+INVERS) + -- else + -- lcd.drawText(2+i*6,1+8*line,string.char(value),SMLSIZE) + -- end + -- end + -- end + -- end + -- end + end +end + +-- Init +local function DSM_Init() + --Set protocol to talk to + multiBuffer( 0, string.byte('D') ) + --test if value has been written + if multiBuffer( 0 ) ~= string.byte('D') then + error("Not enough memory!") + return 2 + end + --Init TX buffer + multiBuffer( 3, 0x00 ) + --Init RX buffer + multiBuffer( 10, 0x00 ) + --Init telemetry + multiBuffer( 0, string.byte('D') ) + multiBuffer( 1, string.byte('S') ) + multiBuffer( 2, string.byte('M') ) + + --Text to be displayed -> need to use a file instead? + --RX names-- + Text[0x0014]="SPM4651T" + Text[0x0015]="AR637T" + --Lists-- + Text[0x0036]="Throttle" + Text[0x0037]="Aileron" + Text[0x0038]="Elevator" + Text[0x0039]="Rudder" + Text[0x003A]="Gear" + for i=1,7 do -- 3B..41 + Text[0x003A+i]="Aux"..i + end + for i=1,8 do -- 41..49 + Text[0x0041+i]="XPlus-"..i + end + Text[0x004A]="Failsafe" + Text[0x004B]="Main Menu" + Text[0x004E]="Position" + Text[0x0050]="Outputs" + --Text[0x005E]="Inhibit" + Text[0x005F]="Hold Last" + Text[0x0060]="Preset" + --Text[0x0061]="Custom" + --Messages-- + Text[0x0090]="Apply" + Text[0x0093]="Complete" + Text[0x0094]="Done" + Text[0x0097]="Factory Reset" + Text[0x009A]="Capture Failsafe Positions" + Text[0x009C]="Custom Failsafe" + Text[0x00A5]="First Time Setup" + Text[0x00F9]="Gyro settings" + Text[0x0100]="Make sure the model has been" + Text[0x0101]="configured, including wing type," + Text[0x0102]="reversing, travel, trimmed, etc." + Text[0x0103]="before continuing setup." + Text[0x0104]="" + Text[0x0105]="" + Text[0x0106]="Any wing type, channel assignment," + Text[0x0107]="subtrim, or servo reversing changes" + Text[0x0108]="require running through initial" + Text[0x0109]="setup again." + Text[0x010A]="" + Text[0x010B]="" + + Text[0x019C]="Enter Receiver Bind Mode" + Text[0x020A]="Restore from Backup" + Text[0x0209]="Save to Backup" + + Text[0x021A]="Set the model level," + Text[0x021B]="and press Continue." + Text[0x021C]="" + Text[0x021D]="" + + Text[0x021F]="Set the model on its nose," + Text[0x0220]="and press Continue. If the" + Text[0x0221]="orientation on the next" + Text[0x0222]="screen is wrong go back" + Text[0x0223]="and try again." + Text[0x0224]="Continue" + Text[0x0229]="Set Orientation Manually" + + Text[0x0227]="Other settings" + Text[0x022B]="WARNING!" + Text[0x022C]="This will reset the" + Text[0x022D]="configuration to factory" + Text[0x022E]="defaults. This does not" + Text[0x022F]="affect the backup config." + Text[0x0230]="" + Text[0x0231]="This will overwrite the" + Text[0x0232]="backup memory with your" + Text[0x0233]="current configuartion." + Text[0x0234]="" + Text[0x0235]="" + Text[0x0236]="This will overwrite the" + Text[0x0237]="current config with" + Text[0x0238]="that which is in" + Text[0x0239]="the backup memory." + Text[0x023A]="" +end + +-- Main +local function DSM_Run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_EXIT then + DSM_Release() + return 2 + else + DSM_Menu(event) + DSM_Send_Receive() + DSM_Display() + return 0 + end +end + +return { init=DSM_Init, run=DSM_Run } diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt index bf61d8ca6..b03b2c860 100644 --- a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChan.txt @@ -1,3 +1,47 @@ +24,0,Assan,Std,0,CH5,CH6,CH7,CH8 +14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 +14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates +14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf +14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop +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 +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,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess +13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess +37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 +37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 +37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +12,0,CX10,Green,1,Flip,Rate +12,1,CX10,Blue,1,Flip,Rate,Pict,Video +12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess +12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video +12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip +12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH +7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 +7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 +7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +7,3,Devo,6CH,0,CH5,CH6 +7,4,Devo,7CH,0,CH5,CH6,CH7 +33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow +6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +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 +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 +35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +69,0,ESKY150V2,Std,0,CH5_RA,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8 1,1,Flysky,V9x9,1,Flip,Light,Pict,Video 1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL @@ -7,15 +51,11 @@ 28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 -53,0,Flyzone,FZ-410,0 -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 -41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED -60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 -37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 -37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 -37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +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 +53,0,Height,5ch,0,Gear +53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8 3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8 @@ -26,78 +66,50 @@ 15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8 15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +15,5,FrSkyX,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8 64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8 64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,5,FrSkyX2,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 +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 -39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 -39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 -39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 -57,0,HoTT,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 -68,0,Skyartec,Std,0,CH5,CH6,CH7 -7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 -7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 -7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -7,3,Devo,6CH,0,CH5,CH6 -7,4,Devo,7CH,0,CH5,CH6,CH7 -30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 -30,1,WK2x01,WK2401,0 -30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro -30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro -30,4,WK2x01,W6HEL,0,Gear,Col,Gyro -30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro -6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 -24,0,Assan,Std,0,CH5,CH6,CH7,CH8 -14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 -14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates -14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf -14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop -14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop -59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video -42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED -42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol -34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess -13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess -12,0,CX10,Green,1,Flip,Rate -12,1,CX10,Blue,1,Flip,Rate,Pict,Video -12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess -12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video -12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip -12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH -33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow -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 -35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +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 23,0,FY326,FY326,1,Flip,RTH,HLess,Expert +47,0,GD00x,V1,1,Trim,LED,Rate +47,1,GD00x,V2,1,Trim,LED,Rate 32,0,GW008,FY326,1,Flip 36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1 36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8 -9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim -9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim +4,1,Hisky,HK310,0,Aux +39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 +39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 +39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib 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 +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 +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 +73,0,Kyosho,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate 18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate 18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate @@ -111,6 +123,9 @@ 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess 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 +60,1,Pelikan,LITE_V4,0,CH5,CH6,CH7,CH8 51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess 66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train 29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal @@ -120,8 +135,15 @@ 31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal 31,2,Q303,CX10D,1,Arm,Flip 31,3,Q303,CX10WD,1,Arm,Flip +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 +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 +19,0,Shenqi,Cycle,1 +68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch 11,1,SLT,V2,0,CH5,CH6,CH7,CH8 11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib @@ -129,11 +151,21 @@ 11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict 10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess 10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess +61,0,Tiger,Std,1,Flip,Light +43,0,Traxxas,6519,0 5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY 5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD -61,0,Tiger,Std,1,Flip,Light -46,0,V911s,Std,1,Calib -46,1,E119,Std,1,Calib +48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn +48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn +46,0,V911s,V911s,1,Calib +46,1,V911s,E119,1,Calib +22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 +30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 +30,1,WK2x01,WK2401,0 +30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro +30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro +30,4,WK2x01,W6HEL,0,Gear,Col,Gyro +30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro 62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video 62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video 8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess @@ -141,17 +173,4 @@ 8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess -65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 -65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 -56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 -19,0,Shenqi,Cycle,1 -4,1,Hisky,HK310,0,Aux -43,0,Traxxas,6519,0 52,0,ZSX,280,1,Light -48,0,V761,Std,1,Gyro -49,0,KF606,Std,1,Trim -47,0,GD00x,V1,1,Trim,LED,Rate -47,1,GD00x,V2,1,Trim,LED,Rate -58,0,FX816,P38,1 diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChannelsUpdater.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChannelsUpdater.lua index 42de75e4f..14902b782 100644 --- a/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChannelsUpdater.lua +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/MultiChannelsUpdater.lua @@ -249,13 +249,13 @@ local function Multi_Init() --Exceptions on first 4 channels... local stick_names = { "Rud", "Ele", "Thr", "Ail" } - if ( protocol == 4 and sub_protocol ==1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX + if ( protocol == 4 and sub_protocol == 1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX stick_names[2] = "n-a" stick_names[4] = "n-a" elseif protocol == 43 then -- Traxxas stick_names[2] = "Aux4" stick_names[4] = "Aux3" - elseif protocol == 48 then -- V761 + elseif ( protocol == 48 and sub_protocol == 0 ) then -- V761 3CH stick_names[4] = "n-a" elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816 stick_names[1] = "n-a" @@ -278,6 +278,15 @@ local function Multi_Init() ch_order = math.floor(ch_order/4) channel_names[bitand(ch_order,3)+1] = stick_names[1] end + + --Exceptions on first 4 channels... + if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) ) then -- Kyosho or RadioLink Surface + channel_names[1] = "ST" + channel_names[2] = "THR" + channel_names[3] = "CH3" + channel_names[4] = "CH4" + end + --Check MultiChan.txt local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r") if f == nil then return end diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt index bf61d8ca6..b03b2c860 100644 --- a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChan.txt @@ -1,3 +1,47 @@ +24,0,Assan,Std,0,CH5,CH6,CH7,CH8 +14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 +14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates +14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf +14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop +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 +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,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess +13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess +37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 +37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 +37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +12,0,CX10,Green,1,Flip,Rate +12,1,CX10,Blue,1,Flip,Rate,Pict,Video +12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess +12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video +12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip +12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH +7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 +7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 +7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +7,3,Devo,6CH,0,CH5,CH6 +7,4,Devo,7CH,0,CH5,CH6,CH7 +33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow +6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +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 +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 +35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +69,0,ESKY150V2,Std,0,CH5_RA,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8 1,1,Flysky,V9x9,1,Flip,Light,Pict,Video 1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL @@ -7,15 +51,11 @@ 28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 -53,0,Flyzone,FZ-410,0 -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 -41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED -60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 -37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 -37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 -37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +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 +53,0,Height,5ch,0,Gear +53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8 3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8 @@ -26,78 +66,50 @@ 15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8 15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +15,5,FrSkyX,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8 64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8 64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,5,FrSkyX2,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 +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 -39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 -39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 -39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 -57,0,HoTT,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 -68,0,Skyartec,Std,0,CH5,CH6,CH7 -7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 -7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 -7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -7,3,Devo,6CH,0,CH5,CH6 -7,4,Devo,7CH,0,CH5,CH6,CH7 -30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 -30,1,WK2x01,WK2401,0 -30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro -30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro -30,4,WK2x01,W6HEL,0,Gear,Col,Gyro -30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro -6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 -24,0,Assan,Std,0,CH5,CH6,CH7,CH8 -14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 -14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates -14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf -14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop -14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop -59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video -42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED -42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol -34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess -13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess -12,0,CX10,Green,1,Flip,Rate -12,1,CX10,Blue,1,Flip,Rate,Pict,Video -12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess -12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video -12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip -12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH -33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow -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 -35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +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 23,0,FY326,FY326,1,Flip,RTH,HLess,Expert +47,0,GD00x,V1,1,Trim,LED,Rate +47,1,GD00x,V2,1,Trim,LED,Rate 32,0,GW008,FY326,1,Flip 36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1 36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8 -9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim -9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim +4,1,Hisky,HK310,0,Aux +39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 +39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 +39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib 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 +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 +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 +73,0,Kyosho,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate 18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate 18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate @@ -111,6 +123,9 @@ 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess 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 +60,1,Pelikan,LITE_V4,0,CH5,CH6,CH7,CH8 51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess 66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train 29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal @@ -120,8 +135,15 @@ 31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal 31,2,Q303,CX10D,1,Arm,Flip 31,3,Q303,CX10WD,1,Arm,Flip +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 +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 +19,0,Shenqi,Cycle,1 +68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch 11,1,SLT,V2,0,CH5,CH6,CH7,CH8 11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib @@ -129,11 +151,21 @@ 11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict 10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess 10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess +61,0,Tiger,Std,1,Flip,Light +43,0,Traxxas,6519,0 5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY 5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD -61,0,Tiger,Std,1,Flip,Light -46,0,V911s,Std,1,Calib -46,1,E119,Std,1,Calib +48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn +48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn +46,0,V911s,V911s,1,Calib +46,1,V911s,E119,1,Calib +22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 +30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 +30,1,WK2x01,WK2401,0 +30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro +30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro +30,4,WK2x01,W6HEL,0,Gear,Col,Gyro +30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro 62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video 62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video 8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess @@ -141,17 +173,4 @@ 8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess -65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 -65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 -56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 -19,0,Shenqi,Cycle,1 -4,1,Hisky,HK310,0,Aux -43,0,Traxxas,6519,0 52,0,ZSX,280,1,Light -48,0,V761,Std,1,Gyro -49,0,KF606,Std,1,Trim -47,0,GD00x,V1,1,Trim,LED,Rate -47,1,GD00x,V2,1,Trim,LED,Rate -58,0,FX816,P38,1 diff --git a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChannelsUpdater.lua b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChannelsUpdater.lua index 42de75e4f..14902b782 100644 --- a/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChannelsUpdater.lua +++ b/radio/sdcard/taranis-x7/SCRIPTS/TOOLS/MultiChannelsUpdater.lua @@ -249,13 +249,13 @@ local function Multi_Init() --Exceptions on first 4 channels... local stick_names = { "Rud", "Ele", "Thr", "Ail" } - if ( protocol == 4 and sub_protocol ==1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX + if ( protocol == 4 and sub_protocol == 1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX stick_names[2] = "n-a" stick_names[4] = "n-a" elseif protocol == 43 then -- Traxxas stick_names[2] = "Aux4" stick_names[4] = "Aux3" - elseif protocol == 48 then -- V761 + elseif ( protocol == 48 and sub_protocol == 0 ) then -- V761 3CH stick_names[4] = "n-a" elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816 stick_names[1] = "n-a" @@ -278,6 +278,15 @@ local function Multi_Init() ch_order = math.floor(ch_order/4) channel_names[bitand(ch_order,3)+1] = stick_names[1] end + + --Exceptions on first 4 channels... + if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) ) then -- Kyosho or RadioLink Surface + channel_names[1] = "ST" + channel_names[2] = "THR" + channel_names[3] = "CH3" + channel_names[4] = "CH4" + end + --Check MultiChan.txt local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r") if f == nil then return end diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt index bf61d8ca6..b03b2c860 100644 --- a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChan.txt @@ -1,3 +1,47 @@ +24,0,Assan,Std,0,CH5,CH6,CH7,CH8 +14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 +14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates +14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf +14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop +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 +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,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess +13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess +37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 +37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 +37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +12,0,CX10,Green,1,Flip,Rate +12,1,CX10,Blue,1,Flip,Rate,Pict,Video +12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess +12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video +12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip +12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH +7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 +7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 +7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 +7,3,Devo,6CH,0,CH5,CH6 +7,4,Devo,7CH,0,CH5,CH6,CH7 +33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow +6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill +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 +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 +35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +69,0,ESKY150V2,Std,0,CH5_RA,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8 1,1,Flysky,V9x9,1,Flip,Light,Pict,Video 1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL @@ -7,15 +51,11 @@ 28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 -53,0,Flyzone,FZ-410,0 -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 -41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED -60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8 -37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8 -37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8 -37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8 +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 +53,0,Height,5ch,0,Gear +53,1,Height,8ch,0,Gear,Gyro,Flap,Light 25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8 3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8 3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8 @@ -26,78 +66,50 @@ 15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8 15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +15,5,FrSkyX,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8 64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8 64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +64,5,FrSkyX2,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 +65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 +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 -39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 -39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 -39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 -57,0,HoTT,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8 -68,0,Skyartec,Std,0,CH5,CH6,CH7 -7,0,Devo,8CH,0,CH5,CH6,CH7,CH8 -7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10 -7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -7,3,Devo,6CH,0,CH5,CH6 -7,4,Devo,7CH,0,CH5,CH6,CH7 -30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 -30,1,WK2x01,WK2401,0 -30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro -30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro -30,4,WK2x01,W6HEL,0,Gear,Col,Gyro -30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro -6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill -22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12 -22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 -24,0,Assan,Std,0,CH5,CH6,CH7,CH8 -14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2 -14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates -14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf -14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop -14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop -59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video -42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED -42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol -34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess -13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess -12,0,CX10,Green,1,Flip,Rate -12,1,CX10,Blue,1,Flip,Rate,Pict,Video -12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess -12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video -12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip -12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH -33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow -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 -35,1,ESKY150,7CH,0,FMode,Aux6,Aux7 +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 23,0,FY326,FY326,1,Flip,RTH,HLess,Expert +47,0,GD00x,V1,1,Trim,LED,Rate +47,1,GD00x,V2,1,Trim,LED,Rate 32,0,GW008,FY326,1,Flip 36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1 36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal 4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8 -9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim -9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim +4,1,Hisky,HK310,0,Aux +39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9 +39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9 +39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9 26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib 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 +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 +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 +73,0,Kyosho,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate 18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate 18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate @@ -111,6 +123,9 @@ 17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess 17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess 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 +60,1,Pelikan,LITE_V4,0,CH5,CH6,CH7,CH8 51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess 66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train 29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal @@ -120,8 +135,15 @@ 31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal 31,2,Q303,CX10D,1,Arm,Flip 31,3,Q303,CX10WD,1,Arm,Flip +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 +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 +19,0,Shenqi,Cycle,1 +68,0,Skyartec,Std,0,CH5,CH6,CH7 11,0,SLT,V1,0,Gear,Pitch 11,1,SLT,V2,0,CH5,CH6,CH7,CH8 11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib @@ -129,11 +151,21 @@ 11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict 10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess 10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess +61,0,Tiger,Std,1,Flip,Light +43,0,Traxxas,6519,0 5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY 5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD -61,0,Tiger,Std,1,Flip,Light -46,0,V911s,Std,1,Calib -46,1,E119,Std,1,Calib +48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn +48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn +46,0,V911s,V911s,1,Calib +46,1,V911s,E119,1,Calib +22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9 +30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8 +30,1,WK2x01,WK2401,0 +30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro +30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro +30,4,WK2x01,W6HEL,0,Gear,Col,Gyro +30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro 62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video 62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video 8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess @@ -141,17 +173,4 @@ 8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess 8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess 8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess -65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16 -65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8 -65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8 -56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14 -19,0,Shenqi,Cycle,1 -4,1,Hisky,HK310,0,Aux -43,0,Traxxas,6519,0 52,0,ZSX,280,1,Light -48,0,V761,Std,1,Gyro -49,0,KF606,Std,1,Trim -47,0,GD00x,V1,1,Trim,LED,Rate -47,1,GD00x,V2,1,Trim,LED,Rate -58,0,FX816,P38,1 diff --git a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChannelsUpdater.lua b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChannelsUpdater.lua index 42de75e4f..14902b782 100644 --- a/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChannelsUpdater.lua +++ b/radio/sdcard/taranis-x9/SCRIPTS/TOOLS/MultiChannelsUpdater.lua @@ -249,13 +249,13 @@ local function Multi_Init() --Exceptions on first 4 channels... local stick_names = { "Rud", "Ele", "Thr", "Ail" } - if ( protocol == 4 and sub_protocol ==1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX + if ( protocol == 4 and sub_protocol == 1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX stick_names[2] = "n-a" stick_names[4] = "n-a" elseif protocol == 43 then -- Traxxas stick_names[2] = "Aux4" stick_names[4] = "Aux3" - elseif protocol == 48 then -- V761 + elseif ( protocol == 48 and sub_protocol == 0 ) then -- V761 3CH stick_names[4] = "n-a" elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816 stick_names[1] = "n-a" @@ -278,6 +278,15 @@ local function Multi_Init() ch_order = math.floor(ch_order/4) channel_names[bitand(ch_order,3)+1] = stick_names[1] end + + --Exceptions on first 4 channels... + if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) ) then -- Kyosho or RadioLink Surface + channel_names[1] = "ST" + channel_names[2] = "THR" + channel_names[3] = "CH3" + channel_names[4] = "CH4" + end + --Check MultiChan.txt local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r") if f == nil then return end From 0de69c9e037b93cbf2441bde4b24ed9d9a89bec2 Mon Sep 17 00:00:00 2001 From: Audrey <35206674+ItsMeAubey@users.noreply.github.com> Date: Sun, 20 Sep 2020 04:16:53 -0400 Subject: [PATCH 214/267] Add "LQ" and "RF Mode" TTS (#7948) --- radio/util/tts_en.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/util/tts_en.py b/radio/util/tts_en.py index 7edfa1eec..b45308262 100755 --- a/radio/util/tts_en.py +++ b/radio/util/tts_en.py @@ -95,6 +95,8 @@ for i, (s, f) in enumerate([("armed", "armed"), ("crow!, off!", "crowof"), ("rf. signal!, low!", "siglow"), ("rf. signal!, critical!", "sigcrt"), + ("LQ", "lq"), + ("RF mode", "rfmode"), ("high. speed. mode!, active", "spdmod"), ("thermal. mode!, on", "thmmod"), ("normal. mode!, on", "nrmmod"), From 4a72f7b97d3996f4e4f13f190c0a8d5d9e750c22 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 20 Sep 2020 10:22:12 +0200 Subject: [PATCH 215/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 515b38fc4..09f51c937 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1922,3 +1922,4 @@ Johan Jurriens Michael Ledward Dave Waldesbuehl Dale Rhoads +Denis Kelly From 270d26ae5b6884bcb62a829ae2e18bbb70569add Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 20 Sep 2020 10:45:54 +0200 Subject: [PATCH 216/267] Update wording, this fixes #7950 --- radio/src/translations/de.h.txt | 2 +- radio/src/translations/en.h.txt | 2 +- radio/src/translations/fi.h.txt | 2 +- radio/src/translations/fr.h.txt | 2 +- radio/src/translations/nl.h.txt | 2 +- radio/src/translations/pt.h.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 5d0581c18..92d73d56c 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -1053,7 +1053,7 @@ #define TR_LSW_HEADERS_V1 "V1" #define TR_LSW_HEADERS_V2 "V2" #define TR_LSW_HEADERS_ANDSW "UND Schalter" -#define TR_LSW_HEADERS_DURATION "Min Dauer" +#define TR_LSW_HEADERS_DURATION "Dauer" #define TR_LSW_HEADERS_DELAY "Verzögerung" #define TR_GVAR_HEADERS_NAME "Name" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 1bd2d5671..f976fad51 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -1034,7 +1034,7 @@ #define TR_LSW_HEADERS_V1 "V1" #define TR_LSW_HEADERS_V2 "V2" #define TR_LSW_HEADERS_ANDSW "AND Switch" -#define TR_LSW_HEADERS_DURATION "Min Duration" +#define TR_LSW_HEADERS_DURATION "Duration" #define TR_LSW_HEADERS_DELAY "Delay" #define TR_GVAR_HEADERS_NAME "Name" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 3a6944d03..bdd39aeb4 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -1048,7 +1048,7 @@ #define TR_LSW_HEADERS_V1 "V1" #define TR_LSW_HEADERS_V2 "V2" #define TR_LSW_HEADERS_ANDSW "AND Switch" -#define TR_LSW_HEADERS_DURATION "Min Duration" +#define TR_LSW_HEADERS_DURATION "Duration" #define TR_LSW_HEADERS_DELAY "Delay" #define TR_GVAR_HEADERS_NAME "Name" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index b4db92ecc..cfd5e4400 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -1058,7 +1058,7 @@ #define TR_LSW_HEADERS_V1 "V1" #define TR_LSW_HEADERS_V2 "V2" #define TR_LSW_HEADERS_ANDSW "ET suppl." -#define TR_LSW_HEADERS_DURATION "Durée min." +#define TR_LSW_HEADERS_DURATION "Durée" #define TR_LSW_HEADERS_DELAY "Délai" #define TR_GVAR_HEADERS_NAME "Nom" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 97f90ce11..255ffa0bc 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -1042,7 +1042,7 @@ #define TR_LSW_HEADERS_V1 "Var1" #define TR_LSW_HEADERS_V2 "Var2" #define TR_LSW_HEADERS_ANDSW "EN-Schakelaar" -#define TR_LSW_HEADERS_DURATION "Min. Duur" +#define TR_LSW_HEADERS_DURATION "Duur" #define TR_LSW_HEADERS_DELAY "Vertraging" #define TR_GVAR_HEADERS_NAME "Name" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 96c61ec56..27e6d676a 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -1039,7 +1039,7 @@ #define TR_LSW_HEADERS_V1 "V1" #define TR_LSW_HEADERS_V2 "V2" #define TR_LSW_HEADERS_ANDSW "AND Switch" -#define TR_LSW_HEADERS_DURATION "Min Duration" +#define TR_LSW_HEADERS_DURATION "Duration" #define TR_LSW_HEADERS_DELAY "Delay" #define TR_GVAR_HEADERS_NAME "Name" From 6b1141af2866631f2e632aca849b7c760bba6cb9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 20 Sep 2020 14:06:43 +0200 Subject: [PATCH 217/267] Set multi min version to 1.3.1.65 to match SD scripts --- radio/src/telemetry/multi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 599845809..2be662127 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -21,7 +21,7 @@ #include "telemetry.h" #include "multi.h" -constexpr int32_t MULTI_DESIRED_VERSION = (1 << 24) | (3 << 16) | (1 << 8) | 1; +constexpr int32_t MULTI_DESIRED_VERSION = (1 << 24) | (3 << 16) | (1 << 8) | 65; #define MULTI_CHAN_BITS 11 extern uint8_t g_moduleIdx; From 6d9b5238b63d2890edd017b7902aacaa03273040 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 21 Sep 2020 11:56:55 +0200 Subject: [PATCH 218/267] Fix #7954 --- radio/src/translations/fr.h.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index cfd5e4400..20a2eede9 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -731,7 +731,7 @@ #define TR_16CH_WITHOUT_TELEMETRY TR("16CH sans télém.", "16CH sans télémétrie") #define TR_16CH_WITH_TELEMETRY TR("16CH avec télém.", "16CH avec télémétrie") #define TR_8CH_WITH_TELEMETRY TR("8CH avec télém.", "8CH avec télémétrie") -#define TR_EXT_ANTENNA "Antenne externe" +#define TR_EXT_ANTENNA "Antenne Ext." #define TR_PIN "Pin" #define TR_UPDATE_RX_OPTIONS "Sauver options RX?" #define TR_UPDATE_TX_OPTIONS "Sauver options TX?" From 2d3684277ef4118685e71dcf9cf6358288628217 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 22 Sep 2020 15:36:53 +0200 Subject: [PATCH 219/267] Handles alt in a different unit. (#7957) Handles alt in a different unit. This fixes #7951 --- radio/src/telemetry/telemetry_sensors.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index abf531b39..9cebabd3c 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -380,7 +380,7 @@ void TelemetryItem::eval(const TelemetrySensor & sensor) result = isqrt32(result); if (altItem) { - dist = abs(altItem->value) / g_model.telemetrySensors[sensor.dist.alt-1].getPrecDivisor(); + dist = convertTelemetryValue(abs(altItem->value), g_model.telemetrySensors[sensor.dist.alt-1].unit, g_model.telemetrySensors[sensor.dist.alt-1].prec, UNIT_METERS, 0); result = (dist * dist) + (result * result); result = isqrt32(result); } @@ -605,6 +605,7 @@ PACK(typedef struct { const UnitConversionRule unitConversionTable[] = { /* unitFrom unitTo multiplier divisor */ { UNIT_METERS, UNIT_FEET, 105, 32}, + { UNIT_FEET, UNIT_METERS, 32, 105}, { UNIT_METERS_PER_SECOND, UNIT_FEET_PER_SECOND, 105, 32}, { UNIT_KTS, UNIT_KMH, 1852, 1000}, // 1 knot = 1.85200 kilometers per hour From b4d85045779b4e8cb6c0461690ad7d9702d98b14 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Tue, 22 Sep 2020 23:44:54 +0800 Subject: [PATCH 220/267] Wismy/fport2 (#7956) Add Fport2 option RX6R/RX4R/G-RX8/G-RX6/RXSR OTA support --- radio/src/gui/480x272/model_receiver_options.cpp | 10 ++++++++++ .../src/gui/common/stdlcd/model_receiver_options.cpp | 9 +++++++++ radio/src/pulses/pulses.h | 1 + radio/src/pulses/pxx2.cpp | 2 ++ radio/src/pulses/pxx2.h | 12 +++++++----- radio/src/telemetry/frsky_pxx2.cpp | 3 +++ 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 90db6f7e3..38e46728c 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -48,6 +48,7 @@ enum { ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, ITEM_RECEIVER_SETTINGS_SPORT_FPORT, + ITEM_RECEIVER_SETTINGS_FPORT2, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -82,6 +83,7 @@ bool menuModelReceiverOptions(event_t event) isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), + IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -177,6 +179,14 @@ bool menuModelReceiverOptions(event_t event) } break; + case ITEM_RECEIVER_SETTINGS_FPORT2: + lcdDrawText(MENUS_MARGIN_LEFT, y, "F.Port2"); + reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport2, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); + if (attr && checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; + } + break; + case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); break; diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index 6ce66ee29..de3547b75 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -48,6 +48,7 @@ enum { ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, ITEM_RECEIVER_SETTINGS_SPORT_FPORT, + ITEM_RECEIVER_SETTINGS_FPORT2, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -82,6 +83,7 @@ void menuModelReceiverOptions(event_t event) isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), + IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -171,6 +173,13 @@ void menuModelReceiverOptions(event_t event) } break; + case ITEM_RECEIVER_SETTINGS_FPORT2: + reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport2, RECEIVER_OPTIONS_2ND_COLUMN, y, "F.Port2", attr, event); + if (attr && checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; + } + break; + case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); break; diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 5bff463f6..84ecee015 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -111,6 +111,7 @@ class ReceiverSettings { uint8_t pwmRate; uint8_t fport; uint8_t enablePwmCh5Ch6; + uint8_t fport2; uint8_t outputsCount; uint8_t outputsMapping[24]; }; diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index 944be8a3a..e396db4bb 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -227,6 +227,8 @@ void Pxx2Pulses::setupReceiverSettingsFrame(uint8_t module) flag1 |= PXX2_RX_SETTINGS_FLAG1_TELEMETRY_25MW; if (reusableBuffer.hardwareAndSettings.receiverSettings.enablePwmCh5Ch6) flag1 |= PXX2_RX_SETTINGS_FLAG1_ENABLE_PWM_CH5_CH6; + if (reusableBuffer.hardwareAndSettings.receiverSettings.fport2) + flag1 |= PXX2_RX_SETTINGS_FLAG1_FPORT2; Pxx2Transport::addByte(flag1); uint8_t outputsCount = min(24, reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount); for (int i = 0; i < outputsCount; i++) { diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h index 2fa64dcad..d6b3402fe 100644 --- a/radio/src/pulses/pxx2.h +++ b/radio/src/pulses/pxx2.h @@ -55,6 +55,7 @@ #define PXX2_RX_SETTINGS_FLAG1_FPORT (1 << 3) #define PXX2_RX_SETTINGS_FLAG1_TELEMETRY_25MW (1 << 2) #define PXX2_RX_SETTINGS_FLAG1_ENABLE_PWM_CH5_CH6 (1 << 1) +#define PXX2_RX_SETTINGS_FLAG1_FPORT2 (1 << 0) #define PXX2_TX_SETTINGS_FLAG0_WRITE (1 << 6) #define PXX2_TX_SETTINGS_FLAG1_EXTERNAL_ANTENNA (1 << 3) @@ -220,16 +221,16 @@ static const uint8_t PXX2ReceiverOptions[] = { 0b11111110, // X8R 0b11111110, // RX8R 0b11111110, // RX8R-PRO - 0b11111110, // RX6R - 0b11111110, // RX4R - 0b11111110, // G-RX8 - 0b11111110, // G-RX6 + 0b11111111, // RX6R + 0b11111111, // RX4R + 0b11111111, // G-RX8 + 0b11111111, // G-RX6 0b11111110, // X6R 0b11111110, // X4R 0b11111110, // X4R-SB 0b11111110, // XSR 0b11111110, // XSR-M - 0b11111110, // RXSR + 0b11111111, // RXSR 0b11111110, // S6R 0b11111110, // S8R 0b11111110, // XM @@ -266,6 +267,7 @@ enum ReceiverCapabilities { RECEIVER_CAPABILITY_FPORT, RECEIVER_CAPABILITY_TELEMETRY_25MW, RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6, + RECEIVER_CAPABILITY_FPORT2, RECEIVER_CAPABILITY_COUNT }; diff --git a/radio/src/telemetry/frsky_pxx2.cpp b/radio/src/telemetry/frsky_pxx2.cpp index 8ed11be1c..dd8d63106 100644 --- a/radio/src/telemetry/frsky_pxx2.cpp +++ b/radio/src/telemetry/frsky_pxx2.cpp @@ -92,6 +92,9 @@ void processReceiverSettingsFrame(uint8_t module, const uint8_t * frame) if (frame[4] & PXX2_RX_SETTINGS_FLAG1_ENABLE_PWM_CH5_CH6) destination->enablePwmCh5Ch6 = 1; + if (frame[4] & PXX2_RX_SETTINGS_FLAG1_FPORT2) + destination->fport2 = 1; + uint8_t outputsCount = min(16, frame[0] - 4); destination->outputsCount = outputsCount; for (uint8_t pin = 0; pin < outputsCount; pin++) { From 869f8127ec0a25f65b7230571497ee34dd20a6cb Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Tue, 22 Sep 2020 16:49:15 +0100 Subject: [PATCH 221/267] Add cursor to custom curves - Closes #6399 (#7707) Add cursor to custom curves Refactor functions declaration Cosmetics --- radio/src/gui/128x64/gui.h | 5 +- radio/src/gui/128x64/menu_model.cpp | 2 + radio/src/gui/128x64/model_curve_edit.cpp | 4 +- radio/src/gui/128x64/model_input_edit.cpp | 59 +- radio/src/gui/128x64/model_inputs_mixes.cpp | 849 ------------------- radio/src/gui/128x64/model_mix_edit.cpp | 2 + radio/src/gui/212x64/gui.h | 5 +- radio/src/gui/212x64/menu_model.cpp | 2 + radio/src/gui/212x64/model_curve_edit.cpp | 2 + radio/src/gui/212x64/model_input_edit.cpp | 45 +- radio/src/gui/212x64/model_mix_edit.cpp | 2 + radio/src/gui/480x272/curves.cpp | 33 + radio/src/gui/480x272/menu_model.cpp | 2 + radio/src/gui/480x272/menus.h | 3 + radio/src/gui/480x272/model_curves.cpp | 3 + radio/src/gui/480x272/model_inputs.cpp | 37 +- radio/src/gui/480x272/model_mixes.cpp | 4 + radio/src/gui/common/stdlcd/draw_functions.h | 5 + radio/src/gui/common/stdlcd/model_curves.cpp | 52 +- radio/src/gui/gui_common.h | 2 - 20 files changed, 137 insertions(+), 981 deletions(-) delete mode 100644 radio/src/gui/128x64/model_inputs_mixes.cpp diff --git a/radio/src/gui/128x64/gui.h b/radio/src/gui/128x64/gui.h index 1e48d6746..f5a2be095 100644 --- a/radio/src/gui/128x64/gui.h +++ b/radio/src/gui/128x64/gui.h @@ -217,6 +217,8 @@ extern int8_t s_copySrcRow; extern int8_t s_copyTgtOfs; extern uint8_t s_currIdx; extern uint8_t s_currIdxSubMenu; +extern uint16_t s_currSrcRaw; +extern uint16_t s_currScale; extern uint8_t s_copySrcIdx; extern uint8_t s_copySrcCh; extern int8_t s_currCh; @@ -276,9 +278,6 @@ uint8_t getMixesCount(); void insertMix(uint8_t idx); void deleteMix(uint8_t idx); -typedef int (*FnFuncP) (int x); -void drawFunction(FnFuncP fn, uint8_t offset=0); - void onSourceLongEnterPress(const char *result); uint8_t switchToMix(uint8_t source); diff --git a/radio/src/gui/128x64/menu_model.cpp b/radio/src/gui/128x64/menu_model.cpp index b6f5b3bb3..b98372bd3 100644 --- a/radio/src/gui/128x64/menu_model.cpp +++ b/radio/src/gui/128x64/menu_model.cpp @@ -46,3 +46,5 @@ void editSingleName(coord_t x, coord_t y, const char * label, char * name, uint8 uint8_t s_currIdx; uint8_t s_currIdxSubMenu; +uint16_t s_currSrcRaw; +uint16_t s_currScale; diff --git a/radio/src/gui/128x64/model_curve_edit.cpp b/radio/src/gui/128x64/model_curve_edit.cpp index ebf2228d5..2cbe5f758 100644 --- a/radio/src/gui/128x64/model_curve_edit.cpp +++ b/radio/src/gui/128x64/model_curve_edit.cpp @@ -180,7 +180,9 @@ void menuModelCurveOne(event_t event) #endif } - drawCurve(0); + drawCurve(); + if (s_currSrcRaw != MIXSRC_NONE) + drawCursor(applyCurrentCurve); attr = (s_editMode > 0 ? INVERS|BLINK : INVERS); for (uint8_t i=0; i<5+crv.points; i++) { diff --git a/radio/src/gui/128x64/model_input_edit.cpp b/radio/src/gui/128x64/model_input_edit.cpp index 419456ac0..ab1b5aced 100644 --- a/radio/src/gui/128x64/model_input_edit.cpp +++ b/radio/src/gui/128x64/model_input_edit.cpp @@ -21,7 +21,6 @@ #include "opentx.h" #define EXPO_ONE_2ND_COLUMN (7*FW+3*FW+2) -#define EXPO_ONE_FM_WIDTH (5*FW) int expoFn(int x) { @@ -31,28 +30,6 @@ int expoFn(int x) return anas[ed->chn]; } -void drawFunction(FnFuncP fn, uint8_t offset) -{ - lcdDrawVerticalLine(CURVE_CENTER_X-offset, 0/*TODO CURVE_CENTER_Y-CURVE_SIDE_WIDTH*/, CURVE_SIDE_WIDTH*2, 0xee); - lcdDrawHorizontalLine(CURVE_CENTER_X-CURVE_SIDE_WIDTH-offset, CURVE_CENTER_Y, CURVE_SIDE_WIDTH*2, 0xee); - - coord_t prev_yv = (coord_t)-1; - - for (int xv=-CURVE_SIDE_WIDTH; xv<=CURVE_SIDE_WIDTH; xv++) { - coord_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/CURVE_SIDE_WIDTH))) / 2 * (LCD_H-1) / RESX); - if (prev_yv != (coord_t)-1) { - if (abs((int8_t)yv-prev_yv) <= 1) { - lcdDrawPoint(CURVE_CENTER_X+xv-offset-1, prev_yv, FORCE); - } - else { - uint8_t tmp = (prev_yv < yv ? 0 : 1); - lcdDrawSolidVerticalLine(CURVE_CENTER_X+xv-offset-1, yv+tmp, prev_yv-yv); - } - } - prev_yv = yv; - } -} - enum ExposFields { EXPO_FIELD_INPUT_NAME, EXPO_FIELD_LINE_NAME, @@ -113,13 +90,15 @@ void menuModelExpoOne(event_t event) case EXPO_FIELD_SOURCE: lcdDrawTextAlignedLeft(y, STR_SOURCE); drawSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, RIGHT|STREXPANDED|attr); - if (attr) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailableInInputs); + if (attr) + ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailableInInputs); break; case EXPO_FIELD_SCALE: lcdDrawTextAlignedLeft(y, STR_SCALE); drawSensorCustomValue(EXPO_ONE_2ND_COLUMN, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), RIGHT | attr); - if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); + if (attr) + ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); break; case EXPO_FIELD_WEIGHT: @@ -159,34 +138,20 @@ void menuModelExpoOne(event_t event) break; case EXPO_FIELD_TRIM: - uint8_t not_stick = (ed->srcRaw > MIXSRC_Ail); + uint8_t notStick = (ed->srcRaw > MIXSRC_Ail); int8_t carryTrim = -ed->carryTrim; lcdDrawTextAlignedLeft(y, STR_TRIM); - lcdDrawTextAtIndex(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, RIGHT | (menuHorizontalPosition==0 ? attr : 0)); - if (attr) ed->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_LAST); + lcdDrawTextAtIndex(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (notStick && carryTrim == 0) ? 0 : carryTrim+1, RIGHT | (menuHorizontalPosition==0 ? attr : 0)); + if (attr) + ed->carryTrim = -checkIncDecModel(event, carryTrim, notStick ? TRIM_ON : -TRIM_OFF, -TRIM_LAST); break; } y += FH; } drawFunction(expoFn); - - int x512 = getValue(ed->srcRaw); - if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - drawSensorCustomValue(LCD_W-FW, 6*FH, (ed->srcRaw - MIXSRC_FIRST_TELEM) / 3, x512, 0); - if (ed->scale > 0) x512 = (x512 * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); - } - else { - lcdDrawNumber(LCD_W-FW, 6*FH, calcRESXto1000(x512), RIGHT | PREC1); - } - x512 = limit(-1024, x512, 1024); - int y512 = expoFn(x512); - y512 = limit(-1024, y512, 1024); - lcdDrawNumber(CURVE_CENTER_X-FWNUM, 1*FH, calcRESXto1000(y512), RIGHT | PREC1); - - x512 = CURVE_CENTER_X+x512/(RESX/CURVE_SIDE_WIDTH); - y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX; - - lcdDrawSolidVerticalLine(x512, y512-3, 3*2+1); - lcdDrawSolidHorizontalLine(x512-3, y512, 3*2+1); + // those parameters are global so that they can be reused in the curve edit screen + s_currSrcRaw = ed->srcRaw; + s_currScale = ed->scale; + drawCursor(expoFn); } diff --git a/radio/src/gui/128x64/model_inputs_mixes.cpp b/radio/src/gui/128x64/model_inputs_mixes.cpp deleted file mode 100644 index 953ee64b1..000000000 --- a/radio/src/gui/128x64/model_inputs_mixes.cpp +++ /dev/null @@ -1,849 +0,0 @@ -/* - * 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 "opentx.h" - -#define EXPO_ONE_2ND_COLUMN (7*FW+3*FW+2) -#define EXPO_ONE_FM_WIDTH (5*FW) - -int expoFn(int x) -{ - ExpoData *ed = expoAddress(s_currIdx); - int16_t anas[MAX_INPUTS] = {0}; - anas[ed->chn] = x; - applyExpos(anas, e_perout_mode_inactive_flight_mode); - return anas[ed->chn]; -} - -void drawFunction(FnFuncP fn, uint8_t offset) -{ - lcdDrawVerticalLine(CURVE_CENTER_X-offset, 0, LCD_H, 0xee); - lcdDrawHorizontalLine(CURVE_CENTER_X-CURVE_SIDE_WIDTH-offset, CURVE_CENTER_Y, CURVE_SIDE_WIDTH*2, 0xee); - - coord_t prev_yv = (coord_t)-1; - - for (int8_t xv=-CURVE_SIDE_WIDTH; xv<=CURVE_SIDE_WIDTH; xv++) { - coord_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/CURVE_SIDE_WIDTH))) / 2 * (LCD_H-1) / RESX); - if (prev_yv != (coord_t)-1) { - if (abs((int8_t)yv-prev_yv) <= 1) { - lcdDrawPoint(CURVE_CENTER_X+xv-offset-1, prev_yv, FORCE); - } - else { - uint8_t tmp = (prev_yv < yv ? 0 : 1); - lcdDrawSolidVerticalLine(CURVE_CENTER_X+xv-offset-1, yv+tmp, prev_yv-yv); - } - } - prev_yv = yv; - } -} - - -uint8_t getExpoMixCount(uint8_t expo) -{ - uint8_t count = 0; - uint8_t ch ; - - for(int8_t i=(expo ? MAX_EXPOS-1 : MAX_MIXERS-1); i>=0; i--) { - ch = (expo ? EXPO_VALID(expoAddress(i)) : mixAddress(i)->srcRaw); - if (ch != 0) { - count++; - } - } - return count; -} - -bool reachExpoMixCountLimit(uint8_t expo) -{ - // check mixers count limit - if (getExpoMixCount(expo) >= (expo ? MAX_EXPOS : MAX_MIXERS)) { - POPUP_WARNING(expo ? STR_NOFREEEXPO : STR_NOFREEMIXER); - return true; - } - return false; -} - -void deleteExpoMix(uint8_t expo, uint8_t idx) -{ - pauseMixerCalculations(); - if (expo) { - ExpoData *expo = expoAddress(idx); - memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData)); - } - else { - MixData *mix = mixAddress(idx); - memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData)); - } - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -// TODO avoid this global s_currCh on ARM boards ... -int8_t s_currCh; -void insertExpoMix(uint8_t expo, uint8_t idx) -{ - pauseMixerCalculations(); - if (expo) { - ExpoData *expo = expoAddress(idx); - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - memclear(expo, sizeof(ExpoData)); - expo->mode = 3; // pos&neg - expo->chn = s_currCh - 1; - expo->weight = 100; - } - else { - MixData *mix = mixAddress(idx); - memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - memclear(mix, sizeof(MixData)); - mix->destCh = s_currCh-1; - mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channelOrder(s_currCh)); - mix->weight = 100; - } - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -void copyExpoMix(uint8_t expo, uint8_t idx) -{ - pauseMixerCalculations(); - if (expo) { - ExpoData *expo = expoAddress(idx); - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - } - else { - MixData *mix = mixAddress(idx); - memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - } - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -bool swapExpoMix(uint8_t expo, uint8_t &idx, uint8_t up) -{ - void *x, *y; - uint8_t size; - int8_t tgt_idx = (up ? idx-1 : idx+1); - - if (expo) { - x = (ExpoData *)expoAddress(idx); - - if (tgt_idx < 0) { - if (((ExpoData *)x)->chn == 0) - return false; - ((ExpoData *)x)->chn--; - return true; - } - - if (tgt_idx == MAX_EXPOS) { - if (((ExpoData *)x)->chn == MAX_INPUTS-1) - return false; - ((ExpoData *)x)->chn++; - return true; - } - - y = (ExpoData *)expoAddress(tgt_idx); - if(((ExpoData *)x)->chn != ((ExpoData *)y)->chn || !EXPO_VALID((ExpoData *)y)) { - if (up) { - if (((ExpoData *)x)->chn>0) ((ExpoData *)x)->chn--; - else return false; - } - else { - if (((ExpoData *)x)->chnchn++; - else return false; - } - return true; - } - - size = sizeof(ExpoData); - } - else { - x = (MixData *)mixAddress(idx); - - if (tgt_idx < 0) { - if (((MixData *)x)->destCh == 0) - return false; - ((MixData *)x)->destCh--; - return true; - } - - if (tgt_idx == MAX_MIXERS) { - if (((MixData *)x)->destCh == MAX_OUTPUT_CHANNELS-1) - return false; - ((MixData *)x)->destCh++; - return true; - } - - y = (MixData *)mixAddress(tgt_idx); - uint8_t destCh = ((MixData *)x)->destCh; - if(!((MixData *)y)->srcRaw || destCh != ((MixData *)y)->destCh) { - if (up) { - if (destCh>0) ((MixData *)x)->destCh--; - else return false; - } - else { - if (destChdestCh++; - else return false; - } - return true; - } - - size = sizeof(MixData); - } - - pauseMixerCalculations(); - memswap(x, y, size); - resumeMixerCalculations(); - - idx = tgt_idx; - return true; -} - -enum ExposFields { - EXPO_FIELD_NAME, - EXPO_FIELD_WEIGHT, - EXPO_FIELD_EXPO, - EXPO_FIELD_CURVE, - CASE_FLIGHT_MODES(EXPO_FIELD_FLIGHT_MODES) - EXPO_FIELD_SWITCH, - EXPO_FIELD_SIDE, - EXPO_FIELD_MAX -}; - -#define CURVE_ROWS 0 - -void menuModelExpoOne(event_t event) -{ - ExpoData * ed = expoAddress(s_currIdx); - drawSource(7*FW+FW/2, 0, MIXSRC_Rud+ed->chn, 0); - - SUBMENU(STR_MENUINPUTS, EXPO_FIELD_MAX, {0, 0, 0, CURVE_ROWS, CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/}); - - int8_t sub = menuVerticalPosition; - - coord_t y = MENU_HEADER_HEIGHT + 1; - - for (uint8_t i=0; i0 ? BLINK|INVERS : INVERS) : 0); - switch (i) { - case EXPO_FIELD_NAME: - editSingleName(EXPO_ONE_2ND_COLUMN-sizeof(ed->name)*FW, y, STR_EXPONAME, ed->name, sizeof(ed->name), event, attr); - break; - - case EXPO_FIELD_WEIGHT: - lcdDrawTextAlignedLeft(y, STR_WEIGHT); - ed->weight = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->weight, MIN_EXPO_WEIGHT, 100, attr, 0, event); - break; - - case EXPO_FIELD_EXPO: - lcdDrawTextAlignedLeft(y, STR_EXPO); - if (ed->curveMode==MODE_EXPO || ed->curveParam==0) { - ed->curveMode = MODE_EXPO; - ed->curveParam = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->curveParam, -100, 100, attr, 0, event); - } - else { - lcdDrawText(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); - } - break; - - case EXPO_FIELD_CURVE: - lcdDrawTextAlignedLeft(y, STR_CURVE); - if (ed->curveMode!=MODE_EXPO || ed->curveParam==0) { - drawCurveName(EXPO_ONE_2ND_COLUMN-3*FW, y, ed->curveParam, attr); - if (attr) { - CHECK_INCDEC_MODELVAR_ZERO(event, ed->curveParam, CURVE_BASE+MAX_CURVES-1); - if (ed->curveParam) ed->curveMode = MODE_CURVE; - if (ed->curveParam>=CURVE_BASE && event==EVT_KEY_LONG(KEY_ENTER)) { - s_currIdxSubMenu = ed->curveParam - CURVE_BASE; - pushMenu(menuModelCurveOne); - } - } - } - else { - lcdDrawText(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_NA, attr); - } - break; - -#if defined(FLIGHT_MODES) - case EXPO_FIELD_FLIGHT_MODES: - ed->flightModes = editFlightModes(EXPO_ONE_2ND_COLUMN-EXPO_ONE_FM_WIDTH, y, event, ed->flightModes, attr); - break; -#endif - - case EXPO_FIELD_SWITCH: - ed->swtch = editSwitch(EXPO_ONE_2ND_COLUMN-3*FW, y, ed->swtch, attr, event); - break; - - case EXPO_FIELD_SIDE: - ed->mode = 4 - editChoice(EXPO_ONE_2ND_COLUMN-3*FW, y, STR_SIDE, STR_VCURVEFUNC, 4-ed->mode, 1, 3, attr, event); - break; - } - y += FH; - } - - drawFunction(expoFn); - - int16_t x512 = calibratedAnalogs[ed->chn]; - lcdDrawNumber(LCD_W-8, 6*FH, calcRESXto100(x512), 0); - int16_t y512 = expoFn(x512); - lcdDrawNumber(LCD_W-8-6*FW, 1*FH, calcRESXto100(y512), 0); - - x512 = CURVE_CENTER_X+x512/(RESX/CURVE_SIDE_WIDTH); - y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX; - - lcdDrawSolidVerticalLine(x512, y512-3, 3*2+1); - lcdDrawSolidHorizontalLine(x512-3, y512, 3*2+1); -} - -enum MixFields { - MIX_FIELD_NAME, - MIX_FIELD_SOURCE, - MIX_FIELD_WEIGHT, - MIX_FIELD_OFFSET, - MIX_FIELD_TRIM, - MIX_FIELD_CURVE, - CASE_FLIGHT_MODES(MIX_FIELD_FLIGHT_PHASE) - MIX_FIELD_SWITCH, - MIX_FIELD_WARNING, - MIX_FIELD_MLTPX, - MIX_FIELD_DELAY_UP, - MIX_FIELD_DELAY_DOWN, - MIX_FIELD_SLOW_UP, - MIX_FIELD_SLOW_DOWN, - MIX_FIELD_COUNT -}; - -#define GAUGE_WIDTH 33 -#define GAUGE_HEIGHT 6 -void drawOffsetBar(uint8_t x, uint8_t y, MixData * md) -{ - int offset = GET_GVAR(MD_OFFSET(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode); - int weight = GET_GVAR(MD_WEIGHT(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode); - int barMin = offset - weight; - int barMax = offset + weight; - if (y > 15) { - lcdDrawNumber(x-((barMin >= 0) ? 2 : 3), y-6, barMin, TINSIZE|LEFT); - lcdDrawNumber(x+GAUGE_WIDTH+1, y-6, barMax, TINSIZE); - } - if (weight < 0) { - barMin = -barMin; - barMax = -barMax; - } - if (barMin < -101) - barMin = -101; - if (barMax > 101) - barMax = 101; - lcdDrawHorizontalLine(x-2, y, GAUGE_WIDTH+2, DOTTED); - lcdDrawHorizontalLine(x-2, y+GAUGE_HEIGHT, GAUGE_WIDTH+2, DOTTED); - lcdDrawSolidVerticalLine(x-2, y+1, GAUGE_HEIGHT-1); - lcdDrawSolidVerticalLine(x+GAUGE_WIDTH-1, y+1, GAUGE_HEIGHT-1); - if (barMin <= barMax) { - int8_t right = (barMax * GAUGE_WIDTH) / 200; - int8_t left = ((barMin * GAUGE_WIDTH) / 200)-1; - lcdDrawSolidFilledRect(x+GAUGE_WIDTH/2+left, y+2, right-left, GAUGE_HEIGHT-3); - } - lcdDrawSolidVerticalLine(x+GAUGE_WIDTH/2-1, y, GAUGE_HEIGHT+1); - if (barMin == -101) { - for (uint8_t i=0; i<3; ++i) { - lcdDrawPoint(x+i, y+4-i); - lcdDrawPoint(x+3+i, y+4-i); - } - } - if (barMax == 101) { - for (uint8_t i=0; i<3; ++i) { - lcdDrawPoint(x+GAUGE_WIDTH-8+i, y+4-i); - lcdDrawPoint(x+GAUGE_WIDTH-5+i, y+4-i); - } - } -} -#undef GAUGE_WIDTH -#undef GAUGE_HEIGHT - -void menuModelMixOne(event_t event) -{ - title(STR_MIXES); - MixData * md2 = mixAddress(s_currIdx) ; - putsChn(lcdLastRightPos+1*FW, 0, md2->destCh+1,0); - - SUBMENU_NOTITLE(MIX_FIELD_COUNT, {0, 0, 0, 0, 1, 1, CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/}); - - int8_t sub = menuVerticalPosition; - int8_t editMode = s_editMode; - - for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); - switch (i) { - case MIX_FIELD_NAME: - editSingleName(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXNAME, md2->name, sizeof(md2->name), event, attr); - break; - case MIX_FIELD_SOURCE: - drawFieldLabel(COLUMN_X, y, STR_SOURCE); - drawSource(COLUMN_X+MIXES_2ND_COLUMN, y, md2->srcRaw, STREXPANDED|attr); - if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST); - break; - case MIX_FIELD_WEIGHT: - drawFieldLabel(COLUMN_X, y, STR_WEIGHT); - gvarWeightItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2, attr|LEFT, event); - break; - case MIX_FIELD_OFFSET: - { - drawFieldLabel(COLUMN_X, y, STR_OFFSET); - u_int8int16_t offset; - MD_OFFSET_TO_UNION(md2, offset); - offset.word = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_OFFSET_NEG, GV_RANGELARGE_OFFSET, attr|LEFT, 0, event); - MD_UNION_TO_OFFSET(offset, md2); - drawOffsetBar(COLUMN_X+MIXES_2ND_COLUMN+22, y, md2); - break; - } - - case MIX_FIELD_TRIM: - { - uint8_t not_stick = (md2->srcRaw > NUM_STICKS); - int8_t carryTrim = -md2->carryTrim; - drawFieldLabel(COLUMN_X, y, STR_TRIM); - lcdDrawTextAtIndex((not_stick ? COLUMN_X+MIXES_2ND_COLUMN : COLUMN_X+6*FW-3), y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, menuHorizontalPosition==0 ? attr : 0); - if (attr && menuHorizontalPosition==0 && (not_stick || editMode>0)) md2->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_AIL); - if (!not_stick) { - lcdDrawText(COLUMN_X+MIXES_2ND_COLUMN, y, STR_DREX); - drawCheckBox(COLUMN_X+MIXES_2ND_COLUMN+DREX_CHBOX_OFFSET, y, !md2->noExpo, menuHorizontalPosition==1 ? attr : 0); - if (attr && menuHorizontalPosition==1 && editMode>0) md2->noExpo = !checkIncDecModel(event, !md2->noExpo, 0, 1); - } - else if (attr) { - REPEAT_LAST_CURSOR_MOVE(); - } - break; - } - - case MIX_FIELD_CURVE: - { - drawFieldLabel(COLUMN_X, y, STR_CURVE); - int8_t curveParam = md2->curveParam; - if (md2->curveMode == MODE_CURVE) { - drawCurveName(COLUMN_X+MIXES_2ND_COLUMN, y, curveParam, attr); - if (attr) { - if (event==EVT_KEY_LONG(KEY_ENTER) && (curveParam<0 || curveParam>=CURVE_BASE)){ - s_currIdxSubMenu = (curveParam<0 ? -curveParam-1 : curveParam-CURVE_BASE); - pushMenu(menuModelCurveOne); - } - else { - CHECK_INCDEC_MODELVAR(event, md2->curveParam, -MAX_CURVES, CURVE_BASE+MAX_CURVES-1); - if (md2->curveParam == 0) - md2->curveMode = MODE_DIFFERENTIAL; - } - } - } - else { - lcdDrawText(COLUMN_X+MIXES_2ND_COLUMN, y, "Diff", menuHorizontalPosition==0 ? attr : 0); - md2->curveParam = GVAR_MENU_ITEM(COLUMN_X+MIXES_2ND_COLUMN+5*FW, y, curveParam, -100, 100, LEFT|(menuHorizontalPosition==1 ? attr : 0), 0, editMode>0 ? event : 0); - if (attr && editMode>0 && menuHorizontalPosition==0) { - int8_t tmp = 0; - CHECK_INCDEC_MODELVAR(event, tmp, -1, 1); - if (tmp != 0) { - md2->curveMode = MODE_CURVE; - md2->curveParam = tmp; - } - } - } - break; - } -#if defined(FLIGHT_MODES) - case MIX_FIELD_FLIGHT_PHASE: - md2->flightModes = editFlightModes(COLUMN_X+MIXES_2ND_COLUMN, y, event, md2->flightModes, attr); - break; -#endif - case MIX_FIELD_SWITCH: - md2->swtch = editSwitch(COLUMN_X+MIXES_2ND_COLUMN, y, md2->swtch, attr, event); - break; - case MIX_FIELD_WARNING: - drawFieldLabel(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MIXWARNING); - if (md2->mixWarn) - lcdDrawNumber(COLUMN_X+MIXES_2ND_COLUMN, y, md2->mixWarn, attr|LEFT); - else - lcdDrawText(COLUMN_X+MIXES_2ND_COLUMN, y, STR_OFF, attr); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, md2->mixWarn, 3); - break; - case MIX_FIELD_MLTPX: - md2->mltpx = editChoice(COLUMN_X+MIXES_2ND_COLUMN, y, STR_MULTPX, STR_VMLTPX, md2->mltpx, 0, 2, attr, event); - break; - case MIX_FIELD_DELAY_UP: - md2->delayUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYUP, md2->delayUp); - break; - case MIX_FIELD_DELAY_DOWN: - md2->delayDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_DELAYDOWN, md2->delayDown); - break; - case MIX_FIELD_SLOW_UP: - md2->speedUp = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWUP, md2->speedUp); - break; - case MIX_FIELD_SLOW_DOWN: - md2->speedDown = EDIT_DELAY(COLUMN_X, y, event, attr, STR_SLOWDOWN, md2->speedDown); - break; - } - } -} - -#define _STR_MAX(x) "/" #x -#define STR_MAX(x) _STR_MAX(x) - - #define EXPO_LINE_WEIGHT_POS 7*FW+1 - #define EXPO_LINE_EXPO_POS 10*FW+5 - #define EXPO_LINE_SWITCH_POS 11*FW+2 - #define EXPO_LINE_SIDE_POS 14*FW+2 - #define EXPO_LINE_SELECT_POS 24 - #define EXPO_LINE_FM_POS - #define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW - #define MIX_LINE_SRC_POS 4*FW-1 - #define MIX_LINE_WEIGHT_POS 11*FW+3 - #define MIX_LINE_CURVE_POS 12*FW+2 - #define MIX_LINE_SWITCH_POS 16*FW - #define MIX_LINE_DELAY_POS 19*FW+7 - -void onExpoMixMenu(const char *result) -{ - bool expo = (menuHandlers[menuLevel] == menuModelExposAll); - uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); - - if (result == STR_EDIT) { - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - } - else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) { - if (!reachExpoMixCountLimit(expo)) { - s_currCh = chn; - if (result == STR_INSERT_AFTER) { s_currIdx++; menuVerticalPosition++; } - insertExpoMix(expo, s_currIdx); - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - } - } - else if (result == STR_COPY || result == STR_MOVE) { - s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = menuVerticalPosition; - } - else if (result == STR_DELETE) { - deleteExpoMix(expo, s_currIdx); - } -} - -void displayMixInfos(coord_t y, MixData *md) -{ - if (md->curveParam) { - if (md->curveMode == MODE_CURVE) - drawCurveName(MIX_LINE_CURVE_POS, y, md->curveParam); - else - displayGVar(MIX_LINE_CURVE_POS+3*FW, y, md->curveParam, -100, 100); - } - - if (md->swtch) { - drawSwitch(MIX_LINE_SWITCH_POS, y, md->swtch); - } -} - -void displayMixLine(coord_t y, MixData * md) -{ - if (md->name[0]) { - lcdDrawSizedText(EXPO_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR); - } - else { - displayMixInfos(y, md); - } -} - -void displayExpoInfos(coord_t y, ExpoData *ed) -{ - if (ed->curveMode == MODE_CURVE) - drawCurveName(EXPO_LINE_EXPO_POS-3*FW, y, ed->curveParam); - else - displayGVar(EXPO_LINE_EXPO_POS, y, ed->curveParam, -100, 100); - - drawSwitch(EXPO_LINE_SWITCH_POS, y, ed->swtch, 0); -} - -void displayExpoLine(coord_t y, ExpoData *ed) -{ - displayExpoInfos(y, ed); - - if (ed->name[0]) { - lcdDrawSizedText(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR); - } -} - -void menuModelExpoMix(uint8_t expo, event_t event) -{ - uint8_t sub = menuVerticalPosition; - - if (s_editMode > 0) - s_editMode = 0; - - uint8_t chn = (expo ? expoAddress(s_currIdx)->chn+1 : mixAddress(s_currIdx)->destCh+1); - - switch (event) - { - case EVT_ENTRY: - case EVT_ENTRY_UP: - s_copyMode = 0; - s_copyTgtOfs = 0; - break; - case EVT_KEY_LONG(KEY_EXIT): - if (s_copyMode && s_copyTgtOfs == 0) { - deleteExpoMix(expo, s_currIdx); - killEvents(event); - event = 0; - } - // no break -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_LONG: - if (s_copyMode) { - killEvents(event); - } -#endif - case EVT_KEY_BREAK(KEY_EXIT): - if (s_copyMode) { - if (s_copyTgtOfs) { - // cancel the current copy / move operation - if (s_copyMode == COPY_MODE) { - deleteExpoMix(expo, s_currIdx); - } - else { - do { - swapExpoMix(expo, s_currIdx, s_copyTgtOfs > 0); - s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1); - } while (s_copyTgtOfs != 0); - storageDirty(EE_MODEL); - } - menuVerticalPosition = s_copySrcRow; - s_copyTgtOfs = 0; - } - s_copyMode = 0; - event = 0; - } - break; - case EVT_KEY_BREAK(KEY_ENTER): - if (sub != 0 && (!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) { - s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = sub; - break; - } - // no break - - case EVT_KEY_LONG(KEY_ENTER): - killEvents(event); - if (s_copyTgtOfs) { - s_copyMode = 0; - s_copyTgtOfs = 0; - } - else if (sub != 0) { - if (READ_ONLY()) { - if (!s_currCh) { - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - return; - } - } - else { - if (s_copyMode) s_currCh = 0; - if (s_currCh) { - if (reachExpoMixCountLimit(expo)) break; - insertExpoMix(expo, s_currIdx); - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - s_copyMode = 0; - return; - } - else { - event = 0; - s_copyMode = 0; - POPUP_MENU_ADD_ITEM(STR_EDIT); - POPUP_MENU_ADD_ITEM(STR_INSERT_BEFORE); - POPUP_MENU_ADD_ITEM(STR_INSERT_AFTER); - POPUP_MENU_ADD_ITEM(STR_COPY); - POPUP_MENU_ADD_ITEM(STR_MOVE); - POPUP_MENU_ADD_ITEM(STR_DELETE); - POPUP_MENU_START(onExpoMixMenu); - } - } - } - break; - case EVT_KEY_LONG(KEY_LEFT): - case EVT_KEY_LONG(KEY_RIGHT): - if (s_copyMode && !s_copyTgtOfs) { - if (reachExpoMixCountLimit(expo)) break; - s_currCh = chn; - if (event == EVT_KEY_LONG(KEY_RIGHT)) { s_currIdx++; menuVerticalPosition++; } - insertExpoMix(expo, s_currIdx); - pushMenu(expo ? menuModelExpoOne : menuModelMixOne); - s_copyMode = 0; - killEvents(event); - return; - } - break; -#if defined(ROTARY_ENCODER_NAVIGATION) - case EVT_ROTARY_LEFT: - case EVT_ROTARY_RIGHT: -#endif - case EVT_KEY_FIRST(KEY_UP): - case EVT_KEY_REPT(KEY_UP): - case EVT_KEY_FIRST(KEY_DOWN): - case EVT_KEY_REPT(KEY_DOWN): - if (s_copyMode) { - uint8_t key = (event & 0x1f); - uint8_t next_ofs = ((IS_ROTARY_LEFT(event) || key==KEY_UP) ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1); - - if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) { - // insert a mix on the same channel (just above / just below) - if (reachExpoMixCountLimit(expo)) break; - copyExpoMix(expo, s_currIdx); - if (IS_ROTARY_RIGHT(event) || key==KEY_DOWN) s_currIdx++; - else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++; - } - else if (next_ofs==0 && s_copyMode==COPY_MODE) { - // delete the mix - deleteExpoMix(expo, s_currIdx); - if (IS_ROTARY_LEFT(event) || key==KEY_UP) s_currIdx--; - } - else { - // only swap the mix with its neighbor - if (!swapExpoMix(expo, s_currIdx, IS_ROTARY_LEFT(event) || key==KEY_UP)) break; - storageDirty(EE_MODEL); - } - - s_copyTgtOfs = next_ofs; - } - break; - } - - lcdDrawNumber(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXES))+FW+FW/2, 0, getExpoMixCount(expo)); - lcdDrawText(FW*max(sizeof(TR_MENUINPUTS), sizeof(TR_MIXES))+FW+FW/2, 0, expo ? STR_MAX(MAX_EXPOS) : STR_MAX(MAX_MIXERS)); - - SIMPLE_MENU(expo ? STR_MENUINPUTS : STR_MIXES, menuTabModel, expo ? MENU_MODEL_INPUTS : MENU_MODEL_MIXES, s_maxLines); - - sub = menuVerticalPosition; - s_currCh = 0; - uint8_t cur = 1; - uint8_t i = 0; - - for (uint8_t ch=1; ch<=(expo ? MAX_INPUTS : MAX_OUTPUT_CHANNELS); ch++) { - void *pointer = NULL; MixData * &md = (MixData * &)pointer; ExpoData * &ed = (ExpoData * &)pointer; - coord_t y = MENU_HEADER_HEIGHT-FH+1+(cur-menuVerticalOffset)*FH; - if (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)) { - if (menuVerticalOffset < cur && cur-menuVerticalOffset < LCD_LINES) { - if (expo) { - drawSource(0, y, MIXSRC_Rud+ch-1, 0); - } - else { - putsChn(0, y, ch, 0); // show CHx - } - } - uint8_t mixCnt = 0; - do { - if (s_copyMode) { - if (s_copyMode == MOVE_MODE && menuVerticalOffset < cur && cur-menuVerticalOffset < 8 && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lcdDrawRect(expo ? 18 : 22, y-1, expo ? LCD_W-18 : LCD_W-22, 9, DOTTED); - cur++; y+=FH; - } - if (s_currIdx == i) { - sub = menuVerticalPosition = cur; - s_currCh = ch; - } - } - else if (sub == cur) { - s_currIdx = i; - } - if (menuVerticalOffset < cur && cur-menuVerticalOffset < 8) { - uint8_t attr = ((s_copyMode || sub != cur) ? 0 : INVERS); - if (expo) { - ed->weight = GVAR_MENU_ITEM(EXPO_LINE_WEIGHT_POS, y, ed->weight, MIN_EXPO_WEIGHT, 100, attr | (isExpoActive(i) ? BOLD : 0), 0, event); - displayExpoLine(y, ed); - if (ed->mode!=3) { - lcdDrawChar(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? 126 : 127); - } - } - else { - if (mixCnt > 0) lcdDrawTextAtIndex(FW, y, STR_VMLTPX2, md->mltpx, 0); - - drawSource(MIX_LINE_SRC_POS, y, md->srcRaw, 0); - - gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, attr | (isMixActive(i) ? BOLD : 0), event); - - displayMixLine(y, md); - - char cs = ' '; - if (md->speedDown || md->speedUp) - cs = 'S'; - if (md->delayUp || md->delayDown) - cs = (cs =='S' ? '*' : 'D'); - lcdDrawChar(MIX_LINE_DELAY_POS, y, cs); - } - if (s_copyMode) { - if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - /* draw a border around the raw on selection mode (copy/move) */ - lcdDrawRect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, s_copyMode == COPY_MODE ? SOLID : DOTTED); - } - if (cur == sub) { - /* invert the raw when it's the current one */ - lcdDrawSolidFilledRect(expo ? EXPO_LINE_SELECT_POS+1 : 23, y, expo ? (LCD_W-EXPO_LINE_SELECT_POS-2) : (LCD_W-24), 7); - } - } - } - cur++; y+=FH; mixCnt++; i++; if (expo) ed++; else md++; - } while (expo ? (ichn+1 == ch && EXPO_VALID(ed)) : (isrcRaw && md->destCh+1 == ch)); - if (s_copyMode == MOVE_MODE && menuVerticalOffset < cur && cur-menuVerticalOffset < LCD_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lcdDrawRect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? LCD_W-EXPO_LINE_SELECT_POS : LCD_W-22, 9, DOTTED); - cur++; y+=FH; - } - } - else { - uint8_t attr = 0; - if (sub == cur) { - s_currIdx = i; - s_currCh = ch; - if (!s_copyMode) { - attr = INVERS; - } - } - if (menuVerticalOffset < cur && cur-menuVerticalOffset < LCD_LINES) { - if (expo) { - drawSource(0, y, MIXSRC_Rud+ch-1, attr); - } - else { - putsChn(0, y, ch, attr); // show CHx - } - if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) { - lcdDrawRect(expo ? EXPO_LINE_SELECT_POS : 22, y-1, expo ? (LCD_W-EXPO_LINE_SELECT_POS) : (LCD_W-22), 9, DOTTED); - } - } - cur++; y+=FH; - } - } - s_maxLines = cur; - if (sub >= s_maxLines-1) menuVerticalPosition = s_maxLines-1; -} - -void menuModelExposAll(event_t event) -{ - return menuModelExpoMix(1, event); -} - -void menuModelMixAll(event_t event) -{ - return menuModelExpoMix(0, event); -} diff --git a/radio/src/gui/128x64/model_mix_edit.cpp b/radio/src/gui/128x64/model_mix_edit.cpp index 5c1f95e65..45e8cd383 100644 --- a/radio/src/gui/128x64/model_mix_edit.cpp +++ b/radio/src/gui/128x64/model_mix_edit.cpp @@ -150,6 +150,8 @@ void menuModelMixOne(event_t event) case MIX_FIELD_CURVE: lcdDrawTextAlignedLeft(y, STR_CURVE); + s_currSrcRaw = md2->srcRaw; + s_currScale = 0; editCurveRef(MIXES_2ND_COLUMN, y, md2->curve, s_editMode > 0 ? event : 0, attr); break; diff --git a/radio/src/gui/212x64/gui.h b/radio/src/gui/212x64/gui.h index 5968bd320..738c146ea 100644 --- a/radio/src/gui/212x64/gui.h +++ b/radio/src/gui/212x64/gui.h @@ -247,6 +247,8 @@ extern int8_t s_copySrcRow; extern int8_t s_copyTgtOfs; extern uint8_t s_currIdx; extern uint8_t s_currIdxSubMenu; +extern uint16_t s_currSrcRaw; +extern uint16_t s_currScale; extern uint8_t s_maxLines; extern uint8_t s_copySrcIdx; extern uint8_t s_copySrcCh; @@ -284,9 +286,6 @@ void menuChannelsView(event_t event); #define POS_HORZ_INIT(posVert) ((COLATTR(posVert) & NAVIGATION_LINE_BY_LINE) ? -1 : 0) #define EDIT_MODE_INIT 0 // TODO enum -typedef int (*FnFuncP) (int x); -void drawFunction(FnFuncP fn, uint8_t offset=0); - void onSourceLongEnterPress(const char *result); uint8_t switchToMix(uint8_t source); diff --git a/radio/src/gui/212x64/menu_model.cpp b/radio/src/gui/212x64/menu_model.cpp index f8058499b..5147bf7a2 100644 --- a/radio/src/gui/212x64/menu_model.cpp +++ b/radio/src/gui/212x64/menu_model.cpp @@ -68,3 +68,5 @@ void editSingleName(coord_t x, coord_t y, const char * label, char *name, uint8_ uint8_t s_currIdx; uint8_t s_currIdxSubMenu; +uint16_t s_currSrcRaw; +uint16_t s_currScale; diff --git a/radio/src/gui/212x64/model_curve_edit.cpp b/radio/src/gui/212x64/model_curve_edit.cpp index 5cc622903..324847c3d 100644 --- a/radio/src/gui/212x64/model_curve_edit.cpp +++ b/radio/src/gui/212x64/model_curve_edit.cpp @@ -180,6 +180,8 @@ void menuModelCurveOne(event_t event) } drawCurve(FW); + if (s_currSrcRaw != MIXSRC_NONE) + drawCursor(applyCurrentCurve, FW); uint8_t posY = FH+1; attr = (s_editMode > 0 ? INVERS|BLINK : INVERS); diff --git a/radio/src/gui/212x64/model_input_edit.cpp b/radio/src/gui/212x64/model_input_edit.cpp index 4d2ab97da..49bea6834 100644 --- a/radio/src/gui/212x64/model_input_edit.cpp +++ b/radio/src/gui/212x64/model_input_edit.cpp @@ -30,28 +30,6 @@ int expoFn(int x) return anas[ed->chn]; } -void drawFunction(FnFuncP fn, uint8_t offset) -{ - lcdDrawVerticalLine(CURVE_CENTER_X-offset, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, CURVE_SIDE_WIDTH*2, 0xee); - lcdDrawHorizontalLine(CURVE_CENTER_X-CURVE_SIDE_WIDTH-offset, CURVE_CENTER_Y, CURVE_SIDE_WIDTH*2, 0xee); - - coord_t prev_yv = (coord_t)-1; - - for (int xv=-CURVE_SIDE_WIDTH; xv<=CURVE_SIDE_WIDTH; xv++) { - coord_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/CURVE_SIDE_WIDTH))) / 2 * (LCD_H-1) / RESX); - if (prev_yv != (coord_t)-1) { - if (abs((int8_t)yv-prev_yv) <= 1) { - lcdDrawPoint(CURVE_CENTER_X+xv-offset-1, prev_yv, FORCE); - } - else { - uint8_t tmp = (prev_yv < yv ? 0 : 1); - lcdDrawSolidVerticalLine(CURVE_CENTER_X+xv-offset-1, yv+tmp, prev_yv-yv); - } - } - prev_yv = yv; - } -} - enum ExposFields { EXPO_FIELD_INPUT_NAME, EXPO_FIELD_LINE_NAME, @@ -168,23 +146,8 @@ void menuModelExpoOne(event_t event) } drawFunction(expoFn); - - int x512 = getValue(ed->srcRaw); - if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - drawSensorCustomValue(LCD_W-8, 6*FH, (ed->srcRaw - MIXSRC_FIRST_TELEM) / 3, x512, 0); - if (ed->scale > 0) x512 = (x512 * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); - } - else { - lcdDrawNumber(LCD_W-8, 6*FH, calcRESXto1000(x512), RIGHT | PREC1); - } - x512 = limit(-1024, x512, 1024); - int y512 = expoFn(x512); - y512 = limit(-1024, y512, 1024); - lcdDrawNumber(LCD_W-8-6*FW, 1*FH, calcRESXto1000(y512), RIGHT | PREC1); - - x512 = CURVE_CENTER_X+x512/(RESX/CURVE_SIDE_WIDTH); - y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX; - - lcdDrawSolidVerticalLine(x512, y512-3, 3*2+1); - lcdDrawSolidHorizontalLine(x512-3, y512, 3*2+1); + // those parameters are global so that they can be reused in the curve edit screen + s_currSrcRaw = ed->srcRaw; + s_currScale = ed->scale; + drawCursor(expoFn); } diff --git a/radio/src/gui/212x64/model_mix_edit.cpp b/radio/src/gui/212x64/model_mix_edit.cpp index 1faec6d69..749b0ed05 100644 --- a/radio/src/gui/212x64/model_mix_edit.cpp +++ b/radio/src/gui/212x64/model_mix_edit.cpp @@ -140,6 +140,8 @@ void menuModelMixOne(event_t event) case MIX_FIELD_CURVE: lcdDrawTextAlignedLeft(y, STR_CURVE); + s_currSrcRaw = md2->srcRaw; + s_currScale = 0; editCurveRef(MIXES_2ND_COLUMN, y, md2->curve, event, attr); break; diff --git a/radio/src/gui/480x272/curves.cpp b/radio/src/gui/480x272/curves.cpp index b18d15d4a..54feb3d56 100644 --- a/radio/src/gui/480x272/curves.cpp +++ b/radio/src/gui/480x272/curves.cpp @@ -94,3 +94,36 @@ void drawCurvePoint(int x, int y, LcdFlags color) lcdDrawBitmapPattern(x, y, LBM_CURVE_POINT, color); lcdDrawBitmapPattern(x, y, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR); } + +void drawCursor(FnFuncP fn) +{ + char textx[5]; + char texty[5]; + int x = getValue(s_currSrcRaw); + if (s_currSrcRaw >= MIXSRC_FIRST_TELEM) { + strAppendUnsigned(textx, calcRESXto100(x)); + // TODO drawSensorCustomValue(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x); + if (s_currScale > 0) + x = (x * 1024) / convertTelemValue(s_currSrcRaw - MIXSRC_FIRST_TELEM + 1, s_currScale); + } + else { + strAppendSigned(textx, calcRESXto100(x)); + } + + x = limit(-1024, x, 1024); + int y = limit(-1024, applyCurrentCurve(x), 1024); + strAppendSigned(texty, calcRESXto100(y)); + + x = divRoundClosest(x * CURVE_SIDE_WIDTH, RESX); + y = CURVE_CENTER_Y + getCurveYCoord(applyCurrentCurve, x, CURVE_SIDE_WIDTH); + + lcdDrawSolidFilledRect(CURVE_CENTER_X + x, CURVE_CENTER_Y - CURVE_SIDE_WIDTH, 2, 2 * CURVE_SIDE_WIDTH + 2, CURVE_CURSOR_COLOR); + lcdDrawSolidFilledRect(CURVE_CENTER_X - CURVE_SIDE_WIDTH-2, y-1, 2*CURVE_SIDE_WIDTH + 2, 2, CURVE_CURSOR_COLOR); + lcdDrawBitmapPattern(CURVE_CENTER_X + x - 4, y - 4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR); + lcdDrawBitmapPattern(CURVE_CENTER_X + x - 4, y - 4, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR); + + int left = limit(CURVE_CENTER_X - CURVE_SIDE_WIDTH, CURVE_CENTER_X - CURVE_COORD_WIDTH / 2 + x, CURVE_CENTER_X + CURVE_SIDE_WIDTH - CURVE_COORD_WIDTH + 2); + drawCurveCoord(left, CURVE_CENTER_Y + CURVE_SIDE_WIDTH + 2, textx); + int top = limit(CURVE_CENTER_Y - CURVE_SIDE_WIDTH - 1, -CURVE_COORD_HEIGHT / 2 + y, CURVE_CENTER_Y + CURVE_SIDE_WIDTH - CURVE_COORD_HEIGHT + 1); + drawCurveCoord(CURVE_CENTER_X-CURVE_SIDE_WIDTH - 37, top, texty); +} diff --git a/radio/src/gui/480x272/menu_model.cpp b/radio/src/gui/480x272/menu_model.cpp index 09ce6523f..f376e36a2 100644 --- a/radio/src/gui/480x272/menu_model.cpp +++ b/radio/src/gui/480x272/menu_model.cpp @@ -152,6 +152,8 @@ void editName(coord_t x, coord_t y, char * name, uint8_t size, event_t event, ui uint8_t s_currIdx; uint8_t s_currIdxSubMenu; +uint16_t s_currSrcRaw; +uint16_t s_currScale; void copySelection(char * dst, const char * src, uint8_t size) { diff --git a/radio/src/gui/480x272/menus.h b/radio/src/gui/480x272/menus.h index 290ff43ac..ce558404c 100644 --- a/radio/src/gui/480x272/menus.h +++ b/radio/src/gui/480x272/menus.h @@ -460,6 +460,8 @@ extern int8_t s_copySrcRow; extern int8_t s_copyTgtOfs; extern uint8_t s_currIdx; extern uint8_t s_currIdxSubMenu; +extern uint16_t s_currSrcRaw; +extern uint16_t s_currScale; extern int8_t s_currCh; extern uint8_t s_copySrcIdx; extern uint8_t s_copySrcCh; @@ -626,6 +628,7 @@ void readModelNotes(); typedef int (*FnFuncP) (int x); void drawFunction(FnFuncP fn, int x, int y, int width); +void drawCursor(FnFuncP fn); void onSourceLongEnterPress(const char *result); diff --git a/radio/src/gui/480x272/model_curves.cpp b/radio/src/gui/480x272/model_curves.cpp index e53f9aad4..852e80f2e 100644 --- a/radio/src/gui/480x272/model_curves.cpp +++ b/radio/src/gui/480x272/model_curves.cpp @@ -203,6 +203,8 @@ bool menuModelCurveOne(event_t event) drawCurve(CURVE_CENTER_X, CURVE_CENTER_Y, CURVE_SIDE_WIDTH); drawCurveHorizontalScale(); if (menuVerticalPosition < ITEM_CURVE_COORDS1) drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH-15); + if (s_currSrcRaw != MIXSRC_NONE) + drawCursor(applyCurrentCurve); coord_t posX = 47; attr = (s_editMode > 0 ? INVERS|BLINK : INVERS); @@ -324,6 +326,7 @@ bool menuModelCurvesAll(event_t event) switch (event) { case EVT_KEY_BREAK(KEY_ENTER): if (!READ_ONLY()) { + s_currSrcRaw = MIXSRC_NONE; pushMenu(menuModelCurveOne); } break; diff --git a/radio/src/gui/480x272/model_inputs.cpp b/radio/src/gui/480x272/model_inputs.cpp index 4e1aa7dec..8f59cf94b 100644 --- a/radio/src/gui/480x272/model_inputs.cpp +++ b/radio/src/gui/480x272/model_inputs.cpp @@ -185,38 +185,11 @@ bool menuModelExpoOne(event_t event) drawFunction(expoFn, CURVE_CENTER_X, CURVE_CENTER_Y, CURVE_SIDE_WIDTH); drawCurveHorizontalScale(); - drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH-15); - - { - char textx[5]; - char texty[5]; - int x = getValue(ed->srcRaw); - if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - strAppendUnsigned(textx, calcRESXto100(x)); - // TODO drawSensorCustomValue(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x); - if (ed->scale > 0) x = (x * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); - } - else { - strAppendSigned(textx, calcRESXto100(x)); - } - - x = limit(-1024, x, 1024); - int y = limit(-1024, expoFn(x), 1024); - strAppendSigned(texty, calcRESXto100(y)); - - x = divRoundClosest(x*CURVE_SIDE_WIDTH, RESX); - y = CURVE_CENTER_Y + getCurveYCoord(expoFn, x, CURVE_SIDE_WIDTH); - - lcdDrawSolidFilledRect(CURVE_CENTER_X+x, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, 2, 2*CURVE_SIDE_WIDTH+2, CURVE_CURSOR_COLOR); - lcdDrawSolidFilledRect(CURVE_CENTER_X-CURVE_SIDE_WIDTH-2, y-1, 2*CURVE_SIDE_WIDTH+2, 2, CURVE_CURSOR_COLOR); - lcdDrawBitmapPattern(CURVE_CENTER_X+x-4, y-4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR); - lcdDrawBitmapPattern(CURVE_CENTER_X+x-4, y-4, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR); - - int left = limit(CURVE_CENTER_X-CURVE_SIDE_WIDTH, CURVE_CENTER_X-CURVE_COORD_WIDTH/2+x, CURVE_CENTER_X+CURVE_SIDE_WIDTH-CURVE_COORD_WIDTH+2); - drawCurveCoord(left, CURVE_CENTER_Y+CURVE_SIDE_WIDTH+2, textx); - int top = limit(CURVE_CENTER_Y-CURVE_SIDE_WIDTH-1, -CURVE_COORD_HEIGHT/2+y, CURVE_CENTER_Y+CURVE_SIDE_WIDTH-CURVE_COORD_HEIGHT+1); - drawCurveCoord(CURVE_CENTER_X-CURVE_SIDE_WIDTH-37, top, texty); - } + drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH - 15); + // those parameters are global so that they can be reused in the curve edit screen + s_currSrcRaw = ed->srcRaw; + s_currScale = ed->scale; + drawCursor(expoFn); for (uint8_t k=0; ksrcRaw, attr); if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST); break; + case MIX_FIELD_WEIGHT: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WEIGHT); gvarWeightItem(MIXES_2ND_COLUMN, y, md2, attr|LEFT, event); break; + case MIX_FIELD_OFFSET: { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_OFFSET); @@ -230,6 +232,8 @@ bool menuModelMixOne(event_t event) break; case MIX_FIELD_CURVE: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURVE); + s_currSrcRaw = md2->srcRaw; + s_currScale = 0; editCurveRef(MIXES_2ND_COLUMN, y, md2->curve, event, attr); break; #if defined(FLIGHT_MODES) diff --git a/radio/src/gui/common/stdlcd/draw_functions.h b/radio/src/gui/common/stdlcd/draw_functions.h index 7755206e3..9a98999a6 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.h +++ b/radio/src/gui/common/stdlcd/draw_functions.h @@ -45,4 +45,9 @@ void drawSleepBitmap(); void lcdDrawMMM(coord_t x, coord_t y, LcdFlags flags=0); void drawTrimMode(coord_t x, coord_t y, uint8_t flightMode, uint8_t idx, LcdFlags att=0); +typedef int (*FnFuncP) (int x); +void drawFunction(FnFuncP fn, uint8_t offset = 0); +void drawCursor(FnFuncP fn, uint8_t offset = 0); +void drawCurve(coord_t offset = 0); + #endif // _STDLCD_DRAW_FUNCTIONS_H_ diff --git a/radio/src/gui/common/stdlcd/model_curves.cpp b/radio/src/gui/common/stdlcd/model_curves.cpp index 6fd7c1d0b..f0bd5ab2c 100644 --- a/radio/src/gui/common/stdlcd/model_curves.cpp +++ b/radio/src/gui/common/stdlcd/model_curves.cpp @@ -61,6 +61,7 @@ void menuModelCurvesAll(event_t event) case EVT_KEY_FIRST(KEY_ENTER): if (CURVE_SELECTED() && !READ_ONLY()) { s_currIdxSubMenu = sub; + s_currSrcRaw = MIXSRC_NONE; pushMenu(menuModelCurveOne); } break; @@ -140,9 +141,9 @@ void editCurveRef(coord_t x, coord_t y, CurveRef & curve, event_t event, LcdFlag break; case CURVE_REF_CUSTOM: drawCurveName(x, y, curve.value, flags); - if (active && menuHorizontalPosition==1) { - if (event==EVT_KEY_LONG(KEY_ENTER) && curve.value!=0) { - s_currIdxSubMenu = (curve.value<0 ? -curve.value-1 : curve.value-1); + if (active && menuHorizontalPosition == 1) { + if (event == EVT_KEY_LONG(KEY_ENTER) && curve.value != 0) { + s_currIdxSubMenu = abs(curve.value) - 1; pushMenu(menuModelCurveOne); } else { @@ -152,3 +153,48 @@ void editCurveRef(coord_t x, coord_t y, CurveRef & curve, event_t event, LcdFlag break; } } + +void drawFunction(FnFuncP fn, uint8_t offset) +{ + lcdDrawVerticalLine(CURVE_CENTER_X - offset, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, CURVE_SIDE_WIDTH * 2, 0xee); + lcdDrawHorizontalLine(CURVE_CENTER_X - CURVE_SIDE_WIDTH - offset, CURVE_CENTER_Y, CURVE_SIDE_WIDTH * 2, 0xee); + + coord_t prev_yv = (coord_t) - 1; + + for (int xv = -CURVE_SIDE_WIDTH; xv <= CURVE_SIDE_WIDTH; xv++) { + coord_t yv = (LCD_H - 1) - (((uint16_t)RESX + fn(xv * (RESX/CURVE_SIDE_WIDTH))) / 2 * (LCD_H - 1) / RESX); + if (prev_yv != (coord_t) - 1) { + if (abs((int8_t)yv-prev_yv) <= 1) { + lcdDrawPoint(CURVE_CENTER_X + xv - offset - 1, prev_yv, FORCE); + } + else { + uint8_t tmp = (prev_yv < yv ? 0 : 1); + lcdDrawSolidVerticalLine(CURVE_CENTER_X + xv - offset - 1, yv + tmp, prev_yv - yv); + } + } + prev_yv = yv; + } +} + +void drawCursor(FnFuncP fn, uint8_t offset) +{ + int x512 = getValue(s_currSrcRaw); + if (s_currSrcRaw >= MIXSRC_FIRST_TELEM) { + if (s_currScale > 0) + x512 = (x512 * 1024) / convertTelemValue(s_currSrcRaw - MIXSRC_FIRST_TELEM + 1, s_currScale); + drawSensorCustomValue(LCD_W - FW - offset, 6 * FH, (s_currSrcRaw - MIXSRC_FIRST_TELEM) / 3, x512, 0); + } + else { + lcdDrawNumber(LCD_W - FW - offset, 6*FH, calcRESXto1000(x512), RIGHT | PREC1); + } + x512 = limit(-1024, x512, 1024); + int y512 = fn(x512); + y512 = limit(-1024, y512, 1024); + lcdDrawNumber(CURVE_CENTER_X - FWNUM - offset, 1*FH, calcRESXto1000(y512), RIGHT | PREC1); + + x512 = CURVE_CENTER_X + x512/(RESX / CURVE_SIDE_WIDTH); + y512 = (LCD_H - 1) - ((y512 + RESX) / 2) * (LCD_H - 1) / RESX; + + lcdDrawSolidVerticalLine(x512 - offset, y512-3, 3 * 2 + 1); + lcdDrawSolidHorizontalLine(x512 - 3 - offset, y512, 3 * 2 + 1); +} diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index ab08dad08..8380d2d49 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -128,8 +128,6 @@ void drawSourceValue(coord_t x, coord_t y, source_t channel, LcdFlags flags=0); int convertMultiToOtx(int type); -void drawCurve(coord_t offset=0); - #if defined(COLORLCD) void drawStringWithIndex(coord_t x, coord_t y, const char * str, int idx, LcdFlags flags=0, const char * prefix=nullptr, const char * suffix=nullptr); uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, LcdFlags flags, event_t event); From 391bf5d4060f08cb951b2b1df62ab936c8a660de Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 22 Sep 2020 21:51:19 +0200 Subject: [PATCH 222/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 09f51c937..a0bb767fa 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1923,3 +1923,4 @@ Michael Ledward Dave Waldesbuehl Dale Rhoads Denis Kelly +SBEurope LTD From c0c205a92df6480f6b075f9e5adfea5428179b3a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 23 Sep 2020 12:59:44 +0200 Subject: [PATCH 223/267] XACT servos telemetry support (#7959) XACT servos telemetry support --- radio/src/telemetry/frsky.h | 22 +++--- radio/src/telemetry/frsky_sport.cpp | 81 ++++++++++++++--------- radio/src/telemetry/telemetry.h | 1 + radio/src/telemetry/telemetry_sensors.cpp | 49 ++++++++++---- radio/src/telemetry/telemetry_sensors.h | 2 + radio/src/translations/cz.h.txt | 6 +- radio/src/translations/de.h.txt | 6 +- radio/src/translations/en.h.txt | 6 +- radio/src/translations/es.h.txt | 6 +- radio/src/translations/fi.h.txt | 6 +- radio/src/translations/fr.h.txt | 6 +- radio/src/translations/it.h.txt | 6 +- radio/src/translations/nl.h.txt | 6 +- radio/src/translations/pl.h.txt | 6 +- radio/src/translations/pt.h.txt | 6 +- radio/src/translations/se.h.txt | 6 +- 16 files changed, 154 insertions(+), 67 deletions(-) diff --git a/radio/src/telemetry/frsky.h b/radio/src/telemetry/frsky.h index 751196d1c..8621869c6 100644 --- a/radio/src/telemetry/frsky.h +++ b/radio/src/telemetry/frsky.h @@ -139,6 +139,8 @@ enum FrSkyDataState { #define A4_LAST_ID 0x091F #define AIR_SPEED_FIRST_ID 0x0A00 #define AIR_SPEED_LAST_ID 0x0A0F +#define FUEL_QTY_FIRST_ID 0x0A10 +#define FUEL_QTY_LAST_ID 0x0A1F #define RBOX_BATT1_FIRST_ID 0x0B00 #define RBOX_BATT1_LAST_ID 0x0B0F #define RBOX_BATT2_FIRST_ID 0x0B10 @@ -152,9 +154,9 @@ enum FrSkyDataState { #define ESC_POWER_FIRST_ID 0x0B50 #define ESC_POWER_LAST_ID 0x0B5F #define ESC_RPM_CONS_FIRST_ID 0x0B60 -#define ESC_RPM_CONS_LAST_ID 0x0B6f +#define ESC_RPM_CONS_LAST_ID 0x0B6F #define ESC_TEMPERATURE_FIRST_ID 0x0B70 -#define ESC_TEMPERATURE_LAST_ID 0x0B7f +#define ESC_TEMPERATURE_LAST_ID 0x0B7F #define RB3040_OUTPUT_FIRST_ID 0x0B80 #define RB3040_OUTPUT_LAST_ID 0x0B8F #define RB3040_CH1_2_FIRST_ID 0x0B90 @@ -165,10 +167,10 @@ enum FrSkyDataState { #define RB3040_CH5_6_LAST_ID 0x0BBF #define RB3040_CH7_8_FIRST_ID 0x0BC0 #define RB3040_CH7_8_LAST_ID 0x0BCF -#define X8R_FIRST_ID 0x0c20 -#define X8R_LAST_ID 0x0c2F -#define S6R_FIRST_ID 0x0c30 -#define S6R_LAST_ID 0x0c3F +#define X8R_FIRST_ID 0x0C20 +#define X8R_LAST_ID 0x0C2F +#define S6R_FIRST_ID 0x0C30 +#define S6R_LAST_ID 0x0C3F #define GASSUIT_TEMP1_FIRST_ID 0x0D00 #define GASSUIT_TEMP1_LAST_ID 0x0D0F #define GASSUIT_TEMP2_FIRST_ID 0x0D10 @@ -182,15 +184,17 @@ enum FrSkyDataState { #define GASSUIT_FLOW_FIRST_ID 0x0D50 #define GASSUIT_FLOW_LAST_ID 0x0D5F #define GASSUIT_MAX_FLOW_FIRST_ID 0x0D60 -#define GASSUIT_MAX_FLOW_LAST_ID 0x0D6f +#define GASSUIT_MAX_FLOW_LAST_ID 0x0D6F #define GASSUIT_AVG_FLOW_FIRST_ID 0x0D70 -#define GASSUIT_AVG_FLOW_LAST_ID 0x0D7f +#define GASSUIT_AVG_FLOW_LAST_ID 0x0D7F #define SBEC_POWER_FIRST_ID 0x0E50 #define SBEC_POWER_LAST_ID 0x0E5F #define DIY_FIRST_ID 0x5100 #define DIY_LAST_ID 0x52FF #define DIY_STREAM_FIRST_ID 0x5000 #define DIY_STREAM_LAST_ID 0x50FF +#define SERVO_FIRST_ID 0x6800 +#define SERVO_LAST_ID 0x680F #define FACT_TEST_ID 0xF000 #define VALID_FRAME_RATE_ID 0xF010 #define RSSI_ID 0xF101 @@ -202,8 +206,6 @@ enum FrSkyDataState { #define R9_PWR_ID 0xF107 #define SP2UART_A_ID 0xFD00 #define SP2UART_B_ID 0xFD01 -#define FUEL_QTY_FIRST_ID 0x0A10 -#define FUEL_QTY_LAST_ID 0x0A1F #if defined(MULTIMODULE) // Virtual IDs, value can be changed to anything only used for display diff --git a/radio/src/telemetry/frsky_sport.cpp b/radio/src/telemetry/frsky_sport.cpp index 6fa6ad13d..3790736fd 100644 --- a/radio/src/telemetry/frsky_sport.cpp +++ b/radio/src/telemetry/frsky_sport.cpp @@ -94,7 +94,11 @@ const FrSkySportSensor sportSensors[] = { { RB3040_CH5_6_FIRST_ID, RB3040_CH5_6_LAST_ID, 1, ZSTR_RB3040_CHANNEL6, UNIT_AMPS, 2 }, { RB3040_CH7_8_FIRST_ID, RB3040_CH7_8_LAST_ID, 0, ZSTR_RB3040_CHANNEL7, UNIT_AMPS, 2 }, { RB3040_CH7_8_FIRST_ID, RB3040_CH7_8_LAST_ID, 1, ZSTR_RB3040_CHANNEL8, UNIT_AMPS, 2 }, - { 0, 0, 0, NULL, UNIT_RAW, 0 } // sentinel + { SERVO_FIRST_ID, SERVO_LAST_ID, 0, ZSTR_SERVO_CURRENT, UNIT_AMPS, 1 }, + { SERVO_FIRST_ID, SERVO_LAST_ID, 1, ZSTR_SERVO_VOLTAGE, UNIT_VOLTS, 1 }, + { SERVO_FIRST_ID, SERVO_LAST_ID, 2, ZSTR_SERVO_TEMPERATURE, UNIT_CELSIUS, 0 }, + { SERVO_FIRST_ID, SERVO_LAST_ID, 3, ZSTR_SERVO_STATUS, UNIT_TEXT, 0 }, + { 0, 0, 0, nullptr, UNIT_RAW, 0 } // sentinel }; const FrSkySportSensor * getFrSkySportSensor(uint16_t id, uint8_t subId=0) @@ -110,13 +114,13 @@ const FrSkySportSensor * getFrSkySportSensor(uint16_t id, uint8_t subId=0) bool checkSportPacket(const uint8_t * packet) { short crc = 0; - for (int i=1; i> 8; // 0-1FF - crc &= 0x00ff; // 0-FF + crc += crc >> 8u; // 0-1FF + crc &= 0x00FFu; // 0-FF } // TRACE("crc: 0x%02x", crc); - return (crc == 0x00ff); + return crc == 0x00FF; } #define SPORT_DATA_U8(packet) (packet[4]) @@ -137,15 +141,15 @@ void sportProcessTelemetryPacket(uint16_t id, uint8_t subId, uint8_t instance, u precision = sensor->prec; } if (unit == UNIT_CELLS) { - uint8_t cellsCount = (data & 0xF0) >> 4; - uint8_t cellIndex = (data & 0x0F); + uint8_t cellsCount = (data & 0xF0u) >> 4u; + uint8_t cellIndex = (data & 0x0Fu); if (cellIndex >= MAX_CELLS) return; - uint32_t mask = (cellsCount << 24) + (cellIndex << 16); - setTelemetryValue(PROTOCOL_TELEMETRY_FRSKY_SPORT, id, subId, instance, mask + (((data & 0x000FFF00) >> 8) / 5), unit, precision); - if (cellIndex+1 < cellsCount) { - mask += (1 << 16); - setTelemetryValue(PROTOCOL_TELEMETRY_FRSKY_SPORT, id, subId, instance, mask + (((data & 0xFFF00000) >> 20) / 5), unit, precision); + uint32_t mask = (cellsCount << 24u) + (cellIndex << 16u); + setTelemetryValue(PROTOCOL_TELEMETRY_FRSKY_SPORT, id, subId, instance, mask + (((data & 0x000FFF00u) >> 8u) / 5), unit, precision); + if (cellIndex + 1 < cellsCount) { + mask += (1 << 16u); + setTelemetryValue(PROTOCOL_TELEMETRY_FRSKY_SPORT, id, subId, instance, mask + (((data & 0xFFF00000u) >> 20u) / 5), unit, precision); } } else { @@ -219,7 +223,7 @@ void sportProcessTelemetryPacketWithoutCrc(uint8_t origin, const uint8_t * packe } #if defined(MULTIMODULE) if (telemetryProtocol == PROTOCOL_TELEMETRY_MULTIMODULE) { - sportProcessTelemetryPacket(TX_RSSI_ID, 0, instance, packet[5] >> 1, UNIT_DB); + sportProcessTelemetryPacket(TX_RSSI_ID, 0, instance, packet[5] >> 1u, UNIT_DB); sportProcessTelemetryPacket(TX_LQI_ID, 0, instance, packet[7], UNIT_RAW); } #endif @@ -263,40 +267,40 @@ void sportProcessTelemetryPacketWithoutCrc(uint8_t origin, const uint8_t * packe if (dataId >= GPS_LONG_LATI_FIRST_ID && dataId <= GPS_LONG_LATI_LAST_ID) { int32_t value = (data & 0x3fffffff); - if (data & (1 << 30)) + if (data & (1 << 30u)) value = -value; value = (value * 5) / 3; // min/10000 => deg/1000000 - if (data & (1 << 31)) + if (data & (1 << 31u)) sportProcessTelemetryPacket(dataId, 0, instance, value, UNIT_GPS_LONGITUDE); else sportProcessTelemetryPacket(dataId, 0, instance, value, UNIT_GPS_LATITUDE); } else if (dataId >= RBOX_BATT1_FIRST_ID && dataId <= RBOX_BATT2_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, data & 0xffff); - sportProcessTelemetryPacket(dataId, 1, instance, data >> 16); + sportProcessTelemetryPacket(dataId, 0, instance, data & 0xFFFFu); + sportProcessTelemetryPacket(dataId, 1, instance, data >> 16u); } else if (dataId >= RBOX_CNSP_FIRST_ID && dataId <= RBOX_CNSP_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, data & 0xffff); - sportProcessTelemetryPacket(dataId, 1, instance, data >> 16); + sportProcessTelemetryPacket(dataId, 0, instance, data & 0xFFFFu); + sportProcessTelemetryPacket(dataId, 1, instance, data >> 16u); } else if (dataId >= RBOX_STATE_FIRST_ID && dataId <= RBOX_STATE_LAST_ID) { bool static isRB10 = false; uint16_t newServosState; - if (servosState == 0 && (data & 0xff00) == 0xff00) { + if (servosState == 0 && (data & 0xFF00u) == 0xFF00u) { isRB10 = true; } if (isRB10) { - newServosState = data & 0x00ff; // 8ch only RB10 + newServosState = data & 0x00FFu; // 8ch only RB10 } else { - newServosState = data & 0xffff; + newServosState = data & 0xFFFFu; } if (newServosState != 0 && servosState == 0) { audioEvent(AU_SERVO_KO); } - uint16_t newRboxState = data >> 16; - if ((newRboxState & 0x07) && (rboxState & 0x07) == 0) { + uint16_t newRboxState = data >> 16u; + if ((newRboxState & 0x07u) && (rboxState & 0x07u) == 0) { audioEvent(AU_RX_OVERLOAD); } servosState = newServosState; @@ -305,19 +309,19 @@ void sportProcessTelemetryPacketWithoutCrc(uint8_t origin, const uint8_t * packe sportProcessTelemetryPacket(dataId, 1, instance, rboxState); } else if (dataId >= ESC_POWER_FIRST_ID && dataId <= ESC_POWER_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, data & 0xffff); - sportProcessTelemetryPacket(dataId, 1, instance, data >> 16); + sportProcessTelemetryPacket(dataId, 0, instance, data & 0xFFFFu); + sportProcessTelemetryPacket(dataId, 1, instance, data >> 16u); } else if (dataId >= ESC_RPM_CONS_FIRST_ID && dataId <= ESC_RPM_CONS_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, 100 * (data & 0xffff)); - sportProcessTelemetryPacket(dataId, 1, instance, data >> 16); + sportProcessTelemetryPacket(dataId, 0, instance, 100 * (data & 0xFFFFu)); + sportProcessTelemetryPacket(dataId, 1, instance, data >> 16u); } else if (dataId >= ESC_TEMPERATURE_FIRST_ID && dataId <= ESC_TEMPERATURE_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, data & 0x00ff); + sportProcessTelemetryPacket(dataId, 0, instance, data & 0x00FFu); } else if (dataId >= SBEC_POWER_FIRST_ID && dataId <= SBEC_POWER_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, (data & 0xffff) / 10); - sportProcessTelemetryPacket(dataId, 1, instance, (data >> 16) / 10); + sportProcessTelemetryPacket(dataId, 0, instance, (data & 0xFFFFu) / 10); + sportProcessTelemetryPacket(dataId, 1, instance, (data >> 16u) / 10); } else if (dataId >= DIY_STREAM_FIRST_ID && dataId <= DIY_STREAM_LAST_ID) { #if defined(LUA) @@ -334,8 +338,19 @@ void sportProcessTelemetryPacketWithoutCrc(uint8_t origin, const uint8_t * packe #endif } else if (dataId >= RB3040_CH1_2_FIRST_ID && dataId <= RB3040_CH7_8_LAST_ID) { - sportProcessTelemetryPacket(dataId, 0, instance, data & 0xffff); - sportProcessTelemetryPacket(dataId, 1, instance, (data >> 16u) & 0xffff); + sportProcessTelemetryPacket(dataId, 0, instance, data & 0xFFFFu); + sportProcessTelemetryPacket(dataId, 1, instance, (data >> 16u) & 0xFFFFu); + } + else if (dataId >= SERVO_FIRST_ID && dataId <= SERVO_LAST_ID) { + sportProcessTelemetryPacket(dataId, 0, instance, data & 0xFFu); + sportProcessTelemetryPacket(dataId, 1, instance, (data >> 8u) & 0xFFu); + sportProcessTelemetryPacket(dataId, 2, instance, (data >> 16u) & 0xFFu); + uint8_t newServosState = data >> 24u; + setTelemetryText(PROTOCOL_TELEMETRY_FRSKY_SPORT, dataId, 3, instance, newServosState ? "STALL" : "OK"); + if (newServosState != 0 && servosState == 0) { + audioEvent(AU_SERVO_KO); + servosState = newServosState; + } } else { sportProcessTelemetryPacket(dataId, 0, instance, data); diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index e78846845..6599db749 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -97,6 +97,7 @@ PACK(struct CellValue }); int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, uint8_t instance, int32_t value, uint32_t unit, uint32_t prec); +int setTelemetryText(TelemetryProtocol protocol, uint16_t id, uint8_t subId, uint8_t instance, const char * text); void delTelemetryIndex(uint8_t index); int availableTelemetryIndex(); int lastUsedTelemetryIndex(); diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index 9cebabd3c..d14c4dafa 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -78,21 +78,27 @@ uint32_t getDistFromEarthAxis(int32_t latitude) return 139*(((uint32_t)10000000 - ((angle2*(uint32_t)123370)/81) + (angle4/25))/12500); } +void TelemetryItem::setValue(const TelemetrySensor & sensor, const char * val, uint32_t, uint32_t) +{ + strncpy(text, val, sizeof(text)); + setFresh(); +} + void TelemetryItem::setValue(const TelemetrySensor & sensor, int32_t val, uint32_t unit, uint32_t prec) { int32_t newVal = val; - if(prec == 255) { + if (prec == 255) { prec = sensor.prec; } if (unit == UNIT_CELLS) { - uint32_t data = uint32_t(newVal); - uint8_t cellsCount = (data >> 24); - uint8_t cellIndex = ((data >> 16) & 0x0F); + auto data = uint32_t(newVal); + uint8_t cellsCount = (data >> 24u); + uint8_t cellIndex = ((data >> 16u) & 0x0Fu); if (cellIndex >= MAX_CELLS) return; - uint16_t cellValue = (data & 0xFFFF); + uint16_t cellValue = (data & 0xFFFFu); if (cellsCount == 0) { cellsCount = (cellIndex >= cells.count ? cellIndex + 1 : cells.count); if (cellsCount != cells.count) { @@ -178,18 +184,18 @@ void TelemetryItem::setValue(const TelemetrySensor & sensor, int32_t val, uint32 } else if (unit == UNIT_DATETIME_DAY_MONTH) { auto data = uint32_t(newVal); - datetime.month = data >> 8; - datetime.day = data & 0xFF; + datetime.month = data >> 8u; + datetime.day = data & 0xFFu; return; } else if (unit == UNIT_DATETIME_HOUR_MIN) { auto data = uint32_t(newVal); - datetime.hour = (data & 0xFF); - datetime.min = data >> 8; + datetime.hour = (data & 0xFFu); + datetime.min = data >> 8u; return; } else if (unit == UNIT_DATETIME_SEC) { - datetime.sec = newVal & 0xFF; + datetime.sec = newVal & 0xFFu; newVal = 0; } else if (unit == UNIT_RPMS) { @@ -198,8 +204,6 @@ void TelemetryItem::setValue(const TelemetrySensor & sensor, int32_t val, uint32 } } else if (unit == UNIT_TEXT) { - *((uint32_t*)&text[prec]) = newVal; - setFresh(); return; } else { @@ -495,7 +499,8 @@ int lastUsedTelemetryIndex() return -1; } -int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, uint8_t instance, int32_t value, uint32_t unit, uint32_t prec) +template +int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, uint8_t instance, T value, uint32_t unit = 0, uint32_t prec = 0) { bool sensorFound = false; @@ -518,41 +523,50 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, ui case PROTOCOL_TELEMETRY_FRSKY_SPORT: frskySportSetDefault(index, id, subId, instance); break; + case PROTOCOL_TELEMETRY_FRSKY_D: frskyDSetDefault(index, id); break; + #if defined(CROSSFIRE) case PROTOCOL_TELEMETRY_CROSSFIRE: crossfireSetDefault(index, id, instance); break; #endif + #if defined(GHOST) case PROTOCOL_TELEMETRY_GHOST: ghostSetDefault(index, id, instance); break; #endif + #if defined(MULTIMODULE) || defined(AFHDS3) case PROTOCOL_TELEMETRY_FLYSKY_IBUS: flySkySetDefault(index,id, subId, instance); break; #endif + #if defined(MULTIMODULE) case PROTOCOL_TELEMETRY_SPEKTRUM: spektrumSetDefault(index, id, subId, instance); break; + case PROTOCOL_TELEMETRY_HITEC: hitecSetDefault(index, id, subId, instance); break; + case PROTOCOL_TELEMETRY_HOTT: hottSetDefault(index, id, subId, instance); break; #endif + #if defined(LUA) case PROTOCOL_TELEMETRY_LUA: // Sensor will be initialized by calling function // This drops the first value return index; #endif + default: return index; } @@ -563,7 +577,16 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, ui POPUP_WARNING(STR_TELEMETRYFULL); return -1; } +} +int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, uint8_t instance, int32_t value, uint32_t unit, uint32_t prec) +{ + return setTelemetryValue(protocol, id, subId, instance, value, unit, prec); +} + +int setTelemetryText(TelemetryProtocol protocol, uint16_t id, uint8_t subId, uint8_t instance, const char * text) +{ + return setTelemetryValue(protocol, id, subId, instance, text); } void TelemetrySensor::init(const char * label, uint8_t unit, uint8_t prec) diff --git a/radio/src/telemetry/telemetry_sensors.h b/radio/src/telemetry/telemetry_sensors.h index 6ca1c0c97..e2fbcd498 100644 --- a/radio/src/telemetry/telemetry_sensors.h +++ b/radio/src/telemetry/telemetry_sensors.h @@ -91,6 +91,8 @@ class TelemetryItem void eval(const TelemetrySensor & sensor); void per10ms(const TelemetrySensor & sensor); + void setValue(const TelemetrySensor & sensor, const char * newVal, uint32_t unit=UNIT_TEXT, uint32_t prec=0); + void setValue(const TelemetrySensor & sensor, int32_t newVal, uint32_t unit, uint32_t prec=0); inline bool isAvailable() diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index a01ed39d7..3c8e32fa7 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -1355,4 +1355,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 92d73d56c..686df1b24 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -1362,4 +1362,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index f976fad51..7042a6df7 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -1359,4 +1359,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index a9af085b2..deec63071 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -1358,4 +1358,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index bdd39aeb4..c870988ef 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -1366,4 +1366,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 20a2eede9..717c7af1e 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -1383,4 +1383,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 5cd184b65..59486d1e5 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -1376,4 +1376,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" \ No newline at end of file diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 255ffa0bc..3804872f2 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -1368,4 +1368,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index b807f86af..ff2bd6b1c 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1375,4 +1375,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 27e6d676a..ee63e6fa8 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -1364,4 +1364,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index c4db808f7..e5f063e21 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -1375,4 +1375,8 @@ #define ZSTR_VTX_FREQ "VFrq" #define ZSTR_VTX_PWR "VPwr" #define ZSTR_VTX_CHAN "VChn" -#define ZSTR_VTX_BAND "VBan" \ No newline at end of file +#define ZSTR_VTX_BAND "VBan" +#define ZSTR_SERVO_CURRENT "SrvA" +#define ZSTR_SERVO_VOLTAGE "SrvV" +#define ZSTR_SERVO_TEMPERATURE "SrvT" +#define ZSTR_SERVO_STATUS "SrvS" From e64b9925bf0f304e3103051c91f3eed5dd92cbc6 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 23 Sep 2020 13:05:53 +0200 Subject: [PATCH 224/267] Use new telem text function --- radio/src/telemetry/ghost.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 664d7a7a2..406db73d0 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -86,11 +86,8 @@ void processGhostTelemetryValue(uint8_t index, int32_t value) void processGhostTelemetryValueString(const GhostSensor * sensor, const char * str) { - int i = 0; if (TELEMETRY_STREAMING()) { - do { - setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, str[i], UNIT_TEXT, i); - } while (str[i++] != 0); + setTelemetryText(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, str); } } From 83e6d88b5a82ec8035673f90026181c79a7462f8 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 24 Sep 2020 11:55:20 +0200 Subject: [PATCH 225/267] Add TX16SE --- companion/src/firmwares/opentx/opentxinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 71e059c00..8aa03b9e3 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -1269,7 +1269,7 @@ void registerOpenTxFirmwares() registerOpenTxFirmware(firmware); /* Radiomaster TX16S board */ - firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16s / TX16s Hall / TX16s Masterfire"), BOARD_RADIOMASTER_TX16S); + firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16SE / TX16S / TX16S Hall / TX16S Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); addOpenTxRfOptions(firmware, FLEX); static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module")); From 092faeb3d89868b5c63b07054d8316ffbb002306 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 24 Sep 2020 15:36:07 +0200 Subject: [PATCH 226/267] Cosmetics --- companion/src/firmwares/opentx/opentxinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 8aa03b9e3..1d00c99ad 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -1269,7 +1269,7 @@ void registerOpenTxFirmwares() registerOpenTxFirmware(firmware); /* Radiomaster TX16S board */ - firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16SE / TX16S / TX16S Hall / TX16S Masterfire"), BOARD_RADIOMASTER_TX16S); + firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16S / SE / Hall / Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); addOpenTxRfOptions(firmware, FLEX); static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module")); From 4ebe10091189b65faa8550a8275b5a897cde3102 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Sep 2020 08:22:23 +0200 Subject: [PATCH 227/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index a0bb767fa..9dbe6c232 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1924,3 +1924,4 @@ Dave Waldesbuehl Dale Rhoads Denis Kelly SBEurope LTD +Jean-Louis Mouche From dbaa4058df63bb0ca447b07e451205a7d2413492 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Sep 2020 12:54:09 +0200 Subject: [PATCH 228/267] Wismy/ota (#7964) Add "Flash FC by OTA" --- radio/src/gui/480x272/radio_sdmanager.cpp | 18 ++++++++++++++---- .../src/gui/common/stdlcd/radio_sdmanager.cpp | 18 ++++++++++++++---- radio/src/io/frsky_firmware_update.h | 1 + radio/src/translations.cpp | 6 ++++-- radio/src/translations.h | 6 ++++-- radio/src/translations/cz.h.txt | 6 ++++-- radio/src/translations/de.h.txt | 6 ++++-- radio/src/translations/en.h.txt | 6 ++++-- radio/src/translations/es.h.txt | 6 ++++-- radio/src/translations/fi.h.txt | 6 ++++-- radio/src/translations/fr.h.txt | 6 ++++-- radio/src/translations/it.h.txt | 6 ++++-- radio/src/translations/nl.h.txt | 6 ++++-- radio/src/translations/pl.h.txt | 6 ++++-- radio/src/translations/pt.h.txt | 6 ++++-- radio/src/translations/se.h.txt | 6 ++++-- 16 files changed, 81 insertions(+), 34 deletions(-) diff --git a/radio/src/gui/480x272/radio_sdmanager.cpp b/radio/src/gui/480x272/radio_sdmanager.cpp index 71691c85f..2c545c659 100644 --- a/radio/src/gui/480x272/radio_sdmanager.cpp +++ b/radio/src/gui/480x272/radio_sdmanager.cpp @@ -247,10 +247,16 @@ void onSdManagerMenu(const char * result) } #endif #if defined(PXX2) - else if (result == STR_FLASH_RECEIVER_OTA_BY_INTERNAL || result == STR_FLASH_RECEIVER_OTA_BY_EXTERNAL) { + else if (result == STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA || result == STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA) { memclear(&reusableBuffer.sdManager.otaUpdateInformation, sizeof(OtaUpdateInformation)); getSelectionFullPath(reusableBuffer.sdManager.otaUpdateInformation.filename); - reusableBuffer.sdManager.otaUpdateInformation.module = result == STR_FLASH_RECEIVER_OTA_BY_INTERNAL ? INTERNAL_MODULE : EXTERNAL_MODULE; + reusableBuffer.sdManager.otaUpdateInformation.module = (result == STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA ? INTERNAL_MODULE : EXTERNAL_MODULE); + moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].startBind(&reusableBuffer.sdManager.otaUpdateInformation, onUpdateStateChanged); + } + else if (result == STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA || result == STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA) { + memclear(&reusableBuffer.sdManager.otaUpdateInformation, sizeof(OtaUpdateInformation)); + getSelectionFullPath(reusableBuffer.sdManager.otaUpdateInformation.filename); + reusableBuffer.sdManager.otaUpdateInformation.module = (result == STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA ? INTERNAL_MODULE : EXTERNAL_MODULE); moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].startBind(&reusableBuffer.sdManager.otaUpdateInformation, onUpdateStateChanged); } #endif @@ -390,9 +396,13 @@ bool menuRadioSdManager(event_t _event) #if defined(PXX2) if (information.productFamily == FIRMWARE_FAMILY_RECEIVER) { if (isReceiverOTAEnabledFromModule(INTERNAL_MODULE, information.productId)) - POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_OTA_BY_INTERNAL); + POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA); if (isReceiverOTAEnabledFromModule(EXTERNAL_MODULE, information.productId)) - POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_OTA_BY_EXTERNAL); + POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA); + } + if (information.productFamily == FIRMWARE_FAMILY_FLIGHT_CONTROLLER) { + POPUP_MENU_ADD_ITEM(STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA); + POPUP_MENU_ADD_ITEM(STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA); } #endif #if defined(BLUETOOTH) diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index eb20bb283..c6cf9f79b 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -246,10 +246,16 @@ void onSdManagerMenu(const char * result) } #endif #if defined(PXX2) - else if (result == STR_FLASH_RECEIVER_OTA_BY_INTERNAL || result == STR_FLASH_RECEIVER_OTA_BY_EXTERNAL) { + else if (result == STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA || result == STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA) { memclear(&reusableBuffer.sdManager.otaUpdateInformation, sizeof(OtaUpdateInformation)); getSelectionFullPath(reusableBuffer.sdManager.otaUpdateInformation.filename); - reusableBuffer.sdManager.otaUpdateInformation.module = result == STR_FLASH_RECEIVER_OTA_BY_INTERNAL ? INTERNAL_MODULE : EXTERNAL_MODULE; + reusableBuffer.sdManager.otaUpdateInformation.module = (result == STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA ? INTERNAL_MODULE : EXTERNAL_MODULE); + moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].startBind(&reusableBuffer.sdManager.otaUpdateInformation, onUpdateStateChanged); + } + else if (result == STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA || result == STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA) { + memclear(&reusableBuffer.sdManager.otaUpdateInformation, sizeof(OtaUpdateInformation)); + getSelectionFullPath(reusableBuffer.sdManager.otaUpdateInformation.filename); + reusableBuffer.sdManager.otaUpdateInformation.module = (result == STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA ? INTERNAL_MODULE : EXTERNAL_MODULE); moduleState[reusableBuffer.sdManager.otaUpdateInformation.module].startBind(&reusableBuffer.sdManager.otaUpdateInformation, onUpdateStateChanged); } #endif @@ -426,9 +432,13 @@ void menuRadioSdManager(event_t _event) #if defined(PXX2) if (information.productFamily == FIRMWARE_FAMILY_RECEIVER) { if (isReceiverOTAEnabledFromModule(INTERNAL_MODULE, information.productId)) - POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_OTA_BY_INTERNAL); + POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA); if (isReceiverOTAEnabledFromModule(EXTERNAL_MODULE, information.productId)) - POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_OTA_BY_EXTERNAL); + POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA); + } + if (information.productFamily == FIRMWARE_FAMILY_FLIGHT_CONTROLLER) { + POPUP_MENU_ADD_ITEM(STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA); + POPUP_MENU_ADD_ITEM(STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA); } #endif #if defined(BLUETOOTH) diff --git a/radio/src/io/frsky_firmware_update.h b/radio/src/io/frsky_firmware_update.h index 4690e0215..a81218aa0 100644 --- a/radio/src/io/frsky_firmware_update.h +++ b/radio/src/io/frsky_firmware_update.h @@ -34,6 +34,7 @@ enum FrskyFirmwareProductFamily { FIRMWARE_FAMILY_SENSOR, FIRMWARE_FAMILY_BLUETOOTH_CHIP, FIRMWARE_FAMILY_POWER_MANAGEMENT_UNIT, + FIRMWARE_FAMILY_FLIGHT_CONTROLLER, }; enum FrskyFirmwareModuleProductId { diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 5d928f2e7..a6f888678 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -439,8 +439,10 @@ const char STR_FIRMWARE_UPDATE_SUCCESS[] = TR_FIRMWARE_UPDATE_SUCCESS; const char STR_WRITING[] = TR_WRITING; const char STR_FLASH_EXTERNAL_DEVICE[] = TR_FLASH_EXTERNAL_DEVICE; const char STR_FLASH_RECEIVER_OTA[] = TR_FLASH_RECEIVER_OTA; -const char STR_FLASH_RECEIVER_OTA_BY_EXTERNAL[] = TR_FLASH_RECEIVER_OTA_BY_EXTERNAL; -const char STR_FLASH_RECEIVER_OTA_BY_INTERNAL[] = TR_FLASH_RECEIVER_OTA_BY_INTERNAL; +const char STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA[] = TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA; +const char STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA[] = TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA; +const char STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA[] = TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA; +const char STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA[] = TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA; const char STR_FLASH_BLUETOOTH_MODULE[] = TR_FLASH_BLUETOOTH_MODULE; const char STR_FLASH_POWER_MANAGEMENT_UNIT[] = TR_FLASH_POWER_MANAGEMENT_UNIT; const char STR_CONFIRM_FORMAT[] = TR_CONFIRM_FORMAT; diff --git a/radio/src/translations.h b/radio/src/translations.h index e324be5e5..544ad183c 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -659,8 +659,10 @@ extern const char STR_VIEW_TEXT[]; extern const char STR_FLASH_BOOTLOADER[]; extern const char STR_FLASH_EXTERNAL_DEVICE[]; extern const char STR_FLASH_RECEIVER_OTA[]; -extern const char STR_FLASH_RECEIVER_OTA_BY_EXTERNAL[]; -extern const char STR_FLASH_RECEIVER_OTA_BY_INTERNAL[]; +extern const char STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA[]; +extern const char STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA[]; +extern const char STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA[]; +extern const char STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA[]; extern const char STR_FLASH_BLUETOOTH_MODULE[]; extern const char STR_FLASH_POWER_MANAGEMENT_UNIT[]; extern const char STR_CURRENT_VERSION[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 3c8e32fa7..849be4620 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -829,8 +829,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloaderu" #define TR_FLASH_EXTERNAL_DEVICE TR("Flash S.Portem", "Flash S.Portem") #define TR_FLASH_RECEIVER_OTA "Flash příjímače OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash modulu BT", "Flash modulu Bluetooth") #define TR_FLASH_POWER_MANAGEMENT_UNIT TR("Flash řízení spotř.", "Flash jednotky řízení spotřeby") #define TR_CURRENT_VERSION TR("Současná ver. ", "Současná verze: ") diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 686df1b24..20c1a392e 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -831,8 +831,10 @@ #define TR_FLASH_BOOTLOADER TR("Flash bootloader","Flash bootloader") // #define TR_FLASH_EXTERNAL_DEVICE TR("Flash ext. Gerät","Flash externes Gerät") #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetoothmodul") #define TR_FLASH_POWER_MANAGEMENT_UNIT TR("Flash pwr mngt unit", "Flash power management unit") #define TR_CURRENT_VERSION TR("Current vers. ", "Current version: ") diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 7042a6df7..50c662ef9 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -835,8 +835,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloader" #define TR_FLASH_EXTERNAL_DEVICE TR("Flash S.Port", "Flash S.Port device") #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetooth module") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash pwr mngt unit" #define TR_CURRENT_VERSION TR("Curr Vers: ", "Current version: ") diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index deec63071..ed6607694 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -832,8 +832,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloader" #define TR_FLASH_EXTERNAL_DEVICE "Flash disp. externo" #define TR_FLASH_RECEIVER_OTA "Flash RX OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX por ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX por int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX por ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX por int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash módulo BT", "Flash módulo bluetooth") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash unid. pwr mngt" #define TR_CURRENT_VERSION TR("Vers. actual", "Versión actual: ") diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index c870988ef..f4e22844e 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -848,8 +848,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloader" #define TR_FLASH_EXTERNAL_DEVICE "Flash External Device" #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetooth module") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash pwr mngt unit" #define TR_CURRENT_VERSION TR("Current vers. ", "Current version: ") diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 717c7af1e..a173accd8 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -850,8 +850,10 @@ #define TR_FLASH_BOOTLOADER "Flasher BootLoader" #define TR_FLASH_EXTERNAL_DEVICE TR("Flasher S.Port", "Flasher S.Port externe") #define TR_FLASH_RECEIVER_OTA "Flasher RX OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash module BT", "Flash module Bluetooth") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flasher pwr mngt unit" #define TR_CURRENT_VERSION "Version courante :" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 59486d1e5..f0b1db951 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -850,8 +850,10 @@ #define TR_FLASH_BOOTLOADER "Prog. bootloader" #define TR_FLASH_EXTERNAL_DEVICE "Progr. dispositivo Esterno" #define TR_FLASH_RECEIVER_OTA TR("Prog. RX OTA", INDENT "Prog. ricevente OTA") -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Prog. mod. BT", INDENT "Prog. modulo Bluetooth") #define TR_FLASH_POWER_MANAGEMENT_UNIT TR("Prog. PMU", "Program. PMU") #define TR_CURRENT_VERSION TR("Vers. currente ", "Versione corrente: ") diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 3804872f2..5474f7ca9 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -836,8 +836,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloader" #define TR_FLASH_EXTERNAL_DEVICE "Flash extern Apparaat" #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetooth module") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash pwr mngt unit" #define TR_CURRENT_VERSION TR("Current vers. ", "Current version: ") diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index ff2bd6b1c..814e27ae6 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -849,8 +849,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloader" #define TR_FLASH_EXTERNAL_DEVICE "Sflashuj Moduł Zewnętrzny" #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetooth module") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash pwr mngt unit" #define TR_CURRENT_VERSION TR("Current vers. ", "Current version: ") diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index ee63e6fa8..f1b76fc4c 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -839,8 +839,10 @@ #define TR_FLASH_BOOTLOADER "Flash bootloader" #define TR_FLASH_EXTERNAL_DEVICE "Flash External Device" #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetooth module") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash pwr mngt unit" #define TR_CURRENT_VERSION TR("Current vers. ", "Current version: ") diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index e5f063e21..73e863966 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -849,8 +849,10 @@ #define TR_FLASH_BOOTLOADER "Skriv BootLoader" #define TR_FLASH_EXTERNAL_DEVICE "Flash External Device" #define TR_FLASH_RECEIVER_OTA "Flash receiver OTA" -#define TR_FLASH_RECEIVER_OTA_BY_EXTERNAL "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_OTA_BY_INTERNAL "Flash RX by int. OTA" +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" #define TR_FLASH_BLUETOOTH_MODULE TR("Flash BT module", "Flash Bluetooth module") #define TR_FLASH_POWER_MANAGEMENT_UNIT "Flash pwr mngt unit" #define TR_CURRENT_VERSION TR("Current vers. ", "Current version: ") From cd1716b941c1fbdf9561f89b2ba8381cedbfccee Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 25 Sep 2020 12:55:18 +0200 Subject: [PATCH 229/267] Handle large timers (#7962) This fixes #7607 --- radio/src/storage/modelslist.cpp | 2 +- radio/src/strhelpers.cpp | 71 +++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 972e24800..59bff7ce9 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -111,7 +111,7 @@ void ModelCell::loadBitmap() getTimerString(timer, 0); for (uint8_t i = 0; i < MAX_TIMERS; i++) { if (partialmodel.timers[i].mode != 0 && partialmodel.timers[i].persistent) { - getTimerString(timer, partialmodel.timers[i].value); + getTimerString(timer, partialmodel.timers[i].value, 1); break; } } diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 07e6b4e8b..774fe5229 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -170,6 +170,10 @@ char * strAppendStringWithIndex(char * dest, const char * s, int idx) return strAppendUnsigned(strAppend(dest, s), abs(idx)); } +constexpr int32_t secondsPerDay = 24 * 3600; +constexpr int32_t secondsPer99Hours = 99*3600 + 59*60 + 59; +constexpr int32_t secondsPerYear = 365 * secondsPerDay; + char * getTimerString(char * dest, int32_t tme, uint8_t hours) { char * s = dest; @@ -180,28 +184,63 @@ char * getTimerString(char * dest, int32_t tme, uint8_t hours) *s++ = '-'; } - qr = div((int)tme, 60); + if (tme < secondsPerDay) { + qr = div((int) tme, 60); - if (hours) { - div_t qr2 = div(qr.quot, 60); + if (hours) { + div_t qr2 = div(qr.quot, 60); + *s++ = '0' + (qr2.quot / 10); + *s++ = '0' + (qr2.quot % 10); + *s++ = ':'; + qr.quot = qr2.rem; + } + + if (!hours && qr.quot > 99) { + *s++ = '0' + (qr.quot / 100); + qr.quot = qr.quot % 100; + } + + *s++ = '0' + (qr.quot / 10); + *s++ = '0' + (qr.quot % 10); + *s++ = ':'; + *s++ = '0' + (qr.rem / 10); + *s++ = '0' + (qr.rem % 10); + *s = '\0'; + } + else if (tme < secondsPer99Hours) { + qr = div((int) tme, 3600); + div_t qr2 = div(qr.rem, 60); + *s++ = '0' + (qr.quot / 10); + *s++ = '0' + (qr.quot % 10); + *s++ = 'H'; *s++ = '0' + (qr2.quot / 10); *s++ = '0' + (qr2.quot % 10); - *s++ = ':'; - qr.quot = qr2.rem; + *s = '\0'; } - - if (!hours && qr.quot > 99) { + else if (tme < secondsPerYear) { + qr = div((int) tme, secondsPerDay); + div_t qr2 = div(qr.rem, 60); *s++ = '0' + (qr.quot / 100); - qr.quot = qr.quot % 100; + *s++ = '0' + (qr.quot / 10); + *s++ = '0' + (qr.quot % 10); + *s++ = 'D'; + *s++ = '0' + (qr2.quot / 10); + *s++ = '0' + (qr2.quot % 10); + *s++ = 'H'; + *s = '\0'; + } + else { + qr = div((int) tme, secondsPerYear); + div_t qr2 = div(qr.rem, secondsPerDay); + *s++ = '0' + (qr.quot / 10); + *s++ = '0' + (qr.quot % 10); + *s++ = 'Y'; + *s++ = 'Y'; + *s++ = '0' + (qr2.quot / 10); + *s++ = '0' + (qr2.quot % 10); + *s++ = 'D'; + *s = '\0'; } - - *s++ = '0' + (qr.quot / 10); - *s++ = '0' + (qr.quot % 10); - *s++ = ':'; - *s++ = '0' + (qr.rem / 10); - *s++ = '0' + (qr.rem % 10); - *s = '\0'; - return dest; } From c50080492b9c34c32746162f6e9d3293676a3572 Mon Sep 17 00:00:00 2001 From: Anton Haglund Date: Fri, 25 Sep 2020 12:56:00 +0200 Subject: [PATCH 230/267] Add include for stdbool (#7942) --- radio/src/targets/common/arm/stm32/usb_driver.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/targets/common/arm/stm32/usb_driver.h b/radio/src/targets/common/arm/stm32/usb_driver.h index 90e1ccd84..350ceace0 100644 --- a/radio/src/targets/common/arm/stm32/usb_driver.h +++ b/radio/src/targets/common/arm/stm32/usb_driver.h @@ -21,6 +21,8 @@ #ifndef OPENTX_USB_DRIVER_H #define OPENTX_USB_DRIVER_H +#include + // USB driver enum usbMode { USB_UNSELECTED_MODE, From 499328346c7e8db5c243e90ac3421bfc5552c3a6 Mon Sep 17 00:00:00 2001 From: Anton Haglund Date: Fri, 25 Sep 2020 12:56:30 +0200 Subject: [PATCH 231/267] Fix compilation of arm9x flavor (#7941) * Add include for inttypes Signed-off-by: anton * Fix missing declaration for setScaledVolume for PCBSKY9X Signed-off-by: anton --- radio/src/opentx.cpp | 4 ++++ radio/src/targets/sky9x/audio_driver.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index b4ed1941d..78563178e 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -21,6 +21,10 @@ #include #include "opentx.h" +#if defined(PCBSKY9X) +#include "audio_driver.h" +#endif + RadioData g_eeGeneral; ModelData g_model; diff --git a/radio/src/targets/sky9x/audio_driver.h b/radio/src/targets/sky9x/audio_driver.h index dc2e14008..9c69d161f 100644 --- a/radio/src/targets/sky9x/audio_driver.h +++ b/radio/src/targets/sky9x/audio_driver.h @@ -21,6 +21,8 @@ #ifndef _AUDIO_DRIVER_H_ #define _AUDIO_DRIVER_H_ +#include + void audioInit( void ) ; void audioEnd( void ) ; void audioConsumeCurrentBuffer(); From 8a93eb8461f4214a10a8dd1f314df9da9e732898 Mon Sep 17 00:00:00 2001 From: Anton Haglund Date: Fri, 25 Sep 2020 12:56:51 +0200 Subject: [PATCH 232/267] Add tool: clang-format.sh (#7940) Signed-off-by: anton --- tools/clang-format.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 tools/clang-format.sh diff --git a/tools/clang-format.sh b/tools/clang-format.sh new file mode 100755 index 000000000..9a6111beb --- /dev/null +++ b/tools/clang-format.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# Automatic indentation of all .c, .cpp and .h-files using clang-format +# based on https://github.com/opentx/opentx/wiki/OpenTX-Code-Style-Guide + +# clang-format version 10.0.0 + +for i in $(find ./ -name "*.c" -or -name "*.cpp" -or -name "*.h") +do + echo $i + clang-format -i -style="{BasedOnStyle: LLVM, + IndentWidth: 2, + UseTab: Never, + + AllowShortBlocksOnASingleLine: false, + AllowShortIfStatementsOnASingleLine: false, + AllowShortFunctionsOnASingleLine: false, + + BreakBeforeBraces: Custom, + BraceWrapping: { AfterFunction: true, + BeforeElse: true }, + + IndentCaseLabels: true, + PenaltyReturnTypeOnItsOwnLine: 0, + ColumnLimit: 120}" $i +done From c6f2c62919eff14f7f946b670efc6b9eea7114d8 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 25 Sep 2020 13:59:42 +0200 Subject: [PATCH 233/267] 7 of 8 ... --- radio/src/lua/api_general.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index e480eaec5..03d42190b 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -867,7 +867,7 @@ Return the internal GPS position or nil if no valid hardware found static int luaGetTxGPS(lua_State * L) { #if defined(INTERNAL_GPS) - lua_createtable(L, 0, 7); + lua_createtable(L, 0, 8); lua_pushtablenumber(L, "lat", gpsData.latitude * 0.000001); lua_pushtablenumber(L, "lon", gpsData.longitude * 0.000001); lua_pushtableinteger(L, "numsat", gpsData.numSat); From 3715669b9155ec8abac703252c90f1b8644b87c2 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 25 Sep 2020 20:48:14 +0200 Subject: [PATCH 234/267] x9lites also can't draw to many h lines --- radio/src/gui/128x64/widgets.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/widgets.cpp b/radio/src/gui/128x64/widgets.cpp index 505ba057f..398c994c6 100644 --- a/radio/src/gui/128x64/widgets.cpp +++ b/radio/src/gui/128x64/widgets.cpp @@ -67,10 +67,10 @@ void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uin void drawGauge(coord_t x, coord_t y, coord_t w, coord_t h, int32_t val, int32_t max) { -#if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines - lcdDrawRect(x, y, w+1, h); -#else +#if defined(PCBX7) || defined(PCBX9LITES) // X7 LCD doesn't like too many horizontal lines h++; +#else + lcdDrawRect(x, y, w+1, h); #endif lcdDrawFilledRect(x+1, y+1, w-1, 4, SOLID, ERASE); coord_t len = limit((uint8_t)1, uint8_t((abs(val) * w/2 + max/2) / max), uint8_t(w/2)); From 9d1dcfb85e37551c91993dd99fdf46ea9d503cce Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 26 Sep 2020 09:18:41 +0200 Subject: [PATCH 235/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9dbe6c232..c26eae0b8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1925,3 +1925,5 @@ Dale Rhoads Denis Kelly SBEurope LTD Jean-Louis Mouche +Grant Mills +Fernando Rimoldi From 774833ad310c723ad7023459d20ca94cf3ad1fb5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 28 Sep 2020 13:09:53 +0200 Subject: [PATCH 236/267] Fix copy paste issue --- radio/src/telemetry/hott.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/telemetry/hott.cpp b/radio/src/telemetry/hott.cpp index f35b1544d..9dc9e723e 100644 --- a/radio/src/telemetry/hott.cpp +++ b/radio/src/telemetry/hott.cpp @@ -411,22 +411,22 @@ void processHottPacket(const uint8_t * packet) sensor = getHottSensor(HOTT_ID_CELS); // packet[7 ] uint8_t cell1_L; //#07 cell 1 voltage lower value. 0.02V steps, 124=2.48V value = packet[7] << 1; - if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_EAM, 0 << 16 | value, sensor->unit, sensor->precision); + if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_GAM, 0 << 16 | value, sensor->unit, sensor->precision); // packet[8 ] uint8_t cell2_L; //#08 value = packet[8] << 1; - if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_EAM, 1 << 16 | value, sensor->unit, sensor->precision); + if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_GAM, 1 << 16 | value, sensor->unit, sensor->precision); // packet[9 ] uint8_t cell3_L; //#09 value = packet[9] << 1; - if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_EAM, 2 << 16 | value, sensor->unit, sensor->precision); + if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_GAM, 2 << 16 | value, sensor->unit, sensor->precision); // packet[10] uint8_t cell4_L; //#10 value = packet[10] << 1; - if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_EAM, 3 << 16 | value, sensor->unit, sensor->precision); + if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_GAM, 3 << 16 | value, sensor->unit, sensor->precision); // packet[11] uint8_t cell5_L; //#11 value = packet[11] << 1; - if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_EAM, 4 << 16 | value, sensor->unit, sensor->precision); + if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_GAM, 4 << 16 | value, sensor->unit, sensor->precision); // packet[12] uint8_t cell6_L; //#12 value = packet[12] << 1; - if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_EAM, 5 << 16 | value, sensor->unit, sensor->precision); + if (value) setTelemetryValue(PROTOCOL_TELEMETRY_HOTT, HOTT_ID_CELS, 0, HOTT_TELEM_GAM, 5 << 16 | value, sensor->unit, sensor->precision); // packet[13] uint8_t batt1_L; //#13 battery 1 voltage LSB value. 0.1V steps. 50 = 5.5V break; From 6a2bcbad27e0a38565a05039899893d6df64d5c8 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 28 Sep 2020 17:43:43 +0200 Subject: [PATCH 237/267] Fix #7977 --- radio/util/tts_de.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/util/tts_de.py b/radio/util/tts_de.py index 57b759be6..f4f292d61 100755 --- a/radio/util/tts_de.py +++ b/radio/util/tts_de.py @@ -29,7 +29,7 @@ for i, (s, f) in enumerate([("Wolt", "volt0"), ("Grad Celsius", "celsius0"), ("Grad Fahrenheit", "fahr0"), ("Prozent", "percent0"), - ("Milliampär pro stunde", "mamph0"), + ("Milliampär stunde", "mamph0"), ("Watt", "watt0"), ("Milli Watt", "mwatt0"), ("D B", "db0"), From 7ea3042daf614c04413450e87a5f4dc52bd33e4d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 29 Sep 2020 23:06:44 +0200 Subject: [PATCH 238/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c26eae0b8..74edf8483 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1927,3 +1927,4 @@ SBEurope LTD Jean-Louis Mouche Grant Mills Fernando Rimoldi +Jim Cukla From 641cbe93c721482168462e5dfac925fde9db7008 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 30 Sep 2020 08:18:48 +0200 Subject: [PATCH 239/267] I can't remember what was wrong with #pragma once --- radio/src/gui/480x272/layout.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/gui/480x272/layout.h b/radio/src/gui/480x272/layout.h index bd5d562aa..0600ea30d 100644 --- a/radio/src/gui/480x272/layout.h +++ b/radio/src/gui/480x272/layout.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _LAYOUT_H_ -#define _LAYOUT_H_ +#pragma once #include #include "widgets_container.h" @@ -122,5 +121,3 @@ Layout * loadLayout(const char * name, Layout::PersistentData * persistentData); void loadCustomScreens(); std::list & getRegisteredLayouts(); - -#endif // _LAYOUT_H_ From 8a4af2abba925172dc4e4b8920a343fee2e91993 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 1 Oct 2020 09:26:26 +0200 Subject: [PATCH 240/267] Bump SDcard version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d34763ac4..f4719ad2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "10") 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 "0032") +set(SDCARD_REVISION "0033") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) From fa392635002c92ac9be17c957135d07424ebc0ab Mon Sep 17 00:00:00 2001 From: Marco Robustini Date: Thu, 1 Oct 2020 09:42:42 +0200 Subject: [PATCH 241/267] Update it.h.txt (#7973) OTA section and other fixes --- radio/src/translations/it.h.txt | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index f0b1db951..abc1c3acf 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -847,25 +847,25 @@ #define TR_MIXERS_MONITOR "MONITOR MIXER" #define TR_PATH_TOO_LONG "Path troppo lungo" #define TR_VIEW_TEXT "Vedi testo" -#define TR_FLASH_BOOTLOADER "Prog. bootloader" -#define TR_FLASH_EXTERNAL_DEVICE "Progr. dispositivo Esterno" -#define TR_FLASH_RECEIVER_OTA TR("Prog. RX OTA", INDENT "Prog. ricevente OTA") -#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA "Flash RX by ext. OTA" -#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA "Flash RX by int. OTA" -#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA "Flash FC by ext. OTA" -#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA "Flash FC by int. OTA" +#define TR_FLASH_BOOTLOADER TR("Prog. bootloader", "Programma bootloader") +#define TR_FLASH_EXTERNAL_DEVICE TR("Prog. disp. est.", "Programma dispositivo esterno") +#define TR_FLASH_RECEIVER_OTA TR("Prog. RX OTA", INDENT "Programma RX OTA") +#define TR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA TR("Prog. RX da OTA est.", INDENT "Prog. RX da OTA esterno") +#define TR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA TR("Prog. RX da OTA int.", INDENT "Prog. RX da OTA interno") +#define TR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA TR("Prog. FC da OTA est.", INDENT "Prog. FC da OTA esterno") +#define TR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA TR("Prog. FC da OTA int.", INDENT "Prog. FC da OTA interno") #define TR_FLASH_BLUETOOTH_MODULE TR("Prog. mod. BT", INDENT "Prog. modulo Bluetooth") -#define TR_FLASH_POWER_MANAGEMENT_UNIT TR("Prog. PMU", "Program. PMU") +#define TR_FLASH_POWER_MANAGEMENT_UNIT TR("Prog. PMU", "Programma PMU") #define TR_CURRENT_VERSION TR("Vers. currente ", "Versione corrente: ") -#define TR_FLASH_INTERNAL_MODULE "Progr. modulo int." -#define TR_FLASH_EXTERNAL_MODULE "Progr. modulo est." -#define TR_FLASH_INTERNAL_MULTI "Prog. Multi int." -#define TR_FLASH_EXTERNAL_MULTI "Prog. Multi est." +#define TR_FLASH_INTERNAL_MODULE TR("Prog. modulo int.", "Programma modulo interno") +#define TR_FLASH_EXTERNAL_MODULE TR("Prog. modulo est.", "Programma modulo esterno") +#define TR_FLASH_INTERNAL_MULTI TR("Prog. MULTI int.", "Programma MULTI interno") +#define TR_FLASH_EXTERNAL_MULTI TR("Prog. MULTI est.", "Programma MULTI esterno") #define TR_FIRMWARE_UPDATE_ERROR TR(INDENT "Errore agg. FW",INDENT "Errore aggiornamento firmware") #define TR_FIRMWARE_UPDATE_SUCCESS "Aggiornamento" BREAKSPACE "riuscito" #define TR_WRITING "Scrittura..." -#define TR_CONFIRM_FORMAT "Confermi Formattazione?" -#define TR_INTERNALRF "Modulo Interno" +#define TR_CONFIRM_FORMAT "Confermi formattazione?" +#define TR_INTERNALRF "Modulo interno" #define TR_INTERNAL_MODULE TR("Modulo int.", "Modulo interno") #define TR_EXTERNAL_MODULE TR("Modulo est.", "Modulo esterno") #define TR_OPENTX_UPGRADE_REQUIRED "OpenTX richiede aggiornamento" @@ -882,8 +882,8 @@ #define TR_HOLD_UPPERCASE "HOLD" #define TR_NONE "None" #define TR_NONE_UPPERCASE "NONE" -#define TR_MENUSENSOR "SENSOR" -#define TR_POWERMETER_PEAK "Peak" +#define TR_MENUSENSOR "SENSORE" +#define TR_POWERMETER_PEAK "Picco" #define TR_POWERMETER_POWER "Potenza" #define TR_POWERMETER_ATTN "Att." #define TR_POWERMETER_FREQ "Freq." @@ -896,7 +896,7 @@ #define TR_COUNTRYCODE TR("Cod. Paese", "Codice del Paese") #define TR_USBMODE "Modo USB" #define TR_JACKMODE "Modo JACK" -#define TR_VOICELANG "Lingua Voce" +#define TR_VOICELANG "Lingua vocale" #define TR_UNITSSYSTEM "Unità" #define TR_EDIT "Modifica" #define TR_INSERT_BEFORE "Inserisci prima" @@ -1382,4 +1382,4 @@ #define ZSTR_SERVO_CURRENT "SrvA" #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" -#define ZSTR_SERVO_STATUS "SrvS" \ No newline at end of file +#define ZSTR_SERVO_STATUS "SrvS" From 45f87e5017350283479a583a46ca4a03719d0bde Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 2 Oct 2020 09:18:56 +0200 Subject: [PATCH 242/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 74edf8483..f88e096b9 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1928,3 +1928,4 @@ Jean-Louis Mouche Grant Mills Fernando Rimoldi Jim Cukla +Tomas Kirnak From 7979a26f678c8cc9f5d6e2f8757cd73028982c33 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 2 Oct 2020 12:55:11 +0200 Subject: [PATCH 243/267] Do not print EEprom size on radio without EEprom. Fixes #7984 --- companion/src/multimodelprinter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 9d57e1f62..4da889cb1 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -310,7 +310,9 @@ QString MultiModelPrinter::printSetup() MultiColumns columns(modelPrinterMap.size()); columns.appendSectionTableStart(); ROWLABELCOMPARECELL(tr("Name"), 20, model->name, 80); - ROWLABELCOMPARECELL(tr("EEprom Size"), 0, modelPrinter->printEEpromSize(), 0); + if (!IS_FAMILY_HORUS_OR_T16(firmware->getBoard())) { + ROWLABELCOMPARECELL(tr("EEprom Size"), 0, modelPrinter->printEEpromSize(), 0); + } if (firmware->getCapability(ModelImage)) { ROWLABELCOMPARECELL(tr("Model Image"), 0, model->bitmap, 0); } From 54cfd25cebee3196781fac487d327f1800998c3f Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 2 Oct 2020 15:38:33 +0200 Subject: [PATCH 244/267] Increase menus stack size for color LCD radios to prevent potential overflows when loading lua scripts --- radio/src/tasks.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/tasks.h b/radio/src/tasks.h index 921958363..856aaf038 100644 --- a/radio/src/tasks.h +++ b/radio/src/tasks.h @@ -24,7 +24,11 @@ #include "rtos.h" // stack sizes should be in multiples of 8 for better alignment -#define MENUS_STACK_SIZE 2000 +#if defined (COLORLCD) + #define MENUS_STACK_SIZE 4000 +#else + #define MENUS_STACK_SIZE 2000 +#endif #define MIXER_STACK_SIZE 400 #define AUDIO_STACK_SIZE 400 #define CLI_STACK_SIZE 1000 // only consumed with CLI build option From eb2573c9d716e4960cf4d4304c8bd3aa41dfd7ce Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 2 Oct 2020 20:44:48 +0200 Subject: [PATCH 245/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index f88e096b9..c5268cde0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1929,3 +1929,4 @@ Grant Mills Fernando Rimoldi Jim Cukla Tomas Kirnak +Raymond Henderson From a07fb2bb5abf5db24007fcbf875a1a35156ce545 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 3 Oct 2020 10:13:46 +0200 Subject: [PATCH 246/267] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index c5268cde0..f61e8f180 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1930,3 +1930,6 @@ Fernando Rimoldi Jim Cukla Tomas Kirnak Raymond Henderson +James Jewitt +Martin Sauter +Philip Hawkey From 41bb035ce59ed9005475609f978d4a65081da4a1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 3 Oct 2020 11:42:23 +0200 Subject: [PATCH 247/267] Compilation fix --- radio/src/strhelpers.cpp | 14 +++++++------- radio/src/strhelpers.h | 2 +- radio/src/telemetry/telemetry_sensors.cpp | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 774fe5229..50abd71bc 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -170,11 +170,11 @@ char * strAppendStringWithIndex(char * dest, const char * s, int idx) return strAppendUnsigned(strAppend(dest, s), abs(idx)); } -constexpr int32_t secondsPerDay = 24 * 3600; -constexpr int32_t secondsPer99Hours = 99*3600 + 59*60 + 59; -constexpr int32_t secondsPerYear = 365 * secondsPerDay; +constexpr int secondsPerDay = 24 * 3600; +constexpr int secondsPer99Hours = 99*3600 + 59*60 + 59; +constexpr int secondsPerYear = 365 * secondsPerDay; -char * getTimerString(char * dest, int32_t tme, uint8_t hours) +char * getTimerString(char * dest, int tme, uint8_t hours) { char * s = dest; div_t qr; @@ -208,7 +208,7 @@ char * getTimerString(char * dest, int32_t tme, uint8_t hours) *s = '\0'; } else if (tme < secondsPer99Hours) { - qr = div((int) tme, 3600); + qr = div(tme, 3600); div_t qr2 = div(qr.rem, 60); *s++ = '0' + (qr.quot / 10); *s++ = '0' + (qr.quot % 10); @@ -218,7 +218,7 @@ char * getTimerString(char * dest, int32_t tme, uint8_t hours) *s = '\0'; } else if (tme < secondsPerYear) { - qr = div((int) tme, secondsPerDay); + qr = div(tme, secondsPerDay); div_t qr2 = div(qr.rem, 60); *s++ = '0' + (qr.quot / 100); *s++ = '0' + (qr.quot / 10); @@ -230,7 +230,7 @@ char * getTimerString(char * dest, int32_t tme, uint8_t hours) *s = '\0'; } else { - qr = div((int) tme, secondsPerYear); + qr = div(tme, secondsPerYear); div_t qr2 = div(qr.rem, secondsPerDay); *s++ = '0' + (qr.quot / 10); *s++ = '0' + (qr.quot % 10); diff --git a/radio/src/strhelpers.h b/radio/src/strhelpers.h index 114d7a009..4217433c2 100644 --- a/radio/src/strhelpers.h +++ b/radio/src/strhelpers.h @@ -32,7 +32,7 @@ char * strAppendFilename(char * dest, const char * filename, const int size); char * getStringAtIndex(char * dest, const char * s, int idx); char * strAppendStringWithIndex(char * dest, const char * s, int idx); #define LEN_TIMER_STRING 10 // "-00:00:00" -char * getTimerString(char * dest, int32_t tme, uint8_t hours=0); +char * getTimerString(char * dest, int tme, uint8_t hours=0); char * getCurveString(char * dest, int idx); char * getGVarString(char * dest, int idx); char * getSwitchPositionName(char * dest, swsrc_t idx); diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index d14c4dafa..398c15961 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -763,9 +763,9 @@ bool TelemetrySensor::isSameInstance(TelemetryProtocol protocol, uint8_t instanc return true; #else if (((this->instance ^ instance) & 0x9F) == 0 && (this->instance >> 5) != TELEMETRY_ENDPOINT_SPORT && (instance >> 5) != TELEMETRY_ENDPOINT_SPORT) { - this->instance = instance; // update the instance in case we had telemetry switching - return true; - } + this->instance = instance; // update the instance in case we had telemetry switching + return true; + } #endif } From ca7d038aba32f314225cb042f6d263f54bb85c8a Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Sat, 3 Oct 2020 10:59:20 +0100 Subject: [PATCH 248/267] Filter instant trim only default trims (#7683) Filter instant trim only default trims --- radio/src/opentx.cpp | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 78563178e..acccefe41 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1666,28 +1666,40 @@ void opentxResume() void instantTrim() { - int16_t anas_0[MAX_INPUTS]; + int16_t anas_0[MAX_INPUTS]; evalInputs(e_perout_mode_notrainer | e_perout_mode_nosticks); memcpy(anas_0, anas, sizeof(anas_0)); evalInputs(e_perout_mode_notrainer); - for (uint8_t stick=0; sticksrcRaw-MIXSRC_Rud == stick) { - delta = anas[ed->chn] - anas_0[ed->chn]; - break; + uint8_t trimFlightMode = getTrimFlightMode(mixerCurrentFlightMode, stick); + for (uint8_t i = 0; i < MAX_EXPOS; i++) { + ExpoData * expo = expoAddress(i); + if (!EXPO_VALID(expo)) + break; // end of list + if (stick == expo->srcRaw - MIXSRC_FIRST_STICK) { + if (expo->carryTrim < 0) { + // only default trims will be taken into account + addTrim = false; + break; + } + auto newDelta = anas[expo->chn] - anas_0[expo->chn]; + if (addTrim && delta != newDelta) { + // avoid 2 different delta values + addTrim = false; + break; + } + addTrim = true; + delta = newDelta; } } - if (abs(delta) >= INSTANT_TRIM_MARGIN) { + if (addTrim && abs(delta) >= INSTANT_TRIM_MARGIN) { int16_t trim = limit(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); - setTrimValue(trim_phase, stick, trim); + setTrimValue(trimFlightMode, stick, trim); } } } From dcc16b7440e642329a6e83e301686463d448b9e0 Mon Sep 17 00:00:00 2001 From: pascallanger Date: Sat, 3 Oct 2020 12:07:17 +0200 Subject: [PATCH 249/267] Update DSM FwdPrg.lua (#7986) Co-authored-by: Pascal Langer --- radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua b/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua index 8336be507..c756634d9 100644 --- a/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua +++ b/radio/sdcard/horus/SCRIPTS/TOOLS/DSM FwdPrg.lua @@ -359,7 +359,7 @@ local function DSM_Display() attrib = 0 end if Line[i] ~= nil and Line[i].Type ~= nil then - if Line[i].Type ~= MENU then-- list/value + if Line[i].Type ~= MENU then -- list/value if Line[i].Val ~= nil then local text="" if Line[i].Type == LIST_MENU_NOCHANGING or Line[i].Type == LIST_MENU2 then @@ -390,14 +390,14 @@ local function DSM_Display() else lcd.drawText(437,32, "Back", 0) end - lcd.drawRectangle(437-5, 32-2, 48, 25) + lcd.drawRectangle(437-5, 32-2, 47, 25) if Menu.Next ~= 0 then if Menu.SelLine == 7 then lcd.drawText(437,220, "Next",INVERS) else lcd.drawText(437,220, "Next") end - lcd.drawRectangle(437-5, 220-2, 48, 25) + lcd.drawRectangle(437-5, 220-2, 47, 25) end if Menu.Prev ~= 0 then if Menu.SelLine == 8 then @@ -405,7 +405,7 @@ local function DSM_Display() else lcd.drawText(5,220, "Prev") end - lcd.drawRectangle(5-5, 220-2, 48, 25) + lcd.drawRectangle(5-5, 220-2, 47, 25) end end lcd.drawText(170,252, "RX "..RX.Name.." v"..RX.Version) -- display RX info From 096ca789ec5659c9089a7dc49c4df59de5e84a04 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 3 Oct 2020 17:11:31 +0200 Subject: [PATCH 250/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index f61e8f180..8e477a7d8 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1933,3 +1933,4 @@ Raymond Henderson James Jewitt Martin Sauter Philip Hawkey +Lonnie Misner From 7eb76eb935300726a1b1e217dc990d31acbd8ce5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 3 Oct 2020 21:32:01 +0200 Subject: [PATCH 251/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8e477a7d8..5842585df 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1934,3 +1934,4 @@ James Jewitt Martin Sauter Philip Hawkey Lonnie Misner +Michael Hirschauer From adcdf70f3fc962cf3897ea1abd73aab07ef08a1c Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 4 Oct 2020 08:22:26 +0200 Subject: [PATCH 252/267] Fix X9D 2019 (#7987) --- radio/src/pulses/modules_helpers.h | 18 ------------------ radio/src/telemetry/telemetry.cpp | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 24e45e717..01ca2fa49 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -317,24 +317,6 @@ inline bool isModuleAFHDS3(uint8_t idx) return g_model.moduleData[idx].type == MODULE_TYPE_AFHDS3; } -inline bool isModulePxx2(uint8_t idx) -{ - if (idx != EXTERNAL_MODULE) - return false; -#if defined(PXX2) - #if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) - if (g_model.moduleData[idx].type == MODULE_TYPE_XJT_LITE_PXX2 || - g_model.moduleData[idx].type == MODULE_TYPE_R9M_LITE_PRO_PXX2) - return true; - #endif - #if defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) - if (g_model.moduleData[idx].type == MODULE_TYPE_R9M_LITE_PXX2) - return true; - #endif -#endif - return false; -} - // order is the same as in enum Protocols in myeeprom.h (none, ppm, pxx, pxx2, dsm, crossfire, multi, r9m, r9m2, sbus) //qba667 count is not matching! static const int8_t maxChannelsModules[] = { 0, 8, 8, 16, -2, 8, 4, 8, 16, 8, 10}; // relative to 8! diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 39e2f10e0..70c4fdc74 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -118,7 +118,7 @@ void telemetryWakeup() #endif #if defined(EXTMODULE_USART) - while (isModulePxx2(EXTERNAL_MODULE) && extmoduleFifo.getFrame(frame)) { + while (isModulePXX2(EXTERNAL_MODULE) && extmoduleFifo.getFrame(frame)) { processPXX2Frame(EXTERNAL_MODULE, frame); } #endif From 5ec01f90cf127ed030ff8e460e6d1340abada6ab Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 4 Oct 2020 08:24:12 +0200 Subject: [PATCH 253/267] Bump SDCard for release --- CMakeLists.txt | 2 +- radio/util/tts_en.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4719ad2a..38e204835 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "10") 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 "0033") +set(SDCARD_REVISION "0034") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) diff --git a/radio/util/tts_en.py b/radio/util/tts_en.py index b45308262..616abe516 100755 --- a/radio/util/tts_en.py +++ b/radio/util/tts_en.py @@ -129,6 +129,8 @@ for i, (s, f) in enumerate([("armed", "armed"), ("flight mode stabilize", "fm-stb"), ("flight mode horizon", "fm-hor"), ("flight mode angle", "fm-ang"), + ("flight mode idle up 1", "fm-id1"), + ("flight mode idle up 2", "fm-id2"), ("low rate", "ratlow"), ("medium rate", "ratmed"), ("high rate", "rathi"), From 78ccc22eac88f1427f52d462d1c70a213579b501 Mon Sep 17 00:00:00 2001 From: DanielGeA Date: Sun, 4 Oct 2020 08:46:47 +0100 Subject: [PATCH 254/267] Throttle trim - Fixes 5635 (#7677) Add throttle trim switch --- companion/src/firmwares/modeldata.h | 1 + .../src/firmwares/opentx/opentxeeprom.cpp | 3 +- companion/src/modeledit/setup.cpp | 26 + companion/src/modeledit/setup.h | 2 + companion/src/modeledit/setup.ui | 503 +++++++++--------- radio/src/datastructs.h | 19 +- radio/src/gui/128x64/model_setup.cpp | 10 + radio/src/gui/212x64/model_setup.cpp | 9 + radio/src/gui/480x272/model_setup.cpp | 9 + radio/src/mixer.cpp | 12 +- radio/src/tests/mixer.cpp | 149 ++++++ radio/src/translations.cpp | 1 + radio/src/translations.h | 1 + radio/src/translations/cz.h.txt | 1 + radio/src/translations/de.h.txt | 1 + radio/src/translations/en.h.txt | 3 +- radio/src/translations/es.h.txt | 1 + radio/src/translations/fi.h.txt | 1 + radio/src/translations/fr.h.txt | 1 + radio/src/translations/it.h.txt | 1 + radio/src/translations/nl.h.txt | 1 + radio/src/translations/pl.h.txt | 1 + radio/src/translations/pt.h.txt | 1 + radio/src/translations/se.h.txt | 1 + 24 files changed, 503 insertions(+), 255 deletions(-) diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 558b848df..0afc66b72 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -182,6 +182,7 @@ class ModelData { unsigned int thrTraceSrc; uint64_t switchWarningStates; unsigned int switchWarningEnable; + unsigned int thrTrimSwitch; unsigned int potsWarningMode; bool potsWarnEnabled[CPN_MAX_POTS]; int potsWarnPosition[CPN_MAX_POTS]; diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 63b79d147..fcf0fc732 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -2421,7 +2421,8 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig internalField.Append(new UnsignedField<2>(this, modelData.potsWarningMode)); } else { - internalField.Append(new SpareBitsField<6>(this)); + internalField.Append(new SpareBitsField<3>(this)); + internalField.Append(new UnsignedField<3>(this, modelData.thrTrimSwitch)); internalField.Append(new UnsignedField<2>(this, modelData.potsWarningMode)); } diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index dcbe4f101..5d4dfec79 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -1276,6 +1276,14 @@ void SetupPanel::on_throttleSource_currentIndexChanged(int index) } } +void SetupPanel::on_throttleTrimSwitch_currentIndexChanged(int index) +{ + if (!lock) { + model->thrTrimSwitch = ui->throttleTrimSwitch->currentData().toUInt(); + emit modified(); + } +} + void SetupPanel::on_name_editingFinished() { if (QString(model->name) != ui->name->text()) { @@ -1348,11 +1356,29 @@ void SetupPanel::populateThrottleSourceCB() lock = false; } +void SetupPanel::populateThrottleTrimSwitchCB() +{ + Board::Type board = firmware->getBoard(); + lock = true; + ui->throttleTrimSwitch->clear(); + int idx=0; + for (int i=0; ithrottleTrimSwitch->addItem(trim, idx); + } + + int thrTrimSwitchIdx = ui->throttleTrimSwitch->findData(model->thrTrimSwitch); + ui->throttleTrimSwitch->setCurrentIndex(thrTrimSwitchIdx); + lock = false; +} + void SetupPanel::update() { ui->name->setText(model->name); ui->throttleReverse->setChecked(model->throttleReversed); populateThrottleSourceCB(); + populateThrottleTrimSwitchCB(); ui->throttleWarning->setChecked(!model->disableThrottleWarning); ui->trimIncrement->setCurrentIndex(model->trimInc+2); ui->throttleTrim->setChecked(model->thrTrim); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 0aa48ea76..e0c3df507 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -138,6 +138,7 @@ class SetupPanel : public ModelPanel private slots: void on_name_editingFinished(); void on_throttleSource_currentIndexChanged(int index); + void on_throttleTrimSwitch_currentIndexChanged(int index); void on_throttleTrim_toggled(bool checked); void on_extendedLimits_toggled(bool checked); void on_extendedTrims_toggled(bool checked); @@ -176,6 +177,7 @@ class SetupPanel : public ModelPanel void updatePotWarnings(); void updateBeepCenter(); void populateThrottleSourceCB(); + void populateThrottleTrimSwitchCB(); int timersCount; int selectedTimerIndex; bool hasTimerClipboardData(QByteArray * data = nullptr) const; diff --git a/companion/src/modeledit/setup.ui b/companion/src/modeledit/setup.ui index fc499ad63..8b2409191 100644 --- a/companion/src/modeledit/setup.ui +++ b/companion/src/modeledit/setup.ui @@ -6,7 +6,7 @@ 0 0 - 893 + 1007 405 @@ -32,22 +32,6 @@ - - - - - 50 - false - - - - Top LCD Timer - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - @@ -149,6 +133,106 @@ + + + + + 75 + true + + + + Model + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + false + + + + Timer 3 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 50 + false + + + + Throttle Source + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + false + + + + Trim Step + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + false + + + + Trims Display + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 50 + false + + + + Top LCD Timer + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -171,6 +255,66 @@ + + + + + 0 + 0 + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + + 50 + false + + + + Timer 2 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + @@ -275,44 +419,8 @@ - - - - - 75 - true - - - - Model - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + Qt::Horizontal @@ -334,86 +442,13 @@ - - - - Qt::Horizontal - - - - - - - - Qt::LeftToRight - - - Display Checklist - - - - - - - - 0 - 0 - - - - - Never - - - - - On change - - - - - Always - - - - - - - - Qt::LeftToRight - - - Throttle Trim Idle Only - - - - - - - Global Functions - - - - - - - Qt::LeftToRight - - - Throttle Warning - - - - - - - - 0 - 0 - - - + + + 6 + + + @@ -450,6 +485,81 @@ + + + + + 0 + 0 + + + + + Never + + + + + On change + + + + + Always + + + + + + + + Qt::LeftToRight + + + Throttle Warning + + + + + + + Qt::LeftToRight + + + Display Checklist + + + + + + + Global Functions + + + + + + + + 0 + 0 + + + + Edit Checklist... + + + + + + + Qt::LeftToRight + + + Throttle Trim Idle Only + + + @@ -466,16 +576,10 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi - - - - - 0 - 0 - - + + - Edit Checklist... + Throttle trim switch @@ -499,7 +603,17 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi - + + + + + 0 + 0 + + + + + Qt::Horizontal @@ -514,107 +628,6 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi - - - - - - - 50 - false - - - - Throttle Source - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 50 - false - - - - Trim Step - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 50 - false - - - - Trims Display - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - Qt::Horizontal - - - - - - - - 50 - false - - - - Timer 2 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 50 - false - - - - Timer 3 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 9f114c5f8..b37dd3e55 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -605,8 +605,9 @@ PACK(struct ModelData { NOBACKUP(RssiAlarmData rssiAlarms); - NOBACKUP(uint8_t spare1:6); - NOBACKUP(uint8_t potsWarnMode:2); + uint8_t spare1:3; + uint8_t thrTrimSw:3; + uint8_t potsWarnMode:2; ModuleData moduleData[NUM_MODULES]; int16_t failsafeChannels[MAX_OUTPUT_CHANNELS]; @@ -625,6 +626,20 @@ PACK(struct ModelData { CUSTOM_SCREENS_DATA char modelRegistrationID[PXX2_LEN_REGISTRATION_ID]; + + + uint8_t getThrottleStickTrimSource() const + { + // The order here is TERA, so that 0 (default) means Throttle + switch (thrTrimSw) { + case 0: + return MIXSRC_TrimThr; + case 2: + return MIXSRC_TrimRud; + default: + return thrTrimSw + MIXSRC_FIRST_TRIM; + } + } }); /* diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index e864ddc9f..41af5720f 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -66,6 +66,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_THROTTLE_REVERSED, ITEM_MODEL_SETUP_THROTTLE_TRACE, ITEM_MODEL_SETUP_THROTTLE_TRIM, + ITEM_MODEL_SETUP_THROTTLE_TRIM_SWITCH, ITEM_MODEL_SETUP_PREFLIGHT_LABEL, ITEM_MODEL_SETUP_CHECKLIST_DISPLAY, ITEM_MODEL_SETUP_THROTTLE_WARNING, @@ -354,6 +355,7 @@ void menuModelSetup(event_t event) 0, // Throttle reverse 0, // Throttle trace source 0, // Throttle trim + 0, // Throttle trim switch LABEL(PreflightCheck), 0, // Checklist @@ -405,6 +407,7 @@ void menuModelSetup(event_t event) 0, // Throttle reverse 0, // Throttle trace source 0, // Throttle trim + 0, // Throttle trim switch LABEL(PreflightCheck), 0, // Checklist @@ -617,6 +620,13 @@ void menuModelSetup(event_t event) ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event); break; + case ITEM_MODEL_SETUP_THROTTLE_TRIM_SWITCH: + lcdDrawTextAlignedLeft(y, STR_TTRIM_SW); + if (attr) + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTrimSw, NUM_TRIMS - 1); + drawSource(MODEL_SETUP_2ND_COLUMN, y, g_model.getThrottleStickTrimSource(), attr); + break; + case ITEM_MODEL_SETUP_PREFLIGHT_LABEL: lcdDrawTextAlignedLeft(y, STR_PREFLIGHT); break; diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 7503ae92a..5a2d05446 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -66,6 +66,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_THROTTLE_REVERSED, ITEM_MODEL_SETUP_THROTTLE_TRACE, ITEM_MODEL_SETUP_THROTTLE_TRIM, + ITEM_MODEL_SETUP_THROTTLE_TRIM_SWITCH, ITEM_MODEL_SETUP_PREFLIGHT_LABEL, ITEM_MODEL_SETUP_CHECKLIST_DISPLAY, ITEM_MODEL_SETUP_THROTTLE_WARNING, @@ -344,6 +345,7 @@ void menuModelSetup(event_t event) 0, // Thrxottle reverse 0, // Throttle trace source 0, // Throttle trim + 0, // Throttle trim switch LABEL(PreflightCheck), 0, // Checklist @@ -571,6 +573,13 @@ void menuModelSetup(event_t event) ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event); break; + case ITEM_MODEL_SETUP_THROTTLE_TRIM_SWITCH: + lcdDrawTextAlignedLeft(y, STR_TTRIM_SW); + if (attr) + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTrimSw, NUM_TRIMS - 1); + drawSource(MODEL_SETUP_2ND_COLUMN, y, g_model.getThrottleStickTrimSource(), attr); + break; + case ITEM_MODEL_SETUP_PREFLIGHT_LABEL: lcdDrawTextAlignedLeft(y, STR_PREFLIGHT); break; diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 315d5a23e..5eeb7cb93 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -53,6 +53,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_THROTTLE_REVERSED, ITEM_MODEL_SETUP_THROTTLE_TRACE, ITEM_MODEL_SETUP_THROTTLE_TRIM, + ITEM_MODEL_SETUP_THROTTLE_TRIM_SWITCH, ITEM_MODEL_SETUP_PREFLIGHT_LABEL, ITEM_MODEL_SETUP_CHECKLIST_DISPLAY, ITEM_MODEL_SETUP_THROTTLE_WARNING, @@ -614,6 +615,7 @@ bool menuModelSetup(event_t event) 0, // Throttle reverse 0, // Throttle trace source 0, // Throttle trim + 0, // Throttle trim switch LABEL(PreflightCheck), 0, // Display checklist @@ -835,6 +837,13 @@ bool menuModelSetup(event_t event) g_model.thrTrim = editCheckBox(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, attr, event); break; + case ITEM_MODEL_SETUP_THROTTLE_TRIM_SWITCH: + lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TTRIM_SW); + if (attr) + CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTrimSw, NUM_TRIMS - 1); + drawSource(MODEL_SETUP_2ND_COLUMN, y, g_model.getThrottleStickTrimSource(), attr); + break; + case ITEM_MODEL_SETUP_PREFLIGHT_LABEL: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PREFLIGHT); break; diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index ee03c9f47..56abde109 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -527,13 +527,13 @@ int getStickTrimValue(int stick, int stickValue) return 0; int trim = trims[stick]; - if (stick == THR_STICK) { - if (g_model.thrTrim) { - int trimMin = g_model.extendedTrims ? 2*TRIM_EXTENDED_MIN : 2*TRIM_MIN; - trim = ((g_model.throttleReversed ? (trim+trimMin) : (trim-trimMin)) * (RESX-stickValue)) >> (RESX_SHIFT+1); - } - if (g_model.throttleReversed) { + uint8_t thrTrimSw = g_model.getThrottleStickTrimSource() - MIXSRC_FIRST_TRIM; + if (stick == thrTrimSw) { + if (g_model.throttleReversed) trim = -trim; + if (g_model.thrTrim) { + trim = (g_model.extendedTrims) ? 2*TRIM_EXTENDED_MAX + trim : 2*TRIM_MAX + trim; + trim = trim * (1024 - stickValue) / (2*RESX); } } return trim; diff --git a/radio/src/tests/mixer.cpp b/radio/src/tests/mixer.cpp index 074f7f6ca..deaf3ec14 100644 --- a/radio/src/tests/mixer.cpp +++ b/radio/src/tests/mixer.cpp @@ -720,3 +720,152 @@ TEST_F(MixerTest, flightModeOverflow) simuSetSwitch(0, 1); CHECK_FLIGHT_MODE_TRANSITION(0, 1000, 1024, 1024); } + +TEST_F(TrimsTest, throttleTrimWithCrossTrims) +{ + g_model.thrTrim = 1; + g_model.thrTrimSw = MIXSRC_TrimEle - MIXSRC_FIRST_TRIM; + ExpoData *expo = expoAddress(THR_STICK); + expo->carryTrim = TRIM_ELE; + expo = expoAddress(ELE_STICK); + expo->carryTrim = TRIM_THR; + + // stick max + trim max + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], 1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick max + trim min + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], 1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim max + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024 + 500); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim mid + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, 0); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024 + 250); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim min + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024); + EXPECT_EQ(channelOutputs[1], 0); + + // now the same tests with extended Trims + g_model.extendedTrims = 1; + // stick max + trim max + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], 1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick max + trim min + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], 1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim max + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024+2000); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim mid + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, 0); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024+1000); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim min + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024); + EXPECT_EQ(channelOutputs[1], 0); +} + +TEST_F(TrimsTest, invertedThrottlePlusThrottleTrimWithCrossTrims) +{ + g_model.throttleReversed = 1; + g_model.thrTrim = 1; + g_model.thrTrimSw = MIXSRC_TrimEle - MIXSRC_FIRST_TRIM; + ExpoData *expo = expoAddress(THR_STICK); + expo->carryTrim = TRIM_ELE; + expo = expoAddress(ELE_STICK); + expo->carryTrim = TRIM_THR; + + // stick max + trim max + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick max + trim mid + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, 0); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024+250); + EXPECT_EQ(channelOutputs[1], 0); + // stick max + trim min + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024+500); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim max + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], +1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim min + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], +1024); + EXPECT_EQ(channelOutputs[1], 0); + + // now the same tests with extended Trims + g_model.extendedTrims = 1; + // stick max + trim max + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick max + trim mid + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, 0); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024+1000); + EXPECT_EQ(channelOutputs[1], 0); + // stick max + trim min + anaInValues[THR_STICK] = +1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], -1024+2000); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim max + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MAX); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], +1024); + EXPECT_EQ(channelOutputs[1], 0); + // stick min + trim min + anaInValues[THR_STICK] = -1024; + setTrimValue(0, MIXSRC_TrimEle - MIXSRC_FIRST_TRIM, TRIM_EXTENDED_MIN); + evalMixes(1); + EXPECT_EQ(channelOutputs[2], +1024); + EXPECT_EQ(channelOutputs[1], 0); +} diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index a6f888678..f7a6e04f8 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -123,6 +123,7 @@ const char STR_TRIMINC[] = TR_TRIMINC; const char STR_DISPLAY_TRIMS[] = TR_DISPLAY_TRIMS; const char STR_TTRACE[] = TR_TTRACE; const char STR_TTRIM[] = TR_TTRIM; +const char STR_TTRIM_SW[] = TR_TTRIM_SW; const char STR_BEEPCTR[] = TR_BEEPCTR; const char STR_USE_GLOBAL_FUNCS[] = TR_USE_GLOBAL_FUNCS; const char STR_PPMFRAME[] = TR_PPMFRAME; diff --git a/radio/src/translations.h b/radio/src/translations.h index 544ad183c..1e02adcf9 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -215,6 +215,7 @@ extern const char STR_TRIMINC[]; extern const char STR_DISPLAY_TRIMS[]; extern const char STR_TTRACE[]; extern const char STR_TTRIM[]; +extern const char STR_TTRIM_SW[]; extern const char STR_BEEPCTR[]; extern const char STR_USE_GLOBAL_FUNCS[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 849be4620..59b5c72a9 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -432,6 +432,7 @@ #define TR_DISPLAY_TRIMS TR3("Číslo v Trm", "Zobr.hodnotu trimu", "Číslo v liště trimu") #define TR_TTRACE TR("StopaPlynu", INDENT "Stopa plynu") #define TR_TTRIM TR3("TrimVolnob.", INDENT "Trim jen volnoběh", "Trim jen pro volnoběh") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR3("Středy \221\222", "Pípat středy \221\222", "Pípat středy") #define TR_USE_GLOBAL_FUNCS TR("Glob.Funkce", "Použít Glob.Funkce") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 20c1a392e..544df5277 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -434,6 +434,7 @@ #define TR_DISPLAY_TRIMS TR("Trimanzeige", "Trimwerte anzeigen") #define TR_TTRACE TR("Gasquelle", INDENT "Gas-Timerquelle") #define TR_TTRIM TR("Gastrim", INDENT "Gas-Leerlauftrim") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("MittePieps", "Mittelstell. -Pieps") #define TR_USE_GLOBAL_FUNCS TR("Glob. Funkt.", "Globale Funkt verw.") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 50c662ef9..e2f7e4529 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -432,7 +432,8 @@ #define TR_TRIMINC "Trim Step" #define TR_DISPLAY_TRIMS TR("Show Trims", "Display trims") #define TR_TTRACE TR("T-Source", INDENT "Source") -#define TR_TTRIM TR("T-Trim", INDENT "Trim idle only") +#define TR_TTRIM TR("T-Trim-Idle", INDENT "Trim idle only") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Ctr Beep", "Center Beep") #define TR_USE_GLOBAL_FUNCS TR("Glob.Funcs", "Use global funcs") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index ed6607694..7d6b6f3b0 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -432,6 +432,7 @@ #define TR_DISPLAY_TRIMS "Ver trims" #define TR_TTRACE TR("Fuente-A", INDENT "Fuente acelerador") #define TR_TTRIM TR("Trim-A", INDENT "Trim acelerador") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Beep ctr", "Beep centro") #define TR_USE_GLOBAL_FUNCS TR("Funcs. glob.", "Usar func. globales") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index f4e22844e..cfc84aa1e 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -446,6 +446,7 @@ #define TR_DISPLAY_TRIMS "Display Trims" #define TR_TTRACE TR("T-Source", INDENT "Throttle Source") #define TR_TTRIM TR("T-Trim", INDENT "Throttle Trim") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Ctr Beep", "Center Beep") #define TR_USE_GLOBAL_FUNCS "Use Global Funcs" #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index a173accd8..4ac49e586 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -452,6 +452,7 @@ #define TR_DISPLAY_TRIMS TR("Aff. trims", "Affichage trims") #define TR_TTRACE TR("Source gaz", INDENT "Source") #define TR_TTRIM TR("Trim gaz", INDENT "Trim ralenti uniq.") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Bips centr", "Bips centrage") #define TR_USE_GLOBAL_FUNCS TR("Fonc. glob.", "Fonctions globales") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index abc1c3acf..f54d41ad9 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -454,6 +454,7 @@ #define TR_DISPLAY_TRIMS TR("Mos. Trims", "Mostra Trims") #define TR_TTRACE TR("So. motore", INDENT "Sorgente Motore") #define TR_TTRIM TR("Trim Mot.", INDENT "Trim Motore") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Beep al c.", "Beep al centro") #define TR_USE_GLOBAL_FUNCS "Usa Funz. Globali" #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 5474f7ca9..d007f8626 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -433,6 +433,7 @@ #define TR_DISPLAY_TRIMS "Toon Trims" #define TR_TTRACE TR("T-Source", INDENT "Source") #define TR_TTRIM TR("T-Trim", INDENT "Trim Idle Only") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Ctr Beep", "Center Beep") #define TR_USE_GLOBAL_FUNCS TR("Glob.Funcs", "Globale Functies") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 814e27ae6..3e3c08ed8 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -453,6 +453,7 @@ #define TR_DISPLAY_TRIMS TR("Pok.Trym.","Pokaż Trymery") #define TR_TTRACE TR("Źród gaz", INDENT "Źródło gazu ") #define TR_TTRIM TR("TryGaz", INDENT "TrymWolnObrotó") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("ŚrodBeep", "Pikn.Środka") #define TR_USE_GLOBAL_FUNCS TR("Funk.Glb.","Użyj Funkcji Glb") #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index f1b76fc4c..35bda6e7e 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -439,6 +439,7 @@ #define TR_DISPLAY_TRIMS "Display Trims" #define TR_TTRACE TR("T-Trace", INDENT "T-Trace") #define TR_TTRIM TR("T-Trim", INDENT "T-Trim") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR "Ctr Beep" #define TR_USE_GLOBAL_FUNCS "Use Global Funcs" #if defined(PCBSKY9X) && defined(REVX) diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 73e863966..aafa32bcb 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -452,6 +452,7 @@ #define TR_DISPLAY_TRIMS "Display Trims" #define TR_TTRACE TR("Följ Gas", INDENT "Följ Gas") #define TR_TTRIM TR("Gastrim", INDENT "Gastrim") +#define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Cent.pip", "Centerpip") #define TR_USE_GLOBAL_FUNCS TR("Glob.Funkt", "Använd Global Funk.") #if defined(PCBSKY9X) && defined(REVX) From 615fb356b06db4ee43a1844f80cb41e9359cab46 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 4 Oct 2020 12:13:46 +0200 Subject: [PATCH 255/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5842585df..7f1292ca9 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1935,3 +1935,4 @@ Martin Sauter Philip Hawkey Lonnie Misner Michael Hirschauer +Brian Bracken From 67a001b757c1d9f3f899163d40eb95dd5e9f52b3 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 4 Oct 2020 12:21:08 +0200 Subject: [PATCH 256/267] Bump multi rev --- radio/src/telemetry/multi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 2be662127..b1b86eb0a 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -21,7 +21,7 @@ #include "telemetry.h" #include "multi.h" -constexpr int32_t MULTI_DESIRED_VERSION = (1 << 24) | (3 << 16) | (1 << 8) | 65; +constexpr int32_t MULTI_DESIRED_VERSION = (1 << 24) | (3 << 16) | (1 << 8) | 69; #define MULTI_CHAN_BITS 11 extern uint8_t g_moduleIdx; From d349479cd2290ffa9de71743e5e514ed2ec79c04 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 4 Oct 2020 18:34:54 +0200 Subject: [PATCH 257/267] Fix possible EM on TX16S --- radio/src/gui/gui_common.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index d832bc1c4..365880fef 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -390,6 +390,10 @@ bool isAux1ModeAvailable(int mode) #if defined(AUX2_SERIAL) if (mode == UART_MODE_SBUS_TRAINER) return g_eeGeneral.aux2SerialMode != UART_MODE_SBUS_TRAINER; +#if defined(RADIO_TX16S) + else + return (g_model.trainerData.mode != TRAINER_MODE_MASTER_BATTERY_COMPARTMENT || g_eeGeneral.aux2SerialMode == UART_MODE_SBUS_TRAINER); +#endif #endif return true; } @@ -399,6 +403,10 @@ bool isAux2ModeAvailable(int mode) #if defined(AUX_SERIAL) if (mode == UART_MODE_SBUS_TRAINER) return g_eeGeneral.auxSerialMode != UART_MODE_SBUS_TRAINER; +#if defined(RADIO_TX16S) + else + return (g_model.trainerData.mode != TRAINER_MODE_MASTER_BATTERY_COMPARTMENT || g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER); +#endif #endif return true; } @@ -783,7 +791,10 @@ bool isTrainerModeAvailable(int mode) } #endif -#if defined(PCBTARANIS) && !defined(TRAINER_BATTERY_COMPARTMENT) +#if defined(RADIO_TX16S) + if (mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT) + return (g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER || g_eeGeneral.aux2SerialMode == UART_MODE_SBUS_TRAINER); +#elif defined(PCBTARANIS) && !defined(TRAINER_BATTERY_COMPARTMENT) if (mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT) return false; #elif defined(PCBTARANIS) From 4a4a548a9f285a4b85f8a11563e8215d0b022961 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 4 Oct 2020 18:55:29 +0200 Subject: [PATCH 258/267] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7f1292ca9..dddb27c64 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1936,3 +1936,5 @@ Philip Hawkey Lonnie Misner Michael Hirschauer Brian Bracken +Stuart Olson +Terry McKinley From f410d5c74b17c6d113b4207412e3305be2962973 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 5 Oct 2020 09:13:09 +0200 Subject: [PATCH 259/267] Warnings removed --- radio/src/gui/480x272/widgets/modelbmp.cpp | 2 +- radio/src/gui/480x272/widgets/outputs.cpp | 2 +- radio/src/gui/480x272/widgets_container.h | 6 +++--- radio/src/lua/widgets.cpp | 14 +++++++------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/radio/src/gui/480x272/widgets/modelbmp.cpp b/radio/src/gui/480x272/widgets/modelbmp.cpp index 09e632ab3..b9d00f3f2 100644 --- a/radio/src/gui/480x272/widgets/modelbmp.cpp +++ b/radio/src/gui/480x272/widgets/modelbmp.cpp @@ -30,7 +30,7 @@ class ModelBitmapWidget: public Widget { } - virtual ~ModelBitmapWidget() + ~ModelBitmapWidget() override { delete buffer; } diff --git a/radio/src/gui/480x272/widgets/outputs.cpp b/radio/src/gui/480x272/widgets/outputs.cpp index 467657519..387f4f24b 100644 --- a/radio/src/gui/480x272/widgets/outputs.cpp +++ b/radio/src/gui/480x272/widgets/outputs.cpp @@ -89,7 +89,7 @@ const ZoneOption OutputsWidget::options[] = { { STR_FIRST_CHANNEL, ZoneOption::Integer, OPTION_VALUE_UNSIGNED(1), OPTION_VALUE_UNSIGNED(1), OPTION_VALUE_UNSIGNED(32) }, { STR_FILL_BACKGROUND, ZoneOption::Bool, OPTION_VALUE_BOOL(false) }, { STR_BG_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(LIGHTGREY) }, - { NULL, ZoneOption::Bool } + { nullptr, ZoneOption::Bool } }; diff --git a/radio/src/gui/480x272/widgets_container.h b/radio/src/gui/480x272/widgets_container.h index c2fbd9cdd..21fa69bc4 100644 --- a/radio/src/gui/480x272/widgets_container.h +++ b/radio/src/gui/480x272/widgets_container.h @@ -80,7 +80,7 @@ class WidgetsContainer: public WidgetsContainerInterface } } - virtual void createWidget(unsigned int index, const WidgetFactory * factory) + void createWidget(unsigned int index, const WidgetFactory * factory) override { if (widgets) { memset(persistentData->zones[index].widgetName, 0, sizeof(persistentData->zones[index].widgetName)); @@ -123,9 +123,9 @@ class WidgetsContainer: public WidgetsContainerInterface return &persistentData->options[index]; } - virtual unsigned int getZonesCount() const = 0; + unsigned int getZonesCount() const override = 0; - virtual Zone getZone(unsigned int index) const = 0; + Zone getZone(unsigned int index) const override = 0; virtual void refresh() { diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index 4ab6b967e..1b04e9b7b 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -253,23 +253,23 @@ class LuaWidget: public Widget LuaWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData, int widgetData): Widget(factory, zone, persistentData), widgetData(widgetData), - errorMessage(0) + errorMessage(nullptr) { } - virtual ~LuaWidget() + ~LuaWidget() override { luaL_unref(lsWidgets, LUA_REGISTRYINDEX, widgetData); - if (errorMessage) free(errorMessage); + free(errorMessage); } - virtual void update(); + void update() override; void refresh() override; - virtual void background(); + void background() override; - virtual const char * getErrorMessage() const; + const char * getErrorMessage() const override; protected: int widgetData; @@ -300,7 +300,7 @@ class LuaWidgetFactory: public WidgetFactory { } - virtual Widget * create(const Zone & zone, Widget::PersistentData * persistentData, bool init=true) const + Widget * create(const Zone & zone, Widget::PersistentData * persistentData, bool init=true) const override { if (lsWidgets == 0) return 0; if (init) { From 9b0e0d46d865c18615d80abc99bd76cf24732695 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 5 Oct 2020 09:31:57 +0200 Subject: [PATCH 260/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index dddb27c64..86192aa6c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1938,3 +1938,4 @@ Michael Hirschauer Brian Bracken Stuart Olson Terry McKinley +Miklos Turcsin From 21c63df50a7d3b65fbaa2e262f428e0574896dd3 Mon Sep 17 00:00:00 2001 From: musapinar Date: Mon, 5 Oct 2020 10:46:30 +0200 Subject: [PATCH 261/267] fixed typo (#7991) --- radio/src/lua/api_general.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 03d42190b..d33d0812f 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -385,7 +385,7 @@ Return rotary encoder current speed @status current Introduced in 2.3.10 */ -static int luGetRotEncSpeed(lua_State * L) +static int luaGetRotEncSpeed(lua_State * L) { #if defined(ROTARY_ENCODER_NAVIGATION) lua_pushunsigned(L, rotencSpeed); @@ -1700,7 +1700,7 @@ const luaL_Reg opentxLib[] = { { "getVersion", luaGetVersion }, { "getGeneralSettings", luaGetGeneralSettings }, { "getGlobalTimer", luaGetGlobalTimer }, - { "getRotEncSpeed", luGetRotEncSpeed }, + { "getRotEncSpeed", luaGetRotEncSpeed }, { "getValue", luaGetValue }, { "getRAS", luaGetRAS }, { "getTxGPS", luaGetTxGPS }, From 7ac1ecb619665e9c9325b67c06640bda4036bd40 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 5 Oct 2020 16:05:21 +0200 Subject: [PATCH 262/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 86192aa6c..de40336c3 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1939,3 +1939,4 @@ Brian Bracken Stuart Olson Terry McKinley Miklos Turcsin +Edward Skerness From 44e9e1248013c56295490770f94041c9ffaf309f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Oct 2020 23:35:19 +0200 Subject: [PATCH 263/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index de40336c3..cc538f4ae 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1940,3 +1940,4 @@ Stuart Olson Terry McKinley Miklos Turcsin Edward Skerness +Stefano Perinetti From 4db89d524449524388d585564797fa070a637393 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 7 Oct 2020 10:33:33 +0200 Subject: [PATCH 264/267] Fix #7997 Ext multi spectrum not starting --- radio/src/pulses/multi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index ec901abf1..dcc7c7f9c 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -169,7 +169,7 @@ void setupPulsesMulti(uint8_t moduleIdx) // Multi V1.3.X.X -> Send byte 26, Protocol (bits 7 & 6), RX_Num (bits 5 & 4), invert, not used, disable telemetry, disable mapping if (moduleState[moduleIdx].mode == MODULE_MODE_SPECTRUM_ANALYSER) { - sendMulti(moduleIdx, 0); + sendMulti(moduleIdx, invert[moduleIdx] & 0x08); } else { sendMulti(moduleIdx, (uint8_t) (((g_model.moduleData[moduleIdx].getMultiProtocol() + 3) & 0xC0) From bb6830ddc9b3d9143da108c719d9fbcf73cf14b3 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 7 Oct 2020 12:07:35 +0200 Subject: [PATCH 265/267] Allow non inv STM multi flash on external --- radio/src/io/multi_firmware_update.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/io/multi_firmware_update.h b/radio/src/io/multi_firmware_update.h index a4cb255fb..e13880184 100644 --- a/radio/src/io/multi_firmware_update.h +++ b/radio/src/io/multi_firmware_update.h @@ -76,7 +76,7 @@ class MultiFirmwareInformation { bool isMultiExternalFirmware() const { - return (telemetryInversion == true && optibootSupport == true && bootloaderCheck == true && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY); + return ((telemetryInversion == true || boardType == FIRMWARE_MULTI_STM ) && optibootSupport == true && bootloaderCheck == true && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY); } const char * readMultiFirmwareInformation(const char * filename); From 6f212c08b96097bde2659be4815077a74795df60 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 7 Oct 2020 12:11:36 +0200 Subject: [PATCH 266/267] New nighlies are 2.3.11 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38e204835..bfa76d416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ project(OpenTX) set(VERSION_MAJOR "2") set(VERSION_MINOR "3") -set(VERSION_REVISION "10") +set(VERSION_REVISION "11") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) From c07be1eb9b0d4135f73e5bfd974c0375e76508f4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 7 Oct 2020 14:11:19 +0200 Subject: [PATCH 267/267] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index cc538f4ae..95dc3e6a3 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1941,3 +1941,4 @@ Terry McKinley Miklos Turcsin Edward Skerness Stefano Perinetti +Toni Marchante

&}R| z;KrAFH8i9;kHCH2tiKH(J34sotZALiw*2G!U9xrC7OmI)yUPfdziA&6)oH9)y@>fy z%*OKD#5@5X^w5R$-neSw8?`rzmlv<^8b0j>y$GL@(|C^kdB_Z@{YP5K$#?W+D6FnP z|69Nr%nYxDI@VEa&pw&$5dcnH;F{rU26QLBc^A8*^9n5K8|Z*P=V1JGCr4^~=>;*s zvO7BKZuoB0s*v~GwAmNCc3SqlPmS*W<5Iqh`OvK`#*fkXIlTbRjM!g$LaUw0Q_K_< zVpHg&&dM$*X6_p^T!`mo^#R-2-|rF-(Y z>JcRsv)m6% zqyBF=xqR#nI^&^eGF)U4IRc$mxYdXGSV8D)@C}}zpVz#N)~$U=Z1YlmG_^J0<^_E8 z)0bcej^I9ZUHc9&*P=3)y7Nl31ZBj#C47d(_%6&J%GyUC(6bU2c~(5v6{4M<&J3u@ z;!4k`VQ4Jvsgtq=L$>fl!<+`mgmL51*0@tYJssaM4!oR~UB_<9t`nD}YBzIpckYq$ zmIm^I+K-dINpN7wFPFdr6ju}yzrdjp?<;O}u4rx%^J8=9 zv9H(CKh|@5F#Vmj%d$=0vVyg8lOKK3f@S5FFj=}GL>7An$bz-Lve0w8EZpcNi#B-4 z{52b8)^h4UX9i{2tL5BvLhbrzib?mn^HU_2Ra0y-b|zNe+0mjGnntM!Kz$;gi;h%jC7< zHh(j9{ySuQIQ_;V(B21NxA4ifhX#S6ATvWp`?K$eq}OsRvx|upe53reM>+l|bwd1| z>1Z$0_!~3nKbzZxov7F;!FjC`LS8?i{H!FFotA|Bqu8i3l2dm{(#y|CWYz%*NUUd; zXtN~bwlG(edBmCY;LNRqhhY9(sm{U8q@Do|zZTrE%ILYFIH_nt7XUu0z=`^1RoEJ} zITZ#+h2Z8Wbxx6HHla6|(b}~o>z4f8W5EpZm_LI))YJBQZ&~+``?0g!x0p13V8fc_ zvwpdC)3U!t(Ze@0l^H>NC!4pg6EB|)rk7d;-^;^$m*fq(&KLB#eSuH*k{IX3t2@Td zC{7*||0r%gc-$@;>pTZjM^Eg9Om-l=|JOIy3|a(0Y-(%deGL3uDF7K2rKxCQIJ|k<#0Gy;#w^ z-`RCOT87o)zHpOFTSU*MrCYEiTiO3x&^D}+ArqI;BhZK1>PWE|6vS(NsnPf5Y+o;S z!{^IL_j%$v3!h~EDqiEs@9+d~n`Pp>_2S~TM4ZR0pf~7t`Y*+Zm32OGc^>)7IGMSK z8BL+gj!I4z|JZ1Jn?U?GW-&+5AB7n*JE*_)j|Mw&%m|L6W*^=rI4%_1gQfxfgU+l? zha*nGuSzLJze;X0xfI(`NPZv}4PiDtX0mG}hTDmGwUU%y$9#}#__7K~$f2ih9)4c| z^QpmNW--2CQ94>0cooG=eLkOgzOgTAXLPAuQQUN3NbysK-O;)=#SL`?)U9ps#@-m* zY?tM0=A(h1{M#kV=RI4xVtzqr=e0LK;@hXTJm+6Z*n4&4vyzjeWTV#_^ttQlbGuc_ znYn!L_65VGy~57C1UoNZsh@U_euKBLFE_!=O=D*sKVd!&b3Z@O``Dms?Wbk;9_HeJ zox+MdH0R7Y!*?q*ToQFV1$@7`aK_}p(+XrIeVav9(5O8u1;7U6l_g!v8O#UQ+!u<(l)@OQqnZ?FibcvL=}| ze;1mrBk;A&Xc?>FoNCy=b<(qMj0|*MDr09Zm#OnNU@tmwx?GIbvIX7M_4UxZCr;3?z4oZX0pXvc!+ zUA7m!>ONw-R${ki@|4Zw?sv-E5@e-S(KH78ow>$+W*qcXcbDrJe z_D$-}bzbfjV}I`A*qdU0PXTjWbBoY- zG1I!Fs!^7$IfxeJ6nXVs()YbmYJYQBdGK`ga81tHBMuHNlL>(oJB2 zde)(f>FHboZmy7%YL&h{%UH!?(YuPA{T}IO)rcMvjKB@M&FA&tX8OW)GKpH)VG|dC zD=&242k9Ae8qTXkdcB(=y}D<^sTb47tOor{t1MlYE?dI_!43VnBSOFlJ^gh>2XLES zDQa`@*Mh)KI5AHsx5MK@Si$W1c+MI2eGxT0^qnol-e;1#%I34oE`vujxAod_J2?;k z7Hv-&m{a?bQOGYJr}LR-V`DgfYPifD|p**m*p{%pvJUs-*?OmFsXukM-LFPE-ea`2m0$V_~!xhuEHEHE>B8Mv9T44x_+ z-tQRwG-}xE`ur?jnUe7R(cwya>##WTCO+_3n$-$cXW);m_;Ds^2l@ zlIEcQjzo0T$m z)fO?PR!2S;$rq~Bo~(5Hf&NhbCxSNio<+@`}3r^~#lSJ9>I5wB2R za1#t)Ks=+hw=wu-F&f+Wkq@HREx3tD3WfiozhS!0*^L1MaNF5&%uPqpdO_&GL4v4U+XtjZ(@+kvs%vJ7)(HSVeZ-|c+|berdOc0fzEDch12 z?JbcMMT!&$xc3CHI6#o#1W9l&aPKK@aPK`uZClosY+1HE5|8BfNa`kO(lkk1w@K!J(B*%7I@A*@3-;c<@@07dx4)0$Yq^0b$mw1=j_K^bB!mCs}b}V z)^lJVFo3%8JIA>MUBlo*IKOme7IGT$Jz-BvMZSG3@Z;ic_brFRdmZILg8#-$7ZLb* z!O5R`PmUGZ9qR9X)4>+?Qa7(dE~YhX(69>tH_UgALMFNfKJm(HUlHsA+>iYw;N)vx zzX{*XlLBwy{TG3k=e~Lk``|s;ei`=Z*H9CQ`OgziU#2TJE(-kS^0m`+MW8dtX*ml_ zT@Z2-9)nDL?))<97%n3JYnG0koDt%!Pa$`Gc4-JX*&ygj(+eYrjTr*3>7}tL^hiX# zyFOs12ev!IK--7pH0U;nooVIRl%{&*TlLU|NB)&MDu`-QS132~m*l+vXupydD;mh^%jd{r_urAr<{#40e1e=cM-ojG^1aXl12z2U-sc8X^X|1HHqov4~r~p-h$D>bHXC-t- z@S2_m$i)=7 zedRG)5Z3+zJ^kD@x(T`pdG8XQb@|#^y7u@Z=>2yR_3JJmpZOwPdi+u36)w@^PhF&| zPhO&>6H@|zSwO7y!txk;SD@d{{II}lCT0gvXRMpXrn_kv^1}dP+I!${?ZW@KXApKC zPzP+$9mo;sfSz*{xi>un_`N*(ZAz5>oMMCDBBj@l=!oSl+WzGkVCE799R3#yvi&(F z2E0o-x))G;Bb`DbBH??9#i*uu_>aa0|Cx#tiSjk?QHtVM}<~fSI?t9@>i*yV|MF$C@>|T zf)f&i+?OYAo};Ipy-ZiZV=i9@~XfGn8FffMyC-_bGAbt)oHTWc8oUO3qAgUPUJb%B3{0jHbB1GwGnx5n;(S?_cTev`p};i{lc;# z7ojEw$0Zk()siIOB3W#`M7zH{Njo+U(|YqZT6<3w_!as%Z2`a9`hO_S_rIt>f6mA! zvfy{g$rO5BX6Vu9&AyE`;IkiE+l(C88pwy0un&|#MysT)5BAfp`_7Tn`+3MVO{4){ zw7Iw+dB$4EI;oJ=GGG(PgdHG@Wi;R>zdRSZP3{N$hUKz~M()2`Ea)yAQ!U^LSZOjK zE~2AOz!}$u>w#>{=b3L619#O&kxK_YR9KaVxR?ZzYvJ$BKZ+jljmTYSrIgNTsveuC zcFZ&5hV22}0(S1$(}1UL=tTy|YGaGY0j?;x9hVlff@j?-t<2y1;!U9s0n0aEls{2K zsPF1`u^%1j{cdtPAo3F9G`8qK3B)x*To0ngI zi8{t6VfREIz0Q70FRvwSbqmR$z4 z;5Un?Cwm-qmX`#6GY@@c{@4i3;{PxM`D|ukn3&fXanlc-h_M6xXdH2I&~caqz5{!j z0k))7>V&PJv!8Qr+R0sPpnJbmi#Xy+vbAofgp^6t@~9+pvpTx}ORaR+vI+5Y&47cf!@+!*Dw56fz(Hw`;LQB?tK z*J;2sYG|e-z8|t*N-ktpR?a%%*ON$Sp%2E96AP(katZDY3jBJxuFi zH*5O9CLrW^0z(`xzG=U*XGpN6v79yx8i2jE5Be*|2{6xLIjsxwNLMd@`@nZt z=V|Zd98cKk@IS4_Z>OVuBYx|vP>Z^qlo8{|H7Ubyeg!$$H6mY?^V_NrgHtQ`h^k?$ z<$6xFN6V3;Q%gaCEwG{0!1h&**kRd+#O8tY$Q$(TdmDcG6wO0%(! zGX#5cCNPr;%w+0QK)?_94$ExW7;{SC$H9H9@05bSl;Rzj?=b&idqF{Y4&qN!DGGHT z6!}#o&#ytgbTvh1qi+G^3RMz(cImNn6t?J#kDY*Dw3hs$5#QkLO*_%=;fR|UJ$@qa z+tf`9rw|j@UkCdc`*iRd1!5Zk^PvvZQ0pD*0JbyV(dc4-n^BNjm0OVZMZ(CR`Y!hl z4UIkl-_1&Fd^jZ{W}vJjo6bEtN3Xs99CVl`1Y&;;>-*1x7)xxQo}C*Dv9Ps#b& zsP$n$?aNu3gI+U(nn%-+(WZfyY1r2$L4pk$HUY*=FR-#IqxGWKR}af-;5Dm$oKDE_ zT$ijJxM}S|%@z3a>Jj@H9$rH(N9xGUsR=kLN4y+#<~q&`ZA1(Xd?29u7GMW<5$;J) z(^Q3c2hPcnLd4RQQc5b<6To@}Zz@B*fKp(lq=s$3h;fFV zT;5O${k)Vi^Yx@w)j{s-#&e2MW3CW2w}3CmOKkTlsK|p1hQ3o+>v^y7X$o3o!}{k0oX{vZ+gII+Mx%xcjNzv*jj*LPQySuU{M8!7;`xxPTtSiGH*tB~-wJ&x;iGK8 zcYON!r(kDmfo&raF%jEorge+6Q*kb_c;c);|$WXR){g6`h~jCAz3fKMRS19;$^ z=Qj9284K;;4IRCR1z~Ie7lyuO#0=nn*o$+F8~FVMoFG1%W3*dJ1dRy7|UcVn-u~dtfv$*0UL#tzvnkBi{%Km z=M3mgS>PqmYoLeZ05eA1=)i-38_XMHhjWt?GGgF&j6%=eII0DXzxKlOs26eu@tjR$ z?=PYIxHqGfC1QF|#{jvj-9uf1UKEp}5_F*CoHU9=o{3z8*jA;Qti6H}->Lv!5Z5%% z`H2m{4aXB;-XW)Pd|DUcJ9F{gl4!|ou5F9Fn_s5qr$txS7XP`m_C-2ArAv=r(Lhd% ziIuK^kngObO8Cq!qJHViurqW1%~!#1Za#gHo__WcYy;O}dz__+q-6Ll0#Rce_FLF+ zWoi{{k%-Ax$tPfjMo$lYBwvtkSWdz^Ey_ZVL;S`ULS}-# zo`IM#T`po@;9p6}f)5|-jJK0B(OWnJ{gQNu`A3{mB5W%Osc{sC9IjaKZ#8~DqT`uV zq|&I+CrU+1O(ZE}BPa@EWNajbV>^Pk;lqj4C`qYSK(_#IK-^#?dUHfD#VT3tTk{Zd+PJq$Rdp@u|4z{L zr%;Czz8mIca&;I*C8^Ob5jKN@Y|(Yd zd0u+h!4~M*=P$#qc9U+x|9|EBS=iA!C|DJZHGn>U_?^mt&mj+SRj9>4Lx_cW^xP~x z26^wy*?HvX%z!6!f8~U#7+_#_Pv2nEg=t<-QcOaHl1)Vqxya~3Q!c?q_ z2-G`(?X|2F$4elyqc=42mW!*9czu`_Rup#mG-$D#Ed1kRG{ZdgMs!OOOXz zj5-0>=COdc`Q9?r5GdpOLFLGe7Vg6{%8@(D=WvYA>6yx_KsERduycWmQ46P}ir=jk z-v#dqsz6;4rpkJZObs8}M!sDoa(1d3QMazC3BM)CT}2+89`z5{7RNpv_Iu`H4pXx< zcupa55Yl0{$suP?cjP~yca9>GB9c>R`Rp>ChEJfO3p`iQ;Yz_1^Mrf#um^Hq!`Reh zuKK^U z*1ibmL@HI7NUM$J?_%B6Qgt;cQU0sgm{5v{L_JH8LJ0~Fq{wLWB7|M}#OW#UpBr@X z+8M}VI>e%&w?tF~<>_;1a;gV4>j&xbl~d?pa2YXb7vSqWLmU^tvK0653x<6?9-Tr7$W`|DADg68n>=t%y1# z10b$FH3aHK?kndMa9xk;X2_GxTo)%D`FN2O79WRrf@)w3c7ga?1%h&23+FzGd>HsNnt(|B=;wUO0p6W zEBkGfM)viXI9Xv)zIGK8qU`J>5qiU{?tc+Sk&myPC`KK;`rVT9v$fA@lOo@XRs~bI z(w~$}k?2_@@gmF*HN#hS;>;BKp&UcJegk+y8vK*P(0eY^)4mg=E948s4<`biVZYPDt5JJ1W+U_@BE zeIxy-WiW_dG~+a}Fai7A6wSacF}sYKUXT|TPR^s&*&^(vi?n$17%iPz683pK27CFj z(@V5)g3p0YE#P`w3;+4T@i`DiZ1Z#Gus@IZtc4S^=#7Q_6EiqAgJbv{?9XE4&zw1i zZKh?^l4E}@-e-Cd;}VZJwm3yoOOrH-adH786VCIyPs~ry7-$ss$g$Z`$fzT**BfaR zwyjYZvx*cfSe263~@Fb%cg_w zugyk%8OV}p$m>bTgAS@hpY_lX*y7S*Cr3PV4=@9)u%6$|v77M8w8OSs-_CVOpo>8U zOauQ9RQQo!s3*yyLm`t%DMB6gLv?Iec}hyu`j;M^UR^&tisIwGF!2E&{$3y57I}MH zfB2a7t*0)()(QJbLL%g8U?w~=5cN9(Fa}eo)E9o9XljOi_2ijp;Aatf&tr(qK^z+V zrI$fhv3>FC3HWSI(vvqIg zBhI=RaidL$8RcF%^(_tHXMF<3#__!dNBeN^Fg}0A_#SxPz}OHCPa;NWaRT_6hHaxA z@)g@0xCS-%J3t(NOTB;>#)g0wV8)0O;Ax%-*Z?jj7MOT@3|QikaRWkrI^$^R+%jEy z_A27dnqZH}gWbCf*g-5Bi1|+?>=+!I!~W6gmMW6P$D{tR7x;Y?Y8RlMJN(0(JHS|B zeV=onTl-MM6ud}Rm`suJ3JL^&kw%6vUfzjNOWugqNZ(b*$sQ{yNqOMAe|Le`j;NwI zNf^tEGd{eQ?uP9(BNIZdg#Xnm%m3Ql+wkk;RQ0ut49&F|wd6fboE*8#0q`3_c8dr^ zzu^Fi!JO7L>S=Di53!r4p!*Lld8(`ToJZT8iSPIkk8*+SDZ!>~!5#d?{7tTG6>Wsruj4o9Yd7yLet@>&BK zf0LO9F*YW#=BGhe`vN}z4+0&MjF>lFJO(&GtUBcfRhg7yiKC! zC-abT@B+SO66>=8wWJCwV1Gg^fgZIp^w`ctUCKdV_~awU@%fX~k7u)Qa0u@$Jf2Ym9gxc+pO9&wo^&>?U?+Zwp`AoI9MU~BdJFfU<##JFId#2A@ij4+Po zz=N3oFrQ*f0ZYu6mL6HA^U(2+!q!*{TQ=MKs-UyiLauIXM+_(AuqHu|sl-@|e$P7c zl1WfsI+s-NA(cWdZi7#=5AkLk3s8ywXKEhmEJRC48WDv2CH$|U=c;1muWI6zb=stG z4^5oprZh6(*D;9^%IrJ=FJ|_BUZURSoVx)hAO4P_hNf~+yP;v-^kn~|Y}1d|Mn4^+ zk=a$1XKYQ-$Wu!S)2}BdMNn*P7%9X3C?X0rl&DbPCy2nWsj{X(;2lpseL;}V&R;l2 zm#&6r4uJ1Yk|MaEe_JL6NnF37^HEIp#ksdMLx(JerK;< z=QzUGNnFACQ3-$07TGu(m#sgRr((P7<&HzA?8v4}Z`ibeA^h(N)O(X@=}! ztaV%80(&-cx#qzmmLTJ`LO;BQ^fn~w?Wxe5G*>o#*O1Fmz%HPUmi?uW+u z>C9s%v5v>UgBk@NCu6G+bEXP}N1$Cs4?FFN>Bl`)c)FyKW78AHx`t}5tz z>X@+3w3KMeOr84P9%I~0UQB}YJ$ZE48*#~z`)f+HA`gEbkvPEX!)xk`zAYLV>0SMf z$sO<=ChFqegPo))Gc)$S#UUZS=$`ARmpA2SCnX{O=-EW@85Qe4;N20(dy9^dkX#vv zewh)lBW2P!YHVG7{4|}qaDoaOnkXt&M{Xgw7B<*NU}rmv{wa&gMxJxx)C6*Gz;igJ z0QmzWlU+22^>gj|dE|90(9|*L4a?9Qpf9jY#lO)kkImrN4C^@pkHJ3c6@uP_XR)q= z-}+-`P@53>ZRPlFuLUl+ZzuN>tbmNz48P4B?p-{&0Dd+AUk~T+Gzj@UeBT7tJLhJz zpQaS{YL55i+L{H(qwj_6xQuHC!DE`+;4^}cu^q8K9N)u!(?;Z=PXX7nc#pwJo5f5FDn4wzeCY&?S1k7;Ua~<$g2f2%NmWK9n%7k4~k^sAoQcelF zRQO;@DLg)WB}Cy5J`)N)6UaL4|HQ?~49SU#Lj~#4_g57NdjCBdjf~fdLt>iZn+kO( zDb%vDj12Ya`VmE_KG2m{kA2a{-OoRL`6v0%B^A-~e^AFMJ+vuN{GCOu4OMG%vxIy- zQGI36LxtH1aapPA*W)#Eic+!u6R;AEcr+&FKa8Iu_~{xu8&Pi}6@KR=3Qy$Nfl}ZF zahr&vnLsSy2;{O+)UcZZ@0mhe&BQ!=gt5eOkpk0!iK;)Ru_74^pC){ zi04c|c4Qfo>xi-r#kv*SGZ-t)!00$|Fp0bgmbnBShV>XhE?ea{M$E9DBj`bbtj0PW zbRGdW!>Gl0j84NZkX4*U@oB6-BDXppd2V_g>LF^8ivYVomQb@9@)y_AjE9dV9kt(* zNtqBO_>p8$(%UkY9Uz0tBK+T0sU?$1amquL`PwxLhDKfk_a-Jrm>sS9Ab#e-vB}c0 zxRi+ZrBPwX8&F0S=6`PMJn{GYq<^Bcq?pxvrpgghrHTt*0d7vF>SDe;(D_I45~4!~ zA6&iO?A`DE1TMg+AH>E=SnpY3-3Pxn?+m_F&;ySnCiBF#v*_urK#r*gK~8}D9EQGF z7@?!cRbjwFC`q8F2=W)k2pm_%D_0^l^7mwr7eZkx3zmDoCsFuZO4LXlT53MP2mh}{ zEfu+||ML%zN?lg`kJ&F9gc^f!@ygA|hpIn({%+U~Z)~WfIQX~Wr~ZjLK3t(q`v6b> zukSyVo0sx#T%7!6l{)0tYTzV1GH3<3SP72^T!B4dB}p5xlB!d!BqYmL;u54Q@t}kx z=}MwjhI0zU_b8D2Bco)kjN%g|WPmMC;5V?T&GGsNZ((BDi}{F=r|>qg1I#eZ2z&zT zo^^db#^Vy>0Wu-mouMlk33g}bH1nWYw!85&gy-O1@GpViFb07O=4s3$*e+-MT)qZ2 z1z?0RA;^fhmifoz60idupLrF_i<4|~W@1e7`Bj~T^%vI5SU$ruSpN~^GT7$Wj>de4 zd7Xe8#uN4hj0yN+yx@944>ICr`5bU_`B92OtcQoE&FwIm*KN7NcSWx7yDd}rt%S*a zZcF4|w?k!~IOmCD-nT=-Jl+qKxKn_HM~|P$mHzL_A_9MadSDyq?O&|^zeTx!c@1nD znryz6m=^P^aJAwQwOYQjwL-Ug@7*#gD$DgRC#J;yCMs6(LXuWx*>+U_alX5(q_XHY zy3C|!^hLTYOT%3`smYNq#lY zR-&FxIA!HvKU21n2}(@~qSlroI)C9T@(a+X3hQ?9G;GMH*@n#P7c$uDdKPemW3T}j zu>!e^d6BSh^tBlGSMg@lk5=(FC)lO$JeQv(JQw%lGmJ7H)^-m;Vlvc-$fPpGhTdZ-q%*|3en$@l(0f z>!(tQ=UZ}_&renA&=c@8$TJd@C$%~i|87=iEn)~%xppD+tq!vB0nWLTA82wvF2{W@|n}mp=TT7>D9qa4Il7w)XFLy&i$k<6#6j z6T`4Ca-QBOwnxxMZiMZP93w7l_uxGGn2s9|CpfWk{_%6r-wIw&NR-F<``RbUWgaQf z5dqqm$iS3%wIn%FEz!h;`$b1ey`mI>;wWX1TWnlpkW8PIVE&(9e}UJ@zNYq4ij0wd z3%aTKk6wQIcj{^U%4O5WhYNnwn#~0#h%}@qQmh+>8C0xgOLg z=mzx|@5BBi>P_*r{H#fVu#aPtv*_JEV?fO*&~z8JyV2(dH4UaZQA?)-gj!XI&l;KF zu>-`%LD(L#OU=2qTb$OQC+3(v&=<) zT3Qrh6r?MKMJX#>4~la?$EL7`89T7MO=B%jV|_F6x}TaeZu35``&ArF0|zr89;ZII ze)T-?GSg+GRonn8jG1v9p8$th@Jw_biUB;(y-DKy=80_Un%rI_@ zI1=!}xDoKu2^$&+{={KmVi>XEOdKQ5;}B>NIgRSm`|xEkR*Zz& zX4q%k^bHFq}i1?2k;f`?_~Yx^=X;y=sW^SxvG(loY>Eo%~^_;>NMKF@Uj?`MGP zEIg2uq8ZgBsZonvbw!sEyQN53@DO<4 z&lat{_e=a6w`qK!`oxuUQ}o1@B|3LvX!^p1MH@q3jT_dzczCQ?jI}KuooE%0jvX}; z_Q%Fs#3Q54;-S$NG1j_xaO9|Xa0vUujpAV(ALjj0BVk>Ohj4Cis8KvP*aX7$1I^<8 zfd+B^V54|opi$h{S8s9b_+WlbP43UMum!5&qe@GSM*M#^oBzqd}@-&coc;Q4s&z#xBCt9WF%OFTN%j*;KHQ#?M=C7zh<7EetN zh^Hs}#M4vV;+ffg@!Z0Qcy@kRys$VTUS1v-A3r`NK6PS7eD>tL`0S|#_eahgJ9PfB zGt1{5U7|~mOn-m4yJqmre7o)QFU*O4LeH5=<$j;#e_)rJt$qjjSHBIBcvU9EOFql9 zO!pdRK-AIQ%v)PBaLCUKjV|$e{V@wd)<{Qr|GGy%irrcTH1Yfp~vpk?_3bQ{O09P`mB#$f8V9c zk1V9Zck1S5^UWY1TL)E$?MJUM?SJwaSX*3?U(3C7GEdej%@Y$P=5cX>=CN@>=CPVUbG63TJR;KDTp{-{ zS1NtYT3mk zlHX_Tsj~~_#~wS|^T;{a=jVF9-CUd3apB~k!yEr}S@eycUgv8*?Vkw!zeRROcB%aR z9m&PT>Su1w7IA8Y5N{?*i@vMk&OYrN{<-^{3-p=)8JieI;cEFy@rjXESy{1v?%Gdt zeC^O^n>lm|1M37WEv3J1Z!J$ferkNpkKcZC&GA!zU@O{DUY%V7KWcwuw7(%z_#eDj|T=P#m8Mq|-0vU3uy z^>vjxoWDFSdg;42KFyr3-Xl7BW{S7n5|gAq_VKbMJL~OF__fnJnslTli!0!CI<&AyaPYS2A_#qWH@Jkgs(Y3_6|&VPu$D* zA92Jv?AzHNK;KC8jy$}N$6xsQIu)j9qa1Q_<{hup4*W<&Iu~PA7hr46#8kOd$-;v>S}M`@4l(N zwj?YmC1y^S8sk@x`&W_8K0K#u(=(#X(^D0_85t_%NynoOM)J4XI%>2}J$oev{nWnE z)>-@Q>IVJaWff@O25+Ff`?k^^$WZ(DZ$t0U?cfDFAV=*+pX$A2i@Ndl_PkzsJ@eYe z8pYamu(vc~2W!?DzjV+#xdKOn}Dv)C5raGcM%;u;s@@zr~s z@YxQI*6-t;|1CJg{f*d|uo$wj?&38M7)zz_egOxvG)1~$}{>FzK5>&j^S z7HbP~KmJ-Aa1Cma1y=-=3fAf2Rd~ablv;i!@mZJf)Dkr`-zwrt|uA zb+aRP=yOKZlefXTWBl-F`fyBZ&ZyfXT zbb#)1M7ZDE%ZWUI5j*_;r)t%dg#M?SHa{TXhU?!*kOS$8b?=7Hboan#;y!o27ti3& zX7a={JiQ$6>^q}xp9}f;xstc9D|zAFJ~)SMzTU$V*J9hpSG?jM=z>~pN_=)W#i@LL zRjQBsb#cDtcO|(RG9VYAv?Q6zDv~F%bK}ipq-OH)f}V z?aWIIH2clfI=(`b8s+kbSDN-d%?z07(qcq;*`L|Z_R-HaYiTUuV@LG43E!%%$)cu~ z!mriW7bN#~RV6glW_Q+9W}Yi6)17T@tN2M0V%~y+-Gz1N3rzR{6HMN~ftR;4Mi53m z=kM=^K0j`#S?f;0K_0?(fWHw(0k|gs=lrpr1&r|;#(Fodd&d=)u~N0slz`exn>Vc! zaH%gZpukXXfnRudtzylH9RWX#qdVB;_u+TpX9^?U$&=sB*Z7WJz%CPG$~XeoxCi-7 z|I}kBYa8sZ$?NH=_P>7pk5do7dsfP7K63OQHEyON=Pl~m>@9Ft0MPU=l^P*|uZNkTm+OoH6{Fb^E}6h^7k zgXA(VQh*TiNKzb^^D(&>g@kxO7P5LjFFTPkP+M%v=5^pVwy2Fzj97i-5#zavJ2kVE zoKK9r2?b)2LGLORe!q~*eGyOT{+1-viNU4P`4k2g}VMQ zG}!4!p+Sys%ER1$A`kQYX;_HI&n3aG|Bx6H?AKZ=v9)c8uW{;&BYV#%|(Ol&4q0(4Y?&%rOD~~oR~CyZj7$1 zP_3&fi`Ugwrsx`~GIT9f6q;9g&1!1r$~;W zZClnuE<1u+Zg~Q3!lMM8g-#Da?Ams;=VS>l!O>lvfsxW#uG{X6Gi3 zmX_y@Hnmib_Vl(EbsFl_)z#UWy1E=qV`ENYYfE8DM_X}1N3&kr(OQj~H+}PsC)R)cl^54! z=4#iZWyP(@$V*<6Q<%1BrGYX~eGst&( zMw=FUE2ltrDW@Q9Dk(L#Gg>3-QYVBPG)Ykg_S3(N9~GITNV%4B(DkTyJ8qE$wQXr&52tyJ!R#>2p5* z42YU*{^otXIqqkIF|&E|L+?Gj{(jnsTKyZ=KS1l&-3wpNL$qnrdV27|`)S<+_tFCo z-X|Q}@bClZQ*BNUt~aOk53i$#H{u@9!w)}bya#yOyk!G=iaw0K2AgQx_RXkez8Tw_ zY1gi;w0qBX+5_6VcL(f*yU4Sx6{6TJB{ZL?I-KQ zmSkgR1$(v?9X@hUpd*flfMXkSfGy7fwmwJL@|;}kV4G)b+k)&!%(fKhXdY~rfZpZq z=|nEDvoLu=7Yl$s7%GMAtKd9cvk}#i@L)QCl@7;qny>qLuzIR}q z@7%G4c7V2R+k~Fen`t-Jyyb!2Skrr8AK!^JzmpCe*d>hnfPsB`cF^A4+h8}}hBc3I z&o*EL&)mHQ_Zsity?d*Got=38u2tH(1%0YE3!kxrKWpoku?^bsWxNZ*#KKcZ-Rsp64SYLRJnR+IQR4!x43`W8tGbjCH(GSYLaw&KXbpz#k4{ zz1ZP5-_FhwzDxFB8ux8&Ey&i!0{a$p=+Hj0!F5d5*tR~j2gmln--OX-uW(-AE4&^L z?g4MvOV;2o*1R@x?4T9MayRxdTH`!l13HB5L&CLtglnzuoD-gLaJO)c72<=e4(=jL zj0f)=v$Vu9JkJtj#W)c5`Pfcevr`x?EZ`TjGHzSJKV|_8S>hO9!{;mxY{T{rvc@$B z@qBA0>zx!F?7mc9ob~Wy%QI%kV!Yed&fWZd57admJ?r6ax3X{druSVvohc>*^%Gr> z(6+4)fk$jY{J<9A2EWzdBfOp%1HArM@xc4MhM8<|+{T76f_0CzWn8;F?!=nkje9{D z854ZJHGH>>Gi#Q?7(0v`taoen>oQJojSa39wyk$z+yWyefjhFx%}=sg=_5&?+OS>^_U&MC%{?)_L@=ZTi>o)_v zY~PglJN!6Y;{IJ}pvw(+CyR@&_Iob-x*WU`n;I=?Z!BAT<%O}G!mhI>>kpvuhMIP+@N<^6ZD{7_ptC|R(R})piFD6BYoDUCio%D3tJLN6j zHyz+{a9ZMXcv>0gFs+t)OeaJIOs8oiGg(Q>(d=YpMYO^r-N$)vYJjUnYIuNSsz&Oa znx>JZ7N)6FEAx`mn=7)C+v|(`wdznOhXWg&JstNr2e}_~migH`tHRu!G4wM zv^cqKc#zuxKPL-|NFRHP6s4O*UR;1hnNDumR+hS_qg=O9t8{#*L}s^swmfnDV?8D7 zZ=UGg@Ret-KKR7d3v08JRriFr+uRcs?0iqW(q~O(f_zO~vCeF`rwQ=~e^68EZeLTT z?{As`(+rqqz%&D<88FR&X$DL)V44Bb447uXGy|p?FwKBz223+xngP=cm}bB<1Ev`; z&46hJOfz7b0n-eaX23K9rWr8JfN2IyGhmtl(+rqqz%&D<88FR&X$DL)V44Bb447uX zGy|p?FwKBz223+xngP=cm}bB<1Ev`;&46hJOfz7b0n-eaX23K9rWr8Jz^9*q{|o!P ByP^O9 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Flexi/X10S.bmp b/radio/sdcard/horus/THEMES/Flexi/X10S.bmp new file mode 100644 index 0000000000000000000000000000000000000000..221e25ec477b7792415f696ae8973eb2980cbcdb GIT binary patch literal 160138 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 N002ovPDHLkV1j+Mlt};p literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/right_ent.png b/companion/src/images/simulator/JumperT18/right_ent.png new file mode 100644 index 0000000000000000000000000000000000000000..eaf7c6acc8b44aeb378701012cb37645f81b764b GIT binary patch literal 36767 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/right_rtn.png b/companion/src/images/simulator/JumperT18/right_rtn.png new file mode 100644 index 0000000000000000000000000000000000000000..bf3d51d7da51548f4291ea334df99a4f38fcf1c6 GIT binary patch literal 36822 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT18/top.png b/companion/src/images/simulator/JumperT18/top.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc1784ee2860dd9ffa705850dad1f93983f6aa6 GIT binary patch literal 439 zcmV;o0Z9IdP)B#)F`Hw}IF6?V%{>&{k39kQfE~Hmm5|HPEyeXQZQJ&AA|-?e;5vE) zN_X@X^yUh9nx-H{Jps)H0C2?RN3{(nmGbbJ13-LD>;e#8Zhw0MfLyrT1Fn1*0{{TF hH~|9y5WxEO-yc}D#MSsetupUi(this); + + // add actions in order of appearance on the help menu + + act = new RadioUiAction(3, QList() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 90, 80, 35), "JumperT18/left_mdl.png", act)); + + act = new RadioUiAction(6, QList() << Qt::Key_Left, SIMU_STR_HLP_KEY_LFT, SIMU_STR_HLP_ACT_SYS); + addRadioWidget(ui->leftbuttons->addArea(QRect(25, 5, 100, 40), "JumperT18/left_sys.png", act)); + + act = new RadioUiAction(5, QList() << Qt::Key_Right, SIMU_STR_HLP_KEY_RGT, SIMU_STR_HLP_ACT_TELE); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 180, 80, 35), "JumperT18/left_tele.png", act)); + + act = new RadioUiAction(4, QList() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, + SIMU_STR_HLP_KEY_DN % "
" % SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_RTN); + addRadioWidget(ui->rightbuttons->addArea(QRect(20, 5, 100, 40), "JumperT18/right_rtn.png", act)); + + act = new RadioUiAction(1, QList() << Qt::Key_PageDown, SIMU_STR_HLP_KEY_PGDN, SIMU_STR_HLP_ACT_PGDN); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 140, 80, 35), "JumperT18/left.png", act)); + + m_scrollUpAction = new RadioUiAction(-1, QList() << Qt::Key_Minus, SIMU_STR_HLP_KEY_MIN % "|" % SIMU_STR_HLP_MOUSE_UP, SIMU_STR_HLP_ACT_ROT_LFT); + m_scrollDnAction = new RadioUiAction(-1, QList() << Qt::Key_Plus << Qt::Key_Equal, SIMU_STR_HLP_KEY_PLS % "|" % SIMU_STR_HLP_MOUSE_DN, SIMU_STR_HLP_ACT_ROT_RGT); + connectScrollActions(); + + m_mouseMidClickAction = new RadioUiAction(2, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ROT_DN); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 90, 70, 130), "JumperT18/right_ent.png", m_mouseMidClickAction)); + + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 245, 30, 30), "JumperT18/left_scrnsht.png", m_screenshotAction)); + + m_backlightColors << QColor(47, 123, 227); + + setLcd(ui->lcd); +} + +SimulatedUIWidgetJumperT18::~SimulatedUIWidgetJumperT18() +{ + delete ui; +} diff --git a/companion/src/simulation/simulateduiwidgetJumperT18.ui b/companion/src/simulation/simulateduiwidgetJumperT18.ui new file mode 100644 index 000000000..1c14df404 --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetJumperT18.ui @@ -0,0 +1,197 @@ + + + SimulatedUIWidgetJumperT18 + + + + 0 + 0 + 825 + 292 + + + + + 0 + 0 + + + + + 825 + 292 + + + + + 825 + 292 + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 131 + 289 + + + + + 131 + 289 + + + + background:url(:/images/simulator/JumperT18/right.png) + + + + + + + + 0 + 0 + + + + + 480 + 272 + + + + + 480 + 272 + + + + + 5 + + + + + + + + + 0 + 0 + + + + + 131 + 289 + + + + + 131 + 289 + + + + true + + + background:url(:/images/simulator/JumperT18/left.png); + + + + + + + + 0 + 0 + + + + + 480 + 10 + + + + + 480 + 10 + + + + + 5 + + + + background:url(:/images/simulator/JumperT18/top.png) + + + + + + + + 0 + 0 + + + + + 480 + 10 + + + + + 480 + 10 + + + + + 5 + false + + + + background:url(:/images/simulator/JumperT18/bottom.png) + + + + + + + + LcdWidget + QWidget +