From 6b12010473ca09fa0096f525da6a594027323b66 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 30 Aug 2019 15:49:19 +0200 Subject: [PATCH] Add T16 support Added support for: - T16 radio - internal multi-protocol module. - flashing internal and external multi-protocol modules. Many thx to: - goebish, for the hardware support. - qba667, for the original patches for the internal multi-protocol support (UI and serial com). --- .travis.yml | 1 + companion/src/CMakeLists.txt | 2 + companion/src/companion.qrc | 11 + companion/src/firmwares/boards.cpp | 12 +- companion/src/firmwares/boards.h | 12 +- companion/src/firmwares/generalsettings.cpp | 4 +- .../src/firmwares/opentx/opentxinterface.cpp | 16 +- .../images/simulator/JumperT16/JumperT16.svg | 2594 +++++++++++++++++ .../src/images/simulator/JumperT16/bottom.png | Bin 0 -> 250 bytes .../src/images/simulator/JumperT16/left.png | Bin 0 -> 13936 bytes .../images/simulator/JumperT16/left_mdl.png | Bin 0 -> 13911 bytes .../images/simulator/JumperT16/left_page.png | Bin 0 -> 13954 bytes .../simulator/JumperT16/left_scrnsht.png | Bin 0 -> 13798 bytes .../images/simulator/JumperT16/left_sys.png | Bin 0 -> 13783 bytes .../images/simulator/JumperT16/left_tele.png | Bin 0 -> 13902 bytes .../src/images/simulator/JumperT16/right.png | Bin 0 -> 11742 bytes .../images/simulator/JumperT16/right_ent.png | Bin 0 -> 11467 bytes .../images/simulator/JumperT16/right_rtn.png | Bin 0 -> 11339 bytes .../src/images/simulator/JumperT16/top.png | Bin 0 -> 439 bytes companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 14 + .../simulation/simulateduiwidgetJumperT12.cpp | 1 - .../simulation/simulateduiwidgetJumperT16.cpp | 69 + .../simulation/simulateduiwidgetJumperT16.ui | 197 ++ .../src/simulation/simulatorstartupdialog.cpp | 2 +- companion/src/simulation/simulatorwidget.cpp | 5 +- launchpad/common/rules | 1 + radio/sdcard/horus/THEMES/Darkblue/t16.bmp | Bin 0 -> 164938 bytes radio/sdcard/horus/THEMES/Default/t16.bmp | Bin 0 -> 164938 bytes radio/src/dataconstants.h | 12 +- radio/src/fifo.h | 5 - radio/src/gui/128x64/model_setup.cpp | 79 +- radio/src/gui/212x64/model_setup.cpp | 28 +- radio/src/gui/480x272/model_setup.cpp | 276 +- radio/src/gui/480x272/radio_hardware.cpp | 14 +- radio/src/gui/480x272/radio_sdmanager.cpp | 32 +- radio/src/gui/480x272/themes/default.cpp | 4 +- .../src/gui/common/stdlcd/radio_sdmanager.cpp | 33 +- radio/src/gui/gui_common.cpp | 8 +- radio/src/gui/gui_common.h | 57 +- radio/src/io/frsky_firmware_update.cpp | 8 +- radio/src/io/frsky_firmware_update.h | 2 +- radio/src/io/multi_firmware_update.cpp | 447 +++ radio/src/io/multi_firmware_update.h | 96 + radio/src/io/stk500.h | 39 + radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_general.cpp | 2 + radio/src/opentx.cpp | 4 +- radio/src/opentx.h | 2 + radio/src/options.h | 3 + radio/src/pulses/modules_helpers.h | 8 +- radio/src/pulses/multi.cpp | 132 +- radio/src/pulses/multi.h | 41 + radio/src/pulses/pulses.cpp | 18 +- radio/src/pulses/pulses.h | 14 +- radio/src/pulses/pxx2.cpp | 2 +- radio/src/sdcard.h | 5 +- .../conversions/conversions_218_219.cpp | 2 + radio/src/targets/common/arm/CMakeLists.txt | 2 +- .../targets/common/arm/stm32/adc_driver.cpp | 4 +- .../common/arm/stm32/audio_dac_driver.cpp | 10 +- .../common/arm/stm32/aux_serial_driver.cpp | 4 +- .../arm/stm32/intmodule_serial_driver.cpp | 54 +- .../arm/stm32/rotary_encoder_driver.cpp | 24 +- radio/src/targets/horus/CMakeLists.txt | 27 +- radio/src/targets/horus/board.h | 17 +- radio/src/targets/horus/extmodule_driver.cpp | 32 + radio/src/targets/horus/hal.h | 27 +- radio/src/targets/horus/telemetry_driver.cpp | 2 +- radio/src/targets/sky9x/CMakeLists.txt | 1 + radio/src/targets/taranis/CMakeLists.txt | 38 +- radio/src/targets/taranis/board.h | 5 +- .../src/targets/taranis/extmodule_driver.cpp | 32 + radio/src/targets/taranis/hal.h | 8 - .../src/targets/taranis/telemetry_driver.cpp | 2 +- radio/src/telemetry/flysky_ibus.cpp | 24 +- radio/src/telemetry/flysky_ibus.h | 2 +- radio/src/telemetry/multi.cpp | 280 +- radio/src/telemetry/multi.h | 12 +- radio/src/telemetry/spektrum.cpp | 51 +- radio/src/telemetry/spektrum.h | 4 +- radio/src/telemetry/telemetry.cpp | 32 +- radio/src/telemetry/telemetry.h | 11 +- radio/src/translations.cpp | 2 + radio/src/translations.h | 2 + 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/util/build-firmware.py | 5 + radio/util/fwoptions.py | 13 + 98 files changed, 4585 insertions(+), 481 deletions(-) create mode 100644 companion/src/images/simulator/JumperT16/JumperT16.svg create mode 100644 companion/src/images/simulator/JumperT16/bottom.png create mode 100644 companion/src/images/simulator/JumperT16/left.png create mode 100644 companion/src/images/simulator/JumperT16/left_mdl.png create mode 100644 companion/src/images/simulator/JumperT16/left_page.png create mode 100644 companion/src/images/simulator/JumperT16/left_scrnsht.png create mode 100644 companion/src/images/simulator/JumperT16/left_sys.png create mode 100644 companion/src/images/simulator/JumperT16/left_tele.png create mode 100644 companion/src/images/simulator/JumperT16/right.png create mode 100644 companion/src/images/simulator/JumperT16/right_ent.png create mode 100644 companion/src/images/simulator/JumperT16/right_rtn.png create mode 100644 companion/src/images/simulator/JumperT16/top.png create mode 100644 companion/src/simulation/simulateduiwidgetJumperT16.cpp create mode 100644 companion/src/simulation/simulateduiwidgetJumperT16.ui create mode 100755 radio/sdcard/horus/THEMES/Darkblue/t16.bmp create mode 100755 radio/sdcard/horus/THEMES/Default/t16.bmp create mode 100644 radio/src/io/multi_firmware_update.cpp create mode 100644 radio/src/io/multi_firmware_update.h create mode 100644 radio/src/io/stk500.h create mode 100644 radio/src/pulses/multi.h diff --git a/.travis.yml b/.travis.yml index cd3f1e948..442466067 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ env: # - FLAVOR=X9D # - FLAVOR=X9D+ # - FLAVOR=X9E + - FLAVOR=T16 - FLAVOR=HORUS # - FLAVOR=X10 # - FLAVOR=X12Sr10 diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 61309d394..16245c848 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -347,6 +347,8 @@ elseif(PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) set(FLAVOUR x9d+2019) elseif(PCB STREQUAL X10 AND PCBREV STREQUAL EXPRESS) set(FLAVOUR x10express) +elseif(PCB STREQUAL X10 AND PCBREV STREQUAL T16) + set(FLAVOUR t16) else() string(TOLOWER ${PCB} FLAVOUR) endif() diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index 2b4b18f71..567b52637 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -170,6 +170,17 @@ images/simulator/JumperT12/JumperT12-x.png images/simulator/JumperT12/JumperT12-center.png images/simulator/JumperT12/JumperT12-top.png + images/simulator/JumperT16/left.png + images/simulator/JumperT16/right.png + images/simulator/JumperT16/top.png + images/simulator/JumperT16/bottom.png + images/simulator/JumperT16/left_mdl.png + images/simulator/JumperT16/left_page.png + images/simulator/JumperT16/left_scrnsht.png + images/simulator/JumperT16/left_sys.png + images/simulator/JumperT16/left_tele.png + images/simulator/JumperT16/right_ent.png + images/simulator/JumperT16/right_rtn.png images/wizard/ailerons.png images/wizard/airbrakes.png images/wizard/elevons.png diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 99094edf3..809bc159a 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -89,6 +89,8 @@ uint32_t Boards::getFourCC(Type board) return 0; case BOARD_JUMPER_T12: return 0x3D78746F; + case BOARD_JUMPER_T16: + return 0x3F78746F; case BOARD_UNKNOWN: break; } @@ -127,6 +129,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_HORUS_X12S: case BOARD_X10: case BOARD_X10_EXPRESS: + case BOARD_JUMPER_T16: return 0; } @@ -162,6 +165,7 @@ int Boards::getFlashSize(Type board) case BOARD_HORUS_X12S: case BOARD_X10: case BOARD_X10_EXPRESS: + case BOARD_JUMPER_T16: return FSIZE_HORUS; case BOARD_UNKNOWN: return FSIZE_MAX; @@ -277,7 +281,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 2; else if (IS_TARANIS_X9E(board)) return 4; - else if (IS_HORUS_X10(board)) + else if (IS_HORUS_X10(board) || IS_JUMPER_T16(board)) return 5; else if (IS_HORUS_X12S(board)) return 3; @@ -293,7 +297,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case Sliders: if (IS_HORUS_X12S(board) || IS_TARANIS_X9E(board)) return 4; - else if (IS_TARANIS_X9D(board) || IS_HORUS_X10(board)) + else if (IS_TARANIS_X9D(board) || IS_HORUS_X10(board) || IS_JUMPER_T16(board)) return 2; else return 0; @@ -458,7 +462,7 @@ QString Boards::getAnalogInputName(Board::Type board, int index) if (index < DIM(pots)) return pots[index]; } - else if (IS_HORUS_X10(board)) { + else if (IS_HORUS_X10(board) || IS_JUMPER_T16(board)) { const QString pots[] = { "S1", "6P", @@ -523,6 +527,8 @@ QString Boards::getBoardName(Board::Type board) return "Horus X10/X10S"; case BOARD_X10_EXPRESS: return "Horus X10 Express"; + case BOARD_JUMPER_T16: + return "Jumper T16"; default: return tr("Unknown"); } diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 7a60ad4f4..b7af393b6 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -52,9 +52,10 @@ namespace Board { BOARD_TARANIS_X9LITE, BOARD_TARANIS_X9LITES, BOARD_JUMPER_T12, + BOARD_JUMPER_T16, }; - constexpr int BOARD_TYPE_MAX = BOARD_JUMPER_T12 ; + constexpr int BOARD_TYPE_MAX = BOARD_JUMPER_T16 ; enum PotType { @@ -223,6 +224,11 @@ inline bool IS_JUMPER_T12(Board::Type board) return board == Board::BOARD_JUMPER_T12; } +inline bool IS_JUMPER_T16(Board::Type board) +{ + return board == Board::BOARD_JUMPER_T16; +} + inline bool IS_TARANIS_XLITE(Board::Type board) { return board == Board::BOARD_TARANIS_XLITE || board == Board::BOARD_TARANIS_XLITES; @@ -285,7 +291,7 @@ inline bool IS_HORUS_X12S(Board::Type board) inline bool IS_HORUS(Board::Type board) { - return IS_HORUS_X12S(board) || IS_HORUS_X10(board); + return IS_HORUS_X12S(board) || IS_HORUS_X10(board) || IS_JUMPER_T16(board); } inline bool IS_HORUS_OR_TARANIS(Board::Type board) @@ -310,7 +316,7 @@ inline bool HAS_LARGE_LCD(Board::Type board) inline bool HAS_EXTERNAL_ANTENNA(Board::Type board) { - return (IS_HORUS(board) && board != Board::BOARD_X10_EXPRESS) || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board)); + return (board == Board::BOARD_X10 || board == Board::BOARD_HORUS_X12S || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board))); } #endif // _BOARDS_H_ diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index 4f8532cd4..22934cf57 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -88,7 +88,7 @@ GeneralSettings::GeneralSettings() vBatMin = -5; //8,5V vBatMax = -5; //11,5V } - else if (IS_TARANIS_XLITE(board) || IS_HORUS_X10(board)) { + else if (IS_TARANIS_XLITE(board) || IS_HORUS_X10(board) || IS_JUMPER_T16(board)) { // Lipo 2S vBatWarn = 66; vBatMin = -23; // 6.7V @@ -267,7 +267,7 @@ void GeneralSettings::setDefaultControlTypes(Board::Type board) sliderConfig[2] = Board::SLIDER_WITH_DETENT; sliderConfig[3] = Board::SLIDER_WITH_DETENT; } - else if (IS_TARANIS_X9(board) || IS_HORUS_X10(board)) { + else if (IS_TARANIS_X9(board) || IS_HORUS_X10(board) || IS_JUMPER_T16(board)) { sliderConfig[0] = Board::SLIDER_WITH_DETENT; sliderConfig[1] = Board::SLIDER_WITH_DETENT; } diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 1ab1f3aba..7fcbb0cdc 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -68,6 +68,8 @@ const char * OpenTxEepromInterface::getName() return "OpenTX for Gruvin9x board / 9X"; case BOARD_JUMPER_T12: return "OpenTX for Jumper T12"; + case BOARD_JUMPER_T16: + return "OpenTX for Jumper T16"; case BOARD_TARANIS_X9D: return "OpenTX for FrSky Taranis X9D"; case BOARD_TARANIS_X9DP: @@ -770,18 +772,20 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) return true; case PULSES_PXX_XJT_X16: case PULSES_PXX_XJT_LR12: - return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board)); + return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || IS_JUMPER_T16(board)); case PULSES_PXX_XJT_D8: - return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || id.contains("eu")); + return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || IS_JUMPER_T16(board) || id.contains("eu")); case PULSES_PPM: return id.contains("internalppm"); case PULSES_ACCESS_ISRM: case PULSES_ACCST_ISRM_D16: return IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || board == BOARD_TARANIS_X9DP_2019 || board == BOARD_X10_EXPRESS || (IS_HORUS(board) && id.contains("internalaccess")); + case PULSES_MULTIMODULE: + return id.contains("internalmulti"); default: return false; } - + case 1: switch (proto) { case PULSES_OFF: @@ -1291,6 +1295,12 @@ void registerOpenTxFirmwares() addOpenTxFontOptions(firmware); registerOpenTxFirmware(firmware); + /* Jumper T16 board */ + firmware = new OpenTxFirmware("opentx-t16", Firmware::tr("Jumper T16 / T16+"), BOARD_JUMPER_T16); + addOpenTxFrskyOptions(firmware); + firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module")); + registerOpenTxFirmware(firmware); + /* 9XR-Pro */ firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO); addOpenTxArm9xOptions(firmware, false); diff --git a/companion/src/images/simulator/JumperT16/JumperT16.svg b/companion/src/images/simulator/JumperT16/JumperT16.svg new file mode 100644 index 000000000..649a506c4 --- /dev/null +++ b/companion/src/images/simulator/JumperT16/JumperT16.svg @@ -0,0 +1,2594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Activated button= black/opacity 40% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SYS + SYS + RTN + RTN + MDL + MDL + PAGE + PAGE + TELE + TELE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/companion/src/images/simulator/JumperT16/bottom.png b/companion/src/images/simulator/JumperT16/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/JumperT16/left.png b/companion/src/images/simulator/JumperT16/left.png new file mode 100644 index 0000000000000000000000000000000000000000..ed8f9e27fdd7f47b0c7f27b8edd0f7c918a2989d GIT binary patch literal 13936 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 Yy1IyeLtT^)9s+|X$*aj#$(V=!4+hHMvH$=8 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT16/left_page.png b/companion/src/images/simulator/JumperT16/left_page.png new file mode 100644 index 0000000000000000000000000000000000000000..d135a33074f74bcf2166765b2b8ee5aed3e4d796 GIT binary patch literal 13954 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT16/left_tele.png b/companion/src/images/simulator/JumperT16/left_tele.png new file mode 100644 index 0000000000000000000000000000000000000000..029506325fceac5155732552905ba6cc6dc9abca GIT binary patch literal 13902 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 ZO)?gldXT6Y0E5RMH6?AuI(h5x{{en)89D#} literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT16/right_rtn.png b/companion/src/images/simulator/JumperT16/right_rtn.png new file mode 100644 index 0000000000000000000000000000000000000000..00114f2a7904e3cc85ab92c02cea53c5cf66e40e GIT binary patch literal 11339 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 Af&c&j literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperT16/top.png b/companion/src/images/simulator/JumperT16/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); diff --git a/companion/src/simulation/simulateduiwidgetJumperT16.cpp b/companion/src/simulation/simulateduiwidgetJumperT16.cpp new file mode 100644 index 000000000..317fdb24c --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetJumperT16.cpp @@ -0,0 +1,69 @@ +/* + * 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. + */ + +// NOTE: RadioUiAction(NUMBER,...): NUMBER relates to enum EnumKeys in the specific board.h + +#include "simulateduiwidget.h" +#include "ui_simulateduiwidgetJumperT16.h" + +SimulatedUIWidgetJumperT16::SimulatedUIWidgetJumperT16(SimulatorInterface *simulator, QWidget * parent): + SimulatedUIWidget(simulator, parent), + ui(new Ui::SimulatedUIWidgetJumperT16) +{ + RadioUiAction * act; + + ui->setupUi(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(10, 70, 80, 35), "JumperT16/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(10, 10, 80, 35), "JumperT16/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(10, 190, 80, 35), "JumperT16/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(35, 10, 80, 35), "JumperT16/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(10, 131, 80, 35), "JumperT16/left_page.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(45, 70, 100, 160), "JumperT16/right_ent.png", m_mouseMidClickAction)); + + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 252, 30, 30), "JumperT16/left_scrnsht.png", m_screenshotAction)); + + m_backlightColors << QColor(47, 123, 227); + + setLcd(ui->lcd); +} + +SimulatedUIWidgetJumperT16::~SimulatedUIWidgetJumperT16() +{ + delete ui; +} diff --git a/companion/src/simulation/simulateduiwidgetJumperT16.ui b/companion/src/simulation/simulateduiwidgetJumperT16.ui new file mode 100644 index 000000000..02085d7e7 --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetJumperT16.ui @@ -0,0 +1,197 @@ + + + SimulatedUIWidgetJumperT16 + + + + 0 + 0 + 825 + 292 + + + + + 0 + 0 + + + + + 825 + 292 + + + + + 825 + 292 + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 174 + 292 + + + + + 174 + 292 + + + + background:url(:/images/simulator/JumperT16/right.png) + + + + + + + + 0 + 0 + + + + + 480 + 272 + + + + + 480 + 272 + + + + + 5 + + + + + + + + + 0 + 0 + + + + + 174 + 292 + + + + + 174 + 292 + + + + true + + + background:url(:/images/simulator/JumperT16/left.png); + + + + + + + + 0 + 0 + + + + + 480 + 10 + + + + + 480 + 10 + + + + + 5 + + + + background:url(:/images/simulator/JumperT16/top.png) + + + + + + + + 0 + 0 + + + + + 480 + 10 + + + + + 480 + 10 + + + + + 5 + false + + + + background:url(:/images/simulator/JumperT16/bottom.png) + + + + + + + + LcdWidget + QWidget +

lcdwidget.h
+ 1 + + + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+ + + + diff --git a/companion/src/simulation/simulatorstartupdialog.cpp b/companion/src/simulation/simulatorstartupdialog.cpp index 07766b7da..57272bf99 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)", Qt::CaseInsensitive)); + return name.contains(QRegExp("(x12|x10|horus|t16)", Qt::CaseInsensitive)); } bool SimulatorStartupDialog::usesCategorizedStorage() diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index e5c7e2903..f823769aa 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -87,7 +87,10 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato break; case Board::BOARD_JUMPER_T12: radioUiWidget = new SimulatedUIWidgetJumperT12(simulator, this); - break; + break; + case Board::BOARD_JUMPER_T16: + radioUiWidget = new SimulatedUIWidgetJumperT16(simulator, this); + break; default: radioUiWidget = new SimulatedUIWidget9X(simulator, this); break; diff --git a/launchpad/common/rules b/launchpad/common/rules index 67b06466b..cf96b0fda 100755 --- a/launchpad/common/rules +++ b/launchpad/common/rules @@ -27,6 +27,7 @@ build: cd $(BUILDDIR); cmake $(COMMON_OPTIONS) $(STM32_OPTIONS) -DPCB=X9E ../ ; make libsimulator cd $(BUILDDIR); cmake $(COMMON_OPTIONS) $(STM32_OPTIONS) -DPCB=X12S ../ ; make libsimulator cd $(BUILDDIR); cmake $(COMMON_OPTIONS) $(STM32_OPTIONS) -DPCB=JUMPERT12 ../ ; make libsimulator + cd $(BUILDDIR); cmake $(COMMON_OPTIONS) $(STM32_OPTIONS) -DPCB=JUMPERT16 ../ ; make libsimulator make -C $(BUILDDIR) companion22 simulator22 diff --git a/radio/sdcard/horus/THEMES/Darkblue/t16.bmp b/radio/sdcard/horus/THEMES/Darkblue/t16.bmp new file mode 100755 index 0000000000000000000000000000000000000000..ac0d16cf6119ad1d3e791bf88258734cc443d9e0 GIT binary patch literal 164938 zcmeFa2Xr0Rb^h!3e=B7rcH*W&HBzJ~QWARu8$c2S2o?ZD@4b;AKo9`Ydv63A*u|#a zd$T3kl3ZkUTQ0HVIF6GzPMjVmaqPrN_WOS4;*g_jN^<<(TMLebg}u(q+^J{wcXmB{ zq7$|b{GSi;Hzw*3eOdIX=>PiAj1T?aYG3OAKOg$mM?U08tNzbE^q~v?ABv7k+3SD! zMcdF-FYaw0{C&^^AN0TnJ@7#fe9!|Q^uPx_@IeoJ&;#$+1ON43|7Ab^@sI5%KlzFM z`@jFY{l|a&#|QK)-UmPX+0P96W!@#n{r7+W_xrKUul4U><+Gptteri3*6z9I9(&@6 zC+zv>pLg3n^O?_>C-!;ag%{j*mi;>0!s*2qU$plk_V>TP^wLZA%h2aO_c;qEf1dE| ze*N>G|Gd2$u@ArfBK!JteBldUuyEqH@NMt5{>3kT(S8{Uo{!^$>*34(UW2WFR<`-) z^{wy2KmOxCwtx7Cf3Uy#o4;}6=?6HvI84wNfBxrxZi^Q$w%_`#-?C}brrDY`Yb+@# z$x>5OEhQzz5zA?5Y44!)^mKzVGBPYPGxHskm6c`Tl#`QV+1c6eAhzY^=DsiG<>gs^ ze!hjbY=a643LJ&6`|Z9hEG)ElBlZdYR$N?c??ojgB^FMlrKJXym6g3Km6w-WMMZ_9 z9Xobdko@*w>wgb!^XK>HV%=YxHh5n=Ls3!DJD$z=2d(cjTKbpw!8<*8@Swf^`s;2? z`~LU8?_}3S&iVCTLMQvum%e1b^ENTWjw|b#--CUtj+Y+PinJ1*xH-!G2L{Y-}`791}cGaG7gzZHRjd-Z#$y z1)q_ApwH+Z-?#KR%e3H(>!EibhyL&n|Iq&BU;gFS8!VGgk4=Ltrtf~^H-5vez4lsL zw{D&5Gk86S1C~7=eCx+Q&-1>e-&ywWfOY?V{JUWt3cfqP9>hR!n}657Wj}aKhJN$7Jl^Wo;N+O!mplp!`mR1;CI%8eD9aRNO0L>#e~13vU21!9AHQF) z4K9b{C&(M&=Lui;`_jG_dHxHwLH-QKkmp;^-~4_l-1y%;9z1qvgR&{O-Jd6TZ1DR2 z+O!_;{yBrs@9{z#d{^M6ySv*)MnQ|kg;nxj3|MXA)WcS{Cud_X( zqM{5+PEK}uZES3;+wSFt$An+^GQ{H{h?5{5g71O%K*v7KQp1-f)zQJ<@uf;w1vEK8&=XKBbUS7~PH8nZii`J9> z-LLz7{B~M@PFlb2&(AiGpK$E>b?%vG@XzF*-9LBmy|K)5Ve??)VuxZ2p?9>mx7%Zn zJ@#v(xACluA?RWoHf(VHfgTIb(;xBi@$Pqz3qSS*`_uQe?|aW*o~L*>L0)FN=P|#J ze|O>Ui|cW1@abio*B8C5%l&%7hUdLv$MRmi-vNIFehPdR_%iTmK#ULgJGO7%eu0p$ z2?+`A+2hr2Qy*;G25k#%(>lMipFfUsK%5Vr@;;NGj|Be*_vQT<-rvBzJNt0bci?qi zKPGvZOTW@TzCYY*{mb)l&%6`vAN;V~ z-`{V~KKtzdT7NY@vlAyySX^A3tzNy_y?@>p{Y5|WZomz^=4DGbzj~hx{pNYV`(nHg zCY*2a$-qmVZ{e{Z?|PnvN4IX>>S)WBE$%*%eNaqHjBVVw(NT2t23xm&op2xJ2wAyy z?ONym@j(6PZ~e{Qbh$zPj$!7mAMkCBm)j=1M_>!WP_2F9-S#xq4nN4w|bd3i^hH*Yrj z0b(ozw-C7Xc)b)>885&q@4@>h;d|bbe`mZu-X%B)`gJ^hgE|HnhA%h=x&=7);~K|u zPVjxl9d{Ud|No{xFmv?iQKygb{^(D}p0>6&M=dQaPX6=!^bN9wzM=nuV|)-J9v|Qg zY%r#WV}keW@xc2B7w`jo0d3l}$x%2qE@C9w@e@3Ckv7dKK@_qeL%w(!!}~&hK#YqJ z?-hLTj)OWPItV&nYip}@b#*y@!AAi9U|XT9z#s4f*WwxvKm4%$<3Il6ue8`92Y`4y zx&*ode9m(Z4GlR0JK%UD@l)UPt!&xW^xT%e6hW=wFX5|BgML zJbvI6kD(ynrlno9S5i_!F$jb`&9rEuQ zT?2v#y#4_$z)5d!uj7-^(NP;48?$};_8H3q0|Sn4;1k|4atG{ikF?J{_nebEzlz`? zE<=2m_l0kneg-or^eKDvbdfm`JUr?9xx}WchVE=es7G2Df*L5$YmwC*iFC$Cnd)_x=BLoKgGPXQ4 zI5_BV0WaWZhQI~3?;QKs+tj}G_H=l=QnC?RB|F>O*KRqm9AX7RQ+E3FX*+lBoZWu=?RMv# zceRDb#yG^WY=ivx#IpY#yfND-Hw%P`!)`|Dmuh;kv$%hsljsHo0rwhI(bU)FiIE%$sC*E;9>3PucUAz&W!G3Xs z-Glw(VmXS($WGd>m`p;z*UPqKeCOVOu64F0C9^Gca(QwaaiPh$j#c|&oc3${?e z)`G`fya>OeU!KH65GNiV9vl8#zV-H1u;uz5LmUr&c&Ffkc+1mIKW&dc{Pq2tSLI}0XSHI5@&nfFAx5bDM%k@((pNmy@0CvjzlNwmHo!$v{9fZn zqjZi25p9#!q2?CamJ92lZP;{yZA~02Y9tRp$2Q3RR^K;ZlZ)!P5ALT%IStZjg1*X~ z@?%x#8K81}{~D{&dAuJ5eb@U>a%Fqxs6V{lBQjzSSZ00)a|`_32IeF%uYfrteqW9UgYd%e@Q@pWYwPw%<|wDFv_$VJ z-}&ai-LJyjgMay@sj$-*h3v~Yc+-%Fs zR$L-0!?H8eElb$TOi#58;V>;V$*dIAoulW^)pO_Tz6yo;BHd$& z?v?v3EiSS0(lXnjXQ|Y4Rmlfdt-h!c=JyEmdnPe|(GTYHp)cm-@ZP{S_J2cTXbiwU zr!OmoLv$v5Yr)t7`m^`Lac*>WFhXAMU;Wiz*gqR3S%ZFR&li&(nImM>ahOBc?!B@1q`Me}a51vlSl3+9Gs{*ANkmf5rHrW>xe zxwB^29BAfryK&~VcH{Ne*z6hC*bURKwpr7!v02w$Z8M>3p{qpGTsuq4H)z|eNpj2A zXdPnTYo^)tS6?Z*!mhtYt=4C-ZF1Qiuk+qG{aTxI{dC>KOq)CV2D^FA9GiC&_j|MM zTlYSHo@l-;Ua-)XEm~wN7BA8BEwz=(O<28rg{_TPX;FIi_0mtHrJK?x^bLKK7%zM$ z3a?4qm4A`qVn6gJ<3+LXSY1^u3^mzaVQEk89ybPs@@6jPr1^Lpcp`R#-oZOY_K@TI zt#5tH{`61()Xh^P{_p3CGS`H;z<>Yue{bLW-uIlp4B5jtioS+V4xaMkADD$F&|^6F zRR1GhgFMDgMvgE(&>zT4_#QiiXK~|{_@8H`?|5$XQDnIHlYu8>itl6moAia(5x@)j z1KYriCvXE1V=dQsTC6rnjH^WAZ`W$ujH|TIRoYh+IxciBmao(C zp>3h_hW5Qo*tp!0yPi9q-#=;BUGWLK?(&a^+AFoLTr>D-UICfE`luM+(sjbN`e4R&A>64?=FZeOpnY$;0lHp&Hq&moahBaO zXO_*IbAv6o=|)?4(;QIXa2S%zk$lO| z&9RNj#aejNY@0LvDvjUb<*UVCqM6rRVK->JzTsMtmicYgwU=xAWp4R;t;54JuL{vD z@$dx_AJ5b}zs=G#lUa&r*BC z^w4(pcf`H9``~(^dzh)~&D1$Kk8fEH5ywmRNbbzNVV2~MWTAN85i(I@1~T!+>#wsL zg_F5z=SnutkzAas@#E%M)9og$-=t->&6#nv>$@9gTqSuX(lgwsXSh-KKl@ts>D7{( z8Y}2aj@__!mD3MhevJ4RUiNZ;ZNYW&1CTpDhX7s9=MaD`;+pUbK56DQB5$x)@RKor z8oM9t;J0Q!@WM41m*ES173eVJ;7`>*#svK9*ys3K&^_^?GhU#_;yYwK0W+0#S(I}AGOi$riO2WD9adzeDbKUDL@ncuJZ@V^wy}RQkOUCUih?PBh2 z&TJjSwPw3}nlJrmzGTKc;ed9L&b36(wB+Ww?*0~Y9vwSR_r~>>+%m_O$rf9_aGq$s zEpxQM*=Z~A7gC%NL@Z0=b+_{!&efc7->san_KKj_?y&Y~7;wnk~P}t_w0Ch!MZ+TaOno zSkR{#jvKJz2~2uaSWw9(jSuQ?m`NTGFjJ9OW zlZoFHXK9JDl@*tv%~D(?BPrPu>BFSAXoX#%Ta7Z#tpCnX21@|K^`w& z)*y?}&pc+ltfB8bW;|~Ey2Fk9_sdit=M%sGQJZ$Y!-tD%ttipObx^0JiwT+6mbai%FTZ{7G6cg!C+pG1u-8GiB zJ=yl`+HD2ddDgO5IjN$yrWPCQ?YGXBb}P=4&mue5+M8OfqglDD8Z%lHpKF%Cw7X3? z;krg=v*uDXHftZneFJSzN2ir3RgP*xXdQN_k?b36$G%Ck(&u%?`tu;zVuHCJg2-;q?L%zm}otB@KE4(GR zXG+^Hf4QD5Jt@U9VQY(J>ocJQu$`{%NohjuDRq zj|>01$IPW#&V&Aj?7_$MM}PE3&gVv~omeXNGI|}l8M+TV0gn;)o2qdfVo}6W@Cz^= zfERGXSb+YC&x}6gxxorCWseyzYiNU*0XH5y;rI!+Z9IO!O`dSILjKjuKmG~3@`@{M z;i84MOR<|8#cdiCpMbaE>*o3f>uK+_#@c$v^SPNh8iP};Fel&AQ_~c)+2Q=Fl{+;b zNIVJ8mMiX=mz5))N_FSUN=b9Pw+BCLUA=V)6Ae0Fj`%1eDb@Dwu60yPe56YJBg}$d zu3e|!*aik1Rco%Q)|>QOUS^h+D%QG3<88g-mFR4nth8 zMlqO*68YLQ6hq3)w$j29t1REC^C~8zbCfG?QzIDY0j49qv;TlWkk<7Tdl> zF{CYu6>Z*X$y*gCawOlgFa;$+?BkZ>g*ENFb(^KcC0cqynr%-^Qoh6{mn)Yfo1B=} zrEudh;jt3LkpC^b^>Yl^7hQ}P4f!I(4AI$Mc;Q8tt5{fA;Oqc!1+S6sLku0C_Eh@= z%lOiL9xbs;>>Y6P-S2+a;f7oQo*9|p=L&i`<7Evk*a^oAnDO}WvIpFRV+UQfyiB;s zle}57#IE@0@7a}~_?VXGSz%_H<)tNCL5B1l#S1eOJIq!*lI5)A1S`(Tu)OpX@kOk3 zq*zPd5@+esebN)RI!cS*YTLCvF>a&yD#mgY$IMTcP9`6Bdcro#LUP}&x?Q}kohYouOLt7vJt}Uh`{Vb*EOa`J=_&eM z>uHHw#H%rupSImHlM*a-+h*NIvVLP9>5Gc9@N9+I>3SyVmFY=3k91GzXZbolU)SQ; z(ETJ?rk*i%Yn*74yWey@f6At4OW(%&W<8%M!P6FP3sJhBjXp_D*lJnZ;@tUCG`4Sz zQ=U=uMoX0}V%*uIvGP**xYV|AtoU`08Ct)LKF06Z1Nf)E^rcs*V*9E zpz{rY8+^9-ucz7{xWP}3tRWu>TZR}fxFL>3jve?%KO^q~do6taM^NU3dkaMQQC6E1X?wEQ>i0EPr~UWlt+jd)zt`oi)BaIbpTEKC^44iz z_75FXmm6gb`RlDQf1TB3>A3t@YcAMiy_Lzpq$bW396?#hQz^SX0SX>#I((hQb&f8)xkm@!B4% zW7q1wBdjWOwbd8JS*^}hl`>!Vzd`$LvAWDvYNM@5)L5wdEQzrOUAsPatu^ajdUqwM zUwF=SI=7x>lJ=@~ME~$x!3KAq%`EHsxhY$ny%!f9qdW$1Q{n6zFanSJHhkIR<5I21 zN_dMbLFU|g>#YtqZ@lrQb#%5{PF|Msn`)E?IjrmL7C**2IW!gLvk|)?PDSoI_(q=u zH-GkLf98m~apVdxMj&fEZoHo67f9B8*slBJ zCoLgzzV+|eU?Wwr)>RrMinN}Jb=JFMz4fW>)3%|V8?C!6$~sEcSXcQP>nvNN-y&_W za+3{I##&E#l%xKg>#e7BjcBcP>pUS^Yds}Ur1feW)KeCz>#VVk;am+Y0SeU!_g7X6pXdTer^FB-$nI^{S+vSJOQP&(SE)U7*CD(2 zWS@2IjIn!#fo~ ztk$)o^qe9+Q+Ju3dy;y}xvxm;S6{W4$5=tCa{c5BiPQM`ez>_5M#8s+Vx{+khK<7VDH#i5*5RS`atLQgQ*{Z+y;R z2YD03jn~yKrKfp$b17e%mo*+c=)Q%*O=QFp#VJ2&bEjWx@$2W?$e!)u-5uhY65HRD zY2%_}{o>`ZIy=@=VJAd~TMBGnZKmyS$guq_nKsduZ70R=2igj3v@ydb#BXB_sSXR> z!U=rsczY5r@Ye+%({h*g>CkU|Rah!0yuWok#m@Xiu`W<*%@TUEA#RShJly(4u=w zvB!n02k$y(_nhdpwnAM`)XhCOn#9i}j=CIv72S#P`*&^@o`vJa6dT){W(S&c ztbf;L9kb3_a+ce0ZGyYsTgO`L?EZFp=HYX8+o29UXRY0H>!2Oc7|>G@BkZQyZ3o-z z%>E|pui9o$-hI+O_1J@U$3e|s7XCVfo6z;ShspckUPL@6GP+N4wY5z0CPg`HTQ*5o z+h`fm#dqL~k^l7lY#FfNapQ6Eel55`w?OVNF2DKaTgo%956PR_-OhhY3-j|7pFv0W%&q z1^IclVbyZQz^5sv>U!I{cAgz*$+z1NHrnxlU3N@#puNZrv=-Ruky<-3Tx+Mt8zf^S zZyE~hM0cf~?$;ZwP3Zi7m3$JU9J^R3m+ZD(#?)&6D={u>U-5Fgu-2#ZoEX|AJuO9YWxe#iGU-nJ zYD=tHybjN|!;A2!um?t<3vx#o1heq#Aw9!mw;i&>vTIHa@3rGYHIiZ2Ho~!!5gYWJ z1$Nhoeme&y%Hr(IXoKA+`-QP!pmM8|HwW7C?Cf}x?4%Ows)&`o)?^QyJ7^OcS2~4H z*XkL&A?aqFdRA12 z6lU1war2^b)IKNdFb5}7^ElAc$SXiU12>$Le4MHFN1iu%G~}jXyO8_uF+)op1iBl~ zc`4k$`^Z~l3-M6SK~5g{@O}iAy{rO9*DKE#y%2fh=36MbDEtV+G7W$ zQ+Dr&5pE>sbY1MDzFk|TzvW4WW;^U3(D-mrwjEV+nmd86P43k^}c$)Wyj?Q~T zHb7nmc|DI~!tnz?!8b2_R^LAoG3PTgD_!TTc6I@|_t@JUOP=IZ`@=WRycyJrBa4P0Kcj^D=g@jeaH%jyfC2D&!5@F5o6FSMp}5 z-6VN)^UP~3al=9ztWC0g^-1E?V1?h12kH+Ut@h|od`WkX$P_{aD ze^14xkli3G^vPe>Cx2a+^fTm0SH(sX$JuzT{0NdE-N-lLCFBbfcH|>yl|9re z%=cAp6p!ovbp0;LK4cTNSC?=*Qn%g4biGmG=Cu4D6W!&qh2#_2E8m08&De0TCEM9X z`z4zWb(UzGFkHM|{(?B+MmW;BJ#n2O-B0K_^{o7+XYP~CX)cLXOiFs1Vq5XC(TWR) z`~vWO5I68Sa)jJo@)Q~Os7b*4h7Z6Du~PQqIBXR51xMf}D1$t1o`3Fnd+yoiH0P(< z;fB`DEl?f><1go8PRmsLBVJ3)23|%MkhdDd4OqwD#{Gxm1{p(tVZ(q4{MfXN6CO9- z=3yPe#$i8jitgqx``%Es?39U4`E2D2J|Q0g z_VKXB%tP(+2k&pTqkU!4n~Ln1{J~&rO!5gmt4(@7x)wZ1>qxpi`hA~+Z~1U ze{5$k6Y>$Pw|3b?*d8HUd!3Gnw3dQZYFBAL$p~Q!o#;?=ws=0pjpKNr5`E^K49o#;-;6Y;&`?M^(k!h_2sNwqAUC@jMGF5yNEU6?J;vBR$sT-f$U|tKdtbA}7?>1Pivo`drlL@^zBf z&iRn_URNVu0Q+XmvV}I|GQ~YF`=}+Zz1fZ9@LETqVhtLP+ciG7<}SD9?8UMzmN>h- zGiQbM$gkC!FaN9TjrM%m9O6O8qmIwvAI5&^LIn}d2Syx+m?yHLo%1pdD_+>D>oiLa zI6O#RIR2iLJ#L$L5xn?#4>pir#)pg^H7s84#J_L}+&I0B*y`l{gyfpm;cfg|ip@0S zNNz~RHDxcb#?*P%ma{~;0!wt?dTu>yW9Cw8%2}%WTxLy$dfr0qkF1hS)uDUeo3Ti; zNAgk6)|w|7qi4c)>c|(KM4j5FDPw{9YKd|jHd@Yh)qj$#ak$wIZiug3)YIU3^f2Dl z(xppXeZ8x$y2|FypYL=vWCQP(F_iIlk>+y$=5PL{t53xE2u9!`j~(O)+n6Wt@Iwy^ zFT3PVOBa4B-JDVKB*6{-Ca^Wt{`d|Sc{JdLdj$JF#|FO|^M=S@qYrrw2;4yEpRNv* zYix!{fpeXAaA6rIln+&uB~0Rz^=dIBW|8o!rGfc{-DXRnX$eDdtLst zK8@-4p?VZgZ!20S-jyw`+<{K*)IrCEDWZ$SB zfn-j)<|;6Th2sX>1$l!HiCSUE6ZAB63;4pvwo~?>vcW!v<}((6YY8OQ)J7xD;%H`UBJx6 zV}jc}ZopS_?o#;#6YY*8?eYcGh}SpT9fw-%v3pbp^H7gsQrYr(6u6kq6ZfcQ&>_`# zkk70|vbC#Bwv6!8EIa1zW8L=TT}SPH`K0%&FU~6tdP=e2fy!tbSFH7r?8Ms@dwTTl z6ZY8MN3FLeUHPb**Oaiu<-TIyTuR>jqPRhS2R9%7@P}=Qa&&p;;kfa78sqf6_uOkc zckQ&y>d>KsZvH*|g$@14Baciq*cdW}InUVX$YA6qcwsv&b3U18fM3Mp#^>4a zT=Wso1_fiB*oVk3Y(ofr%wr_j`t8JT$~Aw6x=c|Ki*4?;m&wMt!V)7TZ{%O3a0 zIQtpBTsbMukEHl#yJ8VXm9y1T87CfEAzy2f9aMfzPvz#2oh_`v*IgP{yQKdQ?H0~D zN`wY`Mp!67p-`2-q$^a@(y%v=|01I-g`=Yk!Xj7lRFNz%4SM-x?WFpvORIn z347%BBlhgWXRN0-M>*%r-P@{s?O4SX*R{_zB_$S>t^RJ9h36 zCR7vZjyqN3tHLs5-|Vd1>Fk@~q2UYTGxBJv{V~^u7!$Zbc2f7r;|A=28)OiE$M?fc zICj8|Cyx)`f*qFqI`$uNPjIt-(;*ItjkpiS7#LmquSZ zzrR6#*{i;WHH?qz7B_wm$FL)fwV8-JH zpI4jgwu$aCd+@dck~j5|J*oECeYYvMW?Z^ey49u3asKstj`u20tzWqVosuUx4sSi! zQIf;mve6#9`?P)fi3jy;je7R=E;oq$wv$8pUUkM&_2ow8vYoP*o_@qmDgUBbIn{;f znnNnwkTZ}W+)!_pI1P9~zk=V&v_ zmpd-c2HXg@1+s6VA{N=4Yd@iy6_*+IO|SH{UT0(DTUEZW;w62GQH)7%At#df-$}`v zG3APNNT!S_2Xmsc$mv4lSm(fh zeOU6Ut4#4v@nSf?dpjqzUA*sn*^yS!UTfJC$81k?k^BwHVbOMMiV(Ig;>Kew*WGVTc$COj_lw{3#bH4f@#l=`3y<w_*!!<_wmH+~Kmd;wlQ@{y0YZ_b6w8_wnRwEOP6U-bpf3O5xl z&t_-k4m))CkgHD#Uoln_i=S$Lue|b#>l6H9$WP{Xddwhizzy5T7xMDP+czFJv|e9B zSM%g`HQxqh57_be3CB(3@`ZN8RjMPSoahAk)A}?%f*bN*h$Dt{ALWQhS3?eUi+Xm( zI$sjDanBCr%Scxueu?iDenh_`mPy;KoXB4N=6rO>1b9iZ%*FT7t60`Gy*=f90~h@R zU;r5drsN|KK8S~u^z~ZoeV?=9wo2Lfifxjwrr(`RnUqOx-Q5rVY;5_^@A%zV9vvOK>YKV;xe}i2lxqEixlgT?_gh};#rDO9af$x`Pelf+S3yrBADR| zzDHb!?Je>vlXEbv7|y=t3^z`=b1^n(pVs`PPUmA>Apfotxv6uvNv5^wIO4x;`OD-> zT%`F##kRk5r{=q=?sjyP#chaE?URIe$Q$HCP~M=^6SoQXr+In97|Qoe!{v>~ALsJ8 zq2}1}<0mfQrb2S(u;dLjEzn)?H<9Nx)&8g}$hc3u3cn-kJ|CJE`-VB6)EGhDhU11l zLY~k$)jP6XFu}ay0y)<8WUU&rgW?jek8|`J-R0M-Y-7` za|C+j8}F7J9aMfF&%$qmwf*;z)y2EjU9|^t8 zpNH1#X}8~YyYlUiUC`4iWapr#k=qHc5F;e7c&h!8=fM3%Dh7kkjMyxA!9VTg4co|j z;Q2jnkTDR)62Afq{Ep7%fAd)Kcw!%97P)EYeD98%MK)J{fw?oTv=rs1Iop<;P1*B< zdla*gj37=jt~dsHT4VLhV@Z~*kgZaas(E2~vIo+YW4YPsT+9Vv?Cn+VC;8K3jk&^> za;s$GJEvTX~N1 zF#Gr0P+Of1Yx|gNsd2?m#+!7XiW!Z{P8mTLliWP0`E@R4giHY^kmLpWpVK#mwF50V z@~LSKr2GPXl8Xn0=?UfH9a8QMKEcDnJo%sa7yG5F9#W2UPi=;s8m-lMQlwnG1lMO1 z@_C$)u7)p#v0+@g*#V6)y~rnB-{lKv4#8H%K2x{HYYcmD+{~CU;~nw_zIZ>}AaAe* zg1BMs*Tmt8OX3FoguF)TxKFh|`~dJX@k@MWe0Rai8*qbt!?y13ZifrxCb;mvXk-<6 zd&m~QjTYGiffHJep=JEQo+LhsoIz)!g^=9^c{#Rb>3o~1TA4GaDPJJ+7R`CapSDi# zH`2xNTNT@DlMErQ*)BY^7ipdR0NKagPWK7rqPbW-x(dE5wdg`F_eHXTV_go4bSPpp z#OT{)KQIS}JT*68Qv6GtwB3!>jM339&U!?=dQ$msCl&WOA%E16aKKpKRJ7TeYWG@S z&!BZTRyo;sQoo-PzcTNXylmtZu}J1Jj|ofM4|rkjsnf&Ueaa5geIOGKcNE)gnwvMM zxK6KR!%@YT@R1)IuCslbFK|@ylX=sl8dvOrcym#b_ z#|_wFot9d#$d-^_AXD2VZxr{0;4N|)S)OWt_?^+M)+t|%xpmk()UoltXlf6#jha^A zA&47f3w%!>FfKsgg8Va||IGfxMcK|a`eMO?1y1H5Z?LVxakFaCEq487AF-KNepG&e zTPFQ!;$e;b@K$I}vv>`CM!LGw)A``_e&w;bdICHL{sWP_7yJggUa$1TyA%VxO>=CT za#kqsEza&c)^4|Ie(MqCgP+p%kH~L0){rLIm1p}E`vuG7WS`&HAfNh9cmL#x9uv;V zF~EN~rg;bZg~K8F>w45zZK~^$ofzYMX$i5ahm(|`_flg2U*d)_5jg`^JZ{ia@Cl5M z?{|2?FHlxq>hhuS4Zs)R23$?GKVmcJQH=l89ORon9yiDxVXO692JkMrb{(Ck{GACZRVT|r`wKB*j zVO*BKOVsOlI!d;5r0vaI?rahKO)g)>VJKv8ztfM5{DIKHxvds{7e z&&|qzbK}2wx-omH!wgtsTZd}Hkk^AB+sy@&4a2>(s18onR{3*Pud^r_nT8F6T_YPp zdX$UdxN}ffL^X1}Pfy1(77Pm4!^*Q9DqC%XB`a;PJkkzJ7reD2+eS-OPe6Teye-Ej zr0X4)d^y=Fywq-%uC?06GB%C(d9MIrbm9T9a9!D#zI&GQiTrIcStd&-y z-*%@ix816HRh=RKUG^HQ&Ru0YvsPG%e#_s!%u3TEY-i3|E6Z4E#oD(_$Cjlovy$W` zR+73zziV0BinXmk>opn5%_&x_Gg03V+8C|x2yIZk+XVORvv>1D2XTX6fV?#9Rd3&* zzwo_DkDG8k4LJj5Xqki0x5V%VWaVT|%9{grctY}~q}cHkKIy6UNA3@DfLu#z?hw!M z@&@dnr}6zBaz8z8;CBcNfj49o$NTRTlNalW?+Ze~b#gj6hU=oMg=CI!qdXh@X=|3v zck!E9SAN10qvom(l;jO@8>h!i;zoD~#Xgj$DShW2)!i7VO_NQLqgn&Gk^|(Ut#mj; zHigR^<^(hq0fCdR3pJSMs4>GNaEe>o>Li+C~nJyLa6Kb*H)| z^L7*~R^QTWF++zecJPqJ4IZ#fgX0$4zfUx-^+UF4;Go6H=MdMo-?sMdv$&=Xi?7>j zCGD-&G1z1KPEJ_Y_<&Wm)LLcZ9ve6?W+O)q+R$P7DGrUR-DkrSdPLI^v zapl?^k>BZn{C8uj@3F77#QC^~B#Xu*iw?<;w@>)&*ttzL45cGdyRTrqYW%4-plSg& zX}xih-1Y)$1V+n_P`#D<6K{R-b&_mbC8|mX28WU60yB4?bjHeC-Xp|IsI{t!KcF zoH}DKe(@E1`SrK#^Iv*H^tyd+l30HE)i>=c-}ts2yZ3I*-Poc!!0FERJum7eL;4+@OQ__)Rd+20bktH_YuE80dF+$y#aY5A2j` z2s%G;vv4C_Z$kF=;jS`aN40A-hX#2>U64b{1EYpvTS>H4=WLYUc7tl?uGRd=m5$4@Lh9v`#H5@J=qVar-8N?C7v@)c8&ZPP34W7iDGr%+mvZtG6nXG@;? zhAn;ktG42?uUN$6U$%%R-qLc2mOu7ow|(VPU$>RFJ!A9R$87WPaohj+XYEnt6hHRB zLw3j6+iYxT#GZZX8TiR)neG7Wq(I2RG{`damyLRTG2dpGd_DrF4 zX8E3T3kuygRl$wV`@fXD0XNg8spe2%9zjq~^SBAh8;={XgMCBGoXP(Fz6-c1DOC(^ z|32w&N1U$!-s0P2Qw=wKe;yuZtiWf+_ep}dp_T}`8~5(@G;ixOuCR>Upbc(ERzui8 zkdJ}-*2g{3(|jCC^t%fG|S*iJu zYgQ|lCDO_>B;PfsreByI(ENcS;U?H0*^LAEo#Jldj&%X59>-N@Hzh-X=H*W|(j=uc1z4q2u z?Nzm3e*0_oN8kNZJ1KpmG*{nS7j6nQ*5|0UdA7#05@E>88~7daerRk{@&$r^XvRT5 z|0XDJuyf!K?-u|okhgEBF$iwTDoPD`a{)KUj%dFMcTRj#Qw=xdIUpw(E3o~kfk3?~ z{LuIXzzs5p`qtpW%bOrZz=9`a2;_5W@H;~uA08vfncy~-8H0%5INa!aXET+bM*N1l zv3;s*8=6lb|CGk+F8P4ad5FQ2N7Ao6PqsNfmEt6>&XaUFYzgdf>Xx~VE?7nO7K>WD(iTQU+2W|Jwmde)BDQ3U z@|Ybtd#fG0{j8mT@Ls$7iAU_-C-lwpCmx-o$Lt=>2f6F9N9}geJ&!+Wk3ajO z^&gvX_Dz1SYSwGaN@FZk?AGJPVU zu;1o5sXGfTO_WYsS#0UmvIj*wc9p1(lBljkb;wvQRvofp`H_i97dWb_ zF4Xthi>$7uz-srX?te|OzDZK1@9?N*S)pus)$2@1NYpoQ^u2iL-`>9QxPga}H*Ei( z;Ku7|jES5JSrf#~$nc18(e3P-Z15^QZEWAD?3*L*oA6sUD^@J~VXEPV?~*b`V4t9i z1##o$4K+L84>v)5%wxvOogii|rJvy!fau#?;bt|sQ9YZRW?y4z8<)suwN^DMR1;dU z!XvUlsC9c>et?M%<%)=>$vHc$Wpd3K;%iL=lvKnloFcY2IV3Aq%ZvQnIO%}1O z*j7tl+nb}@IsEJLvGowYmA=-QrMx0Xi>zC1Z;t8&q%X1ZgjH4=zsAb9X`a;9)mFNB zwUum%u%fN{w&a#b%h()g>2WJW5thCw!ZJ3gK2e-V%Wgexl@-Rzmz}c28Zs7IecB?c zOPgmMS*kCfJi3f{`8}gI%C8gc>|OBT{m>pezYK1?U*J->L04m}?&|7vc=7UPbZkWO z=BTUD0bk;;q8{^9`Xfgh+Xh_?Y*1&=%NuaRyf%*;?|Vk>1obp;$N1Lc=2Ef-+#p+! zIZp0q*~uKWr4Bbawo3NR4a$d}soH}Hk@H+`q|0kmoTX1S03dv5)Lmi@OOLh>R7;*K zzS3PD8_G*7C!bONHpTq*>U$Y^(lhf$M&1179_0a0|GZZ@lUu zr*ywA`Mx{F&lA$qCcsU%?m1QV%la5wysgBRq}JK8lp0$h8L~1>awNUR)(ATrGV3im zufbw-8f?9=5+RuskydSMw9gtH6Op>tmTxJtwX3D4$rsmGuDk}#aqdKS)b)t(()#5- z**|-gTU{mFuWIKWt1PY5cXjkl`{Z2ZU#81;P|ilN#`5w?_pPeD>|D#v%(88pwkUq7 zx)#~_R+uY!o?m3Uiil-WG$PWTH_`&d#gPZSs=Q|hd zn|H$v7@~h5ud4;^e=lpmNf1BSG3aBo4map%BDY=rm?v4ia^WpDQ~3hZFaL-otd?J3 z2Yvza;FXgmA}4@0l%t|`jn$!?eEbEzixkrTC?GT0?!KzTw!Ree~_(nmXCsyDT$Rz9{;nS{cI|U8#lKBA2fbq zd>hRF495+=0zYpx=obJp$QEQe@&~^(z7y|X15aKyd0E6d*W*~Q0(p7E_ycYt7AQaM zlOJ)oNnFExPx%F;&ybHsj=igWu6NJ;XIICZIV_Xp=Cf#-{n)=-zO!Df_et(_7pqo) zd<4DZtSPo1ssSJ#BKKwT+@WLmo!n6BY!uj-VnGva%8l3_?Y@aNzNgx5ZEdy&@nc%< zE?b{mX)EG)+q{H2Tbk2hF~vO=Q`l;oW#4R+&bBtGN9MS$JM2@4K_J%Y8G7yfkoAT%vipqeFK5#4%UT z06mQ{gF5C@6}SA+p=56#z9ehtgg;UH@G^+*?E&P<15`aUm>*beg|;nmQ*&dHc6+k{a!-%K)U zqP<+P&_-7uG&w%X3X?Y5J%jD`nR7?&uHy&o=z)DUrZKaxw$aLp_E=nbt3^}}+LH7p zTb5L9%Y~a2#oZQB-ft^+h$?z)MM8Z0W=QJu3D zx)*&5PO@ol?i$UN7ODPOQ@-$&w?^NdTdD84sh*nVx-}L?I%+6TOjqY?kj$)Ajk(&) z#a5pan(IalHRhr=>)DvU#&@)kH`(B3gYrJ2gqwK9bACbG`1nl_H@s&*Pa6Ed2bZ#M z*oF?qx7Ivvii%a=TKNOVPab#QEZn+ftIM1G=}&(;Rbb-ymrcdmX7!-8|!|j`Q z!wq;qUf?4^N5h9t|M}cb);+O}aR|L__Uzd%Kh5c8(%Bq-z>WG5+^k-*K=lPwmt(r> zK&@M>`V^Y4qIoX+HP3UOh`BGLjrhN^w9LE~zSl(k$`s|6QCC^>WK?rwv>sBu0Ok2p z?~-py;V*N?H)&psn?pmMjr?+29&=>T|!8pL226EoWo9okjGI+ctM>&|14b)_BFCY&N8L8Yl zH&!t3P3zK4ky~zFtIp|aE2{2bpU%U4J$x%2n&atoG}&{#@;8nuCz@~BQI{iUdz@;g z>HF={)7-qZ0B-Pqy_=pE#0_I1vL}ce>>I{Vj~mYA{b?RI?Hz5-pN72gxH)<9gqzob zykU<1Pk!=~DTN!pOGrKp`WHD5$eVCI?cHz#4tRF#6>tM)I1gB1jDf%lE&GBGMyGj1ExwAA+dc!hT7aF^q`I(NVfP(lw z&V+nuN4gd7mp_c}dr)tH9OPcLy|VF#R9Em&Q-=Iw%E@G|^&aV^^2Hr&(>HRo?SOo1 z2U?5Wc_uXHwNF2Ow>|sB!}h@0Lz;IMZw+a;xcLDO>H8midxw3Q^LFQHwyG}2<``FZuuOgd<`D$#8}tg^Rj}?L^*Zq>@s6=Y;B$C^_Zyri?Qs)~d$Nu1 z4*VQ$s^$98|i7}L$6(?dD5EeIcMfH)g4@#q0-E zn`uxqF1dl6fbaJUH%H}9KH9As<2|LSXCWSvt>2+>7&`$!0(#K6o2Qv=!;(9F(%Xiq zQy{r?urb}undBR2e9wkCEy$(;`PF(f_dflz-?e7>>r=L^xBU1Rd$6m{zJ9FN-aOQ9 zZ)y(N8~PU78;85?6=CLeeMj)^*@OLUng`G=FxlXh3B8NyG{+ez8O*8_$kfJ zA@((>+@Y4zjrvw~==;)1s%ev+n(X{()zzUoP#!z@1sMCl#5LDkV^>~zrQLk<%`Uf* zoD-jq2rq!A-~R32zA%sAQn+Evbhv2`$(!6f@l&yKr6zHMoB=n~*X5h-Q{@l)hI~}U zfA|{=QJ?eua06x_#u)kwJKy_K7;l2ab9(|W5bMk@<#^{85N>!M^lhjv$71)L!Z|ah zDL-wY%b`5dg>1=DUW{}x)fHooGqK8pnycmJ988yD8df2NKt-_03C*AoU0O6E|jknd9MZ_yku#ZJ3qC-imhvW%Dh+?pJ2q^Ait z_xIEb8~yg$VdcT9X67rZuSENraP!*Ho=`i{XRjYo{)M)^t!-abzV_FS_t;yirFnes zE?c{bZ(+-36K?osZL92l5gwns_oR(Uj&@eW+0zf*VPF5+H#J98-}{lT{DkVl zjY@9v9R=pSf{!n}_`E%+I)jZ_sz0c0O`1D&yK2}z{m^;!PqBNZPd$E*edgK6T+Fyl z_7Sz=sM&)*x>a>QLb%cQSM=TX^t9wl`qPj%yd&hzb=O^IAN$zHT%BmfKy-a-yFl2> z;OUAhu5g$_H%HGvKMTqmaD%U`t*!MPdfMRNfRi`O2ZLXzcle_p{b;Jd#>HTng9;Bb zMlct`f{(%ZQ!s6K0sde@62q2P!w) zh~^XTcIzq9OQ+I21Libz;v19?1AH@o!PRwFKGDN>ow6@0 zACvEhwkj^#B%7sGzH{axwaRyZ{C!OIJYRnCGt%3(TT8a`Dkc={laPH=SS+1luwS^*90&Q)zzuS4 zDt(Kc+y!hJc%SEG?j7}OkT)JT-~RTuT};R0hCcDQVHv!D2l~+y_F<5`EJFYFIX(1; zw|P8neB3i?#S)u$y>N5gmHKYfV)2r`cO+e&8cpy!{DlvU+OXt!QVZaIeGBu5<}QzD zKC-LJcjtti7q1>sZUJ(NoKIJeR%1K9IO_AClbm?!-s75k->3LYh2)oVqP73Mw~pDP zcODV;_Syk*tM9$vPMtqyg_@(1DgFO^({3k6z|Gr|4{x88T_e5h4dLP|=xIl!!%4<` zMOb2=H-*hFYaRXVOOjDzyNhl4iU_O9k-X9U;0s3PF#oL~eUWUJ)tc|T%btAjjD7y4 z&)Ng$kLZ4hSMhBd`HzJQ=L3*#*DilL^={vK^<{mhu+8ey=gD`VI)s&5T-{!3W}Q&Z z_w%26L^;wAYkppaHA^nJxprlm`-wjd+@!`^Y9hGV;&4+^T4KATr&aFS<^0F!YRC#? z4t58+6M8!D5gegYF@Foa3;E#pqt**~)j?Uqc`k(;^t8gF0_z{>b+|zm#VhZJbMd{I zsq#k*79SclpcpUkt%c(Tc|%Nx*baE{xWP7I9hrd)^1d`$wt>SS`SA!_h-LbkvB>FZ z($Ub~cE9r8(o6?FLy*)h}`d*qJe zc1HEm_tmSOjck&K&h2;K-=sFwP_1gy2zShLX6^xVE16ez-)%=!_oYSkF!#Bf9eh#D z!GBWY_k(A~^*zlIv9~a1e5?+XVhU%lKwz_IWlM8Xb zYIMHw`YZP8D_^vu{WYp(D;Xh~&~A-~R32 zP8Ha=+y!Db=%ny6J`Cy!1mz95A%`FwH(&vcOB}WK3a*>Kf2^I!uN`9!#8VscIsQo_n&upW&4#A z^T7E-cBkY3HJ;k?^}BLssCP-Mk2ooL0OZo#cV6+Afd;$h)IQal$#ZrA-)(qgYcBCxo4;gGJfGB!gtUqZ}A>1M0#=7lB{Na?r4Y>>826=-V;Jebyiw^e-fE)BQaDu#HUymEM zp>O#XIpPUCdGeUTMhxQ0W6H@J**AvRx}mFVWp zn&-^7TaHT)dHwTG%Ac$_jBJ``9=q4>Jv}a;wZ7${F?m3FXMEeTT|U@$)w=9ZPEDiQ zJC1bNXP&s%9+V!0U#%%?fv}dKI_4vyQJs6WO|)jJ4s4zB>u6YM*}OZu{mRd`sVv6Yh1-p_~bQ zo1s|WPLz&D9T95X3<~3~ef|YK%OR`YHcNd{ZD0Jt3zDC^obFbeqHj;h&Oyd>$X9Z> zwa|?xeX94-u6%)lltfFFy_y`q)iNY+$WN;Z=xKiL8a(VNhy{-kZ~y!CU>n?@{TMq# z_DzfP3-Ipq3-YX|w|f#dozCyXaX>rLD z@Ds$0$BJ)*cnL1M`q0wZ%F4)x)?Ail`u@!|SJs5zcHd0Ie zV&}iQRWoi9}HWIO*(dtdMkDSn5@wT@p z)~d2L*zO#We9BdMv9_y>Z$mQ(zhjZvLq|Z#M9_n;3Y^NGvV#K;UkEZ;IhXJc2P@f zi^~_t%g=SV>F({aTTh>IdC>TtkYC^b{`aR0Zpiro59n!^k~iQ6zd+EwA+Ny88;>3E z;>qKN*5k#uL98HWSaeR_^ z#vFpL!}Mz2bf0`%#NM0r%|miPPy9dB-3hp6Rdpx)m^frINhg|q@)$$66R|Z2$P5M< z3W}-%s*17ZdAd~vMHLi-Vu=FIvx&9?i1RFQNNcwVk3*uyjG|FVB7y?yhlz=4heUI} z_1o|L7n`^5Ey&RA%dKbEKEpoy?6cPTuW9dd-g@umjnBVqcujo+?PZtMcgQQwdHsua z4c9$?Tg{`^_Xf-MT~&7DS(_B!-l*ShTX*>Itn$CEZpZ4K()ymjRn_;^8`brGXT7U7 z`mR;Y-L$+wjk{{U!Er^sNB7$`w;~t)!FOFZyrFV(zN2&b>O-4X2M^D6c!TyC8^yzMp59^PNM=-++!wZs4Vo!1R- z`RzS5&spmP%mH6|ZsnqV7pmy6e$KGw?843I^?Sy(#%5%1M(fjh%=9)V7zlps_uDx? zUdM+^UI1<`E!?cDJjBw{ne5He_FUfV&7M7bY8^}chE>@n?@><5AKX}XmTxk7r+}Z9 zbvC`dNqocJh=qDv6CbU&H*nL(J;6@=w7bVPg`4U5o29j0;E4zPx8dQn9{OSb`9BS( z9{0rI%ANIld)v<#o;FqA$=h~T{XW<^^&5rt{eaD-^Oqf6rQS_jQMss9_1jCeZfn<) zBWs@U@L^|tA8FUJvLPiG&#d1RyL`*S;qp!8?`%1>zI$Htj!SBdnDt(@c58QCfAQ-2 zhFrzX7gzkVyZoOEtG;4ueP{LZ4dpA=7`B};T(z_2fG%BE^B}8+J!Nw)DjRW0`HK8S zzq@-`<##V#Rd&R&ibXE2V;9#k{r1jNDlWRDz7cY9*|JNV zuQfD&r{l5>C$;$hqWWHlzAmc!UAUsw*wmPIu9{Q7M=XU%R5^oZv=&cT)a(+>rTeuf4YUX*1!* zyE*b>>}K+1eU2}5zt~zr#Z~c;C=jN5e_O(lfjY}7`cTl#ksr6Dz%a>ZVXjorsi#MEG-wQiyUcJwG z^02&qS9!&v^7j{2j%jhpQ*E!PK3AMxrPg$=tablcUtoW(EJ6m}J z_g_=TH`I0O&aU;$%WFRJoKuH&HRko_o>n$uL5=gIVd;V=59iFU-{-D%KGj)hb?v0u z8}fbKb=U2UZ{VA~5${-gBVR2iVBXSvmb|@O2OOpF;S_5RU<|&pu2UU5rHykve((u> zjN`z^Kdx|7bJ73#p~F!%KX~5OZNtuO=MCp?o*K^IGBsSZ3?#JFk8Nb5u@0rRFagsrdLOOErv0{hpJv556@%@*WLULy`LJrq@^*aJwjIOfy7r8^ z&iLAWYfS2Sb1Khw#+k!Z(PCFU=az~c*H^CZf}-V)Dr=Un7&e#e?%ZBM!phc?nTvMqZnAq> z$>H{Dr;qI=*XJ&(?_bZIUvhYQ<(4W&o-Wy5RQt-}dJlhZDVbf|N*%B3?kc0>rxwnZ z)%%ymb@?Sv8FtlsWxERV3+ozqx#FpN>bZ8_JMbFZ$P3Uxr=Bu*cPjqSFSDy>&nz_fHP~T~q)5<1g^E~rT^1Fo9f7zxZYmM3Aha5Z{f9x^Cxivn1S-;K(j_`?$kOk!SUb*7m*{TccPX{pwf0s&w*I&3A#DuYUEb zlVWb#J9NpXkzv0XH;$Wp=hkl$z{MSR+|lNs%tL+s>tAohT<)EB-Z^~po8KJ1{q1iz zT=?#^-_3zDG|~^;_-$aey_>eVJeLtbT!F<$_^}V*b?ukW%BlR=g{s~2&_H6Ci(e($`T4eV=$gwKUj|!Spsq^Xs z4;VeiuRr=x!^11*|L8~jm-e3Y+FFymz1~rdZzir6t6D>I(@i(E-x2dYmv_DEU2Pjy z=tapx_11>)vJ~d^J*>Fla4yF;ep*D-|CUv9M<-aDA_;w;Def+97zrjExlQ5zx>{T zazq{T-J*7`jvsyGQB6*du47G>>-^Crx7PF?Tyv8O`BV;r!$YRGYmWc0+IDPIYCLJhGOchu;^ocb$TGzqRv9%UKZET+7*s`Zb z)R=8sPiU_C$RldM#(k)9y1tFqm}`u6JkNeOy3}=O0INsTxe*=V_ORiA!X;d>fvZFyK4{H96zBA~@mpVhk9*5htqCg|w5np!mP4p|9c)!; z-||pT+bQkRhZ?Q-ma_93tI%7m)VWRdeV9$t$2WEPAh`)Sq0L=!p3dD;r5&H%w!fv? zwYs*ay8X6Y`^mwoN4choCpPO}-St`hOv&R`$?>hz`>x&S`YLzzS8KlN{HE!$S^l@S zjn94fS#7_+rj9$G{%yDCt>S|JbgS9ZpQ5m+`v+A zYhrI;B?UI(!|~@7SmDcA4<6j0J=qR!BKzci-P-lT#)?_-Z)@QP@9?nkR9$7ebx{?k zo>%!X_4&2d(6-oBv1W=K?3gYWR4#3273&dOskORlYj&+mOtHUXx?E7l+xc3rxKq(C z+f`e~nBu*}Y^T1k&i5tvQ0u$zFW02at~!6=bep~7sc{)k8<#$&_xE0F-*Z;ou9<$; z^Q%tr%;?hSGkyNN+MlYN$zD1aeS?|kAK36dFrSMbqK+@-I4qj`#cRn(6b7pV4ja z?c4DhoBOcC+e>dW-B3EiHvQ4BPnIL2d-~#h(?{&gv`*SK-G+T58{Qods|FwUWKWz= zft|j+Z913_&sP^q!A;^Dd1>}WP8MCr27Ns#e{f@LJ#NH($)^Q3{4_S19ZpQgUMG*{ znp_t@4(?JC`}CMgZh)U?{>OY!FN@|AS}A!j@6#7^teIzJtIn>X_WKiO)wxp(Bj$+k z+kCVf_{%Hj`LfE3rpS-Vg{CBzntZApt0GsL?b&MYU!;fuQXG5bbZOhu=Voe~Yf`7p z-L9+ab3E-{^{Q93XSl4^@O$3qfpbe2@qy)g6x;VymR1}2&1F?uJ?%rAr!#jTFC=X`2@94yJZ;RD>{Jp${j8gA;nwP!u+>XM1c!_9ZT^PM&a6F;rbr;+u< zH=dJkYu%@{IO0FWH`&F6u2aM-Z+qL@T5Kpjlv@!)%Cn@r=RNNkuD|~JR^(vFDcc}^ z8TtPs|D2w4ytgko7Jn@|bE)*!mZ`%}g$&jZ_RVqz?{GVVwA`9bH{Nu}S;vHw?f zKStZ}dHXuY={kO|?|u92Z+;kmlAYi`rr5tXCGGi)w)M+TL(>m`_`@x(5!;NHeVt2N zebB>lNA%{73+lVm)AOEiGxF13+2#i6QnDe>J1Kv@5n}F=y`hu(`5SQs#NtTk#%y%0K+WKMXS!x%Pi+Nq@P=Y<0$! zarSkdA+>Y1J*#IXV|1OuKFm~Ri#ujIE*6Pz5kJGe?Y=lR)A8|Z`guO{Aim4Fb{#*h zY~*wvnxAG)-EY9bjeaNP&o@HM`?EJ}IQxQ(%^#mz-#((9f}d zZSkzegwNIGo=U);hMMu#=3fzj7jDgJ{@A!hhu@AlOjjnYbZ^;;(^s$sa?hS5S z8#x}Y@!380C4>FBae3^|jrX1XaqNua#eQV8m*1JT$$K!Qz9+lpymrQ!d(3qIehjmn zzo+BD4c*vqvpwqtM!4apz4E`mqI{t%Tdqcahx|;+A3FI>9`j>jN^@9|*_m*|SBkA+ zXL?%`pJ3d^1V6DcR`sUc72<^U5@9v?5EB>Qs;Q;99PdIe;Ie`Uhn7mTxYx27q&waIz?f__2ZmDCL zPMve=nU3}6_qA<&o_DXkna0iDd|J)lz|F`{tKU>8*(67k@<&(isds~t7l0eGN7wg$ z8m#cs#&Hw7ll%$0)8nT1&ti9ih1i~P+_*oS=+in=c_TR_c^AI0IFgQ1e2Yhp%XbmX zT`OWNejgc#Z->X>6?qKLd%5i8H8QM@ov=+7w9B#7Ta)s~miM@k6PwN6{CjZI+nkwj6YM0`A@_0(=62-neP8OJ zT0geme)|oNdCX(lw(r_K;t`K%+i)-MY~CloQ3^NFqx38c*|!~DMh?kHFT3O(Z6e?H zqnr7*zEzwem!D!DIoqkzZl;=VG=6X6=#SY}#xWB43uf$VCtpFv%x#*NLOXe%$3FJ4 z?Ke4nN7EV{-#znf=VNQFk2do9);Y?MyJ+^{$7jyOb2Gme|_oWOWKppSEU+$7HF?M;sx z_hfVAt$kOBuEEy>Ykj2eKl;Wh9^<#~tl~Yn(8ipD`Ez+SI#*wbx%iaysdF$9UaH9( zTEK_ec`~iWU&mlh-rhG($NwDjO)Yb|!HYFaj`>F2OxNfG&fL4ltb662#$($U$BXOe z05Kt+kxBk8+{ihigSk=PvGX209piUweeaIn^YDj1eE9jF|M^Bw?^K!dHOI&%(Odet zHyFvd?x}6x&bH^g%ieVB(`t>nIX8X=-@=&40N-X({?Oj_t)%`g7qFE;%HTkzyN;ogzL z|DHzo1`~Z7W9;`skBnd4Cz=_>^*MqRID&5f5vZZcyMFRlCBxY%}nu4 z?9Klz+>m$kn${BFE8oJmyy3`PDE`}LhwvMo;LAEiYf$kD7R~<{znlUa=NO*F??MCE za9lfW zT(F~d9b3Z>@QkjZC)@{bd|R1aMicb$Oui)YyzYv%D}QvUFhFEkTwyPi{FS5`6MBbezZ=nU5 zMqgNRANSN({|9sWHU`*5Z#F|gYwUiF*jfhBl#jaUU9*kG99BYlNusqxM>zLV$h-?8AyvG9}(l6mJG zbA51we|#)#*WxBJFdJ@SU&scT zQH)8k&F+vNh3;ps*aH5O0;}Wp%=ldo1NT($G(048+S4U)6&Z~DIM>HF{I{Ib9&X<7 zhBpqoFWx=uys*A`uyynB{tvuwug!r!_v>wr{v21}Mt|C54DM+hj;m9Qx!<;*XHh#x z?}c`uA0L#xM@Ra~n9&QJ)vhrPHcRo0YU@hq2;+3G+|RvIa{S)Z#uwbc>iIj)-}~J` zewue8%ss$OzU4ypCgqQ81~+oJ@=fU1+Z(iIBgb*WPwVGz?ybGSFMfdHUihdlYk2r4 z*1s7CKgm7mKfV);!2^BDZy|qZA35peC~`-hQ)a@DYuN&}Pb@)4TMsI>F()MV79Xj{ zjT{+SB1gHed%pIy*R*dMz56}y8eaOcm(;onoHG8fQL)oFji$;ghk0@;`hIIZ`%}|4xUJ z<>+WO%k#u$k>ixvu!EQMsWq$e348)$a9=qb?-<0FA|J6g^2_o6$pCpG2j*dSm5=5* z-}CPGG=1~UZ;o=>uGiLh^QS-8kwrSe+^)Q)XXclx`J?Ebw)*I0aHehdO3|Kfpl4uK zOhHfKwRa?}7l|M2-tOo5`NZ;;&hdZg5_G~>H1^Et(-_<@b?!aW*c>ya;O2!dd|~;K zJBAG#N4Qy1xVd2G1x<&+4a_Lkc}>b6S>ykTZ)U;`4DAayTpJ(qre@58^>bj+Q1ezTo-&&W&ouRqth2dtS( zlRNZ%I^&Tu^xHz7Q(UC2K9t#H((%YJoRDwF(1|TKCn6@K$K?X_FW=_3!&1;8v{&Pe zcJc=Fo17cFWX$dXm*^k5?rUt$g$}N#YkUW4Q@R><@e5zzWhUH2ej{IS1JlN2d}yK8_nun27u;xXJAZ6M z=h;&7 ztoCgyaK--7A!0*%#dy#iR@iHph%YD?*?)_nr@w7JGTQS;T<3c9etdlc2kp(Tu|Ih2 z*~C@-crvcOcAoyA>s;IHP2tA+v~!o7Q+a~(hL^qkrG=a4HXm)$>{>n|zPsMtpN^eFKpnVonHNtlhDW%iI=RC9Y3=L+V0hwrk}DM!4BtxOvIlaKo-m%HKF{@Q2T-zztb2XDN4%Ch^n8a|3Xr z@X=t7EfR0y7n-sSVoka&{Ob8)JA50j>AUe`c&BnM=Ie*WcO{^ceoat+3hhvXJ^ z#&ZK?nEb(wn3rBRFQ^amf&10`s&|Bg8`qQX{PERXOD-bEZ0x6s4xg_a)fY?We5v%( zH>Z6ow#In+KlY2P!w=kePWZR}#XP04n6osm*86GhmlE6re|#qP-CVK49*}c=hi2on z@996=xg1Ax_(mJK3$`D|vj&Q;^gL(-Bj`Mi8)NTrbK$P~M)szS!-`dvkEy)C`8(j| zrIk;6UfG+`@0NRqcvAl011;Hb{!;ACzZN%e0ylUs7D8is5wyW5qz^+I{sQ|J(nKCVNCEW7aiQdSumsa9PE(3 zbt&Ky|TU;zU1usPRF+P9Z&iXZqULU#iabf2mKM;us7^QaKleCXUWdMyPB<0`uK*g z<{JJS+Vaowjh~QjE8#`>0;BlS7snL*h%W2T_uH_QecSoolY6+QHH`cR{qQq`o#2KI zQt%XS*&ElfTlBL2_-*hX+-MIA{U7|0XZhI3Mr49MCC~7}cM4`)lRy1M=FyM8BF>^` z#C&{O@s{{2xal!tTTG#UegT@0TVv-p!3{kS|7@IY_WOeV^blRD?)j8BtB-*^n` z*23|bt>y9k8#Kr~S8sDdBhM`NE(Y_RNw|?0fSZ@R^u^6jlNaI>P9kpb290L()5dWl zwn=;gKjInlH{^`JL}!viv{ca6IWh?s3ch5!uh|K?Qo5e(vr=pBB3V6V1NVJzz+mieuz`Cd|MN z8xh>F)t(vV-CKO6HXgbA;D)`4%`q=C~0soWF%$)ED6`8wp6?hIyn+RwD@7&$lJMGk_m z@XGn|>vBwue{>%`i;wsR$Lu{{icFJ#rN@o;9^r}!-5-YeJ@N$n=W*Q7<*>;o@J%8hvG}@!2wmdTgylpu_F+CE1VI9DW1bh#!odtdc*mxMGcP^!)!V z+>oW%;{z4#@*GF#Kz8#)(O`4+2IuWWvrZTW)WX43pL{+Q?87jD=T zYaDvqh;7W(sO6ja;N&xQT1<+j{3g1>bHmnnaXjZ!i-pBQ&hv+{9jt+mY<&d|%IY4qjkEO;4GB7ay>X zif#Hk>jmWQ;KrEE5wHpHMOV>N=)=yEUv+XabpE|5=%)`@ghTtT!<&7T+=srRyJG8& z9o>&P=ICbA=_$|KRU;DLRYyFWS zJ_qc;A^yqD!iBk8a+(;G?090PjEGbm^GyNYb>qkFyU#ED!Y>Rz^;199VlsNuIK-E9 zmKg1*qmFF9&2i^9?;QT&+oN@`^3c}QUUl`A!+Ga#-|MGM)w@eCdhrYI)3{+<%-#5& z1>DFx^WDs0(<$^1Tf@I$v(QV7&9B8fIVyACzNKO={9zA!*xgEhOq*Z%m0xMac1nMZ z_9^;S?3?3%@Pi-Rc<23Twj;hGyFc_(Tx-6G zt>MG5pX?$X&(4b@$SJI#J-Ybj4?2cUJuO4und-C)t;cKU(^PR}4buwQq%zUl zKSfh^J^Z5U@s!W4@P!q23BFQX7tA^5IwjZXJ0*U&cCdwCbf0x*w#BvNDRHj(B==+^ z*&S={qRZ)eIEwF0zr)N|>Rq^7Zn=5**0=wD_~#$~VEDnm)Vz#;1%V&dvY8*&MvMDL zM++3NaaT!B;I6tZ`j;P6o2u&%Mq$GJj0Jwi3*5jHpT;wYY52r!jKb!~1@M*0ap)Vl zMn5z||L_1E93Q8DPjhXw554=EZbV0IV9R$t=q=cRBY2S~<3FBs(#gZo<|z(8w0@)G z;^CHCZ)yH7-NkQ_J0pjy*Yvn~{@rjhN%kglZSrY+tmL6#=jOr?PX*BzpsB|`2IhAuP|dCvx)kD z*nbUy)hU}Ij)I?Y?AV7JxD)T7sa&5t0G`2&ZTejv1|8^jG(|JdihlA0iZ&_elkJ?p zry37?KH>p9;frm0l1A0fZT=bj|om}}#+pdXoHQ*JK21UKxBTr^& zD1TvL>6XhIeaJBEh$;9<^o==@yfetxovFyX%vAC{Jia_Vs~`RGQ`|@X0Uz;)Sm8$QdeB#zW9X@f(pANVD?QP9gHPci`WKWWV2zKI|$s@px`C#;r zM>EGkEv3vGYYnPIoxLTih6rQO0zWBnQ;)Ad= zY!-hT@5Q_PtdmbZsc?MYaN?X3hPS@$t;2|Jd#I1SG9uwscmOp z8}hE?n(U`$@A%F7#Y=d?cgJj#AwC3oqL*O@Z(y1Jg{gPE^Bu$AeQV?!^|l6Xe5>Hb z&)hiN`sMm=!7X1NzINN~!=L`ujl(4$c-5ZB_@7Xxa?~46-*Qab6 z=3igmNKE;iO~c&l%jUgx!*Ip>UNU^;mRpBAzEbaY-};rdUI144YwQa=kK=|vLv}rr zyt$lVbOJiFHS`BMYJ*m2f}Ua_#c?J3ayr@O1E!>%I>-7t{6c>;XBSpg4qXjL{5E_s z|0-_*pNAfH=y1%Fj;`-bJ-+X6Q|wkH5IGX`ZVw2cxR2hAxH2hHkgz@xG~<| z-tgUGbKph}fDh_@Fh1M$zjOUyJ;Z?rKB3Wz{Nf=!PS4XpbPxHY59LVF9$({+i#O1+ zFX)XIc#S`Jsm2rAbe1;knClh&C09l^?7I&;M9;c!a!s)}7;hugne-UR5?a&w9Lua%`N5!@JQM-n1Q}m-x zJaL|%D8`bTFdn`e9ZEK|n|sRKigC(EIO>R+3%Ic5C(Vh;H(9S?e!w?7gPZ3*uX6Cu zd3Ng?ZYIgz;FV{E8#Wg9$qPSCfg9h_koSDs+uq*n&70o*rutpu#m(lDY4$+=6V2hq zGr|o2Pks$=(G9Q2F`m+a^prvm#RtH5G{+14wjazzFS=i@wT)-^i2rzp|L%eJxi20k zzvDUK=8=zj#58V3-<61;7TkD#^H<;h-uH*^R*vm^-~XrK+yDF9!{_e!!tk*#f4Y^A z-|{EJ$8Y)L8Oo=JkKbCy?^deqt$#9n^p;N#AGx{SZM)-d%Ezy~ol?00*pSw)XFl{2E(jywLz!x2Z-H(=JUYdI&+D(6JLF1~no`J8L-!cFC- z{SLe5Q6}lT9Om*oC*07-J#P4(-d_~Y&vP)j&Iu$-e;(7TP@hH6QsWlg9jm~&x9+G^Z9bV`|+$nyM>w!C% za1YnxtG2Gie|_O=FJ}!mkA7ss&ENjr-El+T!w}qvTiajh`X5Y}|A^E;bv5Xx6+87O z#|^WwH*k{@+*nUQ&gm0*LUf_eJ-=t{pPS5ULvN97_7|Pyos)A@%d1!yXP!4U3(d>{ z%ZI^V^b-EcP3uE04hG@UJ?KdDwff=@lLP!UCS!xi6X%@RaC6Q%XAhtH`icU^Or{7 zk0aCnQu(eQRNVg0l^2kA{`dUxw|@A;DphA&fAZ1pJsWPc32xBAGt*D#LchZhy}{P9 z185X3e|ArRJ$&o-2IHp?m9Q~_UH<@ z;kUpI`w~A*ZQq}Kn&%+va04&NdDus5a*s~z1ij%n+<0dC$^0Z;#n#B#dp8Mv`9Z$3 zh|YAE`37sLV3oZH-C#{0u7^?Q;1}Q7n(&_eKzA}>jQqrtr*X4j!Tc7l$brBOoAcDm zpIUvbYwr=t{)PlFl$hS)~F&luI-Iiu5zl^w(ze##L?9$tR_{5Izt z+_)cm1urcxP&j+ebDp~wH}u^keOFyRgUpZHn=h4bAKZ8*@3MMt&)B{9J?Eqo>zc!g z4ktI+WGnbS$q}HV_(tFIb!-8;lX)}`ZpgiSrF9{EY3s)D%DY%-jrQ~mI|qMwAV(A2 zz#ZQ33)m-^ffeJ3y^(){9XG*`l z{0p{<&B06jLU-{dTXp7H_1kaF-oVYgh8y(Gcap4U8+miIv)-HTf5j_)yK;(^i$C;`MmM@0PV_-G`Al*v=z#w8NOF`p0j&HueU_*cQG@;v2lz z54{=Our-QU&-{s8F#O8DvpMa+QPwGS)!!S2DW zTE5eqnf0C8;R71S-Xy-Em*@@8ckJ|9Pqqe@(1i{W--u1_fB*Zp^)P79X7jbsTr9)S zBj<4Dn+tR$ov45K(8o{z^iK~z@e@DM{A;w=7Qe~4Ys9qrF&;iO9Y$B3amM12zCEJi zn}w%0{~vB(6DF>{`l^->fE)R?tDklC-EbqXJ1KwsX}WySzw8gY&hF4vutT0;%UCw7-!L3{$S5yh-t$|3KlVFW+i zkFQEjA5^#XoiOL4SNGu+Mu6&8mw5iXU-Sx)rxj}1N5Q? z^}$ZL7A=!+;Zr0hhky9NmT1S{XJg<@U&e&Du3xuKKCR{;%1_~^oqbN_(`vo2SdLw@ z=3YEwoq&AW`VH&KM;qbhoXV%shkW%(`7>^Ex-Z;Vn?{$I(=~Q$B+fef%(nK4e@nL6 zXnLLxg=hExk8+#niM9&f#_r;UqCMKizhE2eqm^^w$;?gQXO2fU=sP?TpTVken6Gvp z_jIkc+Q2g$!tbLVHOfPqm;dsYZ*H>5CzJ>A%yRJbA>Ges5@#gVu-{7A2zKJT-x+-? z?0esJ2uGBjO);{;g^sO)XOnxc+dJkQW)_Vo)%{Xr80nhJQJtur*o+h~x zHC^UhPd{`-XZl5r?)t$aG(u0?nH%bPpdZJz(>ILqE9h5o4&0Cn{+KI`npbJ&`cKQci_z>R0)r;XbiIYK_y zt6%+^hMV`j|9x#<_n?ChYH=a#Y}qo^v}ZcQHA7+0)VS#zz{3mjGU=CHxNgKoYz)Z<1@#kw?Yyy!=%2Sa5=8Fk+2|+!dWq2hsm%8eK+b%~tSUaUPx2al^m!RkH9)I~0TQ+ZLzTP7q@n46xzvFFf9hi8Ij^`K2AMzo{E8i)<5yNj+t}I@T z%4q+ptDZSrb>%aLtFNs3>MN_w)x(v~c=~Wv>6$C+_*GZ#8LqzS=~b?%^HrYpjOK&L zu`185LPyJMp*2z0pZ0jlNwX z#^HDK)#yC(OHc6u@DdHhock(r{VC+#w)UyVuXWs-5wgU8cMoy1JdXH>9P!s9TX<_+ zatmTA&&wC{UJF@+Q*y)45bIcHW8XFE$dUE#tJi3|zUa1lSiSP9Va>{|!^*Ww>Yc@n z!>Za}wPN?MY~|)*`Kn86{{?ma#lyzd$%Z|i=s zr}=(b@&cY4ZP^YsSzA69JwXS0X1W6n(FWb{MvOjQv{4*~y_B@e{!F#|zzDmJM#k$t zYz_LulQAYXJ$Twj@jQG~wwJC_)63hoZ)@?*+I4H&ntOiEB;tl&U>ytGkeQiqBd3Co za+Lgkx=@a5QSrt56!Yev((-D%F5K1h5&0FntKo&ely^YnvBZ{r5o;)pWjp(6=en7S zaXZdGlvgkh#HSYfniF^L=*Z|?u@CvuMh=NxffGIj%Z4(S?liJ@_fxwv>NT z-^iQVTE6|3U3Km)!{$w&uKMa>^VWLDe@po(Q-50fZyVNceD$z?(?<%oHw~LNezD}L z+HHPy*}RQ)efgZ#C;PW@<*H`O_=EHmy<@J+^PvSy$y1vbqZ8P5IPq-cmChrdo>y)k z-Oyi+_W0qP{z9|RGj!h2y=jc&$io34Tx=lI3O2IulMXv{bH)TjQa`JVh! zxM6qXoz6RNd&Q#Xw&#Kw&n^DK1HRm({E0=44-Je>3>tew2f)a+wZ?|;3O9Th_EK(o z+tzKv0Z%w!IAz}4qR9#EdNvu2@JgPSd}dx6J>^#%OTlAuo|62I+A-H?llJ=Ti~baC zt$CBvh8Ou(xS(^%I@yvNphNYCzjUBjQ$CAN9=XDQ&tUBdzLL%O_RYU9+-#fT+pjB( z?5^DD{~9)Ix}z}gf|A`+3qwyUT-{mMd}P?X;SHPcCKIenn;LFs=qeL}B+TxY_eD-LIcakd<`y!qD$Ytg%fBXL z^n-EnDdpJtsq}<>`jDQdQ{jdV>2b4lIwqc~Sa_;%Gc{G;iJ#h4nE2gc-Nri#3(qUu z)Nd$meA;l{mOBbhA1mMQ&k8rUl)b6#jUO5|ZTwQ<=}!t{JBICb{kE;*bGTXEaARE) zo6px_FBH!a-0%-zoo(?f#!04ghjD`_LN(6nxPpbPuiftNBjF zzS?}oT=u(Z3{eWmhtAFG_m9~EA{Ugy76 z{@6FG&8@?hZSSl$TgqmYuh#k-t%EUt!Irx(S)-d_fZrJ0WSx%qKuiHU$;XQ&*iCw# zZeTmmKJ<&+kJq7JYUlgy@%HxNqCZYA!VKEeU+~P2VegHTZ1ADU5nnWGzkQ!i{zHs; z{d=x2zh+mHsWod?4Nt#fPs>Na4IXGaDSzaKe*iaZJs%;sAx|(OKPV?A7hsM`ybL#g z@P~geT=9%6$|rmLFz3WMjo0)EUyYp6mGmBakB@LPTj5vqMZ13g-fX)U-kHnBC%HT^ zGt7`JcEniZ-OYu_Td}w7znlP!CnzbxeFy&sz3o z)5fn=9P^T4^VFhx_NNt2?yNrlplr>b)$v<~wHrTL`)?>Ly<%9q`NG1?roz_tVZ*kH zZ_19*r`F%0EBnrm^fcUImwsj5UY9q_+RL9Z|NESpf^8H z3GMrHIhNWzqPOTBV}KDhoW7EWL4Q7f=Dy4w!jPPyWB=oS{BGgC{FUWPhc#=VP<4Yv(z*Z))D=ay>su3^*qKPcPs-SXdl zui}|MtoqJj?WV6(j-YY{8~>uP_TDOfH*;Od)|SGL@4McG8}^;8NgRQG;%mH8_z1oa z#pbY!utP79Ei%gHh?~Slo{7w|S!`EdT&FnKm(X_nnzZlxh&^-IQfApz8YV*9malqr-oR+whjv)tPBD8`>d{v8mW-HFa z9E|ec=_~ggZ|}U~xSAj4+7y`KE4tQsHNGn5vth+Lu;7MoMCRygHp;e~f!|_u*mVmin!kE!N6SHGGRv=qI$bmY<))W_uQLXPd2ocQ(?R z8#V`T$hWwRY@q@Dj90GFuecRI=mvFPoL3ynwtMn#wa>Z!ntofG^sB}*_l|#tj&Kjd z;zjWpnKPejUfH|`EU}^V*)yMc#o!(MRrMXUr#|&5%}-mgd`0Q3sTOlj3T|MYeu5ja zMu){uqifk0`42LM57uqb53xD?3jQgYJ@&D`-ty6MR(uEY$L_#SWZ6CX;&@-sU3+UT zyoVsJpY7W8LlHDR{p2aKO@Mt>h$agO-Rx+3$To?~hC zv$Ar5dG)ir>Xj?a)1J}%W$V(!k;Xt@(?M*H=j(A}o9&^a;ewrH zQ|T!(>ll9-EqnTePUE#Up)>sQL0s>cbK@n~_kE)~`qMMUO=jc**&1_%)|Rt1)?moT z&|S%;{qsNnpz+~(&wFm0*IH3D5z{@h^unf%o7!{W-K6}%4gD|^Zs=NhPrfRD2R+PJ zvq5x&b%*M#p|ZY`-G&)Fgdg&Vp7@vf?H(uN+x!b{*#-O~2e}Tf#@pM^aeP){+sKX_ zfLwsF$YaoT;u+9ce z-SCDtzP|O(&zFz&jv%av7ZiDXx(!a)8?wjd*d~M48IV)otQS`U549kv;=+}Q80?YF}h{BjTYpr6E8aJ*mT+|b?{S#lX-#)tbm2uUc9;}Jzs-!(Mn#b=+bZLZOt4+%43 z89L9s$(uZ_JQZC<&ymmI296vve?z|-YvylYUXFw;i}TP!`TXZc??wK_U))%F<;LN& zH-2`w=?h;RZvN8f-B33A#?m*AfA+@D3|}Z)MhC$r{rcI@ey-&qK3DaPpS^MTtH1uM z;mfy-zPqK3G2sE(Cv$vExpA}zZs_J<$1#P?gnc##exf^I2kp@ZokFvjXqsbuDg{Oy z%QbzSzQ}{V+4x|Ff5~>kuy?k|m@%VebOTz-&C)M&>-=RrVy~_3lv6X8BTuklhHqk!V1}-S8NQA=U4Aisu`l@4V~74DubxRfEgqLk<7ePG z%+QNyVeabEOE0M$sr8NPS}~3nJn#AK+bnN?=R2BxTvz9e%3Ocgo zXr-WWUuWC#_`Urx$NTG@Q{YG6_#4`>@$N4#$j%s_d;nQvSHv}u8}<`!*dSQqvu;0c zYsHgG>f4sv8ol9$Ows3)@@Kr_FE)biNM3-vi23>BWSFmo{$gvq5xdeCd^9wO&4nHD zk3NqreJS4I^PG9+S&hEYQ}kH)pzaHP1Q)ZlNqacd7v9ma>;=BDGi-?Qzy{33*XnVD zm+S%iCKpIo!VUdtogsPF2fT^FJrmz)H23SJ0mX1IWdX3Uh?ziH@&%G4h=K^ z`>y{|YxksyU`iBpp zr&?PDANu3krlc?Zd49CZoCBRnKFF3BjLyOX*s%@Y^awuq;|m8jZ+i0^hx2!w*KlLL zYn{1(8n;7HJWUVw%`8t$lkp9 zEpKYS=fU21hi%uc(QoadnRk*VlXzXm&S zV;leJ=+E5vnc=$EUsw6orM3RE<`io^j{C^z`^^vX5I)SrKj*_gwKi$*oOuK>s2G5a zF|STO#G&S*6VC@TL=O1kw=Y;YUjzKwr_mn8!F~lF#P&s z_iwuEgcFW$^r0Wnvd2zO|6nJWaW3uU&Cs4az=&9it}^G$KC#JnVPW)Jb7*B;_z)d} zK4}ME-}=_y7mfd>a)WO!-0Y~eHluew&p&_k{cF0N&6<=yW0$Mv-|&s{?iAd>0Y43` z$SB;%)1W7P%6EVr*rJog05AhP3O!2RtU1uu?@R6XUw&Iuad?WajvPFClu?hQ+ zH}1vHP^Z9(d%|_{L}D26ll2{ZCfn?r`;lcbNdA=Ba6>PXS^jqLKyJw+zQPPl;CpbA z^O4zJj%~w5bS-&M9Lw=>zN5dAKkZyI{-;guIaBT4{aDf`JIVf$@BJ#jCHA4a*yp87 zM)4{fTu`!l!OjaB?a{w4=#M}6BnA|3SvxTDRe!bRPUY#*+7lF5FzO6K?8Vf^}|aMhD%FCzJAL?B+`N^soUZ{QVv`;u-QPFF?}dt>fF-ooUyen3KpglY$*GPlxdB z<(S1bo`JpLv+&c!5OQjXG3g^Q4&1;RA0PhEhuvXoV21yuPJE5VzWW1fVjbU?h8yb# z`C53YPO;CXsI`$JlVgHiv4I>EdqT$H0Z!nh$4qZ?w4wV_UIvQ-Fq% z^TDhSrK99Hmk5+dX2B-P2gzPrODZ=$_EjbNW+jhvs}y^T0dG-YhHM zepS6s>$^7I{q&oKllDjMqwB2)U^CcEb_0FHGGfYA6$8ReVtdE=Mq+9)F<;z!yy9Om zhU?)3X3$OStZ$e^Be62yU3+#IKlCl{#J0h)`|%&yJpSah{>HfYqHxX^QP4~7%(K#i^o8?^yg|0rIhSvjq8D8w zr$X0w9`s|o=^@XEmd4#^tgUael)aIk zo0LD#%`TuBKaD?xZ|novmRmDkwuRm1Ly=|l6cCJZE$4R zz4Qkw+PDtSkAiXFaCnID=%)w`qJAo(dJu5z=6&(gA@=^3MTcYN_$)$)xS=T)Y&%IsU*MQIp_^J zV)jn#lwvO2Tqv2&Ha!DB@RWjv+Vr(;=hK${(U)WT5J%`UZCoz~H!k+y8X7i%?HVtR z(_1+%zX|_6K6>25M#TOEL;o6CgaiH=zZRWg3r5hJFMzlBp-uXQ758R$*g${!q1WhF z@qw5?uFpIIx`?Uy8s5j}i{OcUvS8dlsz0_DX6QotksgHw@=w2u`S@FMS#ZN9nN#pQ znG-YDXx@jQhP1K%IaV_~xVQ0lv^a%diSLl&!tJz{S zLl?RduT$(R`bh3KwA8nv55B5m+qK4GY@S1%q78l+gK_E?_R*6}lU1?{Cn>>HYUd;0 zYVs_$=R1l2(3XyZ5B@d&L_e|-^rU?@LpwGEPEsP@_-9;vJ%8@Qe$Z_S?C{IsMn1xG zc^>rioNP7Q?zw)HxFOfZ#vkE3$eG{~nL{7n-XPQTG_3dXh4KU&YC&^M0h!~6x?#AnhMEZ`U3#Gitl-X^&RTh4bRLvVuL zWX^N)-#rVv(%TsJ2ye6_!*cB65k>r>rqAIp^3O*lpN=P~hK1&eS-_V^L>INHgX1V3;==IDI(fNiF0$Rb|R zL;N+eO^3k*S?4$78=nDx<;L`lANauEgOmIPGpQZNKYqV;M~=e{T}KY-TRK(FPb@&z zJcs9}%jpGk4g4K(=#TPGT+9!{2ejvB^B?ScMzYFQ&;{ahvH&Y&(R0v!$CiKYS;!Z8 z)F1kid9n{5d^Yqa*K)iHc_;twfo}Ac>)cO!*ZJ1BK48Hy_Q`Skc!_te$2+_vpT@{m zz23Yj$o*TZ|cF!y3 zgAsk8z2{CmiS}qpe(?@J_|x>9cI>}c2b_y-psi>%w=ohD_qAyk(CYdH`SGMSC`t z9;f^G5=otM*-aRK;i!aGCoys1NkG`-~*~cgJcRw*4UgH5-aUYn9e8COc(_MJyoIHa* z+}An2hBe)u7ccmtXh{b1r=LmsW3$;%KBRGz5j-?z+hS$DCcFeUc*4Jf8@hm;pbLE= z2W5UN^K0f~Y>TDHjM$ETC7<}O&^giV{A&C&F1|MXrRF=s7tCqT){slJbuz}3n3CMU zO=@_`*yC?G@3@-mY3mu|XL)Y=8%-SdY|d$?IES|i3^=BCJom64nMOm$9TNkkoqh5R zw`_mz<2vo^EB3V^o8;AXj`w(BbKJ*s(I<2w8*_8nCpB3mxBQ*Rb!4%(5qvu~AvyZ+ zz%}$68J7n%=Yy~2*5m|zOUS+)Yp|n#@_Grk)kRP=3{+D-`_^X~>%t7{i^T9d(9E?Qw z(|7bKUm$*p^Kv$9I=rw`c#da$cJ1j*wh52fZ@QH4C}%~^Bj4vWs_*cxpW`!EDIay)d1 zal}r*1D-~%$pSfMCmaiIU_(22KnK`CL&w;S*q~rcJjDMCZX6FD)UJbfI!Ax#6#Vs= zF$S`WcWebb@UO_GzWGr0*>dN}C7Z<8hE4vrXEhe~0LHXKe{FB5SdxEkUc%fTKNIip zklny%{dnixnAthk;Tv7d?!YDdy2icXi@##r{8!f-4@^z^ANulp`D${`>}#V0H@2N4-0O>g?%;zmc-!lv-rWji1RaVH;O0LHlB-P$f9SKGolw` z56Cr);O~C5K9G#ifoSLb5;S&P9sxG#dG-Vy@s#~B7JR`U{BmB7)G;>6oWr%{FTkI< z#Ml$~PU$g(KYa;yJf~tm+rdq+10y|#Y~%fSNjt}b74kh(nd$sEZi2I525uBMSK}MM z5M*vo~T5`U%a^hmDn=qXW^K?Bh3|I5x`j(@V*3X-_`jMmu<=2jyt^ z{o)+B;P10V^3AX+zoQ-g;iLPo>&8w7`Ed7@KfWv3CU@ScAX9v9^kon53|{y_Y?9dK z(8CUGdcr&e9?+*~LXW@<{lZqmBppLvigD;yJ_A0wk6Z#<OdYoMHhw;qV#d3Hd@5bK}hw$0hd-_u! z{9(_-Cb2ba3EvAI=rn!1UXIRMBC^8v@MGyv_~QTJ5$w3${5!0(U+kN>lRo7e-k1L9 zA#xu*L^eD#y3z}5Da_2DKW|uk#^|?B&pzj@@|mt2jz8}Brnk^T49`D?8$8TB3mREx zt1mtSe_gy|tq=d3y&#M5LNCGtjKHYe9X{xby~S8eGpyilj~Sc0i> zn-nbdb#N7X6gw2$s8eEJ`a0N|sSch}2N$V>sqF8o*4A^v4ZS7CSI9hjLyx05`4>yV z1U(=BjmDMyjqrgI9}3Z8)PB~ zZQq#5x!6?PM+Wgz?2g`WA(zj8@U8$G4nvN!+iH1!z8(E%JqUk`Po+P0#Xdg_u3(Ly z%tv+&e(@E;tIVImn4(V@qKnZBX3zzV`8JcDcitDC#gXI}efUm%UC+q>m5Y))B9rj4 zWLf>bZT%*rca7wG(11*eU+5J&hK#f8>=a$azd&<#MD7wc`4@8d><3>A{n;Be|4v*D zZ}6-?GN5m@0%v%_Kcgez&=}blJPB5e4R*%uPVk^D*;CTSv6Qq^#}4%v3YLPQUao_i z*=ksTi{7T>nB&2U>usYW`-(Pr5_^MZXi4`g?0~{%#HVFr319HUaed-#{A)C$&pZcz(7IRoB=ZdS)jzmF zA9e#T$Td2VH@O@=79?cg$Df*U^7sCbpYB(uR*E zJ`q=vWpuzVyKE5|jSi!y*d*<>gA4i*ZQb9!=}~-Oci_N1`FQAOT<$IB zj#hBY57QTa8&>cNpR`45?eKzZmfxtKe#1#_|N|1E1$%;@LD^0ZUxWeh+s}0j{YMr z`c+4E^+$#k+e$E{4yO7#xN^+4=N>PyNqiPM%J}786gHoo^*qMG*654w;_ILd+Sx`A zIy`gHbPt@s0ep!i#pQBbu%j(~rj3{c{n1_#hp-puN4F*Zrw!SmZpYz@9mCB0pnU;-g7#@g6Jilmit%YL7p1Z2Uhu4K~z# z5;cDfFW`awmP<1Z80I&!O?Y7Jcw_$Cd>)u0PsXT@IqyRyYPr!PFMexv<_;pitFRk%C6Vd=QzYYorIbEdxmQNG<-XEuEY zlM0>0x1i6&lKgOdnltC5Mo;u+`^X6VTz>h}_O6TLGtiORz&2T<+l&>@$Rs<-mY55& zc8)BHixs*7pS59Y=s_6LCbl)>gduub>G2c{srwT9G#kFiS>!0=L0h)Qcrzw)mw8Fp zgf;guhKxzgrX^maOV|?FVLR9uzLPl=bQXKCD=^5P72A+uxdihO^p<#q4Pkf8*<_x8 zK0zz=rKive2H^y~(a?Cb^Nj96Z=fsP>vur$Du3inPL;1lZ{FAbj30K;4^8~`A6+C@ z!Vb|7@Iv3C6}>@M^Cj3JHJJ^~)|8!Dx^zkN>E>1L`uG!$AC7sF->y8m=}$CAYuAZM z=p?*xpDkN9H{Ot4a-!~wwvLM}w{D#(8C%%;4DayIGmt-hy9e1cPJSodZoR2v^6qpx zUb=>C!V?U_G}|RsQ)~xE!H?%nVH5go`#qMj9ov-q@>%5QeXocAXeyy#~P=z)%80G-?i zP4bM!r9Dh}K6rt7*SMYzme+wt-~S+M`NK!=kHhPI^$$+`rUkyq@4^#0ql5U6PU7>T zk>^Jn_L>4+4YDf_Cr9T#c(T0wZS-Am>VoDMoN(gk_jl037}0_a@vM0c_qFdh zKE}qt8y~&%4~y%X(^^?j`G%8EW{*bk zlkXS$=GUBxr%s-G(sVhw^@Vrzn)Ore{pkMoVT+wHE^@?g$-Jpp`r67bkbgb} zd&=*&PcOKKnvC=Xp8C?`YNl<^NnYWBUE@EBX~>zl#hB2Oe=KIVk49`feWtB(kYTz# zMZfff{>Xc<<-D;+m%tYKql?-#Xzn{|i!0yb+4SX}p4~I(!!tVWzHkp4aLJaD6=OC3 zEH;vR;}eKU(VU+x*Ublq4>BUolxGlADdL>q=)Q;@v6;CObB~^d4QK1wn42qpha0*B z9ocs8R9HLfPc6RJP8;)-=Br_g4}jMAfmi5B78Ei=mPc{Pytb`>Jj1*2&-d}FSXT@L+rbGRg90*6_odqS`)7b1bN_6sXEoo9r(!tk2=2{VBK^tH z&^KfP{>TTtgBN@TdYv9slMgk&gj~UeeKz1EzV(9n4L4_;xw!ckWCJe52M>DCgN7gb zu^$`mcfb1$_rL%B+rBuBuK)vV68{}f*+Nf<$7y8F{*fy4o$JmTX zVawPLI^6iRRoo9=9Vf@+nv9cq*T9Tt<3EYrjR}U)$+*<)4*e~j%X($^Vk^|}$8NEy zYzhmkW2U?<8*Ow13P_rzylu8)_Rj=^B8X+>f~3h!}IA+Y{w7aPnmZS7a1SyuoeH_KYe&6^RIa6 zUT~NBj(>0eKkDnraD1)V#-a`En2%KcBmUTE@`|5iKu(m8YaOASB$**kbQb&c`iiy0 z@V-Y!KKadLl~3jR2R`tDcf(7t!~Qr&U-NmaIVFqm!qx;gcnmk>Ue5jhZ|~l6VmG2F z3`_9B141yu4CM?hL&;DQzD|B=tKx2G1t&hlmP+OJrLJr3Tb054^p+kI%gf8(-dB$P)knI*yT*o`BZJ8JTL&3sPYk_;oAmov*-sNU$|NftVX6F9`+l~2 z^?9$p!5G_&zJPse4q}y#&(>)k*dt_jSX}dPg;(mLBVZdY@uh?l?1`d) z$F1>eUc)1PiB;o_@!9f5ujJf2x9VU=dYA4M z@320rFSXmdGu2Q1mh{P38pAn$jaB1%8Q{Oh*g4*a31~ALm0h^kp)Fr!1MbzvqnFDr zv+hEklC|t4F;B;w_gnp7yVC(|bMZB6tGFlz07I|@Q(|i2E9}Bu*Csy0>CC;O4A}PU zir@KrJy%ZGW|>>(o||oQE|yN8k`2a6;&0h&fM4Q-VrXI{)*{)upBcYw2!ESJOh!yj ze2;Cp=P`M_XFcDwY`lSSI3UNxbod9&XLEn8|J@hYWTRWJ<>QJI{r0}zz<(D9AoE)T zr7OsBiM?k3z-JS^B6*%&MK{nl^iJ_%*POeq{MKFV*%Z&lXR$ePD>kY%7h^zv&@<`3 z%buh6H6rUfV#B!6I+7T-{jB0D`m5iq6^NJML1S}m>|77OzP}MK$4|UryPn5!M&e!hri2O?K|GZpCxOR68uZ8=UXc+S>tgoZ++4lruD1t`Hq5I2=`n4Z+zyx zu9drI4NXiRr};P9y|=!ZZ932IAA#&Nve|l^eLLhhSxbhdckU?}SLhyl1jQJ{>~NBf zAm8H**}n26uBdlu5La*{zN}oqg_R@m1m7j`LJ6PPWA-px z*PtCi@U5n;8(&5JKp-G zc!JoIbrLZwF;zVL<=u}L=jQ9Oi`su|50^A;B)TUq(L3pyxRh>5=ip2!F5ymlZ`#j* zQzcxI>=%&mNv8`fSVu?y`N z*F2Boj(`{~`A^5d6Wbnd$Ugi2tX;Hc%UVl&2gH2Ee_=-alx^pIaufdX!+vIX;A8MD z{Ou#YM!roxin=}Hy65>np2stO2>T_@(O1e=2mIP!1IzkA7x>M#!Xvnc!O2A&l-PmU zi{35aF+Nw1x@t#l&=2j@e*JM>d+=IYjD;~_d*OsNCF`&D2w2y#CS*)~BS_cy=1>2v zsfzdEyZsL5aoiEW8yJF3a@@YFV!LdsA3y&5yBgLa$yTugb_=-wW^ZQ{Ef8^7bhgS1?jt2n>!qOHBs#lB@=`_ZfF6w6R9 z{`uPht?M5bJ^$a9vD07-cFY$!Tl|;|#U&VmYstLA1g^j{YZtJ(|hbgc4GbvAI7tC*Sg+0ABfFH zZ_;ISm3oZ#84zCD-R^)4Ly?Rt2CF)|)5aFP!*xxu!| zKVhrnpJ)>dyXTqw8~cp$guYQ1-q1g|5^r#*I2q3H>G7#O%6vNe$-Ki~k?7Q!l;J-8 z;CZ&7a?)owrd>Erm+2cBkgb=kNzTxV$q}6N4h|V3oYN2E?b{CD0i4G%M?k#Oyz^W~Jz3g)^X{5^Tg*WPqH}Fj-(JOG7EjV>ux>Ei02f2^KbT5u78{gQZc!Dp=#V7U_pM)JN z_uID?IERDuAYN$~8HD$EMJ^c!VvfF6N=xcUNX;e*W9xg3?JH8Gxx+e#}g@h!ix9cR5^4CPU0FqO4^hCmkuP$?O{&e#2Yph zuH%z@a(VLE^*9Z~c&&czs2}9NezaG}o}2di=%+q=mma1+*l1*qaignz4?us@N%WI( z#3j5|AFgGxrgl7ys<9Ho*`rD2=iwCtR=9g*l~Pk`UciK2T$n=Si*q`VcL%Dzgvl1$WByONgJl_ z%wA;6;!opYO#H?@{Z%(Tj!!s@>k``#=i;5*xO&&P_#W^)jy(eYR)hWc%^O({?_nUF z0weg;o=4~K;=ObQtfVXAMtUOq4VFrAW&I7W^WKVIyzw1b@igyW(>Lm!+{3?XY4XN1 zIM%h9yB?2~tqyo66XH}l3m4)N`Qg5M$s6NnY`f0q+4DPsW25osYMuRw*6GY0E=cUB z{E+zJw>hSZw)6+=NVswPdk=0U vSS_u;yT0c1#8$$Gbcl0_JXurE)dsn1d^db!$N76tzESOa+vB6=t``0QOEiMj literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/Default/t16.bmp b/radio/sdcard/horus/THEMES/Default/t16.bmp new file mode 100755 index 0000000000000000000000000000000000000000..ac0d16cf6119ad1d3e791bf88258734cc443d9e0 GIT binary patch literal 164938 zcmeFa2Xr0Rb^h!3e=B7rcH*W&HBzJ~QWARu8$c2S2o?ZD@4b;AKo9`Ydv63A*u|#a zd$T3kl3ZkUTQ0HVIF6GzPMjVmaqPrN_WOS4;*g_jN^<<(TMLebg}u(q+^J{wcXmB{ zq7$|b{GSi;Hzw*3eOdIX=>PiAj1T?aYG3OAKOg$mM?U08tNzbE^q~v?ABv7k+3SD! zMcdF-FYaw0{C&^^AN0TnJ@7#fe9!|Q^uPx_@IeoJ&;#$+1ON43|7Ab^@sI5%KlzFM z`@jFY{l|a&#|QK)-UmPX+0P96W!@#n{r7+W_xrKUul4U><+Gptteri3*6z9I9(&@6 zC+zv>pLg3n^O?_>C-!;ag%{j*mi;>0!s*2qU$plk_V>TP^wLZA%h2aO_c;qEf1dE| ze*N>G|Gd2$u@ArfBK!JteBldUuyEqH@NMt5{>3kT(S8{Uo{!^$>*34(UW2WFR<`-) z^{wy2KmOxCwtx7Cf3Uy#o4;}6=?6HvI84wNfBxrxZi^Q$w%_`#-?C}brrDY`Yb+@# z$x>5OEhQzz5zA?5Y44!)^mKzVGBPYPGxHskm6c`Tl#`QV+1c6eAhzY^=DsiG<>gs^ ze!hjbY=a643LJ&6`|Z9hEG)ElBlZdYR$N?c??ojgB^FMlrKJXym6g3Km6w-WMMZ_9 z9Xobdko@*w>wgb!^XK>HV%=YxHh5n=Ls3!DJD$z=2d(cjTKbpw!8<*8@Swf^`s;2? z`~LU8?_}3S&iVCTLMQvum%e1b^ENTWjw|b#--CUtj+Y+PinJ1*xH-!G2L{Y-}`791}cGaG7gzZHRjd-Z#$y z1)q_ApwH+Z-?#KR%e3H(>!EibhyL&n|Iq&BU;gFS8!VGgk4=Ltrtf~^H-5vez4lsL zw{D&5Gk86S1C~7=eCx+Q&-1>e-&ywWfOY?V{JUWt3cfqP9>hR!n}657Wj}aKhJN$7Jl^Wo;N+O!mplp!`mR1;CI%8eD9aRNO0L>#e~13vU21!9AHQF) z4K9b{C&(M&=Lui;`_jG_dHxHwLH-QKkmp;^-~4_l-1y%;9z1qvgR&{O-Jd6TZ1DR2 z+O!_;{yBrs@9{z#d{^M6ySv*)MnQ|kg;nxj3|MXA)WcS{Cud_X( zqM{5+PEK}uZES3;+wSFt$An+^GQ{H{h?5{5g71O%K*v7KQp1-f)zQJ<@uf;w1vEK8&=XKBbUS7~PH8nZii`J9> z-LLz7{B~M@PFlb2&(AiGpK$E>b?%vG@XzF*-9LBmy|K)5Ve??)VuxZ2p?9>mx7%Zn zJ@#v(xACluA?RWoHf(VHfgTIb(;xBi@$Pqz3qSS*`_uQe?|aW*o~L*>L0)FN=P|#J ze|O>Ui|cW1@abio*B8C5%l&%7hUdLv$MRmi-vNIFehPdR_%iTmK#ULgJGO7%eu0p$ z2?+`A+2hr2Qy*;G25k#%(>lMipFfUsK%5Vr@;;NGj|Be*_vQT<-rvBzJNt0bci?qi zKPGvZOTW@TzCYY*{mb)l&%6`vAN;V~ z-`{V~KKtzdT7NY@vlAyySX^A3tzNy_y?@>p{Y5|WZomz^=4DGbzj~hx{pNYV`(nHg zCY*2a$-qmVZ{e{Z?|PnvN4IX>>S)WBE$%*%eNaqHjBVVw(NT2t23xm&op2xJ2wAyy z?ONym@j(6PZ~e{Qbh$zPj$!7mAMkCBm)j=1M_>!WP_2F9-S#xq4nN4w|bd3i^hH*Yrj z0b(ozw-C7Xc)b)>885&q@4@>h;d|bbe`mZu-X%B)`gJ^hgE|HnhA%h=x&=7);~K|u zPVjxl9d{Ud|No{xFmv?iQKygb{^(D}p0>6&M=dQaPX6=!^bN9wzM=nuV|)-J9v|Qg zY%r#WV}keW@xc2B7w`jo0d3l}$x%2qE@C9w@e@3Ckv7dKK@_qeL%w(!!}~&hK#YqJ z?-hLTj)OWPItV&nYip}@b#*y@!AAi9U|XT9z#s4f*WwxvKm4%$<3Il6ue8`92Y`4y zx&*ode9m(Z4GlR0JK%UD@l)UPt!&xW^xT%e6hW=wFX5|BgML zJbvI6kD(ynrlno9S5i_!F$jb`&9rEuQ zT?2v#y#4_$z)5d!uj7-^(NP;48?$};_8H3q0|Sn4;1k|4atG{ikF?J{_nebEzlz`? zE<=2m_l0kneg-or^eKDvbdfm`JUr?9xx}WchVE=es7G2Df*L5$YmwC*iFC$Cnd)_x=BLoKgGPXQ4 zI5_BV0WaWZhQI~3?;QKs+tj}G_H=l=QnC?RB|F>O*KRqm9AX7RQ+E3FX*+lBoZWu=?RMv# zceRDb#yG^WY=ivx#IpY#yfND-Hw%P`!)`|Dmuh;kv$%hsljsHo0rwhI(bU)FiIE%$sC*E;9>3PucUAz&W!G3Xs z-Glw(VmXS($WGd>m`p;z*UPqKeCOVOu64F0C9^Gca(QwaaiPh$j#c|&oc3${?e z)`G`fya>OeU!KH65GNiV9vl8#zV-H1u;uz5LmUr&c&Ffkc+1mIKW&dc{Pq2tSLI}0XSHI5@&nfFAx5bDM%k@((pNmy@0CvjzlNwmHo!$v{9fZn zqjZi25p9#!q2?CamJ92lZP;{yZA~02Y9tRp$2Q3RR^K;ZlZ)!P5ALT%IStZjg1*X~ z@?%x#8K81}{~D{&dAuJ5eb@U>a%Fqxs6V{lBQjzSSZ00)a|`_32IeF%uYfrteqW9UgYd%e@Q@pWYwPw%<|wDFv_$VJ z-}&ai-LJyjgMay@sj$-*h3v~Yc+-%Fs zR$L-0!?H8eElb$TOi#58;V>;V$*dIAoulW^)pO_Tz6yo;BHd$& z?v?v3EiSS0(lXnjXQ|Y4Rmlfdt-h!c=JyEmdnPe|(GTYHp)cm-@ZP{S_J2cTXbiwU zr!OmoLv$v5Yr)t7`m^`Lac*>WFhXAMU;Wiz*gqR3S%ZFR&li&(nImM>ahOBc?!B@1q`Me}a51vlSl3+9Gs{*ANkmf5rHrW>xe zxwB^29BAfryK&~VcH{Ne*z6hC*bURKwpr7!v02w$Z8M>3p{qpGTsuq4H)z|eNpj2A zXdPnTYo^)tS6?Z*!mhtYt=4C-ZF1Qiuk+qG{aTxI{dC>KOq)CV2D^FA9GiC&_j|MM zTlYSHo@l-;Ua-)XEm~wN7BA8BEwz=(O<28rg{_TPX;FIi_0mtHrJK?x^bLKK7%zM$ z3a?4qm4A`qVn6gJ<3+LXSY1^u3^mzaVQEk89ybPs@@6jPr1^Lpcp`R#-oZOY_K@TI zt#5tH{`61()Xh^P{_p3CGS`H;z<>Yue{bLW-uIlp4B5jtioS+V4xaMkADD$F&|^6F zRR1GhgFMDgMvgE(&>zT4_#QiiXK~|{_@8H`?|5$XQDnIHlYu8>itl6moAia(5x@)j z1KYriCvXE1V=dQsTC6rnjH^WAZ`W$ujH|TIRoYh+IxciBmao(C zp>3h_hW5Qo*tp!0yPi9q-#=;BUGWLK?(&a^+AFoLTr>D-UICfE`luM+(sjbN`e4R&A>64?=FZeOpnY$;0lHp&Hq&moahBaO zXO_*IbAv6o=|)?4(;QIXa2S%zk$lO| z&9RNj#aejNY@0LvDvjUb<*UVCqM6rRVK->JzTsMtmicYgwU=xAWp4R;t;54JuL{vD z@$dx_AJ5b}zs=G#lUa&r*BC z^w4(pcf`H9``~(^dzh)~&D1$Kk8fEH5ywmRNbbzNVV2~MWTAN85i(I@1~T!+>#wsL zg_F5z=SnutkzAas@#E%M)9og$-=t->&6#nv>$@9gTqSuX(lgwsXSh-KKl@ts>D7{( z8Y}2aj@__!mD3MhevJ4RUiNZ;ZNYW&1CTpDhX7s9=MaD`;+pUbK56DQB5$x)@RKor z8oM9t;J0Q!@WM41m*ES173eVJ;7`>*#svK9*ys3K&^_^?GhU#_;yYwK0W+0#S(I}AGOi$riO2WD9adzeDbKUDL@ncuJZ@V^wy}RQkOUCUih?PBh2 z&TJjSwPw3}nlJrmzGTKc;ed9L&b36(wB+Ww?*0~Y9vwSR_r~>>+%m_O$rf9_aGq$s zEpxQM*=Z~A7gC%NL@Z0=b+_{!&efc7->san_KKj_?y&Y~7;wnk~P}t_w0Ch!MZ+TaOno zSkR{#jvKJz2~2uaSWw9(jSuQ?m`NTGFjJ9OW zlZoFHXK9JDl@*tv%~D(?BPrPu>BFSAXoX#%Ta7Z#tpCnX21@|K^`w& z)*y?}&pc+ltfB8bW;|~Ey2Fk9_sdit=M%sGQJZ$Y!-tD%ttipObx^0JiwT+6mbai%FTZ{7G6cg!C+pG1u-8GiB zJ=yl`+HD2ddDgO5IjN$yrWPCQ?YGXBb}P=4&mue5+M8OfqglDD8Z%lHpKF%Cw7X3? z;krg=v*uDXHftZneFJSzN2ir3RgP*xXdQN_k?b36$G%Ck(&u%?`tu;zVuHCJg2-;q?L%zm}otB@KE4(GR zXG+^Hf4QD5Jt@U9VQY(J>ocJQu$`{%NohjuDRq zj|>01$IPW#&V&Aj?7_$MM}PE3&gVv~omeXNGI|}l8M+TV0gn;)o2qdfVo}6W@Cz^= zfERGXSb+YC&x}6gxxorCWseyzYiNU*0XH5y;rI!+Z9IO!O`dSILjKjuKmG~3@`@{M z;i84MOR<|8#cdiCpMbaE>*o3f>uK+_#@c$v^SPNh8iP};Fel&AQ_~c)+2Q=Fl{+;b zNIVJ8mMiX=mz5))N_FSUN=b9Pw+BCLUA=V)6Ae0Fj`%1eDb@Dwu60yPe56YJBg}$d zu3e|!*aik1Rco%Q)|>QOUS^h+D%QG3<88g-mFR4nth8 zMlqO*68YLQ6hq3)w$j29t1REC^C~8zbCfG?QzIDY0j49qv;TlWkk<7Tdl> zF{CYu6>Z*X$y*gCawOlgFa;$+?BkZ>g*ENFb(^KcC0cqynr%-^Qoh6{mn)Yfo1B=} zrEudh;jt3LkpC^b^>Yl^7hQ}P4f!I(4AI$Mc;Q8tt5{fA;Oqc!1+S6sLku0C_Eh@= z%lOiL9xbs;>>Y6P-S2+a;f7oQo*9|p=L&i`<7Evk*a^oAnDO}WvIpFRV+UQfyiB;s zle}57#IE@0@7a}~_?VXGSz%_H<)tNCL5B1l#S1eOJIq!*lI5)A1S`(Tu)OpX@kOk3 zq*zPd5@+esebN)RI!cS*YTLCvF>a&yD#mgY$IMTcP9`6Bdcro#LUP}&x?Q}kohYouOLt7vJt}Uh`{Vb*EOa`J=_&eM z>uHHw#H%rupSImHlM*a-+h*NIvVLP9>5Gc9@N9+I>3SyVmFY=3k91GzXZbolU)SQ; z(ETJ?rk*i%Yn*74yWey@f6At4OW(%&W<8%M!P6FP3sJhBjXp_D*lJnZ;@tUCG`4Sz zQ=U=uMoX0}V%*uIvGP**xYV|AtoU`08Ct)LKF06Z1Nf)E^rcs*V*9E zpz{rY8+^9-ucz7{xWP}3tRWu>TZR}fxFL>3jve?%KO^q~do6taM^NU3dkaMQQC6E1X?wEQ>i0EPr~UWlt+jd)zt`oi)BaIbpTEKC^44iz z_75FXmm6gb`RlDQf1TB3>A3t@YcAMiy_Lzpq$bW396?#hQz^SX0SX>#I((hQb&f8)xkm@!B4% zW7q1wBdjWOwbd8JS*^}hl`>!Vzd`$LvAWDvYNM@5)L5wdEQzrOUAsPatu^ajdUqwM zUwF=SI=7x>lJ=@~ME~$x!3KAq%`EHsxhY$ny%!f9qdW$1Q{n6zFanSJHhkIR<5I21 zN_dMbLFU|g>#YtqZ@lrQb#%5{PF|Msn`)E?IjrmL7C**2IW!gLvk|)?PDSoI_(q=u zH-GkLf98m~apVdxMj&fEZoHo67f9B8*slBJ zCoLgzzV+|eU?Wwr)>RrMinN}Jb=JFMz4fW>)3%|V8?C!6$~sEcSXcQP>nvNN-y&_W za+3{I##&E#l%xKg>#e7BjcBcP>pUS^Yds}Ur1feW)KeCz>#VVk;am+Y0SeU!_g7X6pXdTer^FB-$nI^{S+vSJOQP&(SE)U7*CD(2 zWS@2IjIn!#fo~ ztk$)o^qe9+Q+Ju3dy;y}xvxm;S6{W4$5=tCa{c5BiPQM`ez>_5M#8s+Vx{+khK<7VDH#i5*5RS`atLQgQ*{Z+y;R z2YD03jn~yKrKfp$b17e%mo*+c=)Q%*O=QFp#VJ2&bEjWx@$2W?$e!)u-5uhY65HRD zY2%_}{o>`ZIy=@=VJAd~TMBGnZKmyS$guq_nKsduZ70R=2igj3v@ydb#BXB_sSXR> z!U=rsczY5r@Ye+%({h*g>CkU|Rah!0yuWok#m@Xiu`W<*%@TUEA#RShJly(4u=w zvB!n02k$y(_nhdpwnAM`)XhCOn#9i}j=CIv72S#P`*&^@o`vJa6dT){W(S&c ztbf;L9kb3_a+ce0ZGyYsTgO`L?EZFp=HYX8+o29UXRY0H>!2Oc7|>G@BkZQyZ3o-z z%>E|pui9o$-hI+O_1J@U$3e|s7XCVfo6z;ShspckUPL@6GP+N4wY5z0CPg`HTQ*5o z+h`fm#dqL~k^l7lY#FfNapQ6Eel55`w?OVNF2DKaTgo%956PR_-OhhY3-j|7pFv0W%&q z1^IclVbyZQz^5sv>U!I{cAgz*$+z1NHrnxlU3N@#puNZrv=-Ruky<-3Tx+Mt8zf^S zZyE~hM0cf~?$;ZwP3Zi7m3$JU9J^R3m+ZD(#?)&6D={u>U-5Fgu-2#ZoEX|AJuO9YWxe#iGU-nJ zYD=tHybjN|!;A2!um?t<3vx#o1heq#Aw9!mw;i&>vTIHa@3rGYHIiZ2Ho~!!5gYWJ z1$Nhoeme&y%Hr(IXoKA+`-QP!pmM8|HwW7C?Cf}x?4%Ows)&`o)?^QyJ7^OcS2~4H z*XkL&A?aqFdRA12 z6lU1war2^b)IKNdFb5}7^ElAc$SXiU12>$Le4MHFN1iu%G~}jXyO8_uF+)op1iBl~ zc`4k$`^Z~l3-M6SK~5g{@O}iAy{rO9*DKE#y%2fh=36MbDEtV+G7W$ zQ+Dr&5pE>sbY1MDzFk|TzvW4WW;^U3(D-mrwjEV+nmd86P43k^}c$)Wyj?Q~T zHb7nmc|DI~!tnz?!8b2_R^LAoG3PTgD_!TTc6I@|_t@JUOP=IZ`@=WRycyJrBa4P0Kcj^D=g@jeaH%jyfC2D&!5@F5o6FSMp}5 z-6VN)^UP~3al=9ztWC0g^-1E?V1?h12kH+Ut@h|od`WkX$P_{aD ze^14xkli3G^vPe>Cx2a+^fTm0SH(sX$JuzT{0NdE-N-lLCFBbfcH|>yl|9re z%=cAp6p!ovbp0;LK4cTNSC?=*Qn%g4biGmG=Cu4D6W!&qh2#_2E8m08&De0TCEM9X z`z4zWb(UzGFkHM|{(?B+MmW;BJ#n2O-B0K_^{o7+XYP~CX)cLXOiFs1Vq5XC(TWR) z`~vWO5I68Sa)jJo@)Q~Os7b*4h7Z6Du~PQqIBXR51xMf}D1$t1o`3Fnd+yoiH0P(< z;fB`DEl?f><1go8PRmsLBVJ3)23|%MkhdDd4OqwD#{Gxm1{p(tVZ(q4{MfXN6CO9- z=3yPe#$i8jitgqx``%Es?39U4`E2D2J|Q0g z_VKXB%tP(+2k&pTqkU!4n~Ln1{J~&rO!5gmt4(@7x)wZ1>qxpi`hA~+Z~1U ze{5$k6Y>$Pw|3b?*d8HUd!3Gnw3dQZYFBAL$p~Q!o#;?=ws=0pjpKNr5`E^K49o#;-;6Y;&`?M^(k!h_2sNwqAUC@jMGF5yNEU6?J;vBR$sT-f$U|tKdtbA}7?>1Pivo`drlL@^zBf z&iRn_URNVu0Q+XmvV}I|GQ~YF`=}+Zz1fZ9@LETqVhtLP+ciG7<}SD9?8UMzmN>h- zGiQbM$gkC!FaN9TjrM%m9O6O8qmIwvAI5&^LIn}d2Syx+m?yHLo%1pdD_+>D>oiLa zI6O#RIR2iLJ#L$L5xn?#4>pir#)pg^H7s84#J_L}+&I0B*y`l{gyfpm;cfg|ip@0S zNNz~RHDxcb#?*P%ma{~;0!wt?dTu>yW9Cw8%2}%WTxLy$dfr0qkF1hS)uDUeo3Ti; zNAgk6)|w|7qi4c)>c|(KM4j5FDPw{9YKd|jHd@Yh)qj$#ak$wIZiug3)YIU3^f2Dl z(xppXeZ8x$y2|FypYL=vWCQP(F_iIlk>+y$=5PL{t53xE2u9!`j~(O)+n6Wt@Iwy^ zFT3PVOBa4B-JDVKB*6{-Ca^Wt{`d|Sc{JdLdj$JF#|FO|^M=S@qYrrw2;4yEpRNv* zYix!{fpeXAaA6rIln+&uB~0Rz^=dIBW|8o!rGfc{-DXRnX$eDdtLst zK8@-4p?VZgZ!20S-jyw`+<{K*)IrCEDWZ$SB zfn-j)<|;6Th2sX>1$l!HiCSUE6ZAB63;4pvwo~?>vcW!v<}((6YY8OQ)J7xD;%H`UBJx6 zV}jc}ZopS_?o#;#6YY*8?eYcGh}SpT9fw-%v3pbp^H7gsQrYr(6u6kq6ZfcQ&>_`# zkk70|vbC#Bwv6!8EIa1zW8L=TT}SPH`K0%&FU~6tdP=e2fy!tbSFH7r?8Ms@dwTTl z6ZY8MN3FLeUHPb**Oaiu<-TIyTuR>jqPRhS2R9%7@P}=Qa&&p;;kfa78sqf6_uOkc zckQ&y>d>KsZvH*|g$@14Baciq*cdW}InUVX$YA6qcwsv&b3U18fM3Mp#^>4a zT=Wso1_fiB*oVk3Y(ofr%wr_j`t8JT$~Aw6x=c|Ki*4?;m&wMt!V)7TZ{%O3a0 zIQtpBTsbMukEHl#yJ8VXm9y1T87CfEAzy2f9aMfzPvz#2oh_`v*IgP{yQKdQ?H0~D zN`wY`Mp!67p-`2-q$^a@(y%v=|01I-g`=Yk!Xj7lRFNz%4SM-x?WFpvORIn z347%BBlhgWXRN0-M>*%r-P@{s?O4SX*R{_zB_$S>t^RJ9h36 zCR7vZjyqN3tHLs5-|Vd1>Fk@~q2UYTGxBJv{V~^u7!$Zbc2f7r;|A=28)OiE$M?fc zICj8|Cyx)`f*qFqI`$uNPjIt-(;*ItjkpiS7#LmquSZ zzrR6#*{i;WHH?qz7B_wm$FL)fwV8-JH zpI4jgwu$aCd+@dck~j5|J*oECeYYvMW?Z^ey49u3asKstj`u20tzWqVosuUx4sSi! zQIf;mve6#9`?P)fi3jy;je7R=E;oq$wv$8pUUkM&_2ow8vYoP*o_@qmDgUBbIn{;f znnNnwkTZ}W+)!_pI1P9~zk=V&v_ zmpd-c2HXg@1+s6VA{N=4Yd@iy6_*+IO|SH{UT0(DTUEZW;w62GQH)7%At#df-$}`v zG3APNNT!S_2Xmsc$mv4lSm(fh zeOU6Ut4#4v@nSf?dpjqzUA*sn*^yS!UTfJC$81k?k^BwHVbOMMiV(Ig;>Kew*WGVTc$COj_lw{3#bH4f@#l=`3y<w_*!!<_wmH+~Kmd;wlQ@{y0YZ_b6w8_wnRwEOP6U-bpf3O5xl z&t_-k4m))CkgHD#Uoln_i=S$Lue|b#>l6H9$WP{Xddwhizzy5T7xMDP+czFJv|e9B zSM%g`HQxqh57_be3CB(3@`ZN8RjMPSoahAk)A}?%f*bN*h$Dt{ALWQhS3?eUi+Xm( zI$sjDanBCr%Scxueu?iDenh_`mPy;KoXB4N=6rO>1b9iZ%*FT7t60`Gy*=f90~h@R zU;r5drsN|KK8S~u^z~ZoeV?=9wo2Lfifxjwrr(`RnUqOx-Q5rVY;5_^@A%zV9vvOK>YKV;xe}i2lxqEixlgT?_gh};#rDO9af$x`Pelf+S3yrBADR| zzDHb!?Je>vlXEbv7|y=t3^z`=b1^n(pVs`PPUmA>Apfotxv6uvNv5^wIO4x;`OD-> zT%`F##kRk5r{=q=?sjyP#chaE?URIe$Q$HCP~M=^6SoQXr+In97|Qoe!{v>~ALsJ8 zq2}1}<0mfQrb2S(u;dLjEzn)?H<9Nx)&8g}$hc3u3cn-kJ|CJE`-VB6)EGhDhU11l zLY~k$)jP6XFu}ay0y)<8WUU&rgW?jek8|`J-R0M-Y-7` za|C+j8}F7J9aMfF&%$qmwf*;z)y2EjU9|^t8 zpNH1#X}8~YyYlUiUC`4iWapr#k=qHc5F;e7c&h!8=fM3%Dh7kkjMyxA!9VTg4co|j z;Q2jnkTDR)62Afq{Ep7%fAd)Kcw!%97P)EYeD98%MK)J{fw?oTv=rs1Iop<;P1*B< zdla*gj37=jt~dsHT4VLhV@Z~*kgZaas(E2~vIo+YW4YPsT+9Vv?Cn+VC;8K3jk&^> za;s$GJEvTX~N1 zF#Gr0P+Of1Yx|gNsd2?m#+!7XiW!Z{P8mTLliWP0`E@R4giHY^kmLpWpVK#mwF50V z@~LSKr2GPXl8Xn0=?UfH9a8QMKEcDnJo%sa7yG5F9#W2UPi=;s8m-lMQlwnG1lMO1 z@_C$)u7)p#v0+@g*#V6)y~rnB-{lKv4#8H%K2x{HYYcmD+{~CU;~nw_zIZ>}AaAe* zg1BMs*Tmt8OX3FoguF)TxKFh|`~dJX@k@MWe0Rai8*qbt!?y13ZifrxCb;mvXk-<6 zd&m~QjTYGiffHJep=JEQo+LhsoIz)!g^=9^c{#Rb>3o~1TA4GaDPJJ+7R`CapSDi# zH`2xNTNT@DlMErQ*)BY^7ipdR0NKagPWK7rqPbW-x(dE5wdg`F_eHXTV_go4bSPpp z#OT{)KQIS}JT*68Qv6GtwB3!>jM339&U!?=dQ$msCl&WOA%E16aKKpKRJ7TeYWG@S z&!BZTRyo;sQoo-PzcTNXylmtZu}J1Jj|ofM4|rkjsnf&Ueaa5geIOGKcNE)gnwvMM zxK6KR!%@YT@R1)IuCslbFK|@ylX=sl8dvOrcym#b_ z#|_wFot9d#$d-^_AXD2VZxr{0;4N|)S)OWt_?^+M)+t|%xpmk()UoltXlf6#jha^A zA&47f3w%!>FfKsgg8Va||IGfxMcK|a`eMO?1y1H5Z?LVxakFaCEq487AF-KNepG&e zTPFQ!;$e;b@K$I}vv>`CM!LGw)A``_e&w;bdICHL{sWP_7yJggUa$1TyA%VxO>=CT za#kqsEza&c)^4|Ie(MqCgP+p%kH~L0){rLIm1p}E`vuG7WS`&HAfNh9cmL#x9uv;V zF~EN~rg;bZg~K8F>w45zZK~^$ofzYMX$i5ahm(|`_flg2U*d)_5jg`^JZ{ia@Cl5M z?{|2?FHlxq>hhuS4Zs)R23$?GKVmcJQH=l89ORon9yiDxVXO692JkMrb{(Ck{GACZRVT|r`wKB*j zVO*BKOVsOlI!d;5r0vaI?rahKO)g)>VJKv8ztfM5{DIKHxvds{7e z&&|qzbK}2wx-omH!wgtsTZd}Hkk^AB+sy@&4a2>(s18onR{3*Pud^r_nT8F6T_YPp zdX$UdxN}ffL^X1}Pfy1(77Pm4!^*Q9DqC%XB`a;PJkkzJ7reD2+eS-OPe6Teye-Ej zr0X4)d^y=Fywq-%uC?06GB%C(d9MIrbm9T9a9!D#zI&GQiTrIcStd&-y z-*%@ix816HRh=RKUG^HQ&Ru0YvsPG%e#_s!%u3TEY-i3|E6Z4E#oD(_$Cjlovy$W` zR+73zziV0BinXmk>opn5%_&x_Gg03V+8C|x2yIZk+XVORvv>1D2XTX6fV?#9Rd3&* zzwo_DkDG8k4LJj5Xqki0x5V%VWaVT|%9{grctY}~q}cHkKIy6UNA3@DfLu#z?hw!M z@&@dnr}6zBaz8z8;CBcNfj49o$NTRTlNalW?+Ze~b#gj6hU=oMg=CI!qdXh@X=|3v zck!E9SAN10qvom(l;jO@8>h!i;zoD~#Xgj$DShW2)!i7VO_NQLqgn&Gk^|(Ut#mj; zHigR^<^(hq0fCdR3pJSMs4>GNaEe>o>Li+C~nJyLa6Kb*H)| z^L7*~R^QTWF++zecJPqJ4IZ#fgX0$4zfUx-^+UF4;Go6H=MdMo-?sMdv$&=Xi?7>j zCGD-&G1z1KPEJ_Y_<&Wm)LLcZ9ve6?W+O)q+R$P7DGrUR-DkrSdPLI^v zapl?^k>BZn{C8uj@3F77#QC^~B#Xu*iw?<;w@>)&*ttzL45cGdyRTrqYW%4-plSg& zX}xih-1Y)$1V+n_P`#D<6K{R-b&_mbC8|mX28WU60yB4?bjHeC-Xp|IsI{t!KcF zoH}DKe(@E1`SrK#^Iv*H^tyd+l30HE)i>=c-}ts2yZ3I*-Poc!!0FERJum7eL;4+@OQ__)Rd+20bktH_YuE80dF+$y#aY5A2j` z2s%G;vv4C_Z$kF=;jS`aN40A-hX#2>U64b{1EYpvTS>H4=WLYUc7tl?uGRd=m5$4@Lh9v`#H5@J=qVar-8N?C7v@)c8&ZPP34W7iDGr%+mvZtG6nXG@;? zhAn;ktG42?uUN$6U$%%R-qLc2mOu7ow|(VPU$>RFJ!A9R$87WPaohj+XYEnt6hHRB zLw3j6+iYxT#GZZX8TiR)neG7Wq(I2RG{`damyLRTG2dpGd_DrF4 zX8E3T3kuygRl$wV`@fXD0XNg8spe2%9zjq~^SBAh8;={XgMCBGoXP(Fz6-c1DOC(^ z|32w&N1U$!-s0P2Qw=wKe;yuZtiWf+_ep}dp_T}`8~5(@G;ixOuCR>Upbc(ERzui8 zkdJ}-*2g{3(|jCC^t%fG|S*iJu zYgQ|lCDO_>B;PfsreByI(ENcS;U?H0*^LAEo#Jldj&%X59>-N@Hzh-X=H*W|(j=uc1z4q2u z?Nzm3e*0_oN8kNZJ1KpmG*{nS7j6nQ*5|0UdA7#05@E>88~7daerRk{@&$r^XvRT5 z|0XDJuyf!K?-u|okhgEBF$iwTDoPD`a{)KUj%dFMcTRj#Qw=xdIUpw(E3o~kfk3?~ z{LuIXzzs5p`qtpW%bOrZz=9`a2;_5W@H;~uA08vfncy~-8H0%5INa!aXET+bM*N1l zv3;s*8=6lb|CGk+F8P4ad5FQ2N7Ao6PqsNfmEt6>&XaUFYzgdf>Xx~VE?7nO7K>WD(iTQU+2W|Jwmde)BDQ3U z@|Ybtd#fG0{j8mT@Ls$7iAU_-C-lwpCmx-o$Lt=>2f6F9N9}geJ&!+Wk3ajO z^&gvX_Dz1SYSwGaN@FZk?AGJPVU zu;1o5sXGfTO_WYsS#0UmvIj*wc9p1(lBljkb;wvQRvofp`H_i97dWb_ zF4Xthi>$7uz-srX?te|OzDZK1@9?N*S)pus)$2@1NYpoQ^u2iL-`>9QxPga}H*Ei( z;Ku7|jES5JSrf#~$nc18(e3P-Z15^QZEWAD?3*L*oA6sUD^@J~VXEPV?~*b`V4t9i z1##o$4K+L84>v)5%wxvOogii|rJvy!fau#?;bt|sQ9YZRW?y4z8<)suwN^DMR1;dU z!XvUlsC9c>et?M%<%)=>$vHc$Wpd3K;%iL=lvKnloFcY2IV3Aq%ZvQnIO%}1O z*j7tl+nb}@IsEJLvGowYmA=-QrMx0Xi>zC1Z;t8&q%X1ZgjH4=zsAb9X`a;9)mFNB zwUum%u%fN{w&a#b%h()g>2WJW5thCw!ZJ3gK2e-V%Wgexl@-Rzmz}c28Zs7IecB?c zOPgmMS*kCfJi3f{`8}gI%C8gc>|OBT{m>pezYK1?U*J->L04m}?&|7vc=7UPbZkWO z=BTUD0bk;;q8{^9`Xfgh+Xh_?Y*1&=%NuaRyf%*;?|Vk>1obp;$N1Lc=2Ef-+#p+! zIZp0q*~uKWr4Bbawo3NR4a$d}soH}Hk@H+`q|0kmoTX1S03dv5)Lmi@OOLh>R7;*K zzS3PD8_G*7C!bONHpTq*>U$Y^(lhf$M&1179_0a0|GZZ@lUu zr*ywA`Mx{F&lA$qCcsU%?m1QV%la5wysgBRq}JK8lp0$h8L~1>awNUR)(ATrGV3im zufbw-8f?9=5+RuskydSMw9gtH6Op>tmTxJtwX3D4$rsmGuDk}#aqdKS)b)t(()#5- z**|-gTU{mFuWIKWt1PY5cXjkl`{Z2ZU#81;P|ilN#`5w?_pPeD>|D#v%(88pwkUq7 zx)#~_R+uY!o?m3Uiil-WG$PWTH_`&d#gPZSs=Q|hd zn|H$v7@~h5ud4;^e=lpmNf1BSG3aBo4map%BDY=rm?v4ia^WpDQ~3hZFaL-otd?J3 z2Yvza;FXgmA}4@0l%t|`jn$!?eEbEzixkrTC?GT0?!KzTw!Ree~_(nmXCsyDT$Rz9{;nS{cI|U8#lKBA2fbq zd>hRF495+=0zYpx=obJp$QEQe@&~^(z7y|X15aKyd0E6d*W*~Q0(p7E_ycYt7AQaM zlOJ)oNnFExPx%F;&ybHsj=igWu6NJ;XIICZIV_Xp=Cf#-{n)=-zO!Df_et(_7pqo) zd<4DZtSPo1ssSJ#BKKwT+@WLmo!n6BY!uj-VnGva%8l3_?Y@aNzNgx5ZEdy&@nc%< zE?b{mX)EG)+q{H2Tbk2hF~vO=Q`l;oW#4R+&bBtGN9MS$JM2@4K_J%Y8G7yfkoAT%vipqeFK5#4%UT z06mQ{gF5C@6}SA+p=56#z9ehtgg;UH@G^+*?E&P<15`aUm>*beg|;nmQ*&dHc6+k{a!-%K)U zqP<+P&_-7uG&w%X3X?Y5J%jD`nR7?&uHy&o=z)DUrZKaxw$aLp_E=nbt3^}}+LH7p zTb5L9%Y~a2#oZQB-ft^+h$?z)MM8Z0W=QJu3D zx)*&5PO@ol?i$UN7ODPOQ@-$&w?^NdTdD84sh*nVx-}L?I%+6TOjqY?kj$)Ajk(&) z#a5pan(IalHRhr=>)DvU#&@)kH`(B3gYrJ2gqwK9bACbG`1nl_H@s&*Pa6Ed2bZ#M z*oF?qx7Ivvii%a=TKNOVPab#QEZn+ftIM1G=}&(;Rbb-ymrcdmX7!-8|!|j`Q z!wq;qUf?4^N5h9t|M}cb);+O}aR|L__Uzd%Kh5c8(%Bq-z>WG5+^k-*K=lPwmt(r> zK&@M>`V^Y4qIoX+HP3UOh`BGLjrhN^w9LE~zSl(k$`s|6QCC^>WK?rwv>sBu0Ok2p z?~-py;V*N?H)&psn?pmMjr?+29&=>T|!8pL226EoWo9okjGI+ctM>&|14b)_BFCY&N8L8Yl zH&!t3P3zK4ky~zFtIp|aE2{2bpU%U4J$x%2n&atoG}&{#@;8nuCz@~BQI{iUdz@;g z>HF={)7-qZ0B-Pqy_=pE#0_I1vL}ce>>I{Vj~mYA{b?RI?Hz5-pN72gxH)<9gqzob zykU<1Pk!=~DTN!pOGrKp`WHD5$eVCI?cHz#4tRF#6>tM)I1gB1jDf%lE&GBGMyGj1ExwAA+dc!hT7aF^q`I(NVfP(lw z&V+nuN4gd7mp_c}dr)tH9OPcLy|VF#R9Em&Q-=Iw%E@G|^&aV^^2Hr&(>HRo?SOo1 z2U?5Wc_uXHwNF2Ow>|sB!}h@0Lz;IMZw+a;xcLDO>H8midxw3Q^LFQHwyG}2<``FZuuOgd<`D$#8}tg^Rj}?L^*Zq>@s6=Y;B$C^_Zyri?Qs)~d$Nu1 z4*VQ$s^$98|i7}L$6(?dD5EeIcMfH)g4@#q0-E zn`uxqF1dl6fbaJUH%H}9KH9As<2|LSXCWSvt>2+>7&`$!0(#K6o2Qv=!;(9F(%Xiq zQy{r?urb}undBR2e9wkCEy$(;`PF(f_dflz-?e7>>r=L^xBU1Rd$6m{zJ9FN-aOQ9 zZ)y(N8~PU78;85?6=CLeeMj)^*@OLUng`G=FxlXh3B8NyG{+ez8O*8_$kfJ zA@((>+@Y4zjrvw~==;)1s%ev+n(X{()zzUoP#!z@1sMCl#5LDkV^>~zrQLk<%`Uf* zoD-jq2rq!A-~R32zA%sAQn+Evbhv2`$(!6f@l&yKr6zHMoB=n~*X5h-Q{@l)hI~}U zfA|{=QJ?eua06x_#u)kwJKy_K7;l2ab9(|W5bMk@<#^{85N>!M^lhjv$71)L!Z|ah zDL-wY%b`5dg>1=DUW{}x)fHooGqK8pnycmJ988yD8df2NKt-_03C*AoU0O6E|jknd9MZ_yku#ZJ3qC-imhvW%Dh+?pJ2q^Ait z_xIEb8~yg$VdcT9X67rZuSENraP!*Ho=`i{XRjYo{)M)^t!-abzV_FS_t;yirFnes zE?c{bZ(+-36K?osZL92l5gwns_oR(Uj&@eW+0zf*VPF5+H#J98-}{lT{DkVl zjY@9v9R=pSf{!n}_`E%+I)jZ_sz0c0O`1D&yK2}z{m^;!PqBNZPd$E*edgK6T+Fyl z_7Sz=sM&)*x>a>QLb%cQSM=TX^t9wl`qPj%yd&hzb=O^IAN$zHT%BmfKy-a-yFl2> z;OUAhu5g$_H%HGvKMTqmaD%U`t*!MPdfMRNfRi`O2ZLXzcle_p{b;Jd#>HTng9;Bb zMlct`f{(%ZQ!s6K0sde@62q2P!w) zh~^XTcIzq9OQ+I21Libz;v19?1AH@o!PRwFKGDN>ow6@0 zACvEhwkj^#B%7sGzH{axwaRyZ{C!OIJYRnCGt%3(TT8a`Dkc={laPH=SS+1luwS^*90&Q)zzuS4 zDt(Kc+y!hJc%SEG?j7}OkT)JT-~RTuT};R0hCcDQVHv!D2l~+y_F<5`EJFYFIX(1; zw|P8neB3i?#S)u$y>N5gmHKYfV)2r`cO+e&8cpy!{DlvU+OXt!QVZaIeGBu5<}QzD zKC-LJcjtti7q1>sZUJ(NoKIJeR%1K9IO_AClbm?!-s75k->3LYh2)oVqP73Mw~pDP zcODV;_Syk*tM9$vPMtqyg_@(1DgFO^({3k6z|Gr|4{x88T_e5h4dLP|=xIl!!%4<` zMOb2=H-*hFYaRXVOOjDzyNhl4iU_O9k-X9U;0s3PF#oL~eUWUJ)tc|T%btAjjD7y4 z&)Ng$kLZ4hSMhBd`HzJQ=L3*#*DilL^={vK^<{mhu+8ey=gD`VI)s&5T-{!3W}Q&Z z_w%26L^;wAYkppaHA^nJxprlm`-wjd+@!`^Y9hGV;&4+^T4KATr&aFS<^0F!YRC#? z4t58+6M8!D5gegYF@Foa3;E#pqt**~)j?Uqc`k(;^t8gF0_z{>b+|zm#VhZJbMd{I zsq#k*79SclpcpUkt%c(Tc|%Nx*baE{xWP7I9hrd)^1d`$wt>SS`SA!_h-LbkvB>FZ z($Ub~cE9r8(o6?FLy*)h}`d*qJe zc1HEm_tmSOjck&K&h2;K-=sFwP_1gy2zShLX6^xVE16ez-)%=!_oYSkF!#Bf9eh#D z!GBWY_k(A~^*zlIv9~a1e5?+XVhU%lKwz_IWlM8Xb zYIMHw`YZP8D_^vu{WYp(D;Xh~&~A-~R32 zP8Ha=+y!Db=%ny6J`Cy!1mz95A%`FwH(&vcOB}WK3a*>Kf2^I!uN`9!#8VscIsQo_n&upW&4#A z^T7E-cBkY3HJ;k?^}BLssCP-Mk2ooL0OZo#cV6+Afd;$h)IQal$#ZrA-)(qgYcBCxo4;gGJfGB!gtUqZ}A>1M0#=7lB{Na?r4Y>>826=-V;Jebyiw^e-fE)BQaDu#HUymEM zp>O#XIpPUCdGeUTMhxQ0W6H@J**AvRx}mFVWp zn&-^7TaHT)dHwTG%Ac$_jBJ``9=q4>Jv}a;wZ7${F?m3FXMEeTT|U@$)w=9ZPEDiQ zJC1bNXP&s%9+V!0U#%%?fv}dKI_4vyQJs6WO|)jJ4s4zB>u6YM*}OZu{mRd`sVv6Yh1-p_~bQ zo1s|WPLz&D9T95X3<~3~ef|YK%OR`YHcNd{ZD0Jt3zDC^obFbeqHj;h&Oyd>$X9Z> zwa|?xeX94-u6%)lltfFFy_y`q)iNY+$WN;Z=xKiL8a(VNhy{-kZ~y!CU>n?@{TMq# z_DzfP3-Ipq3-YX|w|f#dozCyXaX>rLD z@Ds$0$BJ)*cnL1M`q0wZ%F4)x)?Ail`u@!|SJs5zcHd0Ie zV&}iQRWoi9}HWIO*(dtdMkDSn5@wT@p z)~d2L*zO#We9BdMv9_y>Z$mQ(zhjZvLq|Z#M9_n;3Y^NGvV#K;UkEZ;IhXJc2P@f zi^~_t%g=SV>F({aTTh>IdC>TtkYC^b{`aR0Zpiro59n!^k~iQ6zd+EwA+Ny88;>3E z;>qKN*5k#uL98HWSaeR_^ z#vFpL!}Mz2bf0`%#NM0r%|miPPy9dB-3hp6Rdpx)m^frINhg|q@)$$66R|Z2$P5M< z3W}-%s*17ZdAd~vMHLi-Vu=FIvx&9?i1RFQNNcwVk3*uyjG|FVB7y?yhlz=4heUI} z_1o|L7n`^5Ey&RA%dKbEKEpoy?6cPTuW9dd-g@umjnBVqcujo+?PZtMcgQQwdHsua z4c9$?Tg{`^_Xf-MT~&7DS(_B!-l*ShTX*>Itn$CEZpZ4K()ymjRn_;^8`brGXT7U7 z`mR;Y-L$+wjk{{U!Er^sNB7$`w;~t)!FOFZyrFV(zN2&b>O-4X2M^D6c!TyC8^yzMp59^PNM=-++!wZs4Vo!1R- z`RzS5&spmP%mH6|ZsnqV7pmy6e$KGw?843I^?Sy(#%5%1M(fjh%=9)V7zlps_uDx? zUdM+^UI1<`E!?cDJjBw{ne5He_FUfV&7M7bY8^}chE>@n?@><5AKX}XmTxk7r+}Z9 zbvC`dNqocJh=qDv6CbU&H*nL(J;6@=w7bVPg`4U5o29j0;E4zPx8dQn9{OSb`9BS( z9{0rI%ANIld)v<#o;FqA$=h~T{XW<^^&5rt{eaD-^Oqf6rQS_jQMss9_1jCeZfn<) zBWs@U@L^|tA8FUJvLPiG&#d1RyL`*S;qp!8?`%1>zI$Htj!SBdnDt(@c58QCfAQ-2 zhFrzX7gzkVyZoOEtG;4ueP{LZ4dpA=7`B};T(z_2fG%BE^B}8+J!Nw)DjRW0`HK8S zzq@-`<##V#Rd&R&ibXE2V;9#k{r1jNDlWRDz7cY9*|JNV zuQfD&r{l5>C$;$hqWWHlzAmc!UAUsw*wmPIu9{Q7M=XU%R5^oZv=&cT)a(+>rTeuf4YUX*1!* zyE*b>>}K+1eU2}5zt~zr#Z~c;C=jN5e_O(lfjY}7`cTl#ksr6Dz%a>ZVXjorsi#MEG-wQiyUcJwG z^02&qS9!&v^7j{2j%jhpQ*E!PK3AMxrPg$=tablcUtoW(EJ6m}J z_g_=TH`I0O&aU;$%WFRJoKuH&HRko_o>n$uL5=gIVd;V=59iFU-{-D%KGj)hb?v0u z8}fbKb=U2UZ{VA~5${-gBVR2iVBXSvmb|@O2OOpF;S_5RU<|&pu2UU5rHykve((u> zjN`z^Kdx|7bJ73#p~F!%KX~5OZNtuO=MCp?o*K^IGBsSZ3?#JFk8Nb5u@0rRFagsrdLOOErv0{hpJv556@%@*WLULy`LJrq@^*aJwjIOfy7r8^ z&iLAWYfS2Sb1Khw#+k!Z(PCFU=az~c*H^CZf}-V)Dr=Un7&e#e?%ZBM!phc?nTvMqZnAq> z$>H{Dr;qI=*XJ&(?_bZIUvhYQ<(4W&o-Wy5RQt-}dJlhZDVbf|N*%B3?kc0>rxwnZ z)%%ymb@?Sv8FtlsWxERV3+ozqx#FpN>bZ8_JMbFZ$P3Uxr=Bu*cPjqSFSDy>&nz_fHP~T~q)5<1g^E~rT^1Fo9f7zxZYmM3Aha5Z{f9x^Cxivn1S-;K(j_`?$kOk!SUb*7m*{TccPX{pwf0s&w*I&3A#DuYUEb zlVWb#J9NpXkzv0XH;$Wp=hkl$z{MSR+|lNs%tL+s>tAohT<)EB-Z^~po8KJ1{q1iz zT=?#^-_3zDG|~^;_-$aey_>eVJeLtbT!F<$_^}V*b?ukW%BlR=g{s~2&_H6Ci(e($`T4eV=$gwKUj|!Spsq^Xs z4;VeiuRr=x!^11*|L8~jm-e3Y+FFymz1~rdZzir6t6D>I(@i(E-x2dYmv_DEU2Pjy z=tapx_11>)vJ~d^J*>Fla4yF;ep*D-|CUv9M<-aDA_;w;Def+97zrjExlQ5zx>{T zazq{T-J*7`jvsyGQB6*du47G>>-^Crx7PF?Tyv8O`BV;r!$YRGYmWc0+IDPIYCLJhGOchu;^ocb$TGzqRv9%UKZET+7*s`Zb z)R=8sPiU_C$RldM#(k)9y1tFqm}`u6JkNeOy3}=O0INsTxe*=V_ORiA!X;d>fvZFyK4{H96zBA~@mpVhk9*5htqCg|w5np!mP4p|9c)!; z-||pT+bQkRhZ?Q-ma_93tI%7m)VWRdeV9$t$2WEPAh`)Sq0L=!p3dD;r5&H%w!fv? zwYs*ay8X6Y`^mwoN4choCpPO}-St`hOv&R`$?>hz`>x&S`YLzzS8KlN{HE!$S^l@S zjn94fS#7_+rj9$G{%yDCt>S|JbgS9ZpQ5m+`v+A zYhrI;B?UI(!|~@7SmDcA4<6j0J=qR!BKzci-P-lT#)?_-Z)@QP@9?nkR9$7ebx{?k zo>%!X_4&2d(6-oBv1W=K?3gYWR4#3273&dOskORlYj&+mOtHUXx?E7l+xc3rxKq(C z+f`e~nBu*}Y^T1k&i5tvQ0u$zFW02at~!6=bep~7sc{)k8<#$&_xE0F-*Z;ou9<$; z^Q%tr%;?hSGkyNN+MlYN$zD1aeS?|kAK36dFrSMbqK+@-I4qj`#cRn(6b7pV4ja z?c4DhoBOcC+e>dW-B3EiHvQ4BPnIL2d-~#h(?{&gv`*SK-G+T58{Qods|FwUWKWz= zft|j+Z913_&sP^q!A;^Dd1>}WP8MCr27Ns#e{f@LJ#NH($)^Q3{4_S19ZpQgUMG*{ znp_t@4(?JC`}CMgZh)U?{>OY!FN@|AS}A!j@6#7^teIzJtIn>X_WKiO)wxp(Bj$+k z+kCVf_{%Hj`LfE3rpS-Vg{CBzntZApt0GsL?b&MYU!;fuQXG5bbZOhu=Voe~Yf`7p z-L9+ab3E-{^{Q93XSl4^@O$3qfpbe2@qy)g6x;VymR1}2&1F?uJ?%rAr!#jTFC=X`2@94yJZ;RD>{Jp${j8gA;nwP!u+>XM1c!_9ZT^PM&a6F;rbr;+u< zH=dJkYu%@{IO0FWH`&F6u2aM-Z+qL@T5Kpjlv@!)%Cn@r=RNNkuD|~JR^(vFDcc}^ z8TtPs|D2w4ytgko7Jn@|bE)*!mZ`%}g$&jZ_RVqz?{GVVwA`9bH{Nu}S;vHw?f zKStZ}dHXuY={kO|?|u92Z+;kmlAYi`rr5tXCGGi)w)M+TL(>m`_`@x(5!;NHeVt2N zebB>lNA%{73+lVm)AOEiGxF13+2#i6QnDe>J1Kv@5n}F=y`hu(`5SQs#NtTk#%y%0K+WKMXS!x%Pi+Nq@P=Y<0$! zarSkdA+>Y1J*#IXV|1OuKFm~Ri#ujIE*6Pz5kJGe?Y=lR)A8|Z`guO{Aim4Fb{#*h zY~*wvnxAG)-EY9bjeaNP&o@HM`?EJ}IQxQ(%^#mz-#((9f}d zZSkzegwNIGo=U);hMMu#=3fzj7jDgJ{@A!hhu@AlOjjnYbZ^;;(^s$sa?hS5S z8#x}Y@!380C4>FBae3^|jrX1XaqNua#eQV8m*1JT$$K!Qz9+lpymrQ!d(3qIehjmn zzo+BD4c*vqvpwqtM!4apz4E`mqI{t%Tdqcahx|;+A3FI>9`j>jN^@9|*_m*|SBkA+ zXL?%`pJ3d^1V6DcR`sUc72<^U5@9v?5EB>Qs;Q;99PdIe;Ie`Uhn7mTxYx27q&waIz?f__2ZmDCL zPMve=nU3}6_qA<&o_DXkna0iDd|J)lz|F`{tKU>8*(67k@<&(isds~t7l0eGN7wg$ z8m#cs#&Hw7ll%$0)8nT1&ti9ih1i~P+_*oS=+in=c_TR_c^AI0IFgQ1e2Yhp%XbmX zT`OWNejgc#Z->X>6?qKLd%5i8H8QM@ov=+7w9B#7Ta)s~miM@k6PwN6{CjZI+nkwj6YM0`A@_0(=62-neP8OJ zT0geme)|oNdCX(lw(r_K;t`K%+i)-MY~CloQ3^NFqx38c*|!~DMh?kHFT3O(Z6e?H zqnr7*zEzwem!D!DIoqkzZl;=VG=6X6=#SY}#xWB43uf$VCtpFv%x#*NLOXe%$3FJ4 z?Ke4nN7EV{-#znf=VNQFk2do9);Y?MyJ+^{$7jyOb2Gme|_oWOWKppSEU+$7HF?M;sx z_hfVAt$kOBuEEy>Ykj2eKl;Wh9^<#~tl~Yn(8ipD`Ez+SI#*wbx%iaysdF$9UaH9( zTEK_ec`~iWU&mlh-rhG($NwDjO)Yb|!HYFaj`>F2OxNfG&fL4ltb662#$($U$BXOe z05Kt+kxBk8+{ihigSk=PvGX209piUweeaIn^YDj1eE9jF|M^Bw?^K!dHOI&%(Odet zHyFvd?x}6x&bH^g%ieVB(`t>nIX8X=-@=&40N-X({?Oj_t)%`g7qFE;%HTkzyN;ogzL z|DHzo1`~Z7W9;`skBnd4Cz=_>^*MqRID&5f5vZZcyMFRlCBxY%}nu4 z?9Klz+>m$kn${BFE8oJmyy3`PDE`}LhwvMo;LAEiYf$kD7R~<{znlUa=NO*F??MCE za9lfW zT(F~d9b3Z>@QkjZC)@{bd|R1aMicb$Oui)YyzYv%D}QvUFhFEkTwyPi{FS5`6MBbezZ=nU5 zMqgNRANSN({|9sWHU`*5Z#F|gYwUiF*jfhBl#jaUU9*kG99BYlNusqxM>zLV$h-?8AyvG9}(l6mJG zbA51we|#)#*WxBJFdJ@SU&scT zQH)8k&F+vNh3;ps*aH5O0;}Wp%=ldo1NT($G(048+S4U)6&Z~DIM>HF{I{Ib9&X<7 zhBpqoFWx=uys*A`uyynB{tvuwug!r!_v>wr{v21}Mt|C54DM+hj;m9Qx!<;*XHh#x z?}c`uA0L#xM@Ra~n9&QJ)vhrPHcRo0YU@hq2;+3G+|RvIa{S)Z#uwbc>iIj)-}~J` zewue8%ss$OzU4ypCgqQ81~+oJ@=fU1+Z(iIBgb*WPwVGz?ybGSFMfdHUihdlYk2r4 z*1s7CKgm7mKfV);!2^BDZy|qZA35peC~`-hQ)a@DYuN&}Pb@)4TMsI>F()MV79Xj{ zjT{+SB1gHed%pIy*R*dMz56}y8eaOcm(;onoHG8fQL)oFji$;ghk0@;`hIIZ`%}|4xUJ z<>+WO%k#u$k>ixvu!EQMsWq$e348)$a9=qb?-<0FA|J6g^2_o6$pCpG2j*dSm5=5* z-}CPGG=1~UZ;o=>uGiLh^QS-8kwrSe+^)Q)XXclx`J?Ebw)*I0aHehdO3|Kfpl4uK zOhHfKwRa?}7l|M2-tOo5`NZ;;&hdZg5_G~>H1^Et(-_<@b?!aW*c>ya;O2!dd|~;K zJBAG#N4Qy1xVd2G1x<&+4a_Lkc}>b6S>ykTZ)U;`4DAayTpJ(qre@58^>bj+Q1ezTo-&&W&ouRqth2dtS( zlRNZ%I^&Tu^xHz7Q(UC2K9t#H((%YJoRDwF(1|TKCn6@K$K?X_FW=_3!&1;8v{&Pe zcJc=Fo17cFWX$dXm*^k5?rUt$g$}N#YkUW4Q@R><@e5zzWhUH2ej{IS1JlN2d}yK8_nun27u;xXJAZ6M z=h;&7 ztoCgyaK--7A!0*%#dy#iR@iHph%YD?*?)_nr@w7JGTQS;T<3c9etdlc2kp(Tu|Ih2 z*~C@-crvcOcAoyA>s;IHP2tA+v~!o7Q+a~(hL^qkrG=a4HXm)$>{>n|zPsMtpN^eFKpnVonHNtlhDW%iI=RC9Y3=L+V0hwrk}DM!4BtxOvIlaKo-m%HKF{@Q2T-zztb2XDN4%Ch^n8a|3Xr z@X=t7EfR0y7n-sSVoka&{Ob8)JA50j>AUe`c&BnM=Ie*WcO{^ceoat+3hhvXJ^ z#&ZK?nEb(wn3rBRFQ^amf&10`s&|Bg8`qQX{PERXOD-bEZ0x6s4xg_a)fY?We5v%( zH>Z6ow#In+KlY2P!w=kePWZR}#XP04n6osm*86GhmlE6re|#qP-CVK49*}c=hi2on z@996=xg1Ax_(mJK3$`D|vj&Q;^gL(-Bj`Mi8)NTrbK$P~M)szS!-`dvkEy)C`8(j| zrIk;6UfG+`@0NRqcvAl011;Hb{!;ACzZN%e0ylUs7D8is5wyW5qz^+I{sQ|J(nKCVNCEW7aiQdSumsa9PE(3 zbt&Ky|TU;zU1usPRF+P9Z&iXZqULU#iabf2mKM;us7^QaKleCXUWdMyPB<0`uK*g z<{JJS+Vaowjh~QjE8#`>0;BlS7snL*h%W2T_uH_QecSoolY6+QHH`cR{qQq`o#2KI zQt%XS*&ElfTlBL2_-*hX+-MIA{U7|0XZhI3Mr49MCC~7}cM4`)lRy1M=FyM8BF>^` z#C&{O@s{{2xal!tTTG#UegT@0TVv-p!3{kS|7@IY_WOeV^blRD?)j8BtB-*^n` z*23|bt>y9k8#Kr~S8sDdBhM`NE(Y_RNw|?0fSZ@R^u^6jlNaI>P9kpb290L()5dWl zwn=;gKjInlH{^`JL}!viv{ca6IWh?s3ch5!uh|K?Qo5e(vr=pBB3V6V1NVJzz+mieuz`Cd|MN z8xh>F)t(vV-CKO6HXgbA;D)`4%`q=C~0soWF%$)ED6`8wp6?hIyn+RwD@7&$lJMGk_m z@XGn|>vBwue{>%`i;wsR$Lu{{icFJ#rN@o;9^r}!-5-YeJ@N$n=W*Q7<*>;o@J%8hvG}@!2wmdTgylpu_F+CE1VI9DW1bh#!odtdc*mxMGcP^!)!V z+>oW%;{z4#@*GF#Kz8#)(O`4+2IuWWvrZTW)WX43pL{+Q?87jD=T zYaDvqh;7W(sO6ja;N&xQT1<+j{3g1>bHmnnaXjZ!i-pBQ&hv+{9jt+mY<&d|%IY4qjkEO;4GB7ay>X zif#Hk>jmWQ;KrEE5wHpHMOV>N=)=yEUv+XabpE|5=%)`@ghTtT!<&7T+=srRyJG8& z9o>&P=ICbA=_$|KRU;DLRYyFWS zJ_qc;A^yqD!iBk8a+(;G?090PjEGbm^GyNYb>qkFyU#ED!Y>Rz^;199VlsNuIK-E9 zmKg1*qmFF9&2i^9?;QT&+oN@`^3c}QUUl`A!+Ga#-|MGM)w@eCdhrYI)3{+<%-#5& z1>DFx^WDs0(<$^1Tf@I$v(QV7&9B8fIVyACzNKO={9zA!*xgEhOq*Z%m0xMac1nMZ z_9^;S?3?3%@Pi-Rc<23Twj;hGyFc_(Tx-6G zt>MG5pX?$X&(4b@$SJI#J-Ybj4?2cUJuO4und-C)t;cKU(^PR}4buwQq%zUl zKSfh^J^Z5U@s!W4@P!q23BFQX7tA^5IwjZXJ0*U&cCdwCbf0x*w#BvNDRHj(B==+^ z*&S={qRZ)eIEwF0zr)N|>Rq^7Zn=5**0=wD_~#$~VEDnm)Vz#;1%V&dvY8*&MvMDL zM++3NaaT!B;I6tZ`j;P6o2u&%Mq$GJj0Jwi3*5jHpT;wYY52r!jKb!~1@M*0ap)Vl zMn5z||L_1E93Q8DPjhXw554=EZbV0IV9R$t=q=cRBY2S~<3FBs(#gZo<|z(8w0@)G z;^CHCZ)yH7-NkQ_J0pjy*Yvn~{@rjhN%kglZSrY+tmL6#=jOr?PX*BzpsB|`2IhAuP|dCvx)kD z*nbUy)hU}Ij)I?Y?AV7JxD)T7sa&5t0G`2&ZTejv1|8^jG(|JdihlA0iZ&_elkJ?p zry37?KH>p9;frm0l1A0fZT=bj|om}}#+pdXoHQ*JK21UKxBTr^& zD1TvL>6XhIeaJBEh$;9<^o==@yfetxovFyX%vAC{Jia_Vs~`RGQ`|@X0Uz;)Sm8$QdeB#zW9X@f(pANVD?QP9gHPci`WKWWV2zKI|$s@px`C#;r zM>EGkEv3vGYYnPIoxLTih6rQO0zWBnQ;)Ad= zY!-hT@5Q_PtdmbZsc?MYaN?X3hPS@$t;2|Jd#I1SG9uwscmOp z8}hE?n(U`$@A%F7#Y=d?cgJj#AwC3oqL*O@Z(y1Jg{gPE^Bu$AeQV?!^|l6Xe5>Hb z&)hiN`sMm=!7X1NzINN~!=L`ujl(4$c-5ZB_@7Xxa?~46-*Qab6 z=3igmNKE;iO~c&l%jUgx!*Ip>UNU^;mRpBAzEbaY-};rdUI144YwQa=kK=|vLv}rr zyt$lVbOJiFHS`BMYJ*m2f}Ua_#c?J3ayr@O1E!>%I>-7t{6c>;XBSpg4qXjL{5E_s z|0-_*pNAfH=y1%Fj;`-bJ-+X6Q|wkH5IGX`ZVw2cxR2hAxH2hHkgz@xG~<| z-tgUGbKph}fDh_@Fh1M$zjOUyJ;Z?rKB3Wz{Nf=!PS4XpbPxHY59LVF9$({+i#O1+ zFX)XIc#S`Jsm2rAbe1;knClh&C09l^?7I&;M9;c!a!s)}7;hugne-UR5?a&w9Lua%`N5!@JQM-n1Q}m-x zJaL|%D8`bTFdn`e9ZEK|n|sRKigC(EIO>R+3%Ic5C(Vh;H(9S?e!w?7gPZ3*uX6Cu zd3Ng?ZYIgz;FV{E8#Wg9$qPSCfg9h_koSDs+uq*n&70o*rutpu#m(lDY4$+=6V2hq zGr|o2Pks$=(G9Q2F`m+a^prvm#RtH5G{+14wjazzFS=i@wT)-^i2rzp|L%eJxi20k zzvDUK=8=zj#58V3-<61;7TkD#^H<;h-uH*^R*vm^-~XrK+yDF9!{_e!!tk*#f4Y^A z-|{EJ$8Y)L8Oo=JkKbCy?^deqt$#9n^p;N#AGx{SZM)-d%Ezy~ol?00*pSw)XFl{2E(jywLz!x2Z-H(=JUYdI&+D(6JLF1~no`J8L-!cFC- z{SLe5Q6}lT9Om*oC*07-J#P4(-d_~Y&vP)j&Iu$-e;(7TP@hH6QsWlg9jm~&x9+G^Z9bV`|+$nyM>w!C% za1YnxtG2Gie|_O=FJ}!mkA7ss&ENjr-El+T!w}qvTiajh`X5Y}|A^E;bv5Xx6+87O z#|^WwH*k{@+*nUQ&gm0*LUf_eJ-=t{pPS5ULvN97_7|Pyos)A@%d1!yXP!4U3(d>{ z%ZI^V^b-EcP3uE04hG@UJ?KdDwff=@lLP!UCS!xi6X%@RaC6Q%XAhtH`icU^Or{7 zk0aCnQu(eQRNVg0l^2kA{`dUxw|@A;DphA&fAZ1pJsWPc32xBAGt*D#LchZhy}{P9 z185X3e|ArRJ$&o-2IHp?m9Q~_UH<@ z;kUpI`w~A*ZQq}Kn&%+va04&NdDus5a*s~z1ij%n+<0dC$^0Z;#n#B#dp8Mv`9Z$3 zh|YAE`37sLV3oZH-C#{0u7^?Q;1}Q7n(&_eKzA}>jQqrtr*X4j!Tc7l$brBOoAcDm zpIUvbYwr=t{)PlFl$hS)~F&luI-Iiu5zl^w(ze##L?9$tR_{5Izt z+_)cm1urcxP&j+ebDp~wH}u^keOFyRgUpZHn=h4bAKZ8*@3MMt&)B{9J?Eqo>zc!g z4ktI+WGnbS$q}HV_(tFIb!-8;lX)}`ZpgiSrF9{EY3s)D%DY%-jrQ~mI|qMwAV(A2 zz#ZQ33)m-^ffeJ3y^(){9XG*`l z{0p{<&B06jLU-{dTXp7H_1kaF-oVYgh8y(Gcap4U8+miIv)-HTf5j_)yK;(^i$C;`MmM@0PV_-G`Al*v=z#w8NOF`p0j&HueU_*cQG@;v2lz z54{=Our-QU&-{s8F#O8DvpMa+QPwGS)!!S2DW zTE5eqnf0C8;R71S-Xy-Em*@@8ckJ|9Pqqe@(1i{W--u1_fB*Zp^)P79X7jbsTr9)S zBj<4Dn+tR$ov45K(8o{z^iK~z@e@DM{A;w=7Qe~4Ys9qrF&;iO9Y$B3amM12zCEJi zn}w%0{~vB(6DF>{`l^->fE)R?tDklC-EbqXJ1KwsX}WySzw8gY&hF4vutT0;%UCw7-!L3{$S5yh-t$|3KlVFW+i zkFQEjA5^#XoiOL4SNGu+Mu6&8mw5iXU-Sx)rxj}1N5Q? z^}$ZL7A=!+;Zr0hhky9NmT1S{XJg<@U&e&Du3xuKKCR{;%1_~^oqbN_(`vo2SdLw@ z=3YEwoq&AW`VH&KM;qbhoXV%shkW%(`7>^Ex-Z;Vn?{$I(=~Q$B+fef%(nK4e@nL6 zXnLLxg=hExk8+#niM9&f#_r;UqCMKizhE2eqm^^w$;?gQXO2fU=sP?TpTVken6Gvp z_jIkc+Q2g$!tbLVHOfPqm;dsYZ*H>5CzJ>A%yRJbA>Ges5@#gVu-{7A2zKJT-x+-? z?0esJ2uGBjO);{;g^sO)XOnxc+dJkQW)_Vo)%{Xr80nhJQJtur*o+h~x zHC^UhPd{`-XZl5r?)t$aG(u0?nH%bPpdZJz(>ILqE9h5o4&0Cn{+KI`npbJ&`cKQci_z>R0)r;XbiIYK_y zt6%+^hMV`j|9x#<_n?ChYH=a#Y}qo^v}ZcQHA7+0)VS#zz{3mjGU=CHxNgKoYz)Z<1@#kw?Yyy!=%2Sa5=8Fk+2|+!dWq2hsm%8eK+b%~tSUaUPx2al^m!RkH9)I~0TQ+ZLzTP7q@n46xzvFFf9hi8Ij^`K2AMzo{E8i)<5yNj+t}I@T z%4q+ptDZSrb>%aLtFNs3>MN_w)x(v~c=~Wv>6$C+_*GZ#8LqzS=~b?%^HrYpjOK&L zu`185LPyJMp*2z0pZ0jlNwX z#^HDK)#yC(OHc6u@DdHhock(r{VC+#w)UyVuXWs-5wgU8cMoy1JdXH>9P!s9TX<_+ zatmTA&&wC{UJF@+Q*y)45bIcHW8XFE$dUE#tJi3|zUa1lSiSP9Va>{|!^*Ww>Yc@n z!>Za}wPN?MY~|)*`Kn86{{?ma#lyzd$%Z|i=s zr}=(b@&cY4ZP^YsSzA69JwXS0X1W6n(FWb{MvOjQv{4*~y_B@e{!F#|zzDmJM#k$t zYz_LulQAYXJ$Twj@jQG~wwJC_)63hoZ)@?*+I4H&ntOiEB;tl&U>ytGkeQiqBd3Co za+Lgkx=@a5QSrt56!Yev((-D%F5K1h5&0FntKo&ely^YnvBZ{r5o;)pWjp(6=en7S zaXZdGlvgkh#HSYfniF^L=*Z|?u@CvuMh=NxffGIj%Z4(S?liJ@_fxwv>NT z-^iQVTE6|3U3Km)!{$w&uKMa>^VWLDe@po(Q-50fZyVNceD$z?(?<%oHw~LNezD}L z+HHPy*}RQ)efgZ#C;PW@<*H`O_=EHmy<@J+^PvSy$y1vbqZ8P5IPq-cmChrdo>y)k z-Oyi+_W0qP{z9|RGj!h2y=jc&$io34Tx=lI3O2IulMXv{bH)TjQa`JVh! zxM6qXoz6RNd&Q#Xw&#Kw&n^DK1HRm({E0=44-Je>3>tew2f)a+wZ?|;3O9Th_EK(o z+tzKv0Z%w!IAz}4qR9#EdNvu2@JgPSd}dx6J>^#%OTlAuo|62I+A-H?llJ=Ti~baC zt$CBvh8Ou(xS(^%I@yvNphNYCzjUBjQ$CAN9=XDQ&tUBdzLL%O_RYU9+-#fT+pjB( z?5^DD{~9)Ix}z}gf|A`+3qwyUT-{mMd}P?X;SHPcCKIenn;LFs=qeL}B+TxY_eD-LIcakd<`y!qD$Ytg%fBXL z^n-EnDdpJtsq}<>`jDQdQ{jdV>2b4lIwqc~Sa_;%Gc{G;iJ#h4nE2gc-Nri#3(qUu z)Nd$meA;l{mOBbhA1mMQ&k8rUl)b6#jUO5|ZTwQ<=}!t{JBICb{kE;*bGTXEaARE) zo6px_FBH!a-0%-zoo(?f#!04ghjD`_LN(6nxPpbPuiftNBjF zzS?}oT=u(Z3{eWmhtAFG_m9~EA{Ugy76 z{@6FG&8@?hZSSl$TgqmYuh#k-t%EUt!Irx(S)-d_fZrJ0WSx%qKuiHU$;XQ&*iCw# zZeTmmKJ<&+kJq7JYUlgy@%HxNqCZYA!VKEeU+~P2VegHTZ1ADU5nnWGzkQ!i{zHs; z{d=x2zh+mHsWod?4Nt#fPs>Na4IXGaDSzaKe*iaZJs%;sAx|(OKPV?A7hsM`ybL#g z@P~geT=9%6$|rmLFz3WMjo0)EUyYp6mGmBakB@LPTj5vqMZ13g-fX)U-kHnBC%HT^ zGt7`JcEniZ-OYu_Td}w7znlP!CnzbxeFy&sz3o z)5fn=9P^T4^VFhx_NNt2?yNrlplr>b)$v<~wHrTL`)?>Ly<%9q`NG1?roz_tVZ*kH zZ_19*r`F%0EBnrm^fcUImwsj5UY9q_+RL9Z|NESpf^8H z3GMrHIhNWzqPOTBV}KDhoW7EWL4Q7f=Dy4w!jPPyWB=oS{BGgC{FUWPhc#=VP<4Yv(z*Z))D=ay>su3^*qKPcPs-SXdl zui}|MtoqJj?WV6(j-YY{8~>uP_TDOfH*;Od)|SGL@4McG8}^;8NgRQG;%mH8_z1oa z#pbY!utP79Ei%gHh?~Slo{7w|S!`EdT&FnKm(X_nnzZlxh&^-IQfApz8YV*9malqr-oR+whjv)tPBD8`>d{v8mW-HFa z9E|ec=_~ggZ|}U~xSAj4+7y`KE4tQsHNGn5vth+Lu;7MoMCRygHp;e~f!|_u*mVmin!kE!N6SHGGRv=qI$bmY<))W_uQLXPd2ocQ(?R z8#V`T$hWwRY@q@Dj90GFuecRI=mvFPoL3ynwtMn#wa>Z!ntofG^sB}*_l|#tj&Kjd z;zjWpnKPejUfH|`EU}^V*)yMc#o!(MRrMXUr#|&5%}-mgd`0Q3sTOlj3T|MYeu5ja zMu){uqifk0`42LM57uqb53xD?3jQgYJ@&D`-ty6MR(uEY$L_#SWZ6CX;&@-sU3+UT zyoVsJpY7W8LlHDR{p2aKO@Mt>h$agO-Rx+3$To?~hC zv$Ar5dG)ir>Xj?a)1J}%W$V(!k;Xt@(?M*H=j(A}o9&^a;ewrH zQ|T!(>ll9-EqnTePUE#Up)>sQL0s>cbK@n~_kE)~`qMMUO=jc**&1_%)|Rt1)?moT z&|S%;{qsNnpz+~(&wFm0*IH3D5z{@h^unf%o7!{W-K6}%4gD|^Zs=NhPrfRD2R+PJ zvq5x&b%*M#p|ZY`-G&)Fgdg&Vp7@vf?H(uN+x!b{*#-O~2e}Tf#@pM^aeP){+sKX_ zfLwsF$YaoT;u+9ce z-SCDtzP|O(&zFz&jv%av7ZiDXx(!a)8?wjd*d~M48IV)otQS`U549kv;=+}Q80?YF}h{BjTYpr6E8aJ*mT+|b?{S#lX-#)tbm2uUc9;}Jzs-!(Mn#b=+bZLZOt4+%43 z89L9s$(uZ_JQZC<&ymmI296vve?z|-YvylYUXFw;i}TP!`TXZc??wK_U))%F<;LN& zH-2`w=?h;RZvN8f-B33A#?m*AfA+@D3|}Z)MhC$r{rcI@ey-&qK3DaPpS^MTtH1uM z;mfy-zPqK3G2sE(Cv$vExpA}zZs_J<$1#P?gnc##exf^I2kp@ZokFvjXqsbuDg{Oy z%QbzSzQ}{V+4x|Ff5~>kuy?k|m@%VebOTz-&C)M&>-=RrVy~_3lv6X8BTuklhHqk!V1}-S8NQA=U4Aisu`l@4V~74DubxRfEgqLk<7ePG z%+QNyVeabEOE0M$sr8NPS}~3nJn#AK+bnN?=R2BxTvz9e%3Ocgo zXr-WWUuWC#_`Urx$NTG@Q{YG6_#4`>@$N4#$j%s_d;nQvSHv}u8}<`!*dSQqvu;0c zYsHgG>f4sv8ol9$Ows3)@@Kr_FE)biNM3-vi23>BWSFmo{$gvq5xdeCd^9wO&4nHD zk3NqreJS4I^PG9+S&hEYQ}kH)pzaHP1Q)ZlNqacd7v9ma>;=BDGi-?Qzy{33*XnVD zm+S%iCKpIo!VUdtogsPF2fT^FJrmz)H23SJ0mX1IWdX3Uh?ziH@&%G4h=K^ z`>y{|YxksyU`iBpp zr&?PDANu3krlc?Zd49CZoCBRnKFF3BjLyOX*s%@Y^awuq;|m8jZ+i0^hx2!w*KlLL zYn{1(8n;7HJWUVw%`8t$lkp9 zEpKYS=fU21hi%uc(QoadnRk*VlXzXm&S zV;leJ=+E5vnc=$EUsw6orM3RE<`io^j{C^z`^^vX5I)SrKj*_gwKi$*oOuK>s2G5a zF|STO#G&S*6VC@TL=O1kw=Y;YUjzKwr_mn8!F~lF#P&s z_iwuEgcFW$^r0Wnvd2zO|6nJWaW3uU&Cs4az=&9it}^G$KC#JnVPW)Jb7*B;_z)d} zK4}ME-}=_y7mfd>a)WO!-0Y~eHluew&p&_k{cF0N&6<=yW0$Mv-|&s{?iAd>0Y43` z$SB;%)1W7P%6EVr*rJog05AhP3O!2RtU1uu?@R6XUw&Iuad?WajvPFClu?hQ+ zH}1vHP^Z9(d%|_{L}D26ll2{ZCfn?r`;lcbNdA=Ba6>PXS^jqLKyJw+zQPPl;CpbA z^O4zJj%~w5bS-&M9Lw=>zN5dAKkZyI{-;guIaBT4{aDf`JIVf$@BJ#jCHA4a*yp87 zM)4{fTu`!l!OjaB?a{w4=#M}6BnA|3SvxTDRe!bRPUY#*+7lF5FzO6K?8Vf^}|aMhD%FCzJAL?B+`N^soUZ{QVv`;u-QPFF?}dt>fF-ooUyen3KpglY$*GPlxdB z<(S1bo`JpLv+&c!5OQjXG3g^Q4&1;RA0PhEhuvXoV21yuPJE5VzWW1fVjbU?h8yb# z`C53YPO;CXsI`$JlVgHiv4I>EdqT$H0Z!nh$4qZ?w4wV_UIvQ-Fq% z^TDhSrK99Hmk5+dX2B-P2gzPrODZ=$_EjbNW+jhvs}y^T0dG-YhHM zepS6s>$^7I{q&oKllDjMqwB2)U^CcEb_0FHGGfYA6$8ReVtdE=Mq+9)F<;z!yy9Om zhU?)3X3$OStZ$e^Be62yU3+#IKlCl{#J0h)`|%&yJpSah{>HfYqHxX^QP4~7%(K#i^o8?^yg|0rIhSvjq8D8w zr$X0w9`s|o=^@XEmd4#^tgUael)aIk zo0LD#%`TuBKaD?xZ|novmRmDkwuRm1Ly=|l6cCJZE$4R zz4Qkw+PDtSkAiXFaCnID=%)w`qJAo(dJu5z=6&(gA@=^3MTcYN_$)$)xS=T)Y&%IsU*MQIp_^J zV)jn#lwvO2Tqv2&Ha!DB@RWjv+Vr(;=hK${(U)WT5J%`UZCoz~H!k+y8X7i%?HVtR z(_1+%zX|_6K6>25M#TOEL;o6CgaiH=zZRWg3r5hJFMzlBp-uXQ758R$*g${!q1WhF z@qw5?uFpIIx`?Uy8s5j}i{OcUvS8dlsz0_DX6QotksgHw@=w2u`S@FMS#ZN9nN#pQ znG-YDXx@jQhP1K%IaV_~xVQ0lv^a%diSLl&!tJz{S zLl?RduT$(R`bh3KwA8nv55B5m+qK4GY@S1%q78l+gK_E?_R*6}lU1?{Cn>>HYUd;0 zYVs_$=R1l2(3XyZ5B@d&L_e|-^rU?@LpwGEPEsP@_-9;vJ%8@Qe$Z_S?C{IsMn1xG zc^>rioNP7Q?zw)HxFOfZ#vkE3$eG{~nL{7n-XPQTG_3dXh4KU&YC&^M0h!~6x?#AnhMEZ`U3#Gitl-X^&RTh4bRLvVuL zWX^N)-#rVv(%TsJ2ye6_!*cB65k>r>rqAIp^3O*lpN=P~hK1&eS-_V^L>INHgX1V3;==IDI(fNiF0$Rb|R zL;N+eO^3k*S?4$78=nDx<;L`lANauEgOmIPGpQZNKYqV;M~=e{T}KY-TRK(FPb@&z zJcs9}%jpGk4g4K(=#TPGT+9!{2ejvB^B?ScMzYFQ&;{ahvH&Y&(R0v!$CiKYS;!Z8 z)F1kid9n{5d^Yqa*K)iHc_;twfo}Ac>)cO!*ZJ1BK48Hy_Q`Skc!_te$2+_vpT@{m zz23Yj$o*TZ|cF!y3 zgAsk8z2{CmiS}qpe(?@J_|x>9cI>}c2b_y-psi>%w=ohD_qAyk(CYdH`SGMSC`t z9;f^G5=otM*-aRK;i!aGCoys1NkG`-~*~cgJcRw*4UgH5-aUYn9e8COc(_MJyoIHa* z+}An2hBe)u7ccmtXh{b1r=LmsW3$;%KBRGz5j-?z+hS$DCcFeUc*4Jf8@hm;pbLE= z2W5UN^K0f~Y>TDHjM$ETC7<}O&^giV{A&C&F1|MXrRF=s7tCqT){slJbuz}3n3CMU zO=@_`*yC?G@3@-mY3mu|XL)Y=8%-SdY|d$?IES|i3^=BCJom64nMOm$9TNkkoqh5R zw`_mz<2vo^EB3V^o8;AXj`w(BbKJ*s(I<2w8*_8nCpB3mxBQ*Rb!4%(5qvu~AvyZ+ zz%}$68J7n%=Yy~2*5m|zOUS+)Yp|n#@_Grk)kRP=3{+D-`_^X~>%t7{i^T9d(9E?Qw z(|7bKUm$*p^Kv$9I=rw`c#da$cJ1j*wh52fZ@QH4C}%~^Bj4vWs_*cxpW`!EDIay)d1 zal}r*1D-~%$pSfMCmaiIU_(22KnK`CL&w;S*q~rcJjDMCZX6FD)UJbfI!Ax#6#Vs= zF$S`WcWebb@UO_GzWGr0*>dN}C7Z<8hE4vrXEhe~0LHXKe{FB5SdxEkUc%fTKNIip zklny%{dnixnAthk;Tv7d?!YDdy2icXi@##r{8!f-4@^z^ANulp`D${`>}#V0H@2N4-0O>g?%;zmc-!lv-rWji1RaVH;O0LHlB-P$f9SKGolw` z56Cr);O~C5K9G#ifoSLb5;S&P9sxG#dG-Vy@s#~B7JR`U{BmB7)G;>6oWr%{FTkI< z#Ml$~PU$g(KYa;yJf~tm+rdq+10y|#Y~%fSNjt}b74kh(nd$sEZi2I525uBMSK}MM z5M*vo~T5`U%a^hmDn=qXW^K?Bh3|I5x`j(@V*3X-_`jMmu<=2jyt^ z{o)+B;P10V^3AX+zoQ-g;iLPo>&8w7`Ed7@KfWv3CU@ScAX9v9^kon53|{y_Y?9dK z(8CUGdcr&e9?+*~LXW@<{lZqmBppLvigD;yJ_A0wk6Z#<OdYoMHhw;qV#d3Hd@5bK}hw$0hd-_u! z{9(_-Cb2ba3EvAI=rn!1UXIRMBC^8v@MGyv_~QTJ5$w3${5!0(U+kN>lRo7e-k1L9 zA#xu*L^eD#y3z}5Da_2DKW|uk#^|?B&pzj@@|mt2jz8}Brnk^T49`D?8$8TB3mREx zt1mtSe_gy|tq=d3y&#M5LNCGtjKHYe9X{xby~S8eGpyilj~Sc0i> zn-nbdb#N7X6gw2$s8eEJ`a0N|sSch}2N$V>sqF8o*4A^v4ZS7CSI9hjLyx05`4>yV z1U(=BjmDMyjqrgI9}3Z8)PB~ zZQq#5x!6?PM+Wgz?2g`WA(zj8@U8$G4nvN!+iH1!z8(E%JqUk`Po+P0#Xdg_u3(Ly z%tv+&e(@E;tIVImn4(V@qKnZBX3zzV`8JcDcitDC#gXI}efUm%UC+q>m5Y))B9rj4 zWLf>bZT%*rca7wG(11*eU+5J&hK#f8>=a$azd&<#MD7wc`4@8d><3>A{n;Be|4v*D zZ}6-?GN5m@0%v%_Kcgez&=}blJPB5e4R*%uPVk^D*;CTSv6Qq^#}4%v3YLPQUao_i z*=ksTi{7T>nB&2U>usYW`-(Pr5_^MZXi4`g?0~{%#HVFr319HUaed-#{A)C$&pZcz(7IRoB=ZdS)jzmF zA9e#T$Td2VH@O@=79?cg$Df*U^7sCbpYB(uR*E zJ`q=vWpuzVyKE5|jSi!y*d*<>gA4i*ZQb9!=}~-Oci_N1`FQAOT<$IB zj#hBY57QTa8&>cNpR`45?eKzZmfxtKe#1#_|N|1E1$%;@LD^0ZUxWeh+s}0j{YMr z`c+4E^+$#k+e$E{4yO7#xN^+4=N>PyNqiPM%J}786gHoo^*qMG*654w;_ILd+Sx`A zIy`gHbPt@s0ep!i#pQBbu%j(~rj3{c{n1_#hp-puN4F*Zrw!SmZpYz@9mCB0pnU;-g7#@g6Jilmit%YL7p1Z2Uhu4K~z# z5;cDfFW`awmP<1Z80I&!O?Y7Jcw_$Cd>)u0PsXT@IqyRyYPr!PFMexv<_;pitFRk%C6Vd=QzYYorIbEdxmQNG<-XEuEY zlM0>0x1i6&lKgOdnltC5Mo;u+`^X6VTz>h}_O6TLGtiORz&2T<+l&>@$Rs<-mY55& zc8)BHixs*7pS59Y=s_6LCbl)>gduub>G2c{srwT9G#kFiS>!0=L0h)Qcrzw)mw8Fp zgf;guhKxzgrX^maOV|?FVLR9uzLPl=bQXKCD=^5P72A+uxdihO^p<#q4Pkf8*<_x8 zK0zz=rKive2H^y~(a?Cb^Nj96Z=fsP>vur$Du3inPL;1lZ{FAbj30K;4^8~`A6+C@ z!Vb|7@Iv3C6}>@M^Cj3JHJJ^~)|8!Dx^zkN>E>1L`uG!$AC7sF->y8m=}$CAYuAZM z=p?*xpDkN9H{Ot4a-!~wwvLM}w{D#(8C%%;4DayIGmt-hy9e1cPJSodZoR2v^6qpx zUb=>C!V?U_G}|RsQ)~xE!H?%nVH5go`#qMj9ov-q@>%5QeXocAXeyy#~P=z)%80G-?i zP4bM!r9Dh}K6rt7*SMYzme+wt-~S+M`NK!=kHhPI^$$+`rUkyq@4^#0ql5U6PU7>T zk>^Jn_L>4+4YDf_Cr9T#c(T0wZS-Am>VoDMoN(gk_jl037}0_a@vM0c_qFdh zKE}qt8y~&%4~y%X(^^?j`G%8EW{*bk zlkXS$=GUBxr%s-G(sVhw^@Vrzn)Ore{pkMoVT+wHE^@?g$-Jpp`r67bkbgb} zd&=*&PcOKKnvC=Xp8C?`YNl<^NnYWBUE@EBX~>zl#hB2Oe=KIVk49`feWtB(kYTz# zMZfff{>Xc<<-D;+m%tYKql?-#Xzn{|i!0yb+4SX}p4~I(!!tVWzHkp4aLJaD6=OC3 zEH;vR;}eKU(VU+x*Ublq4>BUolxGlADdL>q=)Q;@v6;CObB~^d4QK1wn42qpha0*B z9ocs8R9HLfPc6RJP8;)-=Br_g4}jMAfmi5B78Ei=mPc{Pytb`>Jj1*2&-d}FSXT@L+rbGRg90*6_odqS`)7b1bN_6sXEoo9r(!tk2=2{VBK^tH z&^KfP{>TTtgBN@TdYv9slMgk&gj~UeeKz1EzV(9n4L4_;xw!ckWCJe52M>DCgN7gb zu^$`mcfb1$_rL%B+rBuBuK)vV68{}f*+Nf<$7y8F{*fy4o$JmTX zVawPLI^6iRRoo9=9Vf@+nv9cq*T9Tt<3EYrjR}U)$+*<)4*e~j%X($^Vk^|}$8NEy zYzhmkW2U?<8*Ow13P_rzylu8)_Rj=^B8X+>f~3h!}IA+Y{w7aPnmZS7a1SyuoeH_KYe&6^RIa6 zUT~NBj(>0eKkDnraD1)V#-a`En2%KcBmUTE@`|5iKu(m8YaOASB$**kbQb&c`iiy0 z@V-Y!KKadLl~3jR2R`tDcf(7t!~Qr&U-NmaIVFqm!qx;gcnmk>Ue5jhZ|~l6VmG2F z3`_9B141yu4CM?hL&;DQzD|B=tKx2G1t&hlmP+OJrLJr3Tb054^p+kI%gf8(-dB$P)knI*yT*o`BZJ8JTL&3sPYk_;oAmov*-sNU$|NftVX6F9`+l~2 z^?9$p!5G_&zJPse4q}y#&(>)k*dt_jSX}dPg;(mLBVZdY@uh?l?1`d) z$F1>eUc)1PiB;o_@!9f5ujJf2x9VU=dYA4M z@320rFSXmdGu2Q1mh{P38pAn$jaB1%8Q{Oh*g4*a31~ALm0h^kp)Fr!1MbzvqnFDr zv+hEklC|t4F;B;w_gnp7yVC(|bMZB6tGFlz07I|@Q(|i2E9}Bu*Csy0>CC;O4A}PU zir@KrJy%ZGW|>>(o||oQE|yN8k`2a6;&0h&fM4Q-VrXI{)*{)upBcYw2!ESJOh!yj ze2;Cp=P`M_XFcDwY`lSSI3UNxbod9&XLEn8|J@hYWTRWJ<>QJI{r0}zz<(D9AoE)T zr7OsBiM?k3z-JS^B6*%&MK{nl^iJ_%*POeq{MKFV*%Z&lXR$ePD>kY%7h^zv&@<`3 z%buh6H6rUfV#B!6I+7T-{jB0D`m5iq6^NJML1S}m>|77OzP}MK$4|UryPn5!M&e!hri2O?K|GZpCxOR68uZ8=UXc+S>tgoZ++4lruD1t`Hq5I2=`n4Z+zyx zu9drI4NXiRr};P9y|=!ZZ932IAA#&Nve|l^eLLhhSxbhdckU?}SLhyl1jQJ{>~NBf zAm8H**}n26uBdlu5La*{zN}oqg_R@m1m7j`LJ6PPWA-px z*PtCi@U5n;8(&5JKp-G zc!JoIbrLZwF;zVL<=u}L=jQ9Oi`su|50^A;B)TUq(L3pyxRh>5=ip2!F5ymlZ`#j* zQzcxI>=%&mNv8`fSVu?y`N z*F2Boj(`{~`A^5d6Wbnd$Ugi2tX;Hc%UVl&2gH2Ee_=-alx^pIaufdX!+vIX;A8MD z{Ou#YM!roxin=}Hy65>np2stO2>T_@(O1e=2mIP!1IzkA7x>M#!Xvnc!O2A&l-PmU zi{35aF+Nw1x@t#l&=2j@e*JM>d+=IYjD;~_d*OsNCF`&D2w2y#CS*)~BS_cy=1>2v zsfzdEyZsL5aoiEW8yJF3a@@YFV!LdsA3y&5yBgLa$yTugb_=-wW^ZQ{Ef8^7bhgS1?jt2n>!qOHBs#lB@=`_ZfF6w6R9 z{`uPht?M5bJ^$a9vD07-cFY$!Tl|;|#U&VmYstLA1g^j{YZtJ(|hbgc4GbvAI7tC*Sg+0ABfFH zZ_;ISm3oZ#84zCD-R^)4Ly?Rt2CF)|)5aFP!*xxu!| zKVhrnpJ)>dyXTqw8~cp$guYQ1-q1g|5^r#*I2q3H>G7#O%6vNe$-Ki~k?7Q!l;J-8 z;CZ&7a?)owrd>Erm+2cBkgb=kNzTxV$q}6N4h|V3oYN2E?b{CD0i4G%M?k#Oyz^W~Jz3g)^X{5^Tg*WPqH}Fj-(JOG7EjV>ux>Ei02f2^KbT5u78{gQZc!Dp=#V7U_pM)JN z_uID?IERDuAYN$~8HD$EMJ^c!VvfF6N=xcUNX;e*W9xg3?JH8Gxx+e#}g@h!ix9cR5^4CPU0FqO4^hCmkuP$?O{&e#2Yph zuH%z@a(VLE^*9Z~c&&czs2}9NezaG}o}2di=%+q=mma1+*l1*qaignz4?us@N%WI( z#3j5|AFgGxrgl7ys<9Ho*`rD2=iwCtR=9g*l~Pk`UciK2T$n=Si*q`VcL%Dzgvl1$WByONgJl_ z%wA;6;!opYO#H?@{Z%(Tj!!s@>k``#=i;5*xO&&P_#W^)jy(eYR)hWc%^O({?_nUF z0weg;o=4~K;=ObQtfVXAMtUOq4VFrAW&I7W^WKVIyzw1b@igyW(>Lm!+{3?XY4XN1 zIM%h9yB?2~tqyo66XH}l3m4)N`Qg5M$s6NnY`f0q+4DPsW25osYMuRw*6GY0E=cUB z{E+zJw>hSZw)6+=NVswPdk=0U vSS_u;yT0c1#8$$Gbcl0_JXurE)dsn1d^db!$N76tzESOa+vB6=t``0QOEiMj literal 0 HcmV?d00001 diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 0052b0f6a..f4bc25566 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -208,20 +208,28 @@ enum TrainerMode { #if defined(BLUETOOTH) #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE_BLUETOOTH +#elif defined(RADIO_T16) + #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE #elif defined(PCBX7) || defined(PCBXLITE) #define TRAINER_MODE_MAX() TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE #else #define TRAINER_MODE_MAX() HAS_WIRELESS_TRAINER_HARDWARE() ? TRAINER_MODE_MASTER_BATTERY_COMPARTMENT : TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(HARDWARE_INTERNAL_MODULE) #define IS_INTERNAL_MODULE_ENABLED() (g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE) -#elif defined(PCBSKY9X) +#else #define IS_INTERNAL_MODULE_ENABLED() (false) #endif #define IS_EXTERNAL_MODULE_ENABLED() (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE) +#if defined(HARDWARE_INTERNAL_MODULE) + #define IS_MODULE_ENABLED(moduleIdx) (moduleIdx==INTERNAL_MODULE ? IS_INTERNAL_MODULE_ENABLED() : moduleIdx==EXTERNAL_MODULE ? IS_EXTERNAL_MODULE_ENABLED() : false) +#else + #define IS_MODULE_ENABLED(moduleIdx) (moduleIdx==EXTERNAL_MODULE ? IS_EXTERNAL_MODULE_ENABLED() : false) +#endif + enum UartModes { #if defined(CLI) || defined(DEBUG) UART_MODE_DEBUG, diff --git a/radio/src/fifo.h b/radio/src/fifo.h index 35daef7ef..9aff60f57 100644 --- a/radio/src/fifo.h +++ b/radio/src/fifo.h @@ -77,11 +77,6 @@ class Fifo return (next == ridx); } - void flush() - { - while (!isEmpty()); - } - uint32_t size() const { return (N + widx - ridx) & (N - 1); diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index b89afc152..b70edf616 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -176,9 +176,6 @@ enum MenuModelSetupItems { #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), uint8_t(getSwitchWarningsCount() > MAX_SWITCH_PER_LINE ? TITLE_ROW : HIDDEN_ROW) #endif -#define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW) -#define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW) - #if defined(INTERNAL_MODULE_PXX1) #define INTERNAL_MODULE_TYPE_ROWS ((isModuleXJT(INTERNAL_MODULE) || isModulePXX2(INTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols #else @@ -191,8 +188,6 @@ enum MenuModelSetupItems { #define OUTPUT_TYPE_ROW #endif -#define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 0) - inline uint8_t EXTERNAL_MODULE_TYPE_ROW() { if (isModuleXJT(EXTERNAL_MODULE) || isModuleR9MNonAccess(EXTERNAL_MODULE) || isModuleDSM2(EXTERNAL_MODULE)) @@ -283,7 +278,7 @@ void onBluetoothConnectMenu(const char * result) #define INTERNAL_MODULE_ROWS \ LABEL(InternalModule), \ INTERNAL_MODULE_TYPE_ROWS, \ - INTERNAL_MODULE_CHANNELS_ROWS, \ + MODULE_CHANNELS_ROWS(INTERNAL_MODULE), \ IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1)), \ IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* RxNum */ \ EXTERNAL_ANTENNA_ROW \ @@ -334,13 +329,13 @@ void menuModelSetup(event_t event) LABEL(ExternalModule), EXTERNAL_MODULE_TYPE_ROW(), MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) - EXTERNAL_MODULE_POWER_ROW, - MULTIMODULE_STATUS_ROWS - EXTERNAL_MODULE_CHANNELS_ROWS, - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, EXTERNAL_MODULE_BIND_ROWS), // line reused for PPM: PPM settings + MODULE_POWER_ROW(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 - IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, EXTERNAL_MODULE_OPTION_ROW), - MULTIMODULE_MODULE_ROWS + IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), + MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) FAILSAFE_ROWS(EXTERNAL_MODULE), IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options @@ -355,13 +350,13 @@ void menuModelSetup(event_t event) LABEL(ExternalModule), EXTERNAL_MODULE_TYPE_ROW(), MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) - MULTIMODULE_STATUS_ROWS - EXTERNAL_MODULE_CHANNELS_ROWS, - EXTERNAL_MODULE_BIND_ROWS, + MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) + MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), + MODULE_BIND_ROWS(EXTERNAL_MODULE), OUTPUT_TYPE_ROW - EXTERNAL_MODULE_OPTION_ROW, - MULTIMODULE_MODULE_ROWS - EXTERNAL_MODULE_POWER_ROW, + MODULE_OPTION_ROW(EXTERNAL_MODULE), + MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) + MODULE_POWER_ROW(EXTERNAL_MODULE), FAILSAFE_ROWS(EXTERNAL_MODULE), EXTRA_MODULE_ROWS TRAINER_ROWS @@ -1066,26 +1061,24 @@ void menuModelSetup(event_t event) uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); ModuleData & moduleData = g_model.moduleData[moduleIdx]; lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE); - if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) { - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0); - lcdDrawNumber(lcdLastRightPos, y, moduleData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0)); - lcdDrawChar(lcdLastRightPos, y, '-'); - lcdDrawNumber(lcdLastRightPos + FW+1, y, moduleData.channelsStart+sentModuleChannels(moduleIdx), LEFT | (menuHorizontalPosition==1 ? attr : 0)); - const char * delay = getModuleDelay(moduleIdx); - if (delay) - lcdDrawText(lcdLastRightPos+4, y, delay, SMLSIZE); - if (attr && s_editMode > 0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount); - break; - case 1: - CHECK_INCDEC_MODELVAR_CHECK(event, moduleData.channelsCount, -4, min(maxModuleChannels_M8(moduleIdx), 32-8-moduleData.channelsStart), moduleData.type == MODULE_TYPE_ISRM_PXX2 ? isPxx2IsrmChannelsCountAllowed : nullptr); - if (checkIncDec_Ret && moduleData.type == MODULE_TYPE_PPM) { - setDefaultPpmFrameLength(moduleIdx); - } - break; - } + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0); + lcdDrawNumber(lcdLastRightPos, y, moduleData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0)); + lcdDrawChar(lcdLastRightPos, y, '-'); + lcdDrawNumber(lcdLastRightPos + FW+1, y, moduleData.channelsStart+sentModuleChannels(moduleIdx), LEFT | (menuHorizontalPosition==1 ? attr : 0)); + const char * delay = getModuleDelay(moduleIdx); + if (delay) + lcdDrawText(lcdLastRightPos+4, y, delay, SMLSIZE); + if (attr && s_editMode > 0) { + switch (menuHorizontalPosition) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount); + break; + case 1: + CHECK_INCDEC_MODELVAR_CHECK(event, moduleData.channelsCount, -4, min(maxModuleChannels_M8(moduleIdx), 32-8-moduleData.channelsStart), moduleData.type == MODULE_TYPE_ISRM_PXX2 ? isPxx2IsrmChannelsCountAllowed : nullptr); + if (checkIncDec_Ret && moduleData.type == MODULE_TYPE_PPM) { + setDefaultPpmFrameLength(moduleIdx); + } + break; } } break; @@ -1270,8 +1263,8 @@ void menuModelSetup(event_t event) lcdDrawText(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0); uint8_t newFlag = 0; #if defined(MULTIMODULE) - if (multiBindStatus == MULTI_BIND_FINISHED) { - multiBindStatus = MULTI_NORMAL_OPERATION; + if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) { + setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION); s_editMode = 0; } #endif @@ -1321,7 +1314,7 @@ void menuModelSetup(event_t event) #if defined(MULTIMODULE) if (newFlag == MODULE_MODE_BIND) { - multiBindStatus = MULTI_BIND_INITIATED; + setMultiBindStatus(moduleIdx, MULTI_BIND_INITIATED); } #endif @@ -1532,7 +1525,7 @@ void menuModelSetup(event_t event) lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); char statusText[64]; - multiModuleStatus.getStatusString(statusText); + getMultiModuleStatus(EXTERNAL_MODULE).getStatusString(statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } @@ -1541,7 +1534,7 @@ void menuModelSetup(event_t event) lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC); char statusText[64]; - multiSyncStatus.getRefreshString(statusText); + getMultiSyncStatus(EXTERNAL_MODULE).getRefreshString(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 4637d8634..5ae189763 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -230,8 +230,6 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) #define INTERNAL_MODULE_TYPE_ROWS (0) // Module type + RF protocols #endif -#define PORT_CHANNELS_ROWS(x) (x==INTERNAL_MODULE ? INTERNAL_MODULE_CHANNELS_ROWS : (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 1)) - #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW) #define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW) #define TRAINER_BLUETOOTH_M_ROW ((bluetooth.distantAddr[0] == '\0' || bluetooth.state == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1) @@ -352,7 +350,7 @@ void menuModelSetup(event_t event) LABEL(InternalModule), INTERNAL_MODULE_TYPE_ROWS, - INTERNAL_MODULE_CHANNELS_ROWS, + MODULE_CHANNELS_ROWS(INTERNAL_MODULE), IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(isModuleFailsafeAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1)), IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), // RxNum for ACCESS IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Failsafe @@ -364,13 +362,13 @@ void menuModelSetup(event_t event) LABEL(ExternalModule), EXTERNAL_MODULE_TYPE_ROW(), - MULTIMODULE_STATUS_ROWS - EXTERNAL_MODULE_CHANNELS_ROWS, - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, EXTERNAL_MODULE_BIND_ROWS), + MULTIMODULE_STATUS_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, EXTERNAL_MODULE_OPTION_ROW), - MULTIMODULE_MODULE_ROWS - EXTERNAL_MODULE_POWER_ROW, + IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), + MULTIMODULE_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 IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options @@ -1019,7 +1017,7 @@ void menuModelSetup(event_t event) uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); ModuleData & moduleData = g_model.moduleData[moduleIdx]; lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE); - if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) { + if ((int8_t)MODULE_CHANNELS_ROWS(moduleIdx) >= 0) { lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0); lcdDrawNumber(lcdLastRightPos, y, moduleData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0)); lcdDrawChar(lcdLastRightPos, y, '-'); @@ -1144,8 +1142,8 @@ void menuModelSetup(event_t event) lcdDrawText(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0); uint8_t newFlag = 0; #if defined(MULTIMODULE) - if (multiBindStatus == MULTI_BIND_FINISHED) { - multiBindStatus = MULTI_NORMAL_OPERATION; + if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) { + setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION); s_editMode = 0; } #endif @@ -1178,7 +1176,7 @@ void menuModelSetup(event_t event) moduleState[moduleIdx].mode = newFlag; #if defined(MULTIMODULE) if (newFlag == MODULE_MODE_BIND) - multiBindStatus = MULTI_BIND_INITIATED; + setMultiBindStatus(moduleIdx, MULTI_BIND_INITIATED); #endif } } @@ -1317,7 +1315,7 @@ void menuModelSetup(event_t event) { lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); char statusText[64]; - multiModuleStatus.getStatusString(statusText); + getMultiModuleStatus(EXTERNAL_MODULE).getStatusString(statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } @@ -1326,7 +1324,7 @@ void menuModelSetup(event_t event) { lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC); char statusText[64]; - multiSyncStatus.getRefreshString(statusText); + getMultiSyncStatus(EXTERNAL_MODULE).getRefreshString(statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; } diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index c759bfb77..ed166cb1e 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -66,22 +66,31 @@ enum MenuModelSetupItems { #if defined(PXX2) ITEM_MODEL_SETUP_REGISTRATION_ID, #endif - +#if defined(HARDWARE_INTERNAL_MODULE) ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL, ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE, +#if defined(INTERNAL_MODULE_MULTI) + ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS, + ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS, +#endif ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM, + ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS, +#if defined(INTERNAL_MODULE_MULTI) + ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND, +#endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA, #endif + ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER, ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3, - +#endif ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL, ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE, #if defined(MULTIMODULE) @@ -122,7 +131,11 @@ enum MenuModelSetupItems { #define MODEL_SETUP_SLIDPOT_SPACING 45 #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE) +#if defined(HARDWARE_INTERNAL_MODULE) #define CURRENT_RECEIVER_EDITED(k) (k - (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1 : ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1)) +#else +#define CURRENT_RECEIVER_EDITED(k) (EXTERNAL_MODULE) +#endif #include "common/stdlcd/model_setup_pxx1.cpp" @@ -468,19 +481,15 @@ int getSwitchWarningsCount() #define IF_ACCESS_MODULE_RF(module, xxx) (isModuleRFAccess(module) ? (uint8_t)(xxx) : HIDDEN_ROW) #define IF_NOT_ACCESS_MODULE_RF(module, xxx) (isModuleRFAccess(module) ? HIDDEN_ROW : (uint8_t)(xxx)) -#define INTERNAL_MODULE_TYPE_ROWS ((isModuleXJT(INTERNAL_MODULE) || isModulePXX2(INTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols - -#define MODULE_CHANNELS_ROWS(x) (x==INTERNAL_MODULE ? INTERNAL_MODULE_CHANNELS_ROWS : (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 1)) - #define TIMER_ROWS(x) NAVIGATION_LINE_BY_LINE|1, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t)1 : (uint8_t)0 -inline uint8_t EXTERNAL_MODULE_TYPE_ROW() +inline uint8_t MODULE_TYPE_ROWS(int moduleIdx) { - if (isModuleXJT(EXTERNAL_MODULE) || isModuleR9MNonAccess(EXTERNAL_MODULE) || isModuleDSM2(EXTERNAL_MODULE)) + if (isModuleXJT(moduleIdx) || isModuleR9MNonAccess(moduleIdx) || isModuleDSM2(moduleIdx) || isModulePXX2(moduleIdx)) return 1; #if defined(MULTIMODULE) - else if (isModuleMultimodule(EXTERNAL_MODULE)) { - return 1 + MULTIMODULE_RFPROTO_COLUMNS(EXTERNAL_MODULE); + else if (isModuleMultimodule(moduleIdx)) { + return 1 + MULTIMODULE_RFPROTO_COLUMNS(moduleIdx); } #endif else @@ -495,6 +504,7 @@ inline uint8_t EXTERNAL_MODULE_TYPE_ROW() #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2) #endif +#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) @@ -502,6 +512,11 @@ inline uint8_t EXTERNAL_MODULE_TYPE_ROW() #define TRAINER_BLUETOOTH_S_ROW (bluetooth.distantAddr[0] == '\0' ? HIDDEN_ROW : LABEL()) #define TRAINER_BLUETOOTH_ROW (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW)) #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 #if defined(PXX2) #define REGISTRATION_ID_ROWS uint8_t((isDefaultModelRegistrationID() || (warningText && popupFunc == runPopupRegister)) ? HIDDEN_ROW : READONLY_ROW), @@ -527,11 +542,35 @@ void onModelAntennaSwitchConfirm(const char * result) #define EXTERNAL_ANTENNA_ROW #endif +#if defined(HARDWARE_INTERNAL_MODULE) + #define INTERNAL_MODULE_ROWS \ + LABEL(InternalModule), \ + MODULE_TYPE_ROWS(INTERNAL_MODULE), \ + MULTIMODULE_STATUS_ROWS(INTERNAL_MODULE) \ + MODULE_CHANNELS_ROWS(INTERNAL_MODULE), \ + IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1))), \ + IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), \ + MODULE_OPTION_ROW(INTERNAL_MODULE), \ + MULTIMODULE_MODULE_ROWS(INTERNAL_MODULE) \ + EXTERNAL_ANTENNA_ROW \ + MODULE_POWER_ROW(INTERNAL_MODULE), \ + IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), \ + IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 1), /* Range check and Register buttons */ \ + IF_PXX2_MODULE(INTERNAL_MODULE, 0), /* Module options */ \ + IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 1 */ \ + IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 2 */ \ + IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 3 */ +#else + #define INTERNAL_MODULE_ROWS +#endif + + bool menuModelSetup(event_t event) { bool CURSOR_ON_CELL = (menuHorizontalPosition >= 0); int8_t old_editMode = s_editMode; + MENU(STR_MENUSETUP, MODEL_ICONS, menuTabModel, MENU_MODEL_SETUP, ITEM_MODEL_SETUP_MAX, { 0, // Model name 0, // Model bitmap @@ -559,28 +598,17 @@ bool menuModelSetup(event_t event) REGISTRATION_ID_ROWS - LABEL(InternalModule), - INTERNAL_MODULE_TYPE_ROWS, - INTERNAL_MODULE_CHANNELS_ROWS, - IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1))), - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), // RxNum - EXTERNAL_ANTENNA_ROW - IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 1), /* Range check and Register buttons */ \ - IF_PXX2_MODULE(INTERNAL_MODULE, 0), /* Module options */ \ - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 1 */ \ - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 2 */ \ - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 3 */ + INTERNAL_MODULE_ROWS LABEL(ExternalModule), - EXTERNAL_MODULE_TYPE_ROW(), - MULTIMODULE_STATUS_ROWS - EXTERNAL_MODULE_CHANNELS_ROWS, - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, EXTERNAL_MODULE_BIND_ROWS), + MODULE_TYPE_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)), IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum for ACCESS - IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, EXTERNAL_MODULE_OPTION_ROW), - MULTIMODULE_MODULE_ROWS - EXTERNAL_MODULE_POWER_ROW, + IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), + MULTIMODULE_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 IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options @@ -613,7 +641,7 @@ bool menuModelSetup(event_t event) if (mstate_tab[j] == HIDDEN_ROW) k++; } - + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS); LcdFlags attr = (sub == k ? blink : 0); @@ -945,10 +973,12 @@ bool menuModelSetup(event_t event) if (attr) g_model.noGlobalFunctions = !checkIncDecModel(event, !g_model.noGlobalFunctions, 0, 1); break; +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INTERNALRF); break; +#if !defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_INTERNAL_MODULE_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0); @@ -976,6 +1006,7 @@ bool menuModelSetup(event_t event) } } break; +#endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) case ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA: @@ -993,111 +1024,124 @@ bool menuModelSetup(event_t event) } break; #endif - +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_EXTERNALRF); break; +#if defined(INTERNAL_MODULE_MULTI) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE: lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE); - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0); - if (isModuleXJT(EXTERNAL_MODULE)) - lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_XJT_ACCST_RF_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0)); - else if (isModuleDSM2(EXTERNAL_MODULE)) - lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, (menuHorizontalPosition==1 ? attr : 0)); - else if (isModuleR9MNonAccess(EXTERNAL_MODULE)) - lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_R9M_REGION, g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0)); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, g_model.moduleData[moduleIdx].type, menuHorizontalPosition==0 ? attr : 0); + if (isModuleXJT(moduleIdx)) + lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_XJT_ACCST_RF_PROTOCOLS, 1+g_model.moduleData[moduleIdx].subType, (menuHorizontalPosition==1 ? attr : 0)); + else if (isModuleDSM2(moduleIdx)) + lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_DSM_PROTOCOLS, g_model.moduleData[moduleIdx].rfProtocol, (menuHorizontalPosition==1 ? attr : 0)); + else if (isModuleR9MNonAccess(moduleIdx)) + lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_R9M_REGION, g_model.moduleData[moduleIdx].subType, (menuHorizontalPosition==1 ? attr : 0)); #if defined(MULTIMODULE) - else if (isModuleMultimodule(EXTERNAL_MODULE)) { - int multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false); - if (g_model.moduleData[EXTERNAL_MODULE].multi.customProto) { + else if (isModuleMultimodule(moduleIdx)) { + int multi_rfProto = g_model.moduleData[moduleIdx].getMultiProtocol(false); + if (g_model.moduleData[moduleIdx].multi.customProto) { lcdDrawText(MODEL_SETUP_3RD_COLUMN, y, STR_MULTI_CUSTOM, menuHorizontalPosition == 1 ? attr : 0); lcdDrawNumber(MODEL_SETUP_4TH_COLUMN, y, multi_rfProto, menuHorizontalPosition==2 ? attr : 0, 2); - lcdDrawNumber(MODEL_SETUP_4TH_COLUMN + MODEL_SETUP_BIND_OFS, y, g_model.moduleData[EXTERNAL_MODULE].subType, menuHorizontalPosition==3 ? attr : 0, 2); + lcdDrawNumber(MODEL_SETUP_4TH_COLUMN + MODEL_SETUP_BIND_OFS, y, g_model.moduleData[moduleIdx].subType, menuHorizontalPosition==3 ? attr : 0, 2); } else { const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_rfProto); lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_MULTI_PROTOCOLS, multi_rfProto, menuHorizontalPosition == 1 ? attr : 0); if (pdef->subTypeString != nullptr) - lcdDrawTextAtIndex(MODEL_SETUP_4TH_COLUMN, y, pdef->subTypeString, g_model.moduleData[EXTERNAL_MODULE].subType, menuHorizontalPosition==2 ? attr : 0); + lcdDrawTextAtIndex(MODEL_SETUP_4TH_COLUMN, y, pdef->subTypeString, g_model.moduleData[moduleIdx].subType, menuHorizontalPosition==2 ? attr : 0); } } #endif + if (attr && menuHorizontalPosition == 0 && moduleIdx == EXTERNAL_MODULE) { + if (s_editMode > 0) + EXTERNAL_MODULE_OFF(); + else + EXTERNAL_MODULE_ON(); + } if (attr) { if (s_editMode > 0) { switch (menuHorizontalPosition) { case 0: { - uint8_t moduleType = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].type, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL, isExternalModuleAvailable); +#if defined(HARDWARE_INTERNAL_MODULE) + IsValueAvailable checkFun = moduleIdx == 0 ? isInternalModuleAvailable : isExternalModuleAvailable; +#else + IsValueAvailable checkFun = isExternalModuleAvailable; +#endif + uint8_t moduleType = checkIncDec(event, g_model.moduleData[moduleIdx].type, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL, checkFun); if (checkIncDec_Ret) { - setModuleType(EXTERNAL_MODULE, moduleType); + setModuleType(moduleIdx, moduleType); } break; } - case 1: - if (isModuleDSM2(EXTERNAL_MODULE)) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX); + if (isModuleDSM2(moduleIdx)) + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX); #if defined(MULTIMODULE) - else if (isModuleMultimodule(EXTERNAL_MODULE)) { - int multiRfProto = g_model.moduleData[EXTERNAL_MODULE].multi.customProto == 1 ? MODULE_SUBTYPE_MULTI_CUSTOM : g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false); + else if (isModuleMultimodule(moduleIdx)) { + int multiRfProto = g_model.moduleData[moduleIdx].multi.customProto == 1 ? MODULE_SUBTYPE_MULTI_CUSTOM : g_model.moduleData[moduleIdx].getMultiProtocol(false); CHECK_INCDEC_MODELVAR(event, multiRfProto, MODULE_SUBTYPE_MULTI_FIRST, MODULE_SUBTYPE_MULTI_LAST); if (checkIncDec_Ret) { - g_model.moduleData[EXTERNAL_MODULE].multi.customProto = (multiRfProto == MODULE_SUBTYPE_MULTI_CUSTOM); - if (!g_model.moduleData[EXTERNAL_MODULE].multi.customProto) - g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(multiRfProto); - g_model.moduleData[EXTERNAL_MODULE].subType = 0; + g_model.moduleData[moduleIdx].multi.customProto = (multiRfProto == MODULE_SUBTYPE_MULTI_CUSTOM); + if (!g_model.moduleData[moduleIdx].multi.customProto) + g_model.moduleData[moduleIdx].setMultiProtocol(multiRfProto); + g_model.moduleData[moduleIdx].subType = 0; // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) { - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 1; + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.autoBindMode = 1; } else { - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 0; + g_model.moduleData[moduleIdx].multi.autoBindMode = 0; } - g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = 0; + g_model.moduleData[moduleIdx].multi.optionValue = 0; } } #endif - else if (isModuleR9MNonAccess(EXTERNAL_MODULE)) { - g_model.moduleData[EXTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_R9M_FCC, + else if (isModuleR9MNonAccess(moduleIdx)) { + g_model.moduleData[moduleIdx].subType = checkIncDec(event, g_model.moduleData[moduleIdx].subType, MODULE_SUBTYPE_R9M_FCC, MODULE_SUBTYPE_R9M_LAST, EE_MODEL, isR9MModeAvailable); } else { - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_PXX1_ACCST_D16, MODULE_SUBTYPE_PXX1_LAST); + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, MODULE_SUBTYPE_PXX1_ACCST_D16, MODULE_SUBTYPE_PXX1_LAST); } if (checkIncDec_Ret) { - g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; - g_model.moduleData[EXTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(EXTERNAL_MODULE); + g_model.moduleData[moduleIdx].channelsStart = 0; + g_model.moduleData[moduleIdx].channelsCount = defaultModuleChannels_M8(moduleIdx); } break; #if defined(MULTIMODULE) case 2: - if (g_model.moduleData[EXTERNAL_MODULE].multi.customProto) { - g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false), 0, 63, EE_MODEL)); + if (g_model.moduleData[moduleIdx].multi.customProto) { + g_model.moduleData[moduleIdx].setMultiProtocol(checkIncDec(event, g_model.moduleData[moduleIdx].getMultiProtocol(false), 0, 63, EE_MODEL)); break; } else { - const mm_protocol_definition *pdef = getMultiProtocolDefinition(g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false)); + const mm_protocol_definition *pdef = getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol(false)); if (pdef->maxSubtype > 0) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, pdef->maxSubtype); + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, 0, pdef->maxSubtype); } break; case 3: // Custom protocol, third column is subtype - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, 7); + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, 0, 7); break; #endif } } #if POPUP_LEVEL > 1 else if (old_editMode > 0) { - if (isModuleR9MNonAccess(EXTERNAL_MODULE)) { - if (g_model.moduleData[EXTERNAL_MODULE].subType > MODULE_SUBTYPE_R9M_EU) { + if (isModuleR9MNonAccess(moduleIdx)) { + if (g_model.moduleData[moduleIdx].subType > MODULE_SUBTYPE_R9M_EU) { POPUP_WARNING(STR_MODULE_PROTOCOL_FLEX_WARN_LINE1); SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0); } #if POPUP_LEVEL >= 3 - else if (g_model.moduleData[EXTERNAL_MODULE].subType == MODULE_SUBTYPE_R9M_EU) { + else if (g_model.moduleData[moduleIdx].subType == MODULE_SUBTYPE_R9M_EU) { POPUP_WARNING(STR_MODULE_PROTOCOL_EU_WARN_LINE1); SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0); } @@ -1111,11 +1155,11 @@ bool menuModelSetup(event_t event) #endif } break; - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); ModuleData & moduleData = g_model.moduleData[moduleIdx]; lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHANNELRANGE); if ((int8_t)MODULE_CHANNELS_ROWS(moduleIdx) >= 0) { @@ -1141,7 +1185,6 @@ bool menuModelSetup(event_t event) } break; } - case ITEM_MODEL_SETUP_TRAINER_LABEL: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRAINER); break; @@ -1149,12 +1192,15 @@ bool menuModelSetup(event_t event) case ITEM_MODEL_SETUP_TRAINER_MODE: lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE); g_model.trainerData.mode = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerData.mode, 0, TRAINER_MODE_MAX(), attr, event); +#if defined(BLUETOOTH) if (attr && checkIncDec_Ret) { bluetooth.state = BLUETOOTH_STATE_OFF; bluetooth.distantAddr[0] = 0; } +#endif break; +#if defined(BLUETOOTH) case ITEM_MODEL_SETUP_TRAINER_BLUETOOTH: if (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH) { if (attr) { @@ -1194,6 +1240,7 @@ bool menuModelSetup(event_t event) } } break; +#endif case ITEM_MODEL_SETUP_TRAINER_CHANNELS: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHANNELRANGE); @@ -1242,10 +1289,11 @@ bool menuModelSetup(event_t event) break; #endif +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER_NUM); lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], attr | LEADING0 | LEFT, 2); if (attr) { @@ -1261,11 +1309,11 @@ bool menuModelSetup(event_t event) } } break; - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODULE); drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_REGISTER, (menuHorizontalPosition == 0 ? attr : 0)); drawButton(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_MODULE_RANGE, (menuHorizontalPosition == 1 ? attr : 0)); @@ -1288,8 +1336,9 @@ bool menuModelSetup(event_t event) } } break; - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS: lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_OPTIONS); drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_SET, attr); @@ -1298,15 +1347,15 @@ bool menuModelSetup(event_t event) pushMenu(menuModelModuleOptions); } break; - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1: case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2: case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1: case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2: case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(k); ModuleInformation & moduleInformation = reusableBuffer.moduleSetup.pxx2.moduleInformation; @@ -1382,11 +1431,11 @@ bool menuModelSetup(event_t event) } } break; - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); ModuleData & moduleData = g_model.moduleData[moduleIdx]; if (isModulePPM(moduleIdx)) { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PPMFRAME); @@ -1454,8 +1503,8 @@ bool menuModelSetup(event_t event) drawButton(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==2 ? attr : 0)); uint8_t newFlag = 0; #if defined(MULTIMODULE) - if (multiBindStatus == MULTI_BIND_FINISHED) { - multiBindStatus = MULTI_NORMAL_OPERATION; + if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) { + setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION); s_editMode = 0; } #endif @@ -1488,17 +1537,17 @@ bool menuModelSetup(event_t event) moduleState[moduleIdx].mode = newFlag; #if defined(MULTIMODULE) if (newFlag == MODULE_MODE_BIND) - multiBindStatus = MULTI_BIND_INITIATED; + setMultiBindStatus(moduleIdx, MULTI_BIND_INITIATED); #endif } } break; } - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); ModuleData & moduleData = g_model.moduleData[moduleIdx]; lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FAILSAFE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition==0 ? attr : 0); @@ -1536,15 +1585,16 @@ bool menuModelSetup(event_t event) } break; } - +#if defined(HARDWARE_INTERNAL_MODULE) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); #if defined(MULTIMODULE) if (isModuleMultimodule(moduleIdx)) { int optionValue = g_model.moduleData[moduleIdx].multi.optionValue; - const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true); + const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(true); const mm_protocol_definition *pdef = getMultiProtocolDefinition(multi_proto); if (pdef->optionsstr) lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, pdef->optionsstr); @@ -1580,13 +1630,14 @@ bool menuModelSetup(event_t event) } break; } - +#if defined(HARDWARE_INTERNAL_MODULE) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER: { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); if (isModuleR9MNonAccess(moduleIdx)) { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RFPOWER); - if(isModuleR9M_FCC_VARIANT(moduleIdx)) { + if (isModuleR9M_FCC_VARIANT(moduleIdx)) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr); if (attr) CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_FCC_POWER_MAX); @@ -1596,7 +1647,7 @@ bool menuModelSetup(event_t event) if (attr) CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_LBT_POWER_MAX); if (attr && s_editMode == 0 && reusableBuffer.moduleSetup.r9mPower != g_model.moduleData[moduleIdx].pxx.power) { - if((reusableBuffer.moduleSetup.r9mPower + g_model.moduleData[moduleIdx].pxx.power) < 5) { //switching between mode 2 and 3 does not require rebind + if ((reusableBuffer.moduleSetup.r9mPower + g_model.moduleData[moduleIdx].pxx.power) < 5) { //switching between mode 2 and 3 does not require rebind POPUP_WARNING(STR_WARNING); SET_WARNING_INFO(STR_REBIND, sizeof(TR_REBIND), 0); } @@ -1607,39 +1658,49 @@ bool menuModelSetup(event_t event) #if defined(MULTIMODULE) else if (isModuleMultimodule(moduleIdx)) { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_LOWPOWER); - g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); + g_model.moduleData[moduleIdx].multi.lowPowerMode = editCheckBox(g_model.moduleData[moduleIdx].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); } #endif } - break; + break; #if defined(MULTIMODULE) +#if defined(INTERNAL_MODULE_MULTI) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_DSM_AUTODTECT); else lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_AUTOBIND); - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].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; - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: { +#if defined(INTERNAL_MODULE_MULTI) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS: +#endif + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: + { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_STATUS); char statusText[64]; - multiModuleStatus.getStatusString(statusText); + getMultiModuleStatus(moduleIdx).getStatusString(statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { + } +#if defined(INTERNAL_MODULE_MULTI) + case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS: +#endif + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: + { lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_SYNC); char statusText[64]; - multiSyncStatus.getRefreshString(statusText); + getMultiSyncStatus(moduleIdx).getRefreshString(statusText); lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); break; - } } #endif + } } @@ -1656,7 +1717,7 @@ bool menuModelSetup(event_t event) case ITEM_MODEL_SETUP_NAME: mod_cell->setModelName(g_model.header.name); break; - +#if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND: if (menuHorizontalPosition != 0) break; @@ -1664,7 +1725,7 @@ bool menuModelSetup(event_t event) mod_cell->setRfData(&g_model); checkModelIdUnique(INTERNAL_MODULE); break; - +#endif case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND: if (menuHorizontalPosition != 0) break; @@ -1672,6 +1733,7 @@ bool menuModelSetup(event_t event) mod_cell->setRfData(&g_model); if (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE) checkModelIdUnique(EXTERNAL_MODULE); + break; } } } diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp index db5fbc759..89a5aabff 100644 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ b/radio/src/gui/480x272/radio_hardware.cpp @@ -55,10 +55,11 @@ enum MenuRadioHardwareItems { ITEM_RADIO_HARDWARE_BATTERY_CALIB, ITEM_RADIO_HARDWARE_RTC_BATTERY, ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE, +#if defined(BLUETOOTH) ITEM_RADIO_HARDWARE_BLUETOOTH_MODE, ITEM_RADIO_HARDWARE_BLUETOOTH_PAIRING_CODE, ITEM_RADIO_HARDWARE_BLUETOOTH_NAME, - +#endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) ITEM_RADIO_HARDWARE_EXTERNAL_ANTENNA, #endif @@ -77,7 +78,11 @@ enum MenuRadioHardwareItems { #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #define SWITCHES_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 -#define BLUETOOTH_ROWS 0, uint8_t(g_eeGeneral.bluetoothMode != BLUETOOTH_TELEMETRY ? HIDDEN_ROW : -1), uint8_t(g_eeGeneral.bluetoothMode == BLUETOOTH_OFF ? -1 : 0) +#if defined(BLUETOOTH) + #define BLUETOOTH_ROWS 0, uint8_t(g_eeGeneral.bluetoothMode != BLUETOOTH_TELEMETRY ? HIDDEN_ROW : -1), uint8_t(g_eeGeneral.bluetoothMode == BLUETOOTH_OFF ? -1 : 0), +#else + #define BLUETOOTH_ROWS +#endif // TODO should be moved to the HAL #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SI-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SJ-MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) @@ -121,7 +126,7 @@ bool menuRadioHardware(event_t event) 0, /* max baudrate */ - BLUETOOTH_ROWS, + BLUETOOTH_ROWS EXTERNAL_ANTENNA_ROW @@ -270,7 +275,7 @@ bool menuRadioHardware(event_t event) } } break; - +#if defined(BLUETOOTH) case ITEM_RADIO_HARDWARE_BLUETOOTH_MODE: lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLUETOOTH); g_eeGeneral.bluetoothMode = editChoice(HW_SETTINGS_COLUMN+50, y, STR_BLUETOOTH_MODES, g_eeGeneral.bluetoothMode, BLUETOOTH_OFF, BLUETOOTH_TRAINER, attr, event); @@ -285,6 +290,7 @@ bool menuRadioHardware(event_t event) lcdDrawText(MENUS_MARGIN_LEFT, y, STR_NAME); editName(HW_SETTINGS_COLUMN+50, y, g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME, event, attr); break; +#endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) case ITEM_RADIO_HARDWARE_EXTERNAL_ANTENNA: diff --git a/radio/src/gui/480x272/radio_sdmanager.cpp b/radio/src/gui/480x272/radio_sdmanager.cpp index 4c82c42fe..0534e6bbf 100644 --- a/radio/src/gui/480x272/radio_sdmanager.cpp +++ b/radio/src/gui/480x272/radio_sdmanager.cpp @@ -19,7 +19,8 @@ */ #include -#include +#include "io/frsky_firmware_update.h" +#include "io/multi_firmware_update.h" #include "opentx.h" #include "storage/modelslist.h" @@ -173,6 +174,18 @@ void onSdManagerMenu(const char * result) FrskyDeviceFirmwareUpdate device(SPORT_MODULE); device.flashFirmware(lfn); } +#if defined(MULTIMODULE) +#if defined(INTERNAL_MODULE_MULTI) + else if (result == STR_FLASH_INTERNAL_MULTI) { + getSelectionFullPath(lfn); + multiFlashFirmware(INTERNAL_MODULE, lfn); + } +#endif + else if (result == STR_FLASH_EXTERNAL_MULTI) { + getSelectionFullPath(lfn); + multiFlashFirmware(EXTERNAL_MODULE, lfn); + } +#endif #if defined(LUA) else if (result == STR_EXECUTE_FILE) { getSelectionFullPath(lfn); @@ -258,9 +271,22 @@ bool menuRadioSdManager(event_t _event) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); } - else if (!READ_ONLY() && !strcasecmp(ext, UPDATE_FIRMWARE_EXT)) { +#if defined(MULTIMODULE) + else if (!READ_ONLY() && !strcasecmp(ext, MULTI_FIRMWARE_EXT)) { + TCHAR lfn[_MAX_LFN + 1]; + getSelectionFullPath(lfn); + MultiFirmwareInformation information; + if (information.readMultiFirmwareInformation(line) == nullptr) { +#if defined(INTERNAL_MODULE_MULTI) + POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MULTI); +#endif + POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MULTI); + } +#endif + } + else if (!READ_ONLY() && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { FrSkyFirmwareInformation information; - if (readFirmwareInformation(line, information) == nullptr) { + if (readFrSkyFirmwareInformation(line, information) == nullptr) { if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE) diff --git a/radio/src/gui/480x272/themes/default.cpp b/radio/src/gui/480x272/themes/default.cpp index d444a6994..84b23e168 100644 --- a/radio/src/gui/480x272/themes/default.cpp +++ b/radio/src/gui/480x272/themes/default.cpp @@ -176,7 +176,9 @@ class DefaultTheme: public Theme calibTrackpBackground = BitmapBuffer::load(getThemePath("trackp_background.png")); delete calibHorus; -#if defined(PCBX10) +#if defined(RADIO_T16) + calibHorus = BitmapBuffer::load(getThemePath("t16.bmp")); +#elif defined(PCBX10) if(STICKS_PWM_ENABLED()) { calibHorus = BitmapBuffer::load(getThemePath("X10S.bmp")); } diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index 797717314..763fdcada 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "io/frsky_firmware_update.h" +#include "io/multi_firmware_update.h" #define NODE_TYPE(fname) fname[SD_SCREEN_FILE_LENGTH+1] #define IS_DIRECTORY(fname) ((bool)(!NODE_TYPE(fname))) @@ -219,6 +220,16 @@ void onSdManagerMenu(const char * result) FrskyDeviceFirmwareUpdate device(SPORT_MODULE); device.flashFirmware(lfn); } +#if defined(INTERNAL_MODULE_MULTI) + else if (result == STR_FLASH_INTERNAL_MULTI) { + getSelectionFullPath(lfn); + multiFlashFirmware(INTERNAL_MODULE, lfn); + } +#endif + else if (result == STR_FLASH_EXTERNAL_MULTI) { + getSelectionFullPath(lfn); + multiFlashFirmware(EXTERNAL_MODULE, lfn); + } #if defined(BLUETOOTH) else if (result == STR_FLASH_BLUETOOTH_MODULE) { getSelectionFullPath(lfn); @@ -340,6 +351,9 @@ void menuRadioSdManager(event_t _event) break; } #endif + TCHAR lfn[_MAX_LFN + 1]; + getSelectionFullPath(lfn); + if (SD_CARD_PRESENT() && s_editMode <= 0) { killEvents(_event); int index = menuVerticalPosition - HEADER_LINE - menuVerticalOffset; @@ -364,10 +378,19 @@ void menuRadioSdManager(event_t _event) POPUP_MENU_ADD_ITEM(STR_EXECUTE_FILE); } #endif +#if defined(MULTIMODULE) && !defined(DISABLE_MULTI_UPDATE) + if (!READ_ONLY() && !strcasecmp(ext, MULTI_FIRMWARE_EXT)) { + MultiFirmwareInformation information; + if (information.readMultiFirmwareInformation(line) == nullptr) { +#if defined(INTERNAL_MODULE_MULTI) + POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MULTI); +#endif + POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MULTI); + } + } +#endif #if defined(PCBTARANIS) - else if (!READ_ONLY() && !strcasecmp(ext, FIRMWARE_EXT)) { - TCHAR lfn[_MAX_LFN + 1]; - getSelectionFullPath(lfn); + if (!READ_ONLY() && !strcasecmp(ext, FIRMWARE_EXT)) { if (isBootloader(lfn)) { POPUP_MENU_ADD_ITEM(STR_FLASH_BOOTLOADER); } @@ -378,9 +401,9 @@ void menuRadioSdManager(event_t _event) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); } - else if (!READ_ONLY() && !strcasecmp(ext, UPDATE_FIRMWARE_EXT)) { + else if (!READ_ONLY() && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { FrSkyFirmwareInformation information; - if (readFirmwareInformation(line, information) == nullptr) { + if (readFrSkyFirmwareInformation(line, information) == nullptr) { if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE) POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE) diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 52dc9fd18..3d0c6c1f7 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -567,6 +567,13 @@ bool isInternalModuleAvailable(int moduleType) if (moduleType == MODULE_TYPE_NONE) return true; +#if defined(INTERNAL_MODULE_MULTI) + if (moduleType == MODULE_TYPE_MULTIMODULE) + return true; + else + return false; +#endif + if (moduleType == MODULE_TYPE_XJT_PXX1) { #if defined(PXX1) && defined(INTERNAL_MODULE_PXX1) return !isModuleUsingSport(EXTERNAL_MODULE, g_model.moduleData[EXTERNAL_MODULE].type); @@ -584,7 +591,6 @@ bool isInternalModuleAvailable(int moduleType) return true; #endif } - return false; } #endif diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index b941d2ec9..9f4d12894 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -116,10 +116,33 @@ void drawFatalErrorScreen(const char * message); void runFatalErrorScreen(const char * message); #endif +#define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW) +#define IF_MODULE_ON(moduleIndex, x) (IS_MODULE_ENABLED(moduleIndex) ? (uint8_t)(x) : HIDDEN_ROW) + // model_setup Defines that are used in all uis in the same way -#define INTERNAL_MODULE_CHANNELS_ROWS IF_INTERNAL_MODULE_ON((uint8_t)1) -#define EXTERNAL_MODULE_CHANNELS_ROWS IF_EXTERNAL_MODULE_ON((isModuleDSM2(EXTERNAL_MODULE) || isModuleCrossfire(EXTERNAL_MODULE) || isModuleSBUS(EXTERNAL_MODULE) || (isModuleMultimodule(EXTERNAL_MODULE) && g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) != MODULE_SUBTYPE_MULTI_DSM2)) ? (uint8_t)0 : (uint8_t)1) -#define EXTERNAL_MODULE_BIND_ROWS (isModuleXJTD8(EXTERNAL_MODULE) || isModuleSBUS(EXTERNAL_MODULE)) ? (uint8_t)1 : (isModulePPM(EXTERNAL_MODULE) || isModulePXX1(EXTERNAL_MODULE) || isModulePXX2(EXTERNAL_MODULE) || isModuleDSM2(EXTERNAL_MODULE) || isModuleMultimodule(EXTERNAL_MODULE)) ? (uint8_t)2 : HIDDEN_ROW + +inline uint8_t MODULE_BIND_ROWS(int moduleIdx) +{ + if (isModuleXJTD8(moduleIdx) || isModuleSBUS(moduleIdx)) + return 1; + + if (isModulePPM(moduleIdx) || isModulePXX1(moduleIdx) || isModulePXX2(moduleIdx) || isModuleDSM2(moduleIdx) || isModuleMultimodule(moduleIdx)) + return 2; + else + return HIDDEN_ROW; +} + +inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx) +{ + if (!IS_MODULE_ENABLED(moduleIdx)) + return HIDDEN_ROW; + + if (isModuleDSM2(moduleIdx) || isModuleCrossfire(moduleIdx) || isModuleSBUS(moduleIdx) || (isModuleMultimodule(moduleIdx) && g_model.moduleData[moduleIdx].getMultiProtocol(true) != MODULE_SUBTYPE_MULTI_DSM2)) + return 0; + else + return 1; +} + #if defined(MULTIMODULE) // When using packed, the pointer in here end up not being aligned, which clang and gcc complain about @@ -132,9 +155,9 @@ struct mm_protocol_definition { const char *optionsstr; }; const mm_protocol_definition *getMultiProtocolDefinition (uint8_t protocol); -#define MULTIMODULE_STATUS_ROWS isModuleMultimodule(EXTERNAL_MODULE) ? TITLE_ROW : HIDDEN_ROW, (isModuleMultimodule(EXTERNAL_MODULE) && multiSyncStatus.isValid()) ? TITLE_ROW : HIDDEN_ROW, -#define MULTIMODULE_MODULE_ROWS isModuleMultimodule(EXTERNAL_MODULE) ? (uint8_t) 0 : HIDDEN_ROW, -#define MULTIMODULE_MODE_ROWS(x) (g_model.moduleData[x].multi.customProto) ? (uint8_t) 3 :MULTIMODULE_HAS_SUBTYPE(g_model.moduleData[x].getMultiProtocol(true)) ? (uint8_t)2 : (uint8_t)1 +#define MULTIMODULE_STATUS_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, (isModuleMultimodule(moduleIdx) && getMultiSyncStatus(moduleIdx).isValid()) ? TITLE_ROW : HIDDEN_ROW, +#define MULTIMODULE_MODULE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW, +#define MULTIMODULE_MODE_ROWS(moduleIdx) (g_model.moduleData[moduleIdx].multi.customProto) ? (uint8_t) 3 :MULTIMODULE_HAS_SUBTYPE(g_model.moduleData[moduleIdx].getMultiProtocol(true)) ? (uint8_t)2 : (uint8_t)1 inline bool MULTIMODULE_HAS_SUBTYPE(uint8_t moduleIdx) { return getMultiProtocolDefinition(moduleIdx)->maxSubtype > 0; @@ -147,21 +170,21 @@ inline uint8_t MULTIMODULE_RFPROTO_COLUMNS(uint8_t moduleIdx) return (g_model.moduleData[moduleIdx].multi.customProto ? (uint8_t) 2 : MULTIMODULE_HAS_SUBTYPE(g_model.moduleData[moduleIdx].getMultiProtocol(true)) ? (uint8_t) 1 : 0); #endif } -#define MULTIMODULE_SUBTYPE_ROWS(x) isModuleMultimodule(x) ? MULTIMODULE_RFPROTO_COLUMNS(x) : HIDDEN_ROW, -#define MULTIMODULE_HASOPTIONS(x) (getMultiProtocolDefinition(x)->optionsstr != nullptr) -#define MULTIMODULE_OPTIONS_ROW (isModuleMultimodule(EXTERNAL_MODULE) && MULTIMODULE_HASOPTIONS(g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true))) ? (uint8_t) 0: HIDDEN_ROW +#define MULTIMODULE_SUBTYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW, +#define MULTIMODULE_HASOPTIONS(moduleIdx) (getMultiProtocolDefinition(moduleIdx)->optionsstr != nullptr) +#define MULTIMODULE_OPTIONS_ROW(moduleIdx) (isModuleMultimodule(moduleIdx) && MULTIMODULE_HASOPTIONS(g_model.moduleData[moduleIdx].getMultiProtocol(true))) ? (uint8_t) 0: HIDDEN_ROW #else -#define MULTIMODULE_STATUS_ROWS -#define MULTIMODULE_MODULE_ROWS -#define MULTIMODULE_SUBTYPE_ROWS(x) -#define MULTIMODULE_MODE_ROWS(x) (uint8_t)0 -#define MULTIMODULE_OPTIONS_ROW HIDDEN_ROW +#define MULTIMODULE_STATUS_ROWS(moduleIdx) +#define MULTIMODULE_MODULE_ROWS(moduleIdx) +#define MULTIMODULE_SUBTYPE_ROWS(moduleIdx) +#define MULTIMODULE_MODE_ROWS(moduleIdx) (uint8_t)0 +#define MULTIMODULE_OPTIONS_ROW(moduleIdx) HIDDEN_ROW #endif -#define FAILSAFE_ROWS(x) isModuleFailsafeAvailable(x) ? (g_model.moduleData[x].failsafeMode==FAILSAFE_CUSTOM ? (uint8_t)1 : (uint8_t)0) : HIDDEN_ROW -#define EXTERNAL_MODULE_OPTION_ROW (isModuleR9MNonAccess(EXTERNAL_MODULE) || isModuleSBUS(EXTERNAL_MODULE) ? TITLE_ROW : MULTIMODULE_OPTIONS_ROW) -#define EXTERNAL_MODULE_POWER_ROW (isModuleMultimodule(EXTERNAL_MODULE) || isModuleR9MNonAccess(EXTERNAL_MODULE)) ? (isModuleR9MLiteNonPro(EXTERNAL_MODULE) ? (isModuleR9M_FCC_VARIANT(EXTERNAL_MODULE) ? READONLY_ROW : (uint8_t)0) : (uint8_t)0) : HIDDEN_ROW +#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)) +#define MODULE_POWER_ROW(moduleIdx) (isModuleMultimodule(moduleIdx) || isModuleR9MNonAccess(moduleIdx)) ? (isModuleR9MLiteNonPro(moduleIdx) ? (isModuleR9M_FCC_VARIANT(moduleIdx) ? READONLY_ROW : (uint8_t)0) : (uint8_t)0) : HIDDEN_ROW void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags); diff --git a/radio/src/io/frsky_firmware_update.cpp b/radio/src/io/frsky_firmware_update.cpp index 889139990..d8a8933e9 100644 --- a/radio/src/io/frsky_firmware_update.cpp +++ b/radio/src/io/frsky_firmware_update.cpp @@ -33,7 +33,7 @@ #define PRIM_END_DOWNLOAD 0x83 #define PRIM_DATA_CRC_ERR 0x84 -const char * readFirmwareInformation(const char * filename, FrSkyFirmwareInformation & data) +const char * readFrSkyFirmwareInformation(const char * filename, FrSkyFirmwareInformation & data) { FIL file; UINT count; @@ -305,7 +305,7 @@ const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename) } const char * ext = getFileExtension(filename); - if (ext && !strcasecmp(ext, UPDATE_FIRMWARE_EXT)) { + if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { if (f_read(&file, &information, sizeof(FrSkyFirmwareInformation), &count) != FR_OK || count != sizeof(FrSkyFirmwareInformation)) { f_close(&file); return "Format error"; @@ -321,7 +321,7 @@ const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename) if (module == INTERNAL_MODULE && information.productId == FIRMWARE_ID_XJT) { INTERNAL_MODULE_ON(); RTOS_WAIT_MS(1); - intmoduleSerialStart(38400, true); + intmoduleSerialStart(38400, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); GPIO_SetBits(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN); result = uploadFileToHorusXJT(filename, &file); GPIO_ResetBits(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN); @@ -336,7 +336,7 @@ const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename) // this ifdef can be removed if we use .frsk instead of .frk // theorically it should be possible to use an ISRM module in an XLite case INTERNAL_MODULE: - intmoduleSerialStart(57600, true); + intmoduleSerialStart(57600, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); break; #endif diff --git a/radio/src/io/frsky_firmware_update.h b/radio/src/io/frsky_firmware_update.h index 9d3dcc820..a229edb27 100644 --- a/radio/src/io/frsky_firmware_update.h +++ b/radio/src/io/frsky_firmware_update.h @@ -53,7 +53,7 @@ PACK(struct FrSkyFirmwareInformation { uint16_t crc; }); -const char * readFirmwareInformation(const char * filename, FrSkyFirmwareInformation & data); +const char * readFrSkyFirmwareInformation(const char * filename, FrSkyFirmwareInformation & data); class FrskyDeviceFirmwareUpdate { enum State { diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp new file mode 100644 index 000000000..dcc6d6e7b --- /dev/null +++ b/radio/src/io/multi_firmware_update.cpp @@ -0,0 +1,447 @@ +/* + * 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. + */ + +#if !defined(DISABLE_MULTI_UPDATE) +#include "opentx.h" +#include "multi_firmware_update.h" +#include "stk500.h" +#include "debug.h" + +#define UPDATE_MULTI_EXT_BIN ".bin" + +//#define DEBUG_EXT_MODULE_FLASH + +class MultiFirmwareUpdateDriver +{ + public: + MultiFirmwareUpdateDriver() = default; + const char* flashFirmware(FIL* file, const char* label) const; + + protected: + virtual void init() const = 0; + virtual bool getByte(uint8_t& byte) const = 0; + virtual void sendByte(uint8_t byte) const = 0; + virtual void clear() const = 0; + + private: + bool getRxByte(uint8_t& byte) const; + bool checkRxByte(uint8_t byte) const; + const char * waitForInitialSync() const; + const char * getDeviceSignature(uint8_t* signature) const; + const char * loadAddress(uint32_t offset) const; + const char * progPage(uint8_t* buffer, uint16_t size) const; +}; + +#if defined(INTERNAL_MODULE_MULTI) + +class MultiInternalUpdateDriver: public MultiFirmwareUpdateDriver +{ + public: + MultiInternalUpdateDriver() = default; + + protected: + void init() const override + { + INTERNAL_MODULE_ON(); + intmoduleSerialStart(57600, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); + } + + bool getByte(uint8_t& byte) const override + { + return intmoduleFifo.pop(byte); + } + + void sendByte(uint8_t byte) const override + { + intmoduleSendByte(byte); + } + + void clear() const override + { + intmoduleFifo.clear(); + } +}; + +static const MultiInternalUpdateDriver multiInternalUpdateDriver; + +#endif + +class MultiExternalUpdateDriver: public MultiFirmwareUpdateDriver +{ + public: + MultiExternalUpdateDriver() = default; + + protected: + void init() const override + { +#if !defined(EXTMODULE_USART) + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); +#endif + + EXTERNAL_MODULE_ON(); + telemetryPortInit(57600, TELEMETRY_SERIAL_DEFAULT); + } + + bool getByte(uint8_t& byte) const override + { + return telemetryGetByte(&byte); + } + + void sendByte(uint8_t byte) const override + { + extmoduleSendInvertedByte(byte); + } + + void clear() const override + { + telemetryClearFifo(); + } +}; + +static const MultiExternalUpdateDriver multiExternalUpdateDriver; + +bool MultiFirmwareUpdateDriver::getRxByte(uint8_t& byte) const +{ + uint16_t time; + + time = getTmr2MHz() ; + while ( (uint16_t) (getTmr2MHz() - time) < 25000 ) { // 12.5mS + + if (getByte(byte)) { +#if defined(DEBUG_EXT_MODULE_FLASH) + TRACE("[RX] 0x%X", byte); +#endif + return true; + } + } + + byte = 0; + return false; +} + +bool MultiFirmwareUpdateDriver::checkRxByte(uint8_t byte) const +{ + uint8_t rxchar; + return getRxByte(rxchar) ? rxchar == byte : false; +} + +const char * MultiFirmwareUpdateDriver::waitForInitialSync() const +{ + clear(); + + uint8_t byte; + int retries = 1000; + do { + // Send sync request + sendByte(STK_GET_SYNC); + sendByte(CRC_EOP); + + getRxByte(byte); + wdt_reset(); + + } while((byte != STK_INSYNC) && --retries); + + if (!retries) { + return "NoSync"; + } + + if (byte != STK_INSYNC) { + return "NoSync"; + } + + if (!checkRxByte(STK_OK)) { + return "NoSync"; + } + + return nullptr; +} + +const char * MultiFirmwareUpdateDriver::getDeviceSignature(uint8_t* signature) const +{ + // Read signature + sendByte(STK_READ_SIGN); + sendByte(CRC_EOP); + + if (!checkRxByte(STK_INSYNC)) + return "NoSync"; + + for (uint8_t i=0; i<4; i++) { + if (!getRxByte(signature[i])) { + return "NoSignature"; + } + } + + return nullptr; +} + +const char * MultiFirmwareUpdateDriver::loadAddress(uint32_t offset) const +{ + sendByte(STK_LOAD_ADDRESS); + sendByte(offset & 0xFF); // low byte + sendByte(offset >> 8); // high byte + sendByte(CRC_EOP); + + if (!checkRxByte(STK_INSYNC) || !checkRxByte(STK_OK)) { + return "NoSync"; + } + + return nullptr; +} + +const char * MultiFirmwareUpdateDriver::progPage(uint8_t* buffer, uint16_t size) const +{ + sendByte(STK_PROG_PAGE); + // page size 256 + sendByte(1); + sendByte(0); + // flash/eeprom flag + sendByte(0); + + // #if defined(DEBUG_EXT_MODULE_FLASH) + // TRACE("writing at 0x%X", writeOffset << 1); + // #endif + + for (uint16_t i=0; i < size; i++) { + sendByte(buffer[i]); + } + sendByte(CRC_EOP); + + if (!checkRxByte(STK_INSYNC)) + return "NoSync"; + + uint8_t byte; + uint8_t retries = 4; + do { + getRxByte(byte); + wdt_reset(); + } while(!byte && --retries); + + if (!retries || (byte != STK_OK)) + return "NoPageSync"; + + return nullptr; +} + +const char * MultiFirmwareUpdateDriver::flashFirmware(FIL* file, const char* label) const +{ + const char* result = nullptr; + init(); + + /* wait 500ms for power on */ + watchdogSuspend(500); + RTOS_WAIT_MS(500); + + result = waitForInitialSync(); + if (result != nullptr) + return result; + + unsigned char signature[4]; // 3 bytes signature + STK_OK + result = getDeviceSignature(signature); + if (result != nullptr) + return result; + + uint8_t buffer[256]; // page size = 256 + uint32_t writeOffset = 0x1000; // start offset (word address) + + while (!f_eof(file)) { + + drawProgressScreen(label, STR_WRITING, file->fptr, file->obj.objsize); + + UINT count=0; + memclear(buffer, 256); + if (f_read(file, buffer, 256, &count) != FR_OK) { + result = "Error reading file"; + break; + } + + if (!count) + break; + + clear(); + + result = loadAddress(writeOffset); + if (result != nullptr) { + break; + } + + result = progPage(buffer, 256); + if (result != nullptr) { + break; + } + + writeOffset += 128; // page / 2 + } + + if (f_eof(file)) { + drawProgressScreen(label, STR_WRITING, file->fptr, file->obj.objsize); + } + + sendByte(STK_LEAVE_PROGMODE); + sendByte(CRC_EOP); + + // eat last sync byte + checkRxByte(STK_INSYNC); + + return result; +} + +// example : multi-stm-bcsid-01020176 +#define MULTI_SIGN_SIZE 24 +#define MULTI_SIGN_BOOTLOADER_SUPPORT_OFFSET 10 +#define MULTI_SIGN_BOOTLOADER_CHECK_OFFSET 11 +#define MULTI_SIGN_TELEM_TYPE_OFFSET 12 +#define MULTI_SIGN_TELEM_INVERSION_OFFSET 13 +#define MULTI_SIGN_VERSION_OFFSET 15 + +const char * MultiFirmwareInformation::readMultiFirmwareInformation(const char * filename) +{ + FIL file; + f_open(&file, filename, FA_READ); + char buffer[MULTI_SIGN_SIZE]; + UINT count; + + f_lseek(&file, f_size(&file) - MULTI_SIGN_SIZE); + if (f_read(&file, buffer, MULTI_SIGN_SIZE, &count) != FR_OK || count != MULTI_SIGN_SIZE) { + return "Error reading file"; + } + + if(!memcmp(buffer, "multi-stm", 9)) + boardType = FIRMWARE_MULTI_STM; + else if (!memcmp(buffer, "multi-avr", 9)) + boardType = FIRMWARE_MULTI_AVR; + else if (!memcmp(buffer, "multi-orx", 9)) + boardType = FIRMWARE_MULTI_ORX; + else + return "Wrong format"; + + if(buffer[MULTI_SIGN_BOOTLOADER_SUPPORT_OFFSET] == 'b') + optibootSupport = true; + else + optibootSupport = false; + + if(buffer[MULTI_SIGN_BOOTLOADER_CHECK_OFFSET] == 'c') + bootloaderCheck = true; + else + bootloaderCheck = false; + + if(buffer[MULTI_SIGN_TELEM_TYPE_OFFSET] == 't') + telemetryType = FIRMWARE_MULTI_TELEM_MULTI; + else if (buffer[MULTI_SIGN_TELEM_TYPE_OFFSET] == 's') + telemetryType = FIRMWARE_MULTI_TELEM_STATUS; + else + telemetryType = FIRMWARE_MULTI_TELEM_NONE; + + if(buffer[MULTI_SIGN_TELEM_INVERSION_OFFSET] == 'i') + telemetryInversion = true; + else + telemetryInversion = false; + + return nullptr; +} + + + + +const char * multiFlashFirmware(uint8_t moduleIdx, const char * filename) +{ + FIL file; + const char * result = nullptr; + + const char * ext = getFileExtension(filename); + if (ext && strcasecmp(ext, UPDATE_MULTI_EXT_BIN)) { + return "Wrong file extension"; + } + + if (f_open(&file, filename, FA_READ) != FR_OK) { + return "Error opening file"; + } + + pausePulses(); + +#if defined(INTERNAL_MODULE_MULTI) + uint8_t intPwr = IS_INTERNAL_MODULE_ON(); + INTERNAL_MODULE_OFF(); +#endif + + uint8_t extPwr = IS_EXTERNAL_MODULE_ON(); + EXTERNAL_MODULE_OFF(); + + SPORT_UPDATE_POWER_OFF(); + + drawProgressScreen(getBasename(filename), STR_DEVICE_RESET, 0, 0); + + /* wait 2s off */ + watchdogSuspend(2000); + RTOS_WAIT_MS(2000); + + // TODO: real update here + const MultiFirmwareUpdateDriver* driver = &multiExternalUpdateDriver; +#if defined(INTERNAL_MODULE_MULTI) + if (moduleIdx == INTERNAL_MODULE) + driver = &multiInternalUpdateDriver; +#endif + + result = driver->flashFirmware(&file, getBasename(filename)); + f_close(&file); + + AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 ); + BACKLIGHT_ENABLE(); + + if (result) { + POPUP_WARNING(STR_FIRMWARE_UPDATE_ERROR); + SET_WARNING_INFO(result, strlen(result), 0); + } + else { + POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS); + } + + INTERNAL_MODULE_OFF(); + EXTERNAL_MODULE_OFF(); + SPORT_UPDATE_POWER_OFF(); + + /* wait 2s off */ + watchdogSuspend(2000); + RTOS_WAIT_MS(2000); + telemetryClearFifo(); + +#if defined(INTERNAL_MODULE_MULTI) + if (intPwr) { + INTERNAL_MODULE_ON(); + setupPulsesInternalModule(); + } +#endif + + if (extPwr) { + EXTERNAL_MODULE_ON(); + setupPulsesExternalModule(); + } + + //state = SPORT_IDLE; + resumePulses(); + + return result; +} +#endif \ No newline at end of file diff --git a/radio/src/io/multi_firmware_update.h b/radio/src/io/multi_firmware_update.h new file mode 100644 index 000000000..45c6f8e91 --- /dev/null +++ b/radio/src/io/multi_firmware_update.h @@ -0,0 +1,96 @@ +/* + * 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 OPENTX_MULTI_FIRMWARE_H +#define OPENTX_MULTI_FIRMWARE_H + +/* Signature format is multi-[board type]-[bootloader support][check for bootloader][multi telemetry type][telemetry inversion][debug]-[firmware version] + Where: + [board type] is avr, stm, or orx + [bootloader support] b for optiboot (AVR) / USB (STM) or u (unsupported) + [check for bootloader] is c (CHECK_FOR_BOOTLOADER) or u (undefined) + [telemetry type] is t (MULTI_TELEMETRY), s (MULTI_STATUS), or u (undefined) for neither + [telemetry inversion] is i (INVERT_TELEMETRY) or u (undefined) + [firmware version] is the version padded to two bytes per segment, without seperators e.g. 01020176 + + For example: REM multi-stm-bcsid-01020176 +*/ + +class MultiFirmwareInformation { + public: + enum MultiFirmwareBoardType { + FIRMWARE_MULTI_STM, + FIRMWARE_MULTI_AVR, + FIRMWARE_MULTI_ORX, + }; + + enum MultiFirmwareTelemetryType { + FIRMWARE_MULTI_TELEM_MULTI, + FIRMWARE_MULTI_TELEM_STATUS, + FIRMWARE_MULTI_TELEM_NONE, + }; + + bool isMultiStmFirmware() const + { + return boardType == FIRMWARE_MULTI_STM; + } + + bool isMultiAvrFirmware() const + { + return boardType == FIRMWARE_MULTI_AVR; + } + + bool isMultiOrxFirmware() const + { + return boardType == FIRMWARE_MULTI_ORX; + } + + bool isMultiWithBootloaderFirmware() const + { + return optibootSupport; + } + + bool isMultiInternalFirmware() const + { + return (boardType == FIRMWARE_MULTI_STM && telemetryInversion == false && optibootSupport == true && telemetryType == FIRMWARE_MULTI_TELEM_STATUS); + } + + bool isMultiExternalFirmware() const + { + return (telemetryInversion == false && optibootSupport == true && telemetryType == FIRMWARE_MULTI_TELEM_STATUS); + } + + const char * readMultiFirmwareInformation(const char * filename); + + private: + uint8_t boardType; + uint8_t optibootSupport; + uint8_t bootloaderCheck; + uint8_t telemetryType; + uint8_t telemetryInversion; + uint8_t firmwareVersionMajor; + uint8_t firmwareVersionMinor; + uint8_t firmwareVersionRevision; + uint8_t firmwareVersionSubRevision; +}; + +const char* multiFlashFirmware(uint8_t module, const char * filename); + +#endif //OPENTX_MULTI_FIRMWARE_H diff --git a/radio/src/io/stk500.h b/radio/src/io/stk500.h new file mode 100644 index 000000000..ca0dd91df --- /dev/null +++ b/radio/src/io/stk500.h @@ -0,0 +1,39 @@ +/* STK500 constants list, from AVRDUDE */ +#define STK_OK 0x10 +#define STK_FAILED 0x11 // Not used +#define STK_UNKNOWN 0x12 // Not used +#define STK_NODEVICE 0x13 // Not used +#define STK_INSYNC 0x14 // ' ' +#define STK_NOSYNC 0x15 // Not used +#define ADC_CHANNEL_ERROR 0x16 // Not used +#define ADC_MEASURE_OK 0x17 // Not used +#define PWM_CHANNEL_ERROR 0x18 // Not used +#define PWM_ADJUST_OK 0x19 // Not used +#define CRC_EOP 0x20 // 'SPACE' +#define STK_GET_SYNC 0x30 // '0' +#define STK_GET_SIGN_ON 0x31 // '1' +#define STK_SET_PARAMETER 0x40 // '@' +#define STK_GET_PARAMETER 0x41 // 'A' +#define STK_SET_DEVICE 0x42 // 'B' +#define STK_SET_DEVICE_EXT 0x45 // 'E' +#define STK_ENTER_PROGMODE 0x50 // 'P' +#define STK_LEAVE_PROGMODE 0x51 // 'Q' +#define STK_CHIP_ERASE 0x52 // 'R' +#define STK_CHECK_AUTOINC 0x53 // 'S' +#define STK_LOAD_ADDRESS 0x55 // 'U' +#define STK_UNIVERSAL 0x56 // 'V' +#define STK_PROG_FLASH 0x60 // '`' +#define STK_PROG_DATA 0x61 // 'a' +#define STK_PROG_FUSE 0x62 // 'b' +#define STK_PROG_LOCK 0x63 // 'c' +#define STK_PROG_PAGE 0x64 // 'd' +#define STK_PROG_FUSE_EXT 0x65 // 'e' +#define STK_READ_FLASH 0x70 // 'p' +#define STK_READ_DATA 0x71 // 'q' +#define STK_READ_FUSE 0x72 // 'r' +#define STK_READ_LOCK 0x73 // 's' +#define STK_READ_PAGE 0x74 // 't' +#define STK_READ_SIGN 0x75 // 'u' +#define STK_READ_OSCCAL 0x76 // 'v' +#define STK_READ_FUSE_EXT 0x77 // 'w' +#define STK_READ_OSCCAL_EXT 0x78 // 'x' diff --git a/radio/src/lua/CMakeLists.txt b/radio/src/lua/CMakeLists.txt index 751d4716a..f0ed96e94 100644 --- a/radio/src/lua/CMakeLists.txt +++ b/radio/src/lua/CMakeLists.txt @@ -26,4 +26,5 @@ if(PYTHONINTERP_FOUND) add_lua_export_target(x10 ${LUA_INCLUDES} -DPCBHORUS -DPCBX10) add_lua_export_target(x12s ${LUA_INCLUDES} -DPCBHORUS -DPCBX12S) add_lua_export_target(t12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T12) + add_lua_export_target(t16 ${LUA_INCLUDES} -DPCBHORUS -DPCBX10 -DRADIO_T16) endif() diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index e69ea7060..9dec737d3 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -27,6 +27,8 @@ #if defined(PCBX12S) #include "lua/lua_exports_x12s.inc" // this line must be after lua headers +#elif defined(RADIO_T16) + #include "lua/lua_exports_t16.inc" #elif defined(PCBX10) #include "lua/lua_exports_x10.inc" #elif defined(PCBX9E) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 16419655d..8191cccfa 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1512,7 +1512,7 @@ void doMixerCalculations() static uint8_t countRangecheck = 0; for (uint8_t i=0; i= MODULE_MODE_BEEP_FIRST || multiModuleStatus.isBinding()) { + if (moduleState[i].mode >= MODULE_MODE_BEEP_FIRST || getMultiModuleStatus(i).isBinding()) { #else if (moduleState[i].mode >= MODULE_MODE_BEEP_FIRST) { #endif @@ -1862,7 +1862,7 @@ void opentxInit() sportStopSendByteLoop(); if (f_stat(AUTOUPDATE_FILENAME, nullptr) == FR_OK) { FrSkyFirmwareInformation information; - if (readFirmwareInformation(AUTOUPDATE_FILENAME, information) == nullptr) { + if (readFrSkyFirmwareInformation(AUTOUPDATE_FILENAME, information) == nullptr) { #if defined(BLUETOOTH) if (information.productFamily == FIRMWARE_FAMILY_BLUETOOTH_CHIP) { if (bluetooth.flashFirmware(AUTOUPDATE_FILENAME) == nullptr) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index a2c3d86ca..5d2266202 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -544,6 +544,8 @@ bool setTrimValue(uint8_t phase, uint8_t idx, int trim); #if defined(PCBSKY9X) #define ROTARY_ENCODER_GRANULARITY (2 << g_eeGeneral.rotarySteps) +#elif defined(RADIO_T16) + #define ROTARY_ENCODER_GRANULARITY (1) #else #define ROTARY_ENCODER_GRANULARITY (2) #endif diff --git a/radio/src/options.h b/radio/src/options.h index 1038af319..e7ddb0caf 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -56,6 +56,9 @@ static const char * options[] = { #if defined(INTERNAL_MODULE_PPM) "internalppm", #endif +#if defined(INTERNAL_MODULE_MULTI) + "internalmulti", +#endif #if defined(MULTIMODULE) "multimodule", #endif diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 3bc69e3bc..6a2a6a34b 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -34,7 +34,7 @@ #if defined(MULTIMODULE) inline bool isModuleMultimodule(uint8_t idx) { - return idx == EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_MULTIMODULE; + return g_model.moduleData[idx].type == MODULE_TYPE_MULTIMODULE; } inline bool isModuleMultimoduleDSM2(uint8_t idx) @@ -350,8 +350,10 @@ inline bool isModuleFailsafeAvailable(uint8_t idx) return g_model.moduleData[idx].subType == MODULE_SUBTYPE_PXX1_ACCST_D16; #if defined(MULTIMODULE) - if (isModuleMultimodule(idx)) - return multiModuleStatus.isValid() && multiModuleStatus.supportsFailsafe(); + if (isModuleMultimodule(idx)){ + MultiModuleStatus& status = getMultiModuleStatus(idx); + return status.isValid() && status.supportsFailsafe(); + } #endif if (isModuleR9M(idx)) diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index a84bb04a1..9ceb57886 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -31,27 +31,38 @@ #define MULTI_CHANS 16 #define MULTI_CHAN_BITS 11 -static void sendFrameProtocolHeader(uint8_t port, bool failsafe); +static void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe); -void sendChannels(uint8_t port); +void sendChannels(uint8_t moduleIdx); -static void sendSetupFrame() +static void sendMulti(uint8_t moduleIdx, uint8_t b) +{ +#if defined(INTERNAL_MODULE_MULTI) + if (moduleIdx == INTERNAL_MODULE) { + intmodulePulsesData.multi.sendByte(b); + } + else +#endif + sendByteSbus(b); +} + +static void sendSetupFrame(uint8_t moduleIdx) { // Old multi firmware will mark config messsages as invalid frame and throw them away - sendByteSbus('M'); - sendByteSbus('P'); - sendByteSbus(0x80); // Module Configuration - sendByteSbus(1); // 1 byte data + sendMulti(moduleIdx, 'M'); + sendMulti(moduleIdx, 'P'); + sendMulti(moduleIdx, 0x80); // Module Configuration + sendMulti(moduleIdx, 1); // 1 byte data uint8_t config = 0x01 | 0x02; // inversion + multi_telemetry #if !defined(PPM_PIN_SERIAL) // TODO why PPM_PIN_SERIAL would change MULTI protocol? config |= 0x04; // input synchronsisation #endif - sendByteSbus(config); + sendMulti(moduleIdx, config); } -static void sendFailsafeChannels(uint8_t port) +static void sendFailsafeChannels(uint8_t moduleIdx) { uint32_t bits = 0; uint8_t bitsavailable = 0; @@ -60,61 +71,72 @@ static void sendFailsafeChannels(uint8_t port) int16_t failsafeValue = g_model.failsafeChannels[i]; int pulseValue; - if (g_model.moduleData[port].failsafeMode == FAILSAFE_HOLD) { + if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_HOLD) { pulseValue = 2047; } - else if (g_model.moduleData[port].failsafeMode == FAILSAFE_NOPULSES) { + else if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_NOPULSES) { pulseValue = 0; } else { - failsafeValue += 2 * PPM_CH_CENTER(g_model.moduleData[port].channelsStart + i) - 2 * PPM_CENTER; + failsafeValue += 2 * PPM_CH_CENTER(g_model.moduleData[moduleIdx].channelsStart + i) - 2 * PPM_CENTER; pulseValue = limit(1, (failsafeValue * 800 / 1000) + 1024, 2047); } bits |= pulseValue << bitsavailable; bitsavailable += MULTI_CHAN_BITS; while (bitsavailable >= 8) { - sendByteSbus((uint8_t) (bits & 0xff)); + sendMulti(moduleIdx, (uint8_t) (bits & 0xff)); bits >>= 8; bitsavailable -= 8; } } } -void setupPulsesMultimodule() +void setupPulsesMulti(uint8_t moduleIdx) { - static int counter = 0; + static int counter[2] = {0,0}; //TODO + // Every 1000 cycles (=9s) send a config packet that configures the multimodule (inversion, telemetry type) + counter[moduleIdx]++; + if (counter[moduleIdx] % 1000 == 500) { + sendSetupFrame(moduleIdx); + } + else if (counter[moduleIdx] % 1000 == 0 && g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_NOT_SET && g_model.moduleData[moduleIdx].failsafeMode != FAILSAFE_RECEIVER) { + sendFrameProtocolHeader(moduleIdx, true); + sendFailsafeChannels(moduleIdx); + } + else { + // Normal Frame + sendFrameProtocolHeader(moduleIdx, false); + sendChannels(moduleIdx); + } +} + +void setupPulsesMultiExternalModule() +{ #if defined(PPM_PIN_SERIAL) extmodulePulsesData.dsm2.serialByte = 0 ; extmodulePulsesData.dsm2.serialBitCount = 0 ; #else - extmodulePulsesData.dsm2.rest = multiSyncStatus.getAdjustedRefreshRate(); + extmodulePulsesData.dsm2.rest = getMultiSyncStatus(EXTERNAL_MODULE).getAdjustedRefreshRate(); extmodulePulsesData.dsm2.index = 0; #endif extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses; - // Every 1000 cycles (=9s) send a config packet that configures the multimodule (inversion, telemetry type) - counter++; - if (counter % 1000 == 500) { - sendSetupFrame(); - } - else if (counter % 1000 == 0 && g_model.moduleData[EXTERNAL_MODULE].failsafeMode != FAILSAFE_NOT_SET && g_model.moduleData[EXTERNAL_MODULE].failsafeMode != FAILSAFE_RECEIVER) { - sendFrameProtocolHeader(EXTERNAL_MODULE, true); - sendFailsafeChannels(EXTERNAL_MODULE); - } - else { - // Normal Frame - sendFrameProtocolHeader(EXTERNAL_MODULE, false); - sendChannels(EXTERNAL_MODULE); - } - + setupPulsesMulti(EXTERNAL_MODULE); putDsm2Flush(); } +#if defined(INTERNAL_MODULE_MULTI) +void setupPulsesMultiInternalModule() +{ + intmodulePulsesData.multi.initFrame(); + setupPulsesMulti(INTERNAL_MODULE); +} +#endif -void sendChannels(uint8_t port) +void sendChannels(uint8_t moduleIdx) { uint32_t bits = 0; uint8_t bitsavailable = 0; @@ -123,7 +145,7 @@ void sendChannels(uint8_t port) // Range for pulses (channelsOutputs) is [-1024:+1024] for [-100%;100%] // Multi uses [204;1843] as [-100%;100%] for (int i = 0; i < MULTI_CHANS; i++) { - int channel = g_model.moduleData[port].channelsStart + i; + int channel = g_model.moduleData[moduleIdx].channelsStart + i; int value = channelOutputs[channel] + 2 * PPM_CH_CENTER(channel) - 2 * PPM_CENTER; // Scale to 80% @@ -133,36 +155,36 @@ void sendChannels(uint8_t port) bits |= value << bitsavailable; bitsavailable += MULTI_CHAN_BITS; while (bitsavailable >= 8) { - sendByteSbus((uint8_t) (bits & 0xff)); + sendMulti(moduleIdx, (uint8_t) (bits & 0xff)); bits >>= 8; bitsavailable -= 8; } } } -void sendFrameProtocolHeader(uint8_t port, bool failsafe) +void sendFrameProtocolHeader(uint8_t moduleIdx, bool failsafe) {// byte 1+2, protocol information // Our enumeration starts at 0 - int type = g_model.moduleData[port].getMultiProtocol(false) + 1; - int subtype = g_model.moduleData[port].subType; - int8_t optionValue = g_model.moduleData[port].multi.optionValue; + int type = g_model.moduleData[moduleIdx].getMultiProtocol(false) + 1; + int subtype = g_model.moduleData[moduleIdx].subType; + int8_t optionValue = g_model.moduleData[moduleIdx].multi.optionValue; uint8_t protoByte = 0; - if (moduleState[port].mode == MODULE_MODE_BIND) + if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) protoByte |= MULTI_SEND_BIND; - else if (moduleState[port].mode == MODULE_MODE_RANGECHECK) + else if (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK) protoByte |= MULTI_SEND_RANGECHECK; // rfProtocol - if (g_model.moduleData[port].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) { + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) { // Autobinding should always be done in DSMX 11ms - if (g_model.moduleData[port].multi.autoBindMode && moduleState[port].mode == MODULE_MODE_BIND) + 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 - optionValue = sentModuleChannels(EXTERNAL_MODULE); + optionValue = sentModuleChannels(moduleIdx); } @@ -175,7 +197,7 @@ void sendFrameProtocolHeader(uint8_t port, bool failsafe) if (type >= 25) type = type + 1; - if (g_model.moduleData[port].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_FRSKY) { + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_FRSKY) { if (subtype == MM_RF_FRSKY_SUBTYPE_D8) { //D8 type = 3; @@ -199,12 +221,12 @@ void sendFrameProtocolHeader(uint8_t port, bool failsafe) // Set the highest bit of option byte in AFHDS2A protocol to instruct MULTI to passthrough telemetry bytes instead // of sending Frsky D telemetry - if (g_model.moduleData[port].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) + if (g_model.moduleData[moduleIdx].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) optionValue = optionValue | 0x80; // For custom protocol send unmodified type byte - if (g_model.moduleData[port].getMultiProtocol(true) == MM_RF_CUSTOM_SELECTED) - type = g_model.moduleData[port].getMultiProtocol(false); + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) == MM_RF_CUSTOM_SELECTED) + type = g_model.moduleData[moduleIdx].getMultiProtocol(false); uint8_t headerByte = 0x54; @@ -213,24 +235,24 @@ void sendFrameProtocolHeader(uint8_t port, bool failsafe) // header, byte 0, 0x55 for proto 0-31 0x54 for 32-63 if (type <= 31) - sendByteSbus(headerByte+1); + sendMulti(moduleIdx, headerByte+1); else - sendByteSbus(headerByte); + sendMulti(moduleIdx, headerByte); // protocol byte protoByte |= (type & 0x1f); - if (g_model.moduleData[port].getMultiProtocol(true) != MODULE_SUBTYPE_MULTI_DSM2) - protoByte |= (g_model.moduleData[port].multi.autoBindMode << 6); + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) != MODULE_SUBTYPE_MULTI_DSM2) + protoByte |= (g_model.moduleData[moduleIdx].multi.autoBindMode << 6); - sendByteSbus(protoByte); + sendMulti(moduleIdx, protoByte); // byte 2, subtype, powermode, model id - sendByteSbus((uint8_t) ((g_model.header.modelId[port] & 0x0f) + sendMulti(moduleIdx, (uint8_t) ((g_model.header.modelId[moduleIdx] & 0x0f) | ((subtype & 0x7) << 4) - | (g_model.moduleData[port].multi.lowPowerMode << 7)) + | (g_model.moduleData[moduleIdx].multi.lowPowerMode << 7)) ); // byte 3 - sendByteSbus((uint8_t) optionValue); + sendMulti(moduleIdx, (uint8_t) optionValue); } diff --git a/radio/src/pulses/multi.h b/radio/src/pulses/multi.h new file mode 100644 index 000000000..d3f088b7d --- /dev/null +++ b/radio/src/pulses/multi.h @@ -0,0 +1,41 @@ +/* + * 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 _MULTI_PULSES_H_ +#define _MULTI_PULSES_H_ + +#include "pulses_common.h" + +class UartMultiPulses: public DataBuffer +{ + public: + void initFrame() + { + initBuffer(); + } + + void sendByte(uint8_t b) + { + if (getSize() < 64) + *ptr++ = b; + } +}; + +#endif diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index cec72d8a0..e771d4e90 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -269,7 +269,7 @@ void setupPulsesExternalModule(uint8_t protocol) #if defined(MULTIMODULE) case PROTOCOL_CHANNELS_MULTIMODULE: - setupPulsesMultimodule(); + setupPulsesMultiExternalModule(); scheduleNextMixerCalculation(EXTERNAL_MODULE, MULTIMODULE_PERIOD); break; #endif @@ -306,10 +306,17 @@ void enablePulsesInternalModule(uint8_t protocol) #if defined(PXX2) case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: - intmoduleSerialStart(PXX2_HIGHSPEED_BAUDRATE, true); + intmoduleSerialStart(PXX2_HIGHSPEED_BAUDRATE, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); break; #endif +#if defined(INTERNAL_MODULE_MULTI) + case PROTOCOL_CHANNELS_MULTIMODULE: + intmodulePulsesData.multi.initFrame(); + intmoduleSerialStart(MULTIMODULE_BAUDRATE, true, USART_Parity_Even, USART_StopBits_2, USART_WordLength_9b); + intmoduleTimerStart(MULTIMODULE_PERIOD); + break; +#endif default: break; } @@ -355,6 +362,13 @@ void setupPulsesInternalModule(uint8_t protocol) break; #endif +#if defined(INTERNAL_MODULE_MULTI) + case PROTOCOL_CHANNELS_MULTIMODULE: + setupPulsesMultiInternalModule(); + scheduleNextMixerCalculation(INTERNAL_MODULE, MULTIMODULE_PERIOD); + break; +#endif + default: break; } diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index e2aa1664a..9fa17a196 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -24,8 +24,9 @@ #include "definitions.h" #include "dataconstants.h" #include "pulses_common.h" -#include "pulses/pxx1.h" -#include "pulses/pxx2.h" +#include "pxx1.h" +#include "pxx2.h" +#include "multi.h" #include "modules_helpers.h" #include "ff.h" @@ -248,6 +249,10 @@ union InternalModulePulsesData { Pxx2Pulses pxx2; #endif +#if defined(INTERNAL_MODULE_MULTI) //&& defined(INTMODULE_USART) + UartMultiPulses multi; +#endif + #if defined(INTERNAL_MODULE_PPM) PpmPulsesData ppm; #endif @@ -294,11 +299,14 @@ union TrainerPulsesData { extern TrainerPulsesData trainerPulsesData; +#if defined(HARDWARE_INTERNAL_MODULE) bool setupPulsesInternalModule(); +#endif bool setupPulsesExternalModule(); void setupPulsesDSM2(); void setupPulsesCrossfire(); -void setupPulsesMultimodule(); +void setupPulsesMultiExternalModule(); +void setupPulsesMultiInternalModule(); void setupPulsesSbus(); void setupPulsesPPMInternalModule(); void setupPulsesPPMExternalModule(); diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index b32eede0c..baa421de7 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -469,7 +469,7 @@ const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename) uint32_t size; const char * ext = getFileExtension(filename); - if (ext && !strcasecmp(ext, UPDATE_FIRMWARE_EXT)) { + if (ext && !strcasecmp(ext, FRSKY_FIRMWARE_EXT)) { FrSkyFirmwareInformation * information = (FrSkyFirmwareInformation *) buffer; if (f_read(&file, buffer, sizeof(FrSkyFirmwareInformation), &count) != FR_OK || count != sizeof(FrSkyFirmwareInformation)) { f_close(&file); diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index 0b518eb24..3f7ed8eda 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -70,7 +70,8 @@ const char RADIO_SETTINGS_PATH[] = RADIO_PATH "/radio.bin"; #define FIRMWARE_EXT ".bin" #define EEPROM_EXT ".bin" #define SPORT_FIRMWARE_EXT ".frk" -#define UPDATE_FIRMWARE_EXT ".frsk" +#define FRSKY_FIRMWARE_EXT ".frsk" +#define MULTI_FIRMWARE_EXT ".bin" #define LEN_FILE_EXTENSION_MAX 5 // longest used, including the dot, excluding null term. @@ -125,6 +126,8 @@ const char * getBasename(const char * path); #if defined(PCBX12S) #define OTX_FOURCC 0x3478746F // otx for X12S +#elif defined(RADIO_T16) + #define OTX_FOURCC 0x3F78746F // otx for Jumper T16 #elif defined(PCBX10) #define OTX_FOURCC 0x3778746F // otx for X10 #elif defined(PCBX9E) diff --git a/radio/src/storage/conversions/conversions_218_219.cpp b/radio/src/storage/conversions/conversions_218_219.cpp index f6076917d..bba32aef0 100644 --- a/radio/src/storage/conversions/conversions_218_219.cpp +++ b/radio/src/storage/conversions/conversions_218_219.cpp @@ -124,9 +124,11 @@ void convertModelData_218_to_219(ModelData &model) memclear(&newModel.header.bitmap[10], 4); memcpy(newModel.timers, oldModel.timers, offsetof(ModelData_v218, mixData) - offsetof(ModelData_v218, timers)); +#if defined(BLUETOOTH) // trainer battery compartment removed if (newModel.trainerData.mode >= TRAINER_MODE_MASTER_BLUETOOTH) newModel.trainerData.mode -= 1; +#endif #endif memclear(newModel.mixData, sizeof(ModelData_v219) - offsetof(ModelData_v219, mixData)); diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 9b488138c..04d30f936 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -143,7 +143,7 @@ endif() if(MULTIMODULE) add_definitions(-DMULTIMODULE) - set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/flysky_ibus.cpp telemetry/multi.cpp) + set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/flysky_ibus.cpp telemetry/multi.cpp io/multi_firmware_update.cpp) endif() if(CROSSFIRE) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index 36d0e5a60..49887799e 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -22,8 +22,10 @@ #if defined(SIMU) // 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(PCBX10) - const int8_t adcDirection[NUM_ANALOGS] = {1,-1,1,-1, -1,1,-1, 1,1, 1, -1}; + const int8_t adcDirection[NUM_ANALOGS] = {1,-1,1,-1, -1,1,-1, 1,1, 1, -1}; #elif defined(PCBX9E) #if defined(HORUS_STICKS) const int8_t adcDirection[NUM_ANALOGS] = {1,-1,1,-1, -1,-1,-1,1, -1,1,-1, -1,-1,-1}; diff --git a/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp b/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp index fd1e83183..2962731b7 100644 --- a/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp +++ b/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp @@ -95,8 +95,14 @@ void audioConsumeCurrentBuffer() nextBuffer = audioQueue.buffersFifo.getNextFilledBuffer(); if (nextBuffer) { #if defined(AUDIO_MUTE_GPIO_PIN) - // un-mute - GPIO_ResetBits(AUDIO_MUTE_GPIO, AUDIO_MUTE_GPIO_PIN); + // if muted + if (GPIO_ReadOutputDataBit(AUDIO_MUTE_GPIO, AUDIO_MUTE_GPIO_PIN)) { + // ..un-mute + GPIO_ResetBits(AUDIO_MUTE_GPIO, AUDIO_MUTE_GPIO_PIN); +#if defined(AUDIO_UNMUTE_DELAY) + RTOS_WAIT_MS(AUDIO_UNMUTE_DELAY); +#endif + } #endif AUDIO_DMA_Stream->CR &= ~DMA_SxCR_EN ; // Disable DMA channel AUDIO_DMA->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5 ; // Write ones to clear bits 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 1f22abc48..9499e24f9 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -20,6 +20,7 @@ #include "opentx.h" +#if defined(AUX_SERIAL) uint8_t auxSerialMode = 0; Fifo auxSerialTxFifo; AuxSerialRxFifo auxSerialRxFifo __DMA (AUX_SERIAL_DMA_Stream_RX); @@ -183,4 +184,5 @@ extern "C" void AUX_SERIAL_USART_IRQHandler(void) } #endif } -#endif \ No newline at end of file +#endif +#endif // AUX_SERIAL diff --git a/radio/src/targets/common/arm/stm32/intmodule_serial_driver.cpp b/radio/src/targets/common/arm/stm32/intmodule_serial_driver.cpp index 8102b5eee..e2f01f586 100755 --- a/radio/src/targets/common/arm/stm32/intmodule_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/intmodule_serial_driver.cpp @@ -39,14 +39,20 @@ void intmoduleStop() USART_DeInit(INTMODULE_USART); GPIO_ResetBits(INTMODULE_GPIO, INTMODULE_TX_GPIO_PIN | INTMODULE_RX_GPIO_PIN); + +#if defined(INTERNAL_MODULE_MULTI) + // stop pulses timer + INTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; + INTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; +#endif } void intmodulePxx1SerialStart() { - intmoduleSerialStart(INTMODULE_PXX1_SERIAL_BAUDRATE, false); + intmoduleSerialStart(INTMODULE_PXX1_SERIAL_BAUDRATE, false, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); } -void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable) +void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, uint16_t stopBits, uint16_t wordLength) { INTERNAL_MODULE_ON(); @@ -71,9 +77,9 @@ void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable) USART_DeInit(INTMODULE_USART); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = baudrate; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_Parity = parity; + USART_InitStructure.USART_StopBits = stopBits; + USART_InitStructure.USART_WordLength = wordLength; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(INTMODULE_USART, &USART_InitStructure); @@ -87,6 +93,28 @@ void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable) } } +#if defined(INTERNAL_MODULE_MULTI) +void intmoduleTimerStart(uint32_t periodMs) +{ + // Timer + INTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + INTMODULE_TIMER->PSC = INTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + INTMODULE_TIMER->ARR = periodMs * 2000; + INTMODULE_TIMER->CCR2 = (periodMs - 1) * 2000; + INTMODULE_TIMER->CCER = TIM_CCER_CC3E; + INTMODULE_TIMER->CCMR2 = 0; + INTMODULE_TIMER->EGR = 1; // Restart + + INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0; // Toggle CC1 o/p + INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + INTMODULE_TIMER->CR1 |= TIM_CR1_CEN; + + NVIC_EnableIRQ(INTMODULE_TIMER_IRQn); + NVIC_SetPriority(INTMODULE_TIMER_IRQn, 7); +} +#endif + #define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) extern "C" void INTMODULE_USART_IRQHandler(void) { @@ -98,7 +126,6 @@ extern "C" void INTMODULE_USART_IRQHandler(void) intmoduleFifo.errors++; } else { - hardwareOptions.pxx2Enabled = true; intmoduleFifo.push(data); } status = INTMODULE_USART->SR; @@ -152,5 +179,20 @@ void intmoduleSendNextFrame() intmoduleSendBuffer(intmodulePulsesData.pxx_uart.getData(), intmodulePulsesData.pxx_uart.getSize()); break; #endif + +#if defined(INTERNAL_MODULE_MULTI) + case PROTOCOL_CHANNELS_MULTIMODULE: + intmoduleSendBuffer(intmodulePulsesData.multi.getData(), intmodulePulsesData.multi.getSize()); + break; +#endif } } + +#if defined(INTERNAL_MODULE_MULTI) +extern "C" void INTMODULE_TIMER_IRQHandler() +{ + INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // clear flag + setupPulsesInternalModule(); + intmoduleSendNextFrame(); +} +#endif 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 fc028d3b5..e457de7d7 100644 --- a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp +++ b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp @@ -71,6 +71,27 @@ void rotaryEncoderInit() void rotaryEncoderCheck() { +#if defined(RADIO_T16) + static uint8_t state = 0; + uint8_t pins = ROTARY_ENCODER_POSITION(); + + if (pins != (state & 0x03) && !(readKeys() & (1 << KEY_ENTER))) { + if ((pins & 0x01) ^ ((pins & 0x02) >> 1)) { + if ((state & 0x03) == 3) + ++rotencValue; + else + --rotencValue; + } + else + { + if ((state & 0x03) == 3) + --rotencValue; + else if ((state & 0x03) == 0) + ++rotencValue; + } + state &= ~0x03 ; + state |= pins ; +#else uint8_t newPosition = ROTARY_ENCODER_POSITION(); if (newPosition != rotencPosition && !(readKeys() & (1 << KEY_ENTER))) { if ((rotencPosition & 0x01) ^ ((newPosition & 0x02) >> 1)) { @@ -80,6 +101,7 @@ void rotaryEncoderCheck() ++rotencValue; } rotencPosition = newPosition; +#endif #if !defined(BOOT) if (g_eeGeneral.backlightMode & e_backlight_mode_keys) { backlightOn(); @@ -127,4 +149,4 @@ extern "C" void ROTARY_ENCODER_TIMER_IRQHandler(void) ROTARY_ENCODER_TIMER->SR &= ~TIM_SR_UIF; ROTARY_ENCODER_TIMER->CR1 = 0; rotaryEncoderCheck(); -} \ No newline at end of file +} diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 118426265..065e4d6ba 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -44,15 +44,23 @@ if (PCB STREQUAL X10) set(FLAVOUR x10express) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) + set(BLUETOOTH ON) + elseif (PCBREV STREQUAL T16) + set(FLAVOUR t16) + set(LUA_EXPORT lua_export_t16) + add_definitions(-DRADIO_T16) + option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" OFF) else() set(FLAVOUR x10) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) + set(BLUETOOTH ON) endif() elseif (PCB STREQUAL X12S) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) set(FLAVOUR x12s) + set(BLUETOOTH ON) set(PCBREV "13" CACHE STRING "PCB Revision") if(${PCBREV} GREATER 10) option(INTERNAL_GPS "Internal GPS installed" YES) @@ -91,14 +99,21 @@ if(NOT UNEXPECTED_SHUTDOWN) endif() if(INTERNAL_MODULE_PXX1) + add_definitions(-DHARDWARE_INTERNAL_MODULE) add_definitions(-DINTERNAL_MODULE_PXX1) endif() if(INTERNAL_MODULE_PXX2) set(PXX2 ON) + add_definitions(-DHARDWARE_INTERNAL_MODULE) add_definitions(-DINTERNAL_MODULE_PXX2) endif() +if(INTERNAL_MODULE_MULTI) + add_definitions(-DHARDWARE_INTERNAL_MODULE) + add_definitions(-DINTERNAL_MODULE_MULTI) +endif() + include_directories(${RADIO_SRC_DIRECTORY}/fonts/480x272 gui/${GUI_DIR} gui/${GUI_DIR}/layouts) file(GLOB THEMES_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/480x272 ${RADIO_SRC_DIRECTORY}/gui/480x272/themes/*.cpp) @@ -163,10 +178,15 @@ set(TARGET_SRC ../common/arm/stm32/heartbeat_driver.cpp ../common/arm/stm32/timers_driver.cpp ../common/arm/stm32/intmodule_serial_driver.cpp - ../common/arm/stm32/bluetooth_driver.cpp ../common/arm/stm32/rotary_encoder_driver.cpp ) +if(BLUETOOTH) + add_definitions(-DBLUETOOTH) + set(TARGET_SRC ${TARGET_SRC} ../common/arm/stm32/bluetooth_driver.cpp) + set(SRC ${SRC} bluetooth.cpp) +endif() + set(FIRMWARE_TARGET_SRC ${FIRMWARE_TARGET_SRC} ${LCD_DRIVER} @@ -179,12 +199,10 @@ set(FIRMWARE_TARGET_SRC ../common/arm/loadboot.cpp ) -add_definitions(-DBLUETOOTH) - set(SRC ${SRC} - bluetooth.cpp io/frsky_firmware_update.cpp + io/multi_firmware_update.cpp ) set(STM32LIB_SRC @@ -202,4 +220,3 @@ if(PYTHONINTERP_FOUND) DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py ) endif() - diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 13717068a..fb12142a0 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -59,7 +59,7 @@ extern uint16_t sessionTimer; #define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) -#if defined(PCBX10) +#if defined(PCBX10) && !defined(RADIO_T16) #define TRAINER_CONNECTED() (GPIO_ReadInputDataBit(TRAINER_DETECT_GPIO, TRAINER_DETECT_GPIO_PIN) == Bit_SET) #else #define TRAINER_CONNECTED() (GPIO_ReadInputDataBit(TRAINER_DETECT_GPIO, TRAINER_DETECT_GPIO_PIN) == Bit_RESET) @@ -173,7 +173,10 @@ void SDRAM_Init(); void init_intmodule_heartbeat(); void check_intmodule_heartbeat(); -void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable); +void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, uint16_t stopBits, uint16_t wordLength); +#if defined(INTERNAL_MODULE_MULTI) +void intmoduleTimerStart(uint32_t periodMs); +#endif void intmoduleSendByte(uint8_t byte); void intmoduleSendBuffer(const uint8_t * data, uint8_t size); void intmoduleSendNextFrame(); @@ -182,6 +185,7 @@ void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inver void extmoduleInvertedSerialStart(uint32_t baudrate); void extmoduleSendBuffer(const uint8_t * data, uint8_t size); void extmoduleSendNextFrame(); +void extmoduleSendInvertedByte(uint8_t byte); // Trainer driver void init_trainer_ppm(); @@ -507,6 +511,8 @@ void backlightEnable(uint8_t dutyCycle = 0); #define BACKLIGHT_LEVEL_MAX 100 #if defined(PCBX12S) #define BACKLIGHT_LEVEL_MIN 5 +#elif defined(RADIO_T16) +#define BACKLIGHT_LEVEL_MIN 1 #else #define BACKLIGHT_LEVEL_MIN 46 #endif @@ -521,6 +527,10 @@ void usbJoystickUpdate(); #define USB_NAME "FrSky Horus" #define USB_MANUFACTURER 'F', 'r', 'S', 'k', 'y', ' ', ' ', ' ' /* 8 bytes */ #define USB_PRODUCT 'H', 'o', 'r', 'u', 's', ' ', ' ', ' ' /* 8 Bytes */ +#elif defined(RADIO_T16) + #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(PCBX10) #define USB_NAME "FrSky X10" #define USB_MANUFACTURER 'F', 'r', 'S', 'k', 'y', ' ', ' ', ' ' /* 8 bytes */ @@ -554,7 +564,7 @@ void telemetryPortSetDirectionInput(); void telemetryPortSetDirectionOutput(); void sportSendByte(uint8_t byte); void sportSendBuffer(const uint8_t * buffer, uint32_t count); -uint8_t telemetryGetByte(uint8_t * byte); +bool telemetryGetByte(uint8_t * byte); void telemetryClearFifo(); extern uint32_t telemetryErrors; @@ -605,6 +615,7 @@ void bluetoothDisable(); extern DMAFifo<512> telemetryFifo; typedef DMAFifo<32> AuxSerialRxFifo; extern AuxSerialRxFifo auxSerialRxFifo; +extern volatile uint32_t externalModulePort; #endif #endif // _BOARD_H_ diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 5c628edf1..0da9e46a5 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -350,6 +350,38 @@ void extmoduleSendNextFrame() } } +void extmoduleSendInvertedByte(uint8_t byte) +{ + uint16_t time; + uint32_t i; + + __disable_irq(); + time = getTmr2MHz(); + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + while ((uint16_t) (getTmr2MHz() - time) < 34) { + // wait + } + time += 34; + for (i = 0 ; i < 8 ; i += 1) { + if (byte & 1) { + GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + } + else { + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + } + byte >>= 1 ; + while ((uint16_t) (getTmr2MHz() - time) < 35) { + // wait + } + time += 35 ; + } + GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + __enable_irq() ; // No need to wait for the stop bit to complete + while ((uint16_t) (getTmr2MHz() - time) < 34) { + // wait + } +} + extern "C" void EXTMODULE_TIMER_DMA_IRQHandler() { if (!DMA_GetITStatus(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC)) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 5c07cb7b5..18acb1602 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -183,10 +183,17 @@ #define TRIMS_GPIO_PIN_RVU GPIO_Pin_12 // PJ.12 #define TRIMS_GPIO_REG_RHR GPIOD->IDR #define TRIMS_GPIO_PIN_RHR GPIO_Pin_7 // PD.07 - #define TRIMS_GPIO_REG_LSU GPIOJ->IDR - #define TRIMS_GPIO_PIN_LSU GPIO_Pin_8 // PJ.08 - #define TRIMS_GPIO_REG_LSD GPIOD->IDR - #define TRIMS_GPIO_PIN_LSD GPIO_Pin_13 // PD.13 + #if defined(RADIO_T16) + #define TRIMS_GPIO_REG_LSU GPIOD->IDR + #define TRIMS_GPIO_PIN_LSU GPIO_Pin_13 // PD.13 + #define TRIMS_GPIO_REG_LSD GPIOJ->IDR + #define TRIMS_GPIO_PIN_LSD GPIO_Pin_8 // PJ.08 + #else + #define TRIMS_GPIO_REG_LSU GPIOJ->IDR + #define TRIMS_GPIO_PIN_LSU GPIO_Pin_8 // PJ.08 + #define TRIMS_GPIO_REG_LSD GPIOD->IDR + #define TRIMS_GPIO_PIN_LSD GPIO_Pin_13 // PD.13 + #endif #define TRIMS_GPIO_REG_RSU GPIOB->IDR #define TRIMS_GPIO_PIN_RSU GPIO_Pin_14 // PB.14 #define TRIMS_GPIO_REG_RSD GPIOB->IDR @@ -281,8 +288,12 @@ #define ADC_DMA_Stream DMA2_Stream0 #define ADC_SET_DMA_FLAGS() ADC_DMA->LIFCR = (DMA_LIFCR_CTCIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTEIF0 | DMA_LIFCR_CDMEIF0 | DMA_LIFCR_CFEIF0) #define ADC_TRANSFER_COMPLETE() (ADC_DMA->LISR & DMA_LISR_TCIF0) +#if defined(RADIO_T16) + #define ADC_VREF_PREC2 300 +#else #define ADC_VREF_PREC2 250 #endif +#endif // Power #define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOJ @@ -525,6 +536,10 @@ #define AUDIO_DMA DMA1 #endif +#if defined(RADIO_T16) + #define AUDIO_UNMUTE_DELAY 150 +#endif + // I2C Bus #define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOB #define I2C_RCC_APB1Periph RCC_APB1Periph_I2C1 @@ -563,9 +578,9 @@ #define HAPTIC_TIMER_COMPARE_VALUE HAPTIC_GPIO_TIMER->CCR2 #endif -// Internal Module -#define HARDWARE_INTERNAL_MODULE +#if !defined(RADIO_T16) #define EXTERNAL_ANTENNA +#endif #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA2) #define INTMODULE_PWR_GPIO GPIOA #define INTMODULE_PWR_GPIO_PIN GPIO_Pin_8 // PA.08 diff --git a/radio/src/targets/horus/telemetry_driver.cpp b/radio/src/targets/horus/telemetry_driver.cpp index 8b782f759..2ca7c6c0a 100644 --- a/radio/src/targets/horus/telemetry_driver.cpp +++ b/radio/src/targets/horus/telemetry_driver.cpp @@ -254,7 +254,7 @@ extern "C" void TELEMETRY_USART_IRQHandler(void) } // TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp -uint8_t telemetryGetByte(uint8_t * byte) +bool telemetryGetByte(uint8_t * byte) { #if defined(PCBX12S) if (telemetryFifoMode & TELEMETRY_SERIAL_WITHOUT_DMA) diff --git a/radio/src/targets/sky9x/CMakeLists.txt b/radio/src/targets/sky9x/CMakeLists.txt index 08b120ad8..b2bf06065 100644 --- a/radio/src/targets/sky9x/CMakeLists.txt +++ b/radio/src/targets/sky9x/CMakeLists.txt @@ -10,6 +10,7 @@ set(TARGET_DIR sky9x) set(FIRMWARE_DEPENDENCIES ${FIRMWARE_DEPENDENCIES} 9x_bitmaps) set(PPM_LIMITS_SYMETRICAL YES) set(PXX1 YES) +add_definitions(-DDISABLE_MULTI_UPDATE) if(PCB STREQUAL 9XRPRO) add_definitions(-Dat91sam3s8 -DREVX) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 65ba8c975..bbd0b8a64 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -1,12 +1,13 @@ option(SHUTDOWN_CONFIRMATION "Shutdown confirmation" OFF) option(LCD_DUAL_BUFFER "Dual LCD Buffer" OFF) -option(INTERNAL_MODULE_PPM "Support for PPM internal module hack" OFF) -option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module replacement" OFF) option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) +option(INTERNAL_MODULE_PPM "Support for PPM internal module" OFF) option(AUTOUPDATE "Auto update internal chips from SD" OFF) if(PCB STREQUAL X9E) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(STICKS "STANDARD" CACHE STRING "Sticks type (STANDARD/HORUS)") set(CPU_TYPE STM32F4) @@ -49,6 +50,8 @@ elseif(PCB STREQUAL X9D+) set(PCBREV 2014 CACHE STRING "PCB Revision") add_definitions(-DPCBREV=${PCBREV}) if (${PCBREV} STREQUAL 2019) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F4) set(CPU_TYPE_FULL STM32F407xE) # for size report @@ -64,6 +67,8 @@ elseif(PCB STREQUAL X9D+) set(PXX_FREQUENCY "HIGH") set(FLAVOUR x9d+2019) else() + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) set(CPU_TYPE STM32F2) set(CPU_TYPE_FULL STM32F205xE) # for size report set(LINKER_SCRIPT targets/taranis/stm32f2_flash.ld) @@ -72,6 +77,8 @@ elseif(PCB STREQUAL X9D+) set(FLAVOUR x9d+) endif() elseif(PCB STREQUAL X9D) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) set(CPU_TYPE STM32F2) set(CPU_TYPE_FULL STM32F205xE) # for size report set(LINKER_SCRIPT targets/taranis/stm32f2_flash.ld) @@ -101,6 +108,8 @@ elseif(PCB STREQUAL X7) add_definitions(-DRADIO_T12) add_definitions(-DEEPROM_VARIANT=0x4001) else() + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) set(LUA_EXPORT lua_export_x7) set(FLAVOUR x7) set(NAVIGATION_TYPE x7) @@ -116,6 +125,8 @@ elseif(PCB STREQUAL X7) set(GVAR_SCREEN model_gvars.cpp) set(STATUS_LEDS YES) elseif(PCB STREQUAL X9LITE) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) set(PXX_FREQUENCY "HIGH") set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F2) @@ -137,6 +148,8 @@ elseif(PCB STREQUAL X9LITE) set(GVAR_SCREEN model_gvars.cpp) set(STATUS_LEDS YES) elseif(PCB STREQUAL X9LITES) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) set(PXX_FREQUENCY "HIGH") set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F2) @@ -158,6 +171,8 @@ elseif(PCB STREQUAL X9LITES) set(GVAR_SCREEN model_gvars.cpp) set(STATUS_LEDS YES) elseif(PCB STREQUAL XLITE) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" OFF) set(PXX_FREQUENCY "HIGH" CACHE STRING "PXX frequency (LOW / HIGH)") # always use HIGH except on some prototype boards set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F2) @@ -178,6 +193,8 @@ elseif(PCB STREQUAL XLITE) set(GVAR_SCREEN model_gvars.cpp) set(STATUS_LEDS YES) elseif(PCB STREQUAL XLITES) + option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) + option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) set(PXX_FREQUENCY "HIGH") set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(CPU_TYPE STM32F2) @@ -266,6 +283,7 @@ add_definitions(-DAUDIO -DVOICE -DRTCLOCK) set(SRC ${SRC} io/frsky_firmware_update.cpp + io/multi_firmware_update.cpp ) set(GUI_SRC @@ -322,12 +340,20 @@ if(LCD_DUAL_BUFFER) add_definitions(-DLCD_DUAL_BUFFER) endif() -if(INTERNAL_MODULE_PPM) - add_definitions(-DINTERNAL_MODULE_PPM) -endif() - if(INTERNAL_MODULE_PXX1) + add_definitions(-DHARDWARE_INTERNAL_MODULE) add_definitions(-DINTERNAL_MODULE_PXX1) endif() +if(INTERNAL_MODULE_PXX2) + set(PXX2 ON) + add_definitions(-DHARDWARE_INTERNAL_MODULE) + add_definitions(-DINTERNAL_MODULE_PXX2) +endif() + +if(INTERNAL_MODULE_PPM) + add_definitions(-DHARDWARE_INTERNAL_MODULE) + add_definitions(-DINTERNAL_MODULE_PPM) +endif() + set(FIRMWARE_DEPENDENCIES ${FIRMWARE_DEPENDENCIES} ${BITMAPS_TARGET}) diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index dae1b0f0f..b5c242b00 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -121,7 +121,7 @@ uint32_t isBootloaderStart(const uint8_t * buffer); #define IS_INTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) == Bit_SET) #endif -void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable); +void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, uint16_t stopBits, uint16_t wordLength); void intmoduleSendByte(uint8_t byte); void intmoduleSendBuffer(const uint8_t * data, uint8_t size); void intmoduleSendNextFrame(); @@ -130,6 +130,7 @@ void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inver void extmoduleInvertedSerialStart(uint32_t baudrate); void extmoduleSendBuffer(const uint8_t * data, uint8_t size); void extmoduleSendNextFrame(); +void extmoduleSendInvertedByte(uint8_t byte); // Trainer driver #define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) @@ -670,7 +671,7 @@ void sportSendByte(uint8_t byte); void sportSendByteLoop(uint8_t byte); void sportStopSendByteLoop(); void sportSendBuffer(const uint8_t * buffer, uint32_t count); -uint8_t telemetryGetByte(uint8_t * byte); +bool telemetryGetByte(uint8_t * byte); void telemetryClearFifo(); extern uint32_t telemetryErrors; diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 3ab23f3e9..19bd04525 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -309,6 +309,38 @@ void extmoduleSendNextFrame() } } +void extmoduleSendInvertedByte(uint8_t byte) +{ + uint16_t time; + uint32_t i; + + __disable_irq(); + time = getTmr2MHz(); + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + while ((uint16_t) (getTmr2MHz() - time) < 34) { + // wait + } + time += 34; + for (i = 0 ; i < 8 ; i += 1) { + if (byte & 1) { + GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + } + else { + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + } + byte >>= 1 ; + while ((uint16_t) (getTmr2MHz() - time) < 35) { + // wait + } + time += 35 ; + } + GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + __enable_irq() ; // No need to wait for the stop bit to complete + while ((uint16_t) (getTmr2MHz() - time) < 34) { + // wait + } +} + extern "C" void EXTMODULE_TIMER_DMA_STREAM_IRQHandler() { if (!DMA_GetITStatus(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC)) diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 5b58b1267..89fee14d2 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -929,17 +929,9 @@ #endif // Internal Module -#if !defined(RADIO_T12) - #define HARDWARE_INTERNAL_MODULE -#endif #if defined(PCBXLITE) #define EXTERNAL_ANTENNA #endif -#if defined(PCBXLITES) || defined(PCBX9LITE) || (defined(PCBX9DP) && PCBREV >= 2019) - #define INTERNAL_MODULE_PXX2 -#else - #define INTERNAL_MODULE_PXX1 -#endif #if defined(PCBXLITE) || defined(PCBX9LITE) #define INTMODULE_RCC_APB1Periph 0 #define INTMODULE_RCC_APB2Periph RCC_APB2Periph_USART1 diff --git a/radio/src/targets/taranis/telemetry_driver.cpp b/radio/src/targets/taranis/telemetry_driver.cpp index ec6da0c86..b439dc906 100644 --- a/radio/src/targets/taranis/telemetry_driver.cpp +++ b/radio/src/targets/taranis/telemetry_driver.cpp @@ -218,7 +218,7 @@ extern "C" void TELEMETRY_USART_IRQHandler(void) } // TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp -uint8_t telemetryGetByte(uint8_t * byte) +bool telemetryGetByte(uint8_t * byte) { #if defined(AUX_SERIAL) if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) { diff --git a/radio/src/telemetry/flysky_ibus.cpp b/radio/src/telemetry/flysky_ibus.cpp index cebb60e78..208df3cac 100644 --- a/radio/src/telemetry/flysky_ibus.cpp +++ b/radio/src/telemetry/flysky_ibus.cpp @@ -133,35 +133,35 @@ void processFlySkyPacket(const uint8_t *packet) } } -void processFlySkyTelemetryData(uint8_t data) +void processFlySkyTelemetryData(uint8_t data, uint8_t* rxBuffer, uint8_t& rxBufferCount) { - if (telemetryRxBufferCount == 0 && data != 0xAA) { + if (rxBufferCount == 0 && data != 0xAA) { TRACE("[IBUS] invalid start byte 0x%02X", data); return; } - if (telemetryRxBufferCount < TELEMETRY_RX_PACKET_SIZE) { - telemetryRxBuffer[telemetryRxBufferCount++] = data; + if (rxBufferCount < TELEMETRY_RX_PACKET_SIZE) { + rxBuffer[rxBufferCount++] = data; } else { - TRACE("[IBUS] array size %d error", telemetryRxBufferCount); - telemetryRxBufferCount = 0; + TRACE("[IBUS] array size %d error", rxBufferCount); + rxBufferCount = 0; } - if (telemetryRxBufferCount >= FLYSKY_TELEMETRY_LENGTH) { + if (rxBufferCount >= FLYSKY_TELEMETRY_LENGTH) { // debug print the content of the packets #if 0 debugPrintf("[IBUS] Packet 0x%02X rssi 0x%02X: ", - telemetryRxBuffer[0], telemetryRxBuffer[1]); + rxBuffer[0], rxBuffer[1]); for (int i=0; i<7; i++) { - debugPrintf("[%02X %02X %02X%02X] ", telemetryRxBuffer[i*4+2], telemetryRxBuffer[i*4 + 3], - telemetryRxBuffer[i*4 + 4], telemetryRxBuffer[i*4 + 5]); + debugPrintf("[%02X %02X %02X%02X] ", rxBuffer[i*4+2], rxBuffer[i*4 + 3], + rxBuffer[i*4 + 4], rxBuffer[i*4 + 5]); } debugPrintf("\r\n"); #endif - processFlySkyPacket(telemetryRxBuffer+1); - telemetryRxBufferCount = 0; + processFlySkyPacket(rxBuffer+1); + rxBufferCount = 0; } } diff --git a/radio/src/telemetry/flysky_ibus.h b/radio/src/telemetry/flysky_ibus.h index 71726d64c..f952f7968 100644 --- a/radio/src/telemetry/flysky_ibus.h +++ b/radio/src/telemetry/flysky_ibus.h @@ -21,7 +21,7 @@ #ifndef _FLYSKY_IBUS_H #define _FLYSKY_IBUS_H -void processFlySkyTelemetryData(uint8_t data); +void processFlySkyTelemetryData(uint8_t data, uint8_t* rxBuffer, uint8_t& rxBufferCount); void flySkySetDefault(int index, uint16_t id, uint8_t subId, uint8_t instance); // Used by multi protocol diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 87944a862..c8e4c7867 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -21,11 +21,6 @@ #include "telemetry.h" #include "multi.h" -MultiModuleStatus multiModuleStatus; -MultiModuleSyncStatus multiSyncStatus; -uint8_t multiBindStatus = MULTI_NORMAL_OPERATION; - - enum MultiPacketTypes : uint8_t { MultiStatus = 1, FrSkySportTelemtry, @@ -51,53 +46,142 @@ enum MultiBufferState : uint8_t { MultiStatusOrFrskyData }; -MultiBufferState guessProtocol() + +#if defined(INTERNAL_MODULE_MULTI) + +static MultiModuleStatus multiModuleStatus[NUM_MODULES] = { MultiModuleStatus(), MultiModuleStatus() }; +static MultiModuleSyncStatus multiSyncStatus[NUM_MODULES] = { MultiModuleSyncStatus(), MultiModuleSyncStatus() }; +static uint8_t multiBindStatus[NUM_MODULES] = { MULTI_NORMAL_OPERATION, MULTI_NORMAL_OPERATION }; + +static MultiBufferState multiTelemetryBufferState[NUM_MODULES]; + +MultiModuleStatus& getMultiModuleStatus(uint8_t module) { - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_DSM2) + return multiModuleStatus[module]; +} + +MultiModuleSyncStatus& getMultiSyncStatus(uint8_t module) +{ + return multiSyncStatus[module]; +} + +uint8_t getMultiBindStatus(uint8_t module) +{ + return multiBindStatus[module]; +} + +void setMultiBindStatus(uint8_t module, uint8_t bindStatus) +{ + multiBindStatus[module] = bindStatus; +} + +MultiBufferState getMultiTelemetryBufferState(uint8_t module) +{ + return multiTelemetryBufferState[module]; +} + +void setMultiTelemetryBufferState(uint8_t module, MultiBufferState state) +{ + multiTelemetryBufferState[module] = state; +} + +// Use additional telemetry buffer +uint8_t intTelemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; +uint8_t intTelemetryRxBufferCount; + +#else // !INTERNAL_MODULE_MULTI + +static MultiModuleStatus multiModuleStatus; +static MultiModuleSyncStatus multiSyncStatus; +static uint8_t multiBindStatus = MULTI_NORMAL_OPERATION; + +static MultiBufferState multiTelemetryBufferState; + +MultiModuleStatus& getMultiModuleStatus(uint8_t) +{ + return multiModuleStatus; +} + +MultiModuleSyncStatus& getMultiSyncStatus(uint8_t) +{ + return multiSyncStatus; +} + +uint8_t getMultiBindStatus(uint8_t) +{ + return multiBindStatus; +} + +void setMultiBindStatus(uint8_t, uint8_t bindStatus) +{ + multiBindStatus = bindStatus; +} + +MultiBufferState getMultiTelemetryBufferState(uint8_t) +{ + return multiTelemetryBufferState; +} + +void setMultiTelemetryBufferState(uint8_t, MultiBufferState state) +{ + multiTelemetryBufferState = state; +} + +#endif // INTERNAL_MODULE_MULTI + + +static MultiBufferState guessProtocol(uint8_t module) +{ + if (g_model.moduleData[module].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_DSM2) return SpektrumTelemetryFallback; - else if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) + else if (g_model.moduleData[module].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) return FlyskyTelemetryFallback; else return FrskyTelemetryFallback; } -static void processMultiStatusPacket(const uint8_t *data) +static void processMultiStatusPacket(const uint8_t *data, uint8_t module) { + MultiModuleStatus& status = getMultiModuleStatus(module); + // At least two status packets without bind flag - bool wasBinding = (multiModuleStatus.isBinding()); + bool wasBinding = status.isBinding(); - multiModuleStatus.flags = data[0]; - multiModuleStatus.major = data[1]; - multiModuleStatus.minor = data[2]; - multiModuleStatus.revision = data[3]; - multiModuleStatus.patch = data[4]; - multiModuleStatus.lastUpdate = get_tmr10ms(); + status.flags = data[0]; + status.major = data[1]; + status.minor = data[2]; + status.revision = data[3]; + status.patch = data[4]; + status.lastUpdate = get_tmr10ms(); - if (wasBinding && !multiModuleStatus.isBinding() && multiBindStatus == MULTI_BIND_INITIATED) - multiBindStatus = MULTI_BIND_FINISHED; + if (wasBinding && !status.isBinding() && getMultiBindStatus(module) == MULTI_BIND_INITIATED) + setMultiBindStatus(module, MULTI_BIND_FINISHED); } -static void processMultiSyncPacket(const uint8_t *data) +static void processMultiSyncPacket(const uint8_t *data, uint8_t module) { - multiSyncStatus.lastUpdate = get_tmr10ms(); - multiSyncStatus.interval = data[4]; - multiSyncStatus.target = data[5]; + MultiModuleSyncStatus& status = getMultiSyncStatus(module); + + status.lastUpdate = get_tmr10ms(); + status.interval = data[4]; + status.target = data[5]; #if !defined(PPM_PIN_SERIAL) - auto oldlag = multiSyncStatus.inputLag; + auto oldlag = status.inputLag; (void) oldlag; #endif - multiSyncStatus.calcAdjustedRefreshRate(data[0] << 8 | data[1], data[2] << 8 | data[3]); + status.calcAdjustedRefreshRate(data[0] << 8 | data[1], data[2] << 8 | data[3]); #if !defined(PPM_PIN_SERIAL) - TRACE("MP ADJ: rest: %d, lag %04d, diff: %04d target: %d, interval: %d, Refresh: %d, intAdjRefresh: %d, adjRefresh %d\r\n", extmodulePulsesData.dsm2.rest, - multiSyncStatus.inputLag, oldlag-multiSyncStatus.inputLag, multiSyncStatus.target, multiSyncStatus.interval, multiSyncStatus.refreshRate, multiSyncStatus.adjustedRefreshRate/50, - multiSyncStatus.getAdjustedRefreshRate()); + TRACE("MP ADJ: rest: %d, lag %04d, diff: %04d target: %d, interval: %d, Refresh: %d, intAdjRefresh: %d, adjRefresh %d\r\n", + module == EXTERNAL_MODULE ? extmodulePulsesData.dsm2.rest : 0, + status.inputLag, oldlag-status.inputLag, status.target, status.interval, status.refreshRate, status.adjustedRefreshRate/50, + status.getAdjustedRefreshRate()); #endif } -static void processMultiTelemetryPaket(const uint8_t *packet) +static void processMultiTelemetryPaket(const uint8_t *packet, uint8_t module) { uint8_t type = packet[0]; uint8_t len = packet[1]; @@ -107,12 +191,12 @@ static void processMultiTelemetryPaket(const uint8_t *packet) switch (type) { case MultiStatus: if (len >= 5) - processMultiStatusPacket(data); + processMultiStatusPacket(data, module); break; case DSMBindPacket: if (len >= 10) - processDSMBindPacket(data); + processDSMBindPacket(module, data); break; case SpektrumTelemetry: @@ -147,7 +231,7 @@ static void processMultiTelemetryPaket(const uint8_t *packet) case InputSync: if (len >= 6) - processMultiSyncPacket(data); + processMultiSyncPacket(data, module); else TRACE("[MP] Received input sync len %d < 6", len); break; @@ -294,9 +378,11 @@ void MultiModuleStatus::getStatusString(char *statusText) { if (!isValid()) { #if defined(PCBTARANIS) || defined(PCBHORUS) +#if !defined(INTERNAL_MODULE_MULTI) if (IS_INTERNAL_MODULE_ENABLED()) strcpy(statusText, STR_DISABLE_INTERNAL); else +#endif #endif strcpy(statusText, STR_MODULE_NO_TELEMETRY); return; @@ -333,50 +419,71 @@ void MultiModuleStatus::getStatusString(char *statusText) strcat(statusText, STR_MODULE_BINDING); } - -static MultiBufferState multiTelemetryBufferState; - -static void processMultiTelemetryByte(const uint8_t data) +static uint8_t* getRxBuffer(uint8_t moduleIdx) { - if (telemetryRxBufferCount < TELEMETRY_RX_PACKET_SIZE) { - telemetryRxBuffer[telemetryRxBufferCount++] = data; +#if defined(INTERNAL_MODULE_MULTI) + if (moduleIdx == INTERNAL_MODULE) + return intTelemetryRxBuffer; +#endif + return telemetryRxBuffer; +} + +static uint8_t& getRxBufferCount(uint8_t moduleIdx) +{ +#if defined(INTERNAL_MODULE_MULTI) + if (moduleIdx == INTERNAL_MODULE) + return intTelemetryRxBufferCount; +#endif + return telemetryRxBufferCount; +} + +static void processMultiTelemetryByte(const uint8_t data, uint8_t module) +{ + uint8_t* rxBuffer = getRxBuffer(module); + uint8_t& rxBufferCount = getRxBufferCount(module); + + if (rxBufferCount < TELEMETRY_RX_PACKET_SIZE) { + rxBuffer[rxBufferCount++] = data; } else { - TRACE("[MP] array size %d error", telemetryRxBufferCount); - multiTelemetryBufferState = NoProtocolDetected; + TRACE("[MP] array size %d error", rxBufferCount); + setMultiTelemetryBufferState(module, NoProtocolDetected); } // Length field does not count the header - if (telemetryRxBufferCount >= 2 && telemetryRxBuffer[1] == telemetryRxBufferCount - 2) { + if (rxBufferCount >= 2 && rxBuffer[1] == rxBufferCount - 2) { // debug print the content of the packet #if 0 debugPrintf("[MP] Packet type %02X len 0x%02X: ", - telemetryRxBuffer[0], telemetryRxBuffer[1]); - for (int i=0; i<(telemetryRxBufferCount+3)/4; i++) { - debugPrintf("[%02X%02X %02X%02X] ", telemetryRxBuffer[i*4+2], telemetryRxBuffer[i*4 + 3], - telemetryRxBuffer[i*4 + 4], telemetryRxBuffer[i*4 + 5]); + rxBuffer[0], rxBuffer[1]); + for (int i=0; i<(rxBufferCount+3)/4; i++) { + debugPrintf("[%02X%02X %02X%02X] ", rxBuffer[i*4+2], rxBuffer[i*4 + 3], + rxBuffer[i*4 + 4], rxBuffer[i*4 + 5]); } debugPrintf("\r\n"); #endif // Packet is complete, process it - processMultiTelemetryPaket(telemetryRxBuffer); - multiTelemetryBufferState = NoProtocolDetected; + processMultiTelemetryPaket(rxBuffer, module); + setMultiTelemetryBufferState(module, NoProtocolDetected); } } -void processMultiTelemetryData(const uint8_t data) +void processMultiTelemetryData(uint8_t data, uint8_t module) { - // debugPrintf("State: %d, byte received %02X, buflen: %d\r\n", multiTelemetryBufferState, data, telemetryRxBufferCount); - switch (multiTelemetryBufferState) { + uint8_t* rxBuffer = getRxBuffer(module); + uint8_t& rxBufferCount = getRxBufferCount(module); + + debugPrintf("State: %d, byte received %02X, buflen: %d\r\n", multiTelemetryBufferState, data, rxBufferCount); + switch (getMultiTelemetryBufferState(module)) { case NoProtocolDetected: if (data == 'M') { - multiTelemetryBufferState = MultiFirstByteReceived; + setMultiTelemetryBufferState(module, MultiFirstByteReceived); } else if (data == 0xAA || data == 0x7e) { - multiTelemetryBufferState = guessProtocol(); + setMultiTelemetryBufferState(module, guessProtocol(module)); // Process the first byte by the protocol - processMultiTelemetryData(data); + processMultiTelemetryData(data, module); } else { TRACE("[MP] invalid start byte 0x%02X", data); @@ -384,91 +491,92 @@ void processMultiTelemetryData(const uint8_t data) break; case FrskyTelemetryFallback: - multiTelemetryBufferState = FrskyTelemetryFallbackFirstByte; + setMultiTelemetryBufferState(module, FrskyTelemetryFallbackFirstByte); processFrskyTelemetryData(data); break; case FrskyTelemetryFallbackFirstByte: if (data == 'M') { - multiTelemetryBufferState = MultiStatusOrFrskyData; + setMultiTelemetryBufferState(module, MultiStatusOrFrskyData); } else { processFrskyTelemetryData(data); if (data != 0x7e) - multiTelemetryBufferState = FrskyTelemetryFallbackNextBytes; + setMultiTelemetryBufferState(module, FrskyTelemetryFallbackNextBytes); } break; case FrskyTelemetryFallbackNextBytes: processFrskyTelemetryData(data); - if (data == 0x7e) + if (data == 0x7e) { // end of packet or start of new packet - multiTelemetryBufferState = FrskyTelemetryFallbackFirstByte; + setMultiTelemetryBufferState(module, FrskyTelemetryFallbackFirstByte); + } break; case FlyskyTelemetryFallback: - processFlySkyTelemetryData(data); - if (telemetryRxBufferCount == 0) - multiTelemetryBufferState = NoProtocolDetected; + processFlySkyTelemetryData(data, rxBuffer, rxBufferCount); + if (rxBufferCount == 0) { + setMultiTelemetryBufferState(module, NoProtocolDetected); + } break; case SpektrumTelemetryFallback: - processSpektrumTelemetryData(data); - if (telemetryRxBufferCount == 0) - multiTelemetryBufferState = NoProtocolDetected; + processSpektrumTelemetryData(module, data,rxBuffer, rxBufferCount); + if (rxBufferCount == 0) { + setMultiTelemetryBufferState(module, NoProtocolDetected); + } break; case MultiFirstByteReceived: - telemetryRxBufferCount = 0; + rxBufferCount = 0; if (data == 'P') { - multiTelemetryBufferState = ReceivingMultiProtocol; + setMultiTelemetryBufferState(module, ReceivingMultiProtocol); } else if (data >= 5 && data <= 10) { // Protocol indented for er9x/ersky9, accept only 5-10 as packet length to have // a bit of validation - multiTelemetryBufferState = ReceivingMultiStatus; - processMultiTelemetryData(data); + setMultiTelemetryBufferState(module, ReceivingMultiStatus); + processMultiTelemetryData(data, module); } else { TRACE("[MP] invalid second byte 0x%02X", data); - multiTelemetryBufferState = NoProtocolDetected; + setMultiTelemetryBufferState(module, NoProtocolDetected); } break; case ReceivingMultiProtocol: - processMultiTelemetryByte(data); + processMultiTelemetryByte(data, module); break; case MultiStatusOrFrskyData: // Check len byte if it makes sense for multi if (data >= 5 && data <= 10) { - multiTelemetryBufferState = ReceivingMultiStatus; - telemetryRxBufferCount = 0; + setMultiTelemetryBufferState(module, ReceivingMultiStatus); + rxBufferCount = 0; } else { - multiTelemetryBufferState = FrskyTelemetryFallbackNextBytes; - processMultiTelemetryData('M'); + setMultiTelemetryBufferState(module, FrskyTelemetryFallbackNextBytes); + processMultiTelemetryData('M', module); } - processMultiTelemetryData(data); + processMultiTelemetryData(data, module); break; case ReceivingMultiStatus: - telemetryRxBuffer[telemetryRxBufferCount++] = data; - if (telemetryRxBufferCount > 5 && telemetryRxBuffer[0] == telemetryRxBufferCount-1) { - processMultiStatusPacket(telemetryRxBuffer+1); - telemetryRxBufferCount = 0; - multiTelemetryBufferState = NoProtocolDetected; + rxBuffer[rxBufferCount++] = data; + if (rxBufferCount > 5 && rxBuffer[0] == rxBufferCount-1) { + processMultiStatusPacket(rxBuffer+1, module); + rxBufferCount = 0; + setMultiTelemetryBufferState(module, NoProtocolDetected); } - if (telemetryRxBufferCount > 10) { + if (rxBufferCount > 10) { // too long ignore - TRACE("Overlong multi status packet detected ignoring, wanted %d", telemetryRxBuffer[0]); - telemetryRxBufferCount =0; - multiTelemetryBufferState = NoProtocolDetected; + TRACE("Overlong multi status packet detected ignoring, wanted %d", rxBuffer[0]); + rxBufferCount =0; + setMultiTelemetryBufferState(module, NoProtocolDetected); } - - + break; } - } diff --git a/radio/src/telemetry/multi.h b/radio/src/telemetry/multi.h index 1b439ea24..a538df303 100644 --- a/radio/src/telemetry/multi.h +++ b/radio/src/telemetry/multi.h @@ -82,9 +82,7 @@ Type 0x06 Flysky AFHDS2 telemetry data */ - - -void processMultiTelemetryData(uint8_t data); +void processMultiTelemetryData(uint8_t data, uint8_t module); // This should be put into the Module definition if other modules gain this functionality struct MultiModuleSyncStatus { @@ -107,7 +105,7 @@ struct MultiModuleSyncStatus { }; -extern MultiModuleSyncStatus multiSyncStatus; +MultiModuleSyncStatus& getMultiSyncStatus(uint8_t module); struct MultiModuleStatus { @@ -131,13 +129,15 @@ struct MultiModuleStatus { inline bool inputDetected() { return (bool) (flags & 0x01); } }; -extern MultiModuleStatus multiModuleStatus; +MultiModuleStatus& getMultiModuleStatus(uint8_t module); + enum MultiBindStatus : uint8_t { MULTI_NORMAL_OPERATION, MULTI_BIND_INITIATED, MULTI_BIND_FINISHED, }; -extern uint8_t multiBindStatus; +uint8_t getMultiBindStatus(uint8_t module); +void setMultiBindStatus(uint8_t module, uint8_t bindStatus); #endif //OPENTX_MULTI_H diff --git a/radio/src/telemetry/spektrum.cpp b/radio/src/telemetry/spektrum.cpp index 41b27b88c..ae5435845 100644 --- a/radio/src/telemetry/spektrum.cpp +++ b/radio/src/telemetry/spektrum.cpp @@ -370,28 +370,27 @@ void processSpektrumPacket(const uint8_t *packet) LemonRX+Sat+tele 0xb2 07 1 */ -void processDSMBindPacket(const uint8_t *packet) +void processDSMBindPacket(uint8_t module, const uint8_t *packet) { uint32_t debugval; - if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2 - && g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode) { + if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[module].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2 + && g_model.moduleData[module].multi.autoBindMode) { int channels = packet[5]; // Only sets channel etc when in DSM multi mode - g_model.moduleData[EXTERNAL_MODULE].channelsCount = channels - 8; + g_model.moduleData[module].channelsCount = channels - 8; // bool use11ms = (packet[8] & 0x10) ; if (packet[6] >= 0xb2) - g_model.moduleData[EXTERNAL_MODULE].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_11; else if (packet[6] >= 0xa2) - g_model.moduleData[EXTERNAL_MODULE].subType = MM_RF_DSM2_SUBTYPE_DSMX_22; + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSMX_22; else if (packet[6] >= 0x12) - g_model.moduleData[EXTERNAL_MODULE].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_11; else - g_model.moduleData[EXTERNAL_MODULE].subType = MM_RF_DSM2_SUBTYPE_DSM2_22; + g_model.moduleData[module].subType = MM_RF_DSM2_SUBTYPE_DSM2_22; storageDirty(EE_MODEL); - } debugval = packet[7] << 24 | packet[6] << 16 | packet[5] << 8 | packet[4]; @@ -400,45 +399,45 @@ void processDSMBindPacket(const uint8_t *packet) setTelemetryValue(PROTOCOL_TELEMETRY_SPEKTRUM, (I2C_PSEUDO_TX << 8) + 4, 0, 0, debugval, UNIT_RAW, 0); /* Finally stop binding as the rx just told us that it is bound */ - if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2 && moduleState[EXTERNAL_MODULE].mode == MODULE_MODE_BIND) { - multiBindStatus=MULTI_BIND_FINISHED; + if (g_model.moduleData[module].type == MODULE_TYPE_MULTIMODULE && g_model.moduleData[module].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2 && moduleState[module].mode == MODULE_MODE_BIND) { + setMultiBindStatus(module, MULTI_BIND_FINISHED); } } -void processSpektrumTelemetryData(uint8_t data) +void processSpektrumTelemetryData(uint8_t module, uint8_t data, uint8_t* rxBuffer, uint8_t& rxBufferCount) { - if (telemetryRxBufferCount == 0 && data != 0xAA) { + if (rxBufferCount == 0 && data != 0xAA) { TRACE("[SPK] invalid start byte 0x%02X", data); return; } - if (telemetryRxBufferCount < TELEMETRY_RX_PACKET_SIZE) { - telemetryRxBuffer[telemetryRxBufferCount++] = data; + if (rxBufferCount < TELEMETRY_RX_PACKET_SIZE) { + rxBuffer[rxBufferCount++] = data; } else { - TRACE("[SPK] array size %d error", telemetryRxBufferCount); - telemetryRxBufferCount = 0; + TRACE("[SPK] array size %d error", rxBufferCount); + rxBufferCount = 0; } - if (telemetryRxBuffer[1] == 0x80 && telemetryRxBufferCount >= DSM_BIND_PACKET_LENGTH) { - processDSMBindPacket(telemetryRxBuffer+2); - telemetryRxBufferCount = 0; + if (rxBuffer[1] == 0x80 && rxBufferCount >= DSM_BIND_PACKET_LENGTH) { + processDSMBindPacket(module, rxBuffer+2); + rxBufferCount = 0; return; } - if (telemetryRxBufferCount >= SPEKTRUM_TELEMETRY_LENGTH) { + if (rxBufferCount >= SPEKTRUM_TELEMETRY_LENGTH) { // Debug print content of Telemetry to console #if 0 debugPrintf("[SPK] Packet 0x%02X rssi 0x%02X: ic2 0x%02x, %02x: ", - telemetryRxBuffer[0], telemetryRxBuffer[1], telemetryRxBuffer[2], telemetryRxBuffer[3]); + rxBuffer[0], rxBuffer[1], rxBuffer[2], rxBuffer[3]); for (int i=4; i