From 817b82f76f279bccf217145ef0116f16468dc187 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 8 Dec 2020 11:45:43 +0000 Subject: [PATCH 001/300] Increase popup size for small lcd --- radio/src/gui/128x64/popups.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/popups.h b/radio/src/gui/128x64/popups.h index e5d483e0b..9400d69f7 100644 --- a/radio/src/gui/128x64/popups.h +++ b/radio/src/gui/128x64/popups.h @@ -23,16 +23,16 @@ #include -#define MESSAGEBOX_X 10 +#define MESSAGEBOX_X 1 #define MESSAGEBOX_Y 16 -#define MESSAGEBOX_W (LCD_W - 19) +#define MESSAGEBOX_W (LCD_W - 2) #define MENU_X MESSAGEBOX_X #define MENU_Y MESSAGEBOX_Y #define MENU_W MESSAGEBOX_W #define WARNING_LINE_LEN 20 -#define WARNING_LINE_X 16 +#define WARNING_LINE_X 3 #define WARNING_LINE_Y 3*FH #define POPUP_MENU_MAX_LINES 12 From 02660c763ae1388e02fd7606b795269e522d8e10 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Oct 2019 23:08:51 +0200 Subject: [PATCH 002/300] mixer scheduler --- radio/src/CMakeLists.txt | 1 + radio/src/gui/gui_common.h | 3 +- radio/src/mixer_scheduler.cpp | 79 +++++++++++ radio/src/mixer_scheduler.h | 84 +++++++++++ radio/src/opentx.h | 1 - radio/src/pulses/dsm2.cpp | 6 +- radio/src/pulses/multi.cpp | 2 - radio/src/pulses/pulses.cpp | 109 ++++++++++---- radio/src/pulses/pulses.h | 9 +- radio/src/pulses/pxx.h | 12 +- radio/src/pulses/sbus.cpp | 2 - radio/src/rtos.h | 32 ++++- .../common/arm/stm32/heartbeat_driver.cpp | 3 + .../arm/stm32/intmodule_serial_driver.cpp | 37 ----- .../arm/stm32/mixer_scheduler_driver.cpp | 81 +++++++++++ radio/src/targets/horus/CMakeLists.txt | 1 + radio/src/targets/horus/board.cpp | 1 + radio/src/targets/horus/board.h | 5 +- radio/src/targets/horus/extmodule_driver.cpp | 44 ++++-- radio/src/targets/horus/hal.h | 7 + radio/src/targets/taranis/CMakeLists.txt | 1 + radio/src/targets/taranis/board.cpp | 3 +- radio/src/targets/taranis/board.h | 2 +- .../src/targets/taranis/extmodule_driver.cpp | 39 +++-- radio/src/targets/taranis/hal.h | 7 + radio/src/tasks.cpp | 89 ++++++------ radio/src/telemetry/crossfire.cpp | 20 +++ radio/src/telemetry/crossfire.h | 6 +- radio/src/telemetry/multi.cpp | 133 ++---------------- radio/src/telemetry/multi.h | 22 --- radio/src/telemetry/telemetry.cpp | 86 +++++++++++ radio/src/telemetry/telemetry.h | 31 ++++ 32 files changed, 644 insertions(+), 314 deletions(-) create mode 100644 radio/src/mixer_scheduler.cpp create mode 100644 radio/src/mixer_scheduler.h create mode 100644 radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 47a1fed2a..39ea7e52e 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -378,6 +378,7 @@ set(SRC strhelpers.cpp switches.cpp mixer.cpp + mixer_scheduler.cpp stamp.cpp timers.cpp trainer.cpp diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index dd473eee8..3894bf633 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -185,6 +185,7 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx) } #if defined(MULTIMODULE) + inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) { if (!isModuleMultimodule(moduleIdx)) @@ -276,7 +277,7 @@ inline uint8_t MULTIMODULE_HASOPTIONS(uint8_t moduleIdx) #define MULTIMODULE_MODULE_ROWS(moduleIdx) (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) && !IS_RX_MULTI(moduleIdx)) ? (uint8_t) 0 : HIDDEN_ROW, (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx) && !IS_RX_MULTI(moduleIdx)) ? (uint8_t) 0 : HIDDEN_ROW, MULTI_DISABLE_CHAN_MAP_ROW(moduleIdx), // AUTOBIND, DISABLE TELEM, DISABLE CN.MAP #define MULTIMODULE_TYPE_ROW(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW, -#define MULTIMODULE_STATUS_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, (isModuleMultimodule(moduleIdx) && getMultiSyncStatus(moduleIdx).isValid()) ? TITLE_ROW : HIDDEN_ROW, +#define MULTIMODULE_STATUS_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? TITLE_ROW : HIDDEN_ROW, (isModuleMultimodule(moduleIdx) && getModuleSyncStatus(moduleIdx).isValid()) ? TITLE_ROW : HIDDEN_ROW, #define MULTIMODULE_MODE_ROWS(moduleIdx) (g_model.moduleData[moduleIdx].multi.customProto) ? (uint8_t) 3 : MULTIMODULE_HAS_SUBTYPE(moduleIdx) ? (uint8_t)2 : (uint8_t)1 #define MULTIMODULE_TYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? (uint8_t) 0 : HIDDEN_ROW, #define MULTIMODULE_SUBTYPE_ROWS(moduleIdx) isModuleMultimodule(moduleIdx) ? MULTIMODULE_RFPROTO_COLUMNS(moduleIdx) : HIDDEN_ROW, diff --git a/radio/src/mixer_scheduler.cpp b/radio/src/mixer_scheduler.cpp new file mode 100644 index 000000000..b29c821c8 --- /dev/null +++ b/radio/src/mixer_scheduler.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "opentx.h" +#include "mixer_scheduler.h" + +#if !defined(SIMU) + +// Global trigger flag +RTOS_FLAG_HANDLE mixerFlag; + +// Mixer schedule +struct MixerSchedule { + + // period in us + volatile uint16_t period; +}; + +static MixerSchedule mixerSchedules[NUM_MODULES]; + +uint16_t getMixerSchedulerPeriod() +{ + if (mixerSchedules[INTERNAL_MODULE].period) { + return mixerSchedules[INTERNAL_MODULE].period; + } + else if (mixerSchedules[EXTERNAL_MODULE].period) { + return mixerSchedules[EXTERNAL_MODULE].period; + } + + return MIXER_SCHEDULER_DEFAULT_PERIOD_US; +} + +void mixerSchedulerInit() +{ + RTOS_CREATE_FLAG(mixerFlag); + memset(mixerSchedules, 0, sizeof(mixerSchedules)); +} + +void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs) +{ + if ((periodUs > 0) && (periodUs < MIN_REFRESH_RATE)) { + periodUs = MIN_REFRESH_RATE; + } + else if ((periodUs > 0) && (periodUs > MAX_REFRESH_RATE)) { + periodUs = MAX_REFRESH_RATE; + } + + mixerSchedules[moduleIdx].period = periodUs; +} + +bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs) +{ + RTOS_CLEAR_FLAG(mixerFlag); + return RTOS_WAIT_FLAG(mixerFlag, timeoutMs); +} + +void mixerSchedulerISRTrigger() +{ + RTOS_ISR_SET_FLAG(mixerFlag); +} + +#endif diff --git a/radio/src/mixer_scheduler.h b/radio/src/mixer_scheduler.h new file mode 100644 index 000000000..ccded9a5e --- /dev/null +++ b/radio/src/mixer_scheduler.h @@ -0,0 +1,84 @@ +/* + * 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 _MIXER_SCHEDULER_H_ +#define _MIXER_SCHEDULER_H_ + +#define MIXER_SCHEDULER_DEFAULT_PERIOD_US 4000u // 4ms + +#define MIN_REFRESH_RATE 4000 /* us */ +#define MAX_REFRESH_RATE 25000 /* us */ + +#if !defined(SIMU) + +// Call once to initialize the mixer scheduler +void mixerSchedulerInit(); + +// Configure and start the scheduler timer +void mixerSchedulerStart(); + +// Stop the scheduler timer +void mixerSchedulerStop(); + +// Set the timer counter to 0 +void mixerSchedulerResetTimer(); + +// Set the scheduling period for a given module +void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs); + +// Wait for the scheduler timer to trigger +// returns true if timeout, false otherwise +bool mixerSchedulerWaitForTrigger(uint8_t timeoutMs); + +// Enable the timer trigger +void mixerSchedulerEnableTrigger(); + +// Disable the timer trigger +void mixerSchedulerDisableTrigger(); + +// Fetch the current scheduling period +uint16_t getMixerSchedulerPeriod(); + +// Trigger mixer from an ISR +void mixerSchedulerISRTrigger(); + +#else + +#define mixerSchedulerInit() +#define mixerSchedulerStart() +#define mixerSchedulerStop() +#define mixerSchedulerResetTimer() +#define mixerSchedulerSetPeriod(m,p) + +static inline bool mixerSchedulerWaitForTrigger(uint8_t timeout) +{ + simuSleep(timeout); + return false; +} + +#define mixerSchedulerEnableTrigger() +#define mixerSchedulerDisableTrigger() + +#define getMixerSchedulerPeriod() (MIXER_SCHEDULER_DEFAULT_PERIOD_US) +#define mixerSchedulerISRTrigger() + +#endif + +#endif diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 52a51719b..394a0734f 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -483,7 +483,6 @@ extern uint32_t nextMixerTime[NUM_MODULES]; void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms); void evalMixes(uint8_t tick10ms); void doMixerCalculations(); -void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms); void checkTrims(); extern uint8_t currentBacklightBright; diff --git a/radio/src/pulses/dsm2.cpp b/radio/src/pulses/dsm2.cpp index 3612f60db..34a10b3d6 100644 --- a/radio/src/pulses/dsm2.cpp +++ b/radio/src/pulses/dsm2.cpp @@ -76,7 +76,6 @@ void _send_1(uint8_t v) *extmodulePulsesData.dsm2.ptr++ = v - 1; extmodulePulsesData.dsm2.index += 1; - extmodulePulsesData.dsm2.rest -= v; } void sendByteDsm2(uint8_t b) // max 10 changes 0 10 10 10 10 1 @@ -101,9 +100,9 @@ void sendByteDsm2(uint8_t b) // max 10 changes 0 10 10 10 10 1 void putDsm2Flush() { if (extmodulePulsesData.dsm2.index & 1) - *extmodulePulsesData.dsm2.ptr++ = extmodulePulsesData.dsm2.rest; + *extmodulePulsesData.dsm2.ptr++ = 60000; else - *(extmodulePulsesData.dsm2.ptr - 1) = extmodulePulsesData.dsm2.rest; + *(extmodulePulsesData.dsm2.ptr - 1) = 60000; } #endif @@ -119,7 +118,6 @@ void setupPulsesDSM2() extmodulePulsesData.dsm2.serialBitCount = 0 ; #else extmodulePulsesData.dsm2.index = 0; - extmodulePulsesData.dsm2.rest = DSM2_PERIOD * 2000; #endif extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses; diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index dcc7c7f9c..4f61caf72 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -209,10 +209,8 @@ void setupPulsesMultiExternalModule() extmodulePulsesData.dsm2.serialByte = 0 ; extmodulePulsesData.dsm2.serialBitCount = 0 ; #else - extmodulePulsesData.dsm2.rest = getMultiSyncStatus(EXTERNAL_MODULE).getAdjustedRefreshRate(); extmodulePulsesData.dsm2.index = 0; #endif - extmodulePulsesData.dsm2.ptr = extmodulePulsesData.dsm2.pulses; setupPulsesMulti(EXTERNAL_MODULE); diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index d6833bfe9..1681157bc 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "io/frsky_pxx2.h" #include "pulses/pxx2.h" +#include "mixer_scheduler.h" uint8_t s_pulses_paused = 0; ModuleState moduleState[NUM_MODULES]; @@ -62,7 +63,7 @@ void getModuleSyncStatusString(uint8_t moduleIdx, char * statusText) *statusText = 0; #if defined(MULTIMODULE) if (isModuleMultimodule(moduleIdx)) { - getMultiSyncStatus(moduleIdx).getRefreshString(statusText); + getModuleSyncStatus(moduleIdx).getRefreshString(statusText); } #endif #if defined(AFHDS3) @@ -225,12 +226,14 @@ void enablePulsesExternalModule(uint8_t protocol) #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: extmodulePxx1PulsesStart(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX_PULSES_PERIOD); break; #endif #if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) case PROTOCOL_CHANNELS_PXX1_SERIAL: extmodulePxx1SerialStart(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, EXTMODULE_PXX1_SERIAL_PERIOD); break; #endif @@ -238,59 +241,69 @@ void enablePulsesExternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSMX: - extmoduleSerialStart(DSM2_BAUDRATE, DSM2_PERIOD * 2000, false); + extmoduleSerialStart(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, DSM2_PERIOD); break; #endif #if defined(CROSSFIRE) case PROTOCOL_CHANNELS_CROSSFIRE: EXTERNAL_MODULE_ON(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, CROSSFIRE_PERIOD); break; #endif #if defined(GHOST) case PROTOCOL_CHANNELS_GHOST: EXTERNAL_MODULE_ON(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, GHOST_PERIOD); break; #endif #if defined(PXX2) && defined(EXTMODULE_USART) case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: extmoduleInvertedSerialStart(PXX2_HIGHSPEED_BAUDRATE); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX2_PERIOD); break; case PROTOCOL_CHANNELS_PXX2_LOWSPEED: extmoduleInvertedSerialStart(PXX2_LOWSPEED_BAUDRATE); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, PXX2_PERIOD); break; #endif #if defined(MULTIMODULE) case PROTOCOL_CHANNELS_MULTIMODULE: - extmoduleSerialStart(MULTIMODULE_BAUDRATE, MULTIMODULE_PERIOD * 2000, true); + extmoduleSerialStart(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD); break; #endif #if defined(SBUS) case PROTOCOL_CHANNELS_SBUS: - extmoduleSerialStart(SBUS_BAUDRATE, SBUS_PERIOD_HALF_US, false); + extmoduleSerialStart(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD); break; #endif #if defined(PPM) case PROTOCOL_CHANNELS_PPM: extmodulePpmStart(); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE)); break; #endif #if defined(AFHDS3) case PROTOCOL_CHANNELS_AFHDS3: extmodulePulsesData.afhds3.init(EXTERNAL_MODULE); - extmoduleSerialStart(AFHDS3_BAUDRATE, AFHDS3_COMMAND_TIMEOUT * 2000, false); + extmoduleSerialStart(/*AFHDS3_BAUDRATE, AFHDS3_COMMAND_TIMEOUT * 2000, false*/); + mixerSchedulerSetPeriod(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT * 1000 /* us */); break; #endif - default: + // external module stopped, set period to 50ms (necessary for USB Joystick, for instance) + mixerSchedulerSetPeriod(EXTERNAL_MODULE, 50000/*us*/); break; } } @@ -301,14 +314,12 @@ bool setupPulsesExternalModule(uint8_t protocol) #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: extmodulePulsesData.pxx.setupFrame(EXTERNAL_MODULE); - scheduleNextMixerCalculation(EXTERNAL_MODULE, PXX_PULSES_PERIOD); return true; #endif #if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) case PROTOCOL_CHANNELS_PXX1_SERIAL: extmodulePulsesData.pxx_uart.setupFrame(EXTERNAL_MODULE); - scheduleNextMixerCalculation(EXTERNAL_MODULE, EXTMODULE_PXX1_SERIAL_PERIOD); return true; #endif @@ -316,14 +327,12 @@ bool setupPulsesExternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: case PROTOCOL_CHANNELS_PXX2_LOWSPEED: extmodulePulsesData.pxx2.setupFrame(EXTERNAL_MODULE); - scheduleNextMixerCalculation(EXTERNAL_MODULE, PXX2_PERIOD); return true; #endif #if defined(SBUS) case PROTOCOL_CHANNELS_SBUS: setupPulsesSbus(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, SBUS_PERIOD); return true; #endif @@ -332,47 +341,61 @@ bool setupPulsesExternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSMX: setupPulsesDSM2(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, DSM2_PERIOD); return true; #endif #if defined(CROSSFIRE) case PROTOCOL_CHANNELS_CROSSFIRE: + { + ModuleSyncStatus& status = getModuleSyncStatus(EXTERNAL_MODULE); + if (status.isValid()) + mixerSchedulerSetPeriod(EXTERNAL_MODULE, status.getAdjustedRefreshRate()); + else + mixerSchedulerSetPeriod(EXTERNAL_MODULE, CROSSFIRE_PERIOD); setupPulsesCrossfire(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, CROSSFIRE_PERIOD); return true; + } #endif #if defined(GHOST) case PROTOCOL_CHANNELS_GHOST: + { + ModuleSyncStatus& status = getModuleSyncStatus(EXTERNAL_MODULE); + if (status.isValid()) + mixerSchedulerSetPeriod(EXTERNAL_MODULE, status.getAdjustedRefreshRate()); + else + mixerSchedulerSetPeriod(EXTERNAL_MODULE, GHOST_PERIOD); setupPulsesGhost(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, GHOST_PERIOD); return true; + } #endif #if defined(MULTIMODULE) case PROTOCOL_CHANNELS_MULTIMODULE: + { + ModuleSyncStatus& status = getModuleSyncStatus(EXTERNAL_MODULE); + if (status.isValid()) + mixerSchedulerSetPeriod(EXTERNAL_MODULE, status.getAdjustedRefreshRate()); + else + mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD); setupPulsesMultiExternalModule(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, MULTIMODULE_PERIOD); return true; + } #endif #if defined(PPM) case PROTOCOL_CHANNELS_PPM: setupPulsesPPMExternalModule(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE)); return true; #endif #if defined(AFHDS3) case PROTOCOL_CHANNELS_AFHDS3: extmodulePulsesData.afhds3.setupFrame(); - scheduleNextMixerCalculation(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT); return true; #endif default: - scheduleNextMixerCalculation(EXTERNAL_MODULE, 50/*ms*/); return false; } } @@ -386,12 +409,24 @@ static void enablePulsesInternalModule(uint8_t protocol) #if defined(PXX1) && !defined(INTMODULE_USART) case PROTOCOL_CHANNELS_PXX1_PULSES: intmodulePxx1PulsesStart(); +#if defined(INTMODULE_HEARTBEAT) + // use backup trigger (1 ms later) + mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000/*us*/); +#else + mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); +#endif break; #endif #if defined(PXX1) && defined(INTMODULE_USART) case PROTOCOL_CHANNELS_PXX1_SERIAL: intmodulePxx1SerialStart(); +#if defined(INTMODULE_HEARTBEAT) + // use backup trigger (1 ms later) + mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000/*us*/); +#else + mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); +#endif break; #endif @@ -399,6 +434,13 @@ static void enablePulsesInternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: intmoduleSerialStart(PXX2_HIGHSPEED_BAUDRATE, true, USART_Parity_No, USART_StopBits_1, USART_WordLength_8b); resetAccessAuthenticationCount(); + +#if defined(INTMODULE_HEARTBEAT) + // use backup trigger (1 ms later) + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000/*us*/); +#else + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD); +#endif break; #endif @@ -406,17 +448,20 @@ static void enablePulsesInternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_MULTIMODULE: intmodulePulsesData.multi.initFrame(); intmoduleSerialStart(MULTIMODULE_BAUDRATE, true, USART_Parity_Even, USART_StopBits_2, USART_WordLength_9b); - intmoduleTimerStart(MULTIMODULE_PERIOD); + mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD); break; #endif #if defined(INTERNAL_MODULE_PPM) case PROTOCOL_CHANNELS_PPM: intmodulePpmStart(); + mixerSchedulerSetPeriod(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE)); break; #endif default: + // internal module stopped, set internal period to 0 and start the scheduler + mixerSchedulerSetPeriod(INTERNAL_MODULE, 0); break; } } @@ -427,18 +472,18 @@ bool setupPulsesInternalModule(uint8_t protocol) #if defined(HARDWARE_INTERNAL_MODULE) && defined(PXX1) && !defined(INTMODULE_USART) case PROTOCOL_CHANNELS_PXX1_PULSES: intmodulePulsesData.pxx.setupFrame(INTERNAL_MODULE); - scheduleNextMixerCalculation(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); +#if defined(INTMODULE_HEARTBEAT) + mixerSchedulerResetTimer(); + mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000 /* backup */); +#else + mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); +#endif return true; #endif #if defined(PXX1) && defined(INTMODULE_USART) case PROTOCOL_CHANNELS_PXX1_SERIAL: intmodulePulsesData.pxx_uart.setupFrame(INTERNAL_MODULE); -#if defined(INTMODULE_HEARTBEAT) - scheduleNextMixerCalculation(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1 /* backup */); -#else - scheduleNextMixerCalculation(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); -#endif return true; #endif @@ -447,15 +492,16 @@ bool setupPulsesInternalModule(uint8_t protocol) { bool result = intmodulePulsesData.pxx2.setupFrame(INTERNAL_MODULE); if (moduleState[INTERNAL_MODULE].mode == MODULE_MODE_SPECTRUM_ANALYSER || moduleState[INTERNAL_MODULE].mode == MODULE_MODE_POWER_METER) { - scheduleNextMixerCalculation(INTERNAL_MODULE, PXX2_TOOLS_PERIOD); + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_TOOLS_PERIOD); } else { #if defined(INTMODULE_HEARTBEAT) - scheduleNextMixerCalculation(INTERNAL_MODULE, PXX2_PERIOD + 1 /* backup */); + mixerSchedulerResetTimer(); + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000 /* backup */); #else - scheduleNextMixerCalculation(INTERNAL_MODULE, PXX2_PERIOD); + mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD); #endif - } + } return result; } #endif @@ -463,19 +509,20 @@ bool setupPulsesInternalModule(uint8_t protocol) #if defined(PCBTARANIS) && defined(INTERNAL_MODULE_PPM) case PROTOCOL_CHANNELS_PPM: setupPulsesPPMInternalModule(); - scheduleNextMixerCalculation(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE)); + // probably useless, as the interval did not change since "enable" function + mixerSchedulerSetPeriod(INTERNAL_MODULE, PPM_PERIOD(INTERNAL_MODULE)); return true; #endif #if defined(INTERNAL_MODULE_MULTI) case PROTOCOL_CHANNELS_MULTIMODULE: setupPulsesMultiInternalModule(); - scheduleNextMixerCalculation(INTERNAL_MODULE, MULTIMODULE_PERIOD); + mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD); return true; #endif default: - scheduleNextMixerCalculation(INTERNAL_MODULE, 10 /*ms*/); // used for USB sim for example + mixerSchedulerSetPeriod(INTERNAL_MODULE, 10000 /*us*/); // used for USB sim for example return false; } } diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 84ecee015..4a1fafe65 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -216,21 +216,20 @@ typedef Dsm2SerialPulsesData Dsm2PulsesData; PACK(struct Dsm2TimerPulsesData { pulse_duration_t pulses[MAX_PULSES_TRANSITIONS]; pulse_duration_t * ptr; - uint16_t rest; uint8_t index; }); typedef Dsm2TimerPulsesData Dsm2PulsesData; #endif #define PPM_PERIOD_HALF_US(module) ((g_model.moduleData[module].ppm.frameLength * 5 + 225) * 200) /*half us*/ -#define PPM_PERIOD(module) (PPM_PERIOD_HALF_US(module) / 2000) /*ms*/ +#define PPM_PERIOD(module) (PPM_PERIOD_HALF_US(module) / 2) /*us*/ #define DSM2_BAUDRATE 125000 -#define DSM2_PERIOD 22 /*ms*/ +#define DSM2_PERIOD 22000 /*us*/ #define SBUS_BAUDRATE 100000 #define SBUS_PERIOD_HALF_US ((g_model.moduleData[EXTERNAL_MODULE].sbus.refreshRate * 5 + 225) * 200) /*half us*/ -#define SBUS_PERIOD (SBUS_PERIOD_HALF_US / 2000) /*ms*/ +#define SBUS_PERIOD (SBUS_PERIOD_HALF_US / 2) /*us*/ #define MULTIMODULE_BAUDRATE 100000 -#define MULTIMODULE_PERIOD 7 /*ms*/ +#define MULTIMODULE_PERIOD 7000 /*us*/ #define CROSSFIRE_FRAME_MAXLEN 64 PACK(struct CrossfirePulsesData { diff --git a/radio/src/pulses/pxx.h b/radio/src/pulses/pxx.h index 5d0bfaf05..ae84d77b2 100644 --- a/radio/src/pulses/pxx.h +++ b/radio/src/pulses/pxx.h @@ -29,20 +29,20 @@ #define PXX2_LOWSPEED_BAUDRATE 230400 #define PXX2_HIGHSPEED_BAUDRATE 450000 -#define PXX2_PERIOD 4 // 4ms -#define PXX2_TOOLS_PERIOD 1 // 1ms +#define PXX2_PERIOD 4000/*us*/ +#define PXX2_TOOLS_PERIOD 1000/*us*/ #define PXX2_FRAME_MAXLENGTH 64 -#define PXX_PULSES_PERIOD 9/*ms*/ -#define EXTMODULE_PXX1_SERIAL_PERIOD 4/*ms*/ +#define PXX_PULSES_PERIOD 9000/*us*/ +#define EXTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/ #define EXTMODULE_PXX1_SERIAL_BAUDRATE 420000 #if defined(PXX_FREQUENCY_HIGH) #define INTMODULE_PXX1_SERIAL_BAUDRATE 450000 - #define INTMODULE_PXX1_SERIAL_PERIOD 4/*ms*/ + #define INTMODULE_PXX1_SERIAL_PERIOD 4000/*us*/ #else #define INTMODULE_PXX1_SERIAL_BAUDRATE 115200 - #define INTMODULE_PXX1_SERIAL_PERIOD 9/*ms*/ + #define INTMODULE_PXX1_SERIAL_PERIOD 9000/*us*/ #endif // Used by the Sky9x family boards diff --git a/radio/src/pulses/sbus.cpp b/radio/src/pulses/sbus.cpp index a881730fc..e44abd642 100644 --- a/radio/src/pulses/sbus.cpp +++ b/radio/src/pulses/sbus.cpp @@ -55,7 +55,6 @@ static void _send_level(uint8_t v) *extmodulePulsesData.dsm2.ptr++ = v - 1; extmodulePulsesData.dsm2.index+=1; - extmodulePulsesData.dsm2.rest -=v; } void sendByteSbus(uint8_t b) // max 11 changes 0 10 10 10 10 P 1 @@ -113,7 +112,6 @@ void setupPulsesSbus() extmodulePulsesData.dsm2.serialByte = 0; extmodulePulsesData.dsm2.serialBitCount = 0; #else - extmodulePulsesData.dsm2.rest = SBUS_PERIOD_HALF_US; extmodulePulsesData.dsm2.index = 0; #endif diff --git a/radio/src/rtos.h b/radio/src/rtos.h index 6e42ebf30..a5dbd11b9 100644 --- a/radio/src/rtos.h +++ b/radio/src/rtos.h @@ -36,7 +36,9 @@ extern "C++" { typedef pthread_t RTOS_TASK_HANDLE; typedef pthread_mutex_t RTOS_MUTEX_HANDLE; + typedef uint32_t RTOS_FLAG_HANDLE; + typedef sem_t * RTOS_EVENT_HANDLE; extern uint64_t simuTimerMicros(void); @@ -76,16 +78,26 @@ extern "C++" { pthread_mutex_unlock(&mutex); } - static inline void RTOS_CREATE_FLAG(uint32_t &flag) + static inline void RTOS_CREATE_FLAG(RTOS_FLAG_HANDLE flag) { - flag = 0; // TODO: real flags (use semaphores?) } - static inline void RTOS_SET_FLAG(uint32_t &flag) + static inline void RTOS_SET_FLAG(RTOS_FLAG_HANDLE flag) { - flag = 1; } + static inline void RTOS_CLEAR_FLAG(RTOS_FLAG_HANDLE flag) + { + } + + static inline bool RTOS_WAIT_FLAG(RTOS_FLAG_HANDLE flag, uint32_t timeout) + { + simuSleep(timeout); + return false; + } + + #define RTOS_ISR_SET_FLAG RTOS_SET_FLAG + template class FakeTaskStack { @@ -146,6 +158,7 @@ template { return (uint32_t)(simuTimerMicros() / 1000); } + #elif defined(RTOS_COOS) #ifdef __cplusplus extern "C" { @@ -230,7 +243,18 @@ template #define RTOS_CREATE_FLAG(flag) flag = CoCreateFlag(false, false) #define RTOS_SET_FLAG(flag) (void)CoSetFlag(flag) + #define RTOS_CLEAR_FLAG(flag) (void)CoClearFlag(flag) + #define RTOS_WAIT_FLAG(flag,timeout) (CoWaitForSingleFlag(flag,timeout) == E_TIMEOUT) + static inline void RTOS_ISR_SET_FLAG(RTOS_FLAG_HANDLE flag) + { + CoEnterISR(); + CoSchedLock(); + isr_SetFlag(flag); + CoSchedUnlock(); + CoExitISR(); + } + #ifdef __cplusplus template class TaskStack diff --git a/radio/src/targets/common/arm/stm32/heartbeat_driver.cpp b/radio/src/targets/common/arm/stm32/heartbeat_driver.cpp index 25544c33a..269b40854 100644 --- a/radio/src/targets/common/arm/stm32/heartbeat_driver.cpp +++ b/radio/src/targets/common/arm/stm32/heartbeat_driver.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "mixer_scheduler.h" #if defined(INTMODULE_HEARTBEAT_GPIO) volatile HeartbeatCapture heartbeatCapture; @@ -77,6 +78,8 @@ void check_intmodule_heartbeat() heartbeatCapture.count++; #endif EXTI_ClearITPendingBit(INTMODULE_HEARTBEAT_EXTI_LINE); + + mixerSchedulerISRTrigger(); } } #endif 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 56b71d97c..794c49765 100755 --- a/radio/src/targets/common/arm/stm32/intmodule_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/intmodule_serial_driver.cpp @@ -45,12 +45,6 @@ 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() @@ -103,28 +97,6 @@ void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable, uint16_t parity, } } -#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) { @@ -217,12 +189,3 @@ void intmoduleSendNextFrame() #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/mixer_scheduler_driver.cpp b/radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp new file mode 100644 index 000000000..7f7b23a1c --- /dev/null +++ b/radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "opentx.h" +#include "mixer_scheduler.h" + +#if !defined(SIMU) + +// Start scheduler with default period +void mixerSchedulerStart() +{ + MIXER_SCHEDULER_TIMER->CR1 &= ~TIM_CR1_CEN; + + MIXER_SCHEDULER_TIMER->CR1 = TIM_CR1_URS; // do not generate interrupt on soft update + MIXER_SCHEDULER_TIMER->PSC = MIXER_SCHEDULER_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + MIXER_SCHEDULER_TIMER->CCER = 0; + MIXER_SCHEDULER_TIMER->CCMR1 = 0; + MIXER_SCHEDULER_TIMER->ARR = 2 * getMixerSchedulerPeriod() - 1; + MIXER_SCHEDULER_TIMER->EGR = TIM_EGR_UG; // reset timer + + NVIC_EnableIRQ(MIXER_SCHEDULER_TIMER_IRQn); + NVIC_SetPriority(MIXER_SCHEDULER_TIMER_IRQn, 8); + + MIXER_SCHEDULER_TIMER->SR &= TIM_SR_UIF; // clear interrupt flag + MIXER_SCHEDULER_TIMER->DIER |= TIM_DIER_UIE; // enable interrupt + MIXER_SCHEDULER_TIMER->CR1 |= TIM_CR1_CEN; +} + +void mixerSchedulerStop() +{ + MIXER_SCHEDULER_TIMER->CR1 &= ~TIM_CR1_CEN; + NVIC_DisableIRQ(MIXER_SCHEDULER_TIMER_IRQn); +} + +void mixerSchedulerResetTimer() +{ + mixerSchedulerDisableTrigger(); + MIXER_SCHEDULER_TIMER->CNT = 0; + mixerSchedulerEnableTrigger(); +} + +void mixerSchedulerEnableTrigger() +{ + MIXER_SCHEDULER_TIMER->DIER |= TIM_DIER_UIE; // enable interrupt +} + +void mixerSchedulerDisableTrigger() +{ + MIXER_SCHEDULER_TIMER->DIER &= ~TIM_DIER_UIE; // disable interrupt +} + +extern "C" void MIXER_SCHEDULER_TIMER_IRQHandler(void) +{ + MIXER_SCHEDULER_TIMER->SR &= ~TIM_SR_UIF; // clear flag + mixerSchedulerDisableTrigger(); + + // set next period + MIXER_SCHEDULER_TIMER->ARR = 2 * getMixerSchedulerPeriod() - 1; + + // trigger mixer start + mixerSchedulerISRTrigger(); +} + +#endif diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index fbf963b71..460a24701 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -240,6 +240,7 @@ set(TARGET_SRC led_driver.cpp extmodule_driver.cpp trainer_driver.cpp + ../common/arm/stm32/mixer_scheduler_driver.cpp ../common/arm/stm32/heartbeat_driver.cpp ../common/arm/stm32/timers_driver.cpp ../common/arm/stm32/intmodule_serial_driver.cpp diff --git a/radio/src/targets/horus/board.cpp b/radio/src/targets/horus/board.cpp index cb379e6fe..0f3f9df4a 100644 --- a/radio/src/targets/horus/board.cpp +++ b/radio/src/targets/horus/board.cpp @@ -137,6 +137,7 @@ void boardInit() INTMODULE_RCC_APB1Periph | EXTMODULE_RCC_APB1Periph | I2C_RCC_APB1Periph | + MIXER_SCHEDULER_TIMER_RCC_APB1Periph | GPS_RCC_APB1Periph | BACKLIGHT_RCC_APB1Periph, ENABLE); diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 38daec33c..f1c47ab9f 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -180,14 +180,11 @@ void init_intmodule_heartbeat(); void check_intmodule_heartbeat(); 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(); -void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); +void extmoduleSerialStart(); void extmoduleInvertedSerialStart(uint32_t baudrate); void extmoduleSendBuffer(const uint8_t * data, uint8_t size); void extmoduleSendNextFrame(); diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 47a40ae02..e73d5c3d3 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -143,7 +143,7 @@ void extmodulePxx1PulsesStart() } #endif -void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool inverted) +void extmoduleSerialStart() { EXTERNAL_MODULE_ON(); @@ -176,10 +176,9 @@ void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool i EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0; #endif - EXTMODULE_TIMER->ARR = period_half_us; - EXTMODULE_TIMER->CCR2 = period_half_us - 4000; + EXTMODULE_TIMER->ARR = 40000; // dummy value until the DMA request kicks in EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); @@ -342,23 +341,36 @@ void extmoduleSendNextFrame() #if defined(DSM2) case PROTOCOL_CHANNELS_SBUS: -#if defined(PCBX10) || PCBREV >= 13 - EXTMODULE_TIMER->CCER = TIM_CCER_CC3E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC3P : 0); // reverse polarity for Sbus if needed -#else - EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC1P : 0); // reverse polarity for Sbus if needed -#endif - // no break case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSMX: case PROTOCOL_CHANNELS_MULTIMODULE: - EXTMODULE_TIMER->CCR2 = *(extmodulePulsesData.dsm2.ptr - 1) - 4000; // 2mS in advance + + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + + if (PROTOCOL_CHANNELS_SBUS == moduleState[EXTERNAL_MODULE].protocol) { +#if defined(PCBX10) || PCBREV >= 13 + EXTMODULE_TIMER->CCER = TIM_CCER_CC3E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC3P : 0); // reverse polarity for Sbus if needed +#else + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC1P : 0); // reverse polarity for Sbus if needed +#endif + } + + // disable timer + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + + // send DMA request EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | EXTMODULE_TIMER_DMA_SIZE | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.dsm2.pulses); EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses; EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = 1; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; #endif @@ -419,14 +431,20 @@ extern "C" void EXTMODULE_TIMER_DMA_IRQHandler() DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); - EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + switch (moduleState[EXTERNAL_MODULE].protocol) { + case PROTOCOL_CHANNELS_PXX1_PULSES: + case PROTOCOL_CHANNELS_PPM: + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + break; + } } extern "C" void EXTMODULE_TIMER_IRQHandler() { EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; + if (setupPulsesExternalModule()) extmoduleSendNextFrame(); } diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 479dd88c5..35a135cf4 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -902,6 +902,13 @@ #define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 #define TIMER_2MHz_TIMER TIM7 +// Mixer scheduler timer +#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13 +#define MIXER_SCHEDULER_TIMER TIM13 +#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define MIXER_SCHEDULER_TIMER_IRQn TIM8_UP_TIM13_IRQn +#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler + // Bluetooth #define STORAGE_BLUETOOTH #if defined(BLUETOOTH) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 506f237cd..955a0994c 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -401,6 +401,7 @@ set(TARGET_SRC ../common/arm/stm32/audio_dac_driver.cpp ../common/arm/stm32/adc_driver.cpp ../common/arm/stm32/heartbeat_driver.cpp + ../common/arm/stm32/mixer_scheduler_driver.cpp ) if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES) diff --git a/radio/src/targets/taranis/board.cpp b/radio/src/targets/taranis/board.cpp index b038fc0f7..7d8561636 100644 --- a/radio/src/targets/taranis/board.cpp +++ b/radio/src/targets/taranis/board.cpp @@ -110,6 +110,7 @@ void boardInit() AUX_SERIAL_RCC_APB1Periph | INTMODULE_RCC_APB1Periph | TRAINER_MODULE_RCC_APB1Periph | + MIXER_SCHEDULER_TIMER_RCC_APB1Periph | BT_RCC_APB1Periph | GYRO_RCC_APB1Periph, ENABLE); @@ -339,4 +340,4 @@ void initJackDetect(void) GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(JACK_DETECT_GPIO, &GPIO_InitStructure); } -#endif \ No newline at end of file +#endif diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index d9092fcdc..29fd50432 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -143,7 +143,7 @@ void intmoduleSendByte(uint8_t byte); void intmoduleSendBuffer(const uint8_t * data, uint8_t size); void intmoduleSendNextFrame(); -void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); +void extmoduleSerialStart(); void extmoduleInvertedSerialStart(uint32_t baudrate); void extmoduleSendBuffer(const uint8_t * data, uint8_t size); void extmoduleSendNextFrame(); diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 4287c0f75..7a7927a41 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -83,7 +83,7 @@ void extmodulePpmStart() NVIC_SetPriority(EXTMODULE_TIMER_CC_IRQn, 7); } -void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool inverted) +void extmoduleSerialStart() { EXTERNAL_MODULE_ON(); @@ -99,16 +99,17 @@ void extmoduleSerialStart(uint32_t /*baudrate*/, uint32_t period_half_us, bool i EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS from 30MHz - EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (inverted ? 0 : EXTMODULE_TIMER_OUTPUT_POLARITY); + + EXTMODULE_TIMER->CCR3 = 0; + EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | EXTMODULE_TIMER_OUTPUT_POLARITY; EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs EXTMODULE_TIMER->CCR1 = 0; EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high EXTMODULE_TIMER->EGR = 1; // Restart EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0; - EXTMODULE_TIMER->ARR = period_half_us; - EXTMODULE_TIMER->CCR2 = 40000; // The first frame will be sent in 20ms + EXTMODULE_TIMER->ARR = 40000; // dummy value until the DMA request kicks in EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); @@ -300,19 +301,33 @@ void extmoduleSendNextFrame() #endif #if defined(SBUS) || defined(DSM2) || defined(MULTIMODULE) case PROTOCOL_CHANNELS_SBUS: - EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? 0 : EXTMODULE_TIMER_OUTPUT_POLARITY); // reverse polarity for Sbus if needed // no break case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSMX: case PROTOCOL_CHANNELS_MULTIMODULE: - EXTMODULE_TIMER->CCR2 = *(extmodulePulsesData.dsm2.ptr - 1) - 4000; // 2mS in advance + + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + + if (PROTOCOL_CHANNELS_SBUS == moduleState[EXTERNAL_MODULE].protocol) { + EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? EXTMODULE_TIMER_OUTPUT_POLARITY : 0); // reverse polarity for Sbus if needed + } + + // disable timer + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + + // send DMA request EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.dsm2.pulses); EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.dsm2.ptr - extmodulePulsesData.dsm2.pulses; EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = 1; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; #endif @@ -373,14 +388,20 @@ extern "C" void EXTMODULE_TIMER_DMA_STREAM_IRQHandler() DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); - EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + switch (moduleState[EXTERNAL_MODULE].protocol) { + case PROTOCOL_CHANNELS_PXX1_PULSES: + case PROTOCOL_CHANNELS_PPM: + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + break; + } } extern "C" void EXTMODULE_TIMER_CC_IRQHandler() { EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; + if (setupPulsesExternalModule()) { extmoduleSendNextFrame(); } diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index b7160cbf9..aebff4c88 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2024,4 +2024,11 @@ #define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 #define TIMER_2MHz_TIMER TIM7 +// Mixer scheduler timer +#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13 +#define MIXER_SCHEDULER_TIMER TIM13 +#define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) +#define MIXER_SCHEDULER_TIMER_IRQn TIM8_UP_TIM13_IRQn +#define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_UP_TIM13_IRQHandler + #endif // _HAL_H_ diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 16e120784..e4c209edd 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "mixer_scheduler.h" RTOS_TASK_HANDLE menusTaskId; RTOS_DEFINE_STACK(menusStack, MENUS_STACK_SIZE); @@ -75,13 +76,29 @@ bool isForcePowerOffRequested() bool isModuleSynchronous(uint8_t moduleIdx) { - uint8_t protocol = moduleState[moduleIdx].protocol; - if (protocol == PROTOCOL_CHANNELS_PXX2_HIGHSPEED || protocol == PROTOCOL_CHANNELS_PXX2_LOWSPEED || protocol == PROTOCOL_CHANNELS_CROSSFIRE || protocol == PROTOCOL_CHANNELS_GHOST || protocol == PROTOCOL_CHANNELS_NONE) - return true; -#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) - if (protocol == PROTOCOL_CHANNELS_PXX1_SERIAL) - return true; + switch(moduleState[moduleIdx].protocol) { + + case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: + case PROTOCOL_CHANNELS_PXX2_LOWSPEED: + case PROTOCOL_CHANNELS_CROSSFIRE: + case PROTOCOL_CHANNELS_GHOST: + case PROTOCOL_CHANNELS_AFHDS3: + case PROTOCOL_CHANNELS_NONE: + +#if defined(MULTIMODULE) + case PROTOCOL_CHANNELS_MULTIMODULE: #endif +#if defined(INTMODULE_USART) || defined(EXTMODULE_USART) + case PROTOCOL_CHANNELS_PXX1_SERIAL: +#endif +#if defined(DSM2) + case PROTOCOL_CHANNELS_SBUS: + case PROTOCOL_CHANNELS_DSM2_LP45: + case PROTOCOL_CHANNELS_DSM2_DSM2: + case PROTOCOL_CHANNELS_DSM2_DSMX: +#endif + return true; + } return false; } @@ -100,12 +117,17 @@ void sendSynchronousPulses(uint8_t runMask) } } +//#define DEBUG_MIXER_SCHEDULER + uint32_t nextMixerTime[NUM_MODULES]; TASK_FUNCTION(mixerTask) { s_pulses_paused = true; + mixerSchedulerInit(); + mixerSchedulerStart(); + while (true) { #if defined(SBUS_TRAINER) // SBUS trainer @@ -120,7 +142,16 @@ TASK_FUNCTION(mixerTask) bluetooth.wakeup(); #endif - RTOS_WAIT_TICKS(1); + // run mixer at least every 30ms + bool timeout = mixerSchedulerWaitForTrigger(30); + +#if defined(DEBUG_MIXER_SCHEDULER) + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); + GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); +#endif + + // re-enable trigger + mixerSchedulerEnableTrigger(); #if defined(SIMU) if (pwrCheck() == e_power_off) { @@ -132,23 +163,6 @@ TASK_FUNCTION(mixerTask) } #endif - uint32_t now = RTOS_GET_MS(); - uint8_t runMask = 0; - - if (now >= nextMixerTime[0]) { - runMask |= (1 << 0); - } - -#if NUM_MODULES >= 2 - if (now >= nextMixerTime[1]) { - runMask |= (1 << 1); - } -#endif - - if (!runMask) { - continue; // go back to sleep - } - if (!s_pulses_paused) { uint16_t t0 = getTmr2MHz(); @@ -183,29 +197,18 @@ TASK_FUNCTION(mixerTask) if (t0 > maxMixerDuration) maxMixerDuration = t0; - sendSynchronousPulses(runMask); + // TODO: + // - check the cause of timeouts when switching + // between protocols with multi-proto RF + if (timeout) + serialPrint("mix sched timeout!"); + + // TODO: fix runMask + sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE)); } } } -void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms) -{ - // Schedule next mixer calculation time, - - if (isModuleSynchronous(module)) { - nextMixerTime[module] += period_ms / RTOS_MS_PER_TICK; - if (nextMixerTime[module] < RTOS_GET_TIME()) { - // we are late ... let's add some small delay - nextMixerTime[module] = (uint32_t) RTOS_GET_TIME() + (period_ms / RTOS_MS_PER_TICK); - } - } - else { - // for now assume mixer calculation takes 2 ms. - nextMixerTime[module] = (uint32_t) RTOS_GET_TIME() + (period_ms / RTOS_MS_PER_TICK); - } - - DEBUG_TIMER_STOP(debugTimerMixerCalcToUsage); -} #define MENU_TASK_PERIOD_TICKS (50 / RTOS_MS_PER_TICK) // 50ms diff --git a/radio/src/telemetry/crossfire.cpp b/radio/src/telemetry/crossfire.cpp index b46cc2b44..672dd56f0 100644 --- a/radio/src/telemetry/crossfire.cpp +++ b/radio/src/telemetry/crossfire.cpp @@ -184,6 +184,26 @@ void processCrossfireTelemetryFrame() break; } + case RADIO_ID: + if (telemetryRxBuffer[3] == 0xEA // radio address + && telemetryRxBuffer[5] == 0x10 // timing correction frame + ) { + + uint32_t update_interval; + int32_t offset; + if (getCrossfireTelemetryValue<4>(6, (int32_t&)update_interval) && getCrossfireTelemetryValue<4>(10, offset)) { + + // values are in 10th of micro-seconds + update_interval /= 10; + offset /= 10; + + TRACE("[XF] Rate: %d, Lag: %d", update_interval, offset); + getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset + SAFE_SYNC_LAG); + } + } + break; + + #if defined(LUA) default: if (luaInputTelemetryFifo && luaInputTelemetryFifo->hasSpace(telemetryRxBufferCount-2) ) { diff --git a/radio/src/telemetry/crossfire.h b/radio/src/telemetry/crossfire.h index 9855b78f4..9940986f9 100644 --- a/radio/src/telemetry/crossfire.h +++ b/radio/src/telemetry/crossfire.h @@ -99,15 +99,15 @@ const uint32_t CROSSFIRE_BAUDRATES[] = { 115200, }; const uint8_t CROSSFIRE_PERIODS[] = { - 4, + 4, 16, }; #if SPORT_MAX_BAUDRATE < 400000 #define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate] -#define CROSSFIRE_PERIOD CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate] +#define CROSSFIRE_PERIOD (CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate]*1000) #else #define CROSSFIRE_BAUDRATE 400000 -#define CROSSFIRE_PERIOD 4 // 4ms +#define CROSSFIRE_PERIOD 6666 /* us; 150 Hz */ #endif #define CROSSFIRE_TELEM_MIRROR_BAUDRATE 115200 diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index b1b86eb0a..eb5f6fc08 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -63,7 +63,6 @@ enum MultiBufferState : uint8_t #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]; @@ -74,11 +73,6 @@ MultiModuleStatus &getMultiModuleStatus(uint8_t module) return multiModuleStatus[module]; } -MultiModuleSyncStatus &getMultiSyncStatus(uint8_t module) -{ - return multiSyncStatus[module]; -} - uint8_t getMultiBindStatus(uint8_t module) { return multiBindStatus[module]; @@ -111,7 +105,6 @@ uint8_t intTelemetryRxBufferCount; #else // !INTERNAL_MODULE_MULTI static MultiModuleStatus multiModuleStatus; -static MultiModuleSyncStatus multiSyncStatus; static uint8_t multiBindStatus = MULTI_NORMAL_OPERATION; static MultiBufferState multiTelemetryBufferState; @@ -122,11 +115,6 @@ MultiModuleStatus& getMultiModuleStatus(uint8_t) return multiModuleStatus; } -MultiModuleSyncStatus& getMultiSyncStatus(uint8_t) -{ - return multiSyncStatus; -} - uint8_t getMultiBindStatus(uint8_t) { return multiBindStatus; @@ -254,24 +242,17 @@ static void processMultiStatusPacket(const uint8_t * data, uint8_t module, uint8 static void processMultiSyncPacket(const uint8_t * data, uint8_t module) { - MultiModuleSyncStatus &status = getMultiSyncStatus(module); + ModuleSyncStatus &status = getModuleSyncStatus(module); - status.lastUpdate = get_tmr10ms(); - status.interval = data[4]; - status.target = data[5]; -#if !defined(PPM_PIN_SERIAL) - auto oldlag = status.inputLag; - (void) oldlag; -#endif + uint16_t refreshRate = data[0] << 8 | data[1]; + int16_t inputLag = data[2] << 8 | data[3]; - status.calcAdjustedRefreshRate(data[0] << 8 | data[1], data[2] << 8 | data[3]); + // if (inputLag > refreshRate/2) + // inputLag -= refreshRate; -#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", - module == EXTERNAL_MODULE ? extmodulePulsesData.dsm2.rest : 0, - status.inputLag, oldlag - status.inputLag, status.target, status.interval, status.refreshRate, status.adjustedRefreshRate / 50, - status.getAdjustedRefreshRate()); -#endif + status.update(refreshRate, inputLag); + + serialPrint("MP ADJ: R %d, L %04d", refreshRate, inputLag); } #if defined(PCBTARANIS) || defined(PCBHORUS) @@ -434,104 +415,6 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) } } -#define MIN_REFRESH_RATE 5500 - -void MultiModuleSyncStatus::calcAdjustedRefreshRate(uint16_t newRefreshRate, uint16_t newInputLag) -{ - // Check how far off we are from our target, positive means we are too slow, negative we are too fast - int lagDifference = newInputLag - inputLag; - - // The refresh rate that we target - // Below is least common multiple of MIN_REFRESH_RATE and requested rate - uint16_t targetRefreshRate = (uint16_t) (newRefreshRate * ((MIN_REFRESH_RATE / (newRefreshRate - 1)) + 1)); - - // Overflow, reverse sample - if (lagDifference < -targetRefreshRate / 2) - lagDifference = -lagDifference; - - - // Reset adjusted refresh if rate has changed - if (newRefreshRate != refreshRate) { - refreshRate = newRefreshRate; - adjustedRefreshRate = targetRefreshRate; - if (adjustedRefreshRate >= 30000) - adjustedRefreshRate /= 2; - - // Our refresh rate in ps - adjustedRefreshRate *= 1000; - return; - } - - // Caluclate how many samples went into the reported input Lag (*10) - int numsamples = interval * 10000 / targetRefreshRate; - - // Convert lagDifference to ps - lagDifference = lagDifference * 1000; - - // Calculate the time we intentionally were late/early - if (inputLag > target * 10 + 30) - lagDifference += numsamples * 500; - else if (inputLag < target * 10 - 30) - lagDifference -= numsamples * 500; - - // Caculate the time in ps each frame is to slow (positive), fast(negative) - int perframeps = lagDifference * 10 / numsamples; - - if (perframeps > 20000) - perframeps = 20000; - - if (perframeps < -20000) - perframeps = -20000; - - adjustedRefreshRate = (adjustedRefreshRate + perframeps); - - // Safeguards - if (adjustedRefreshRate < MIN_REFRESH_RATE * 1000) - adjustedRefreshRate = MIN_REFRESH_RATE * 1000; - if (adjustedRefreshRate > 30 * 1000 * 1000) - adjustedRefreshRate = 30 * 1000 * 1000; - - inputLag = newInputLag; -} - -static uint8_t counter; - -const uint16_t MultiModuleSyncStatus::getAdjustedRefreshRate() -{ - if (!isValid() || refreshRate == 0) - return 18000; - - counter = (uint8_t) (counter + 1 % 10); - uint16_t rate = (uint16_t) ((adjustedRefreshRate + counter * 50) / 500); - // Check how far off we are from our target, positive means we are too slow, negative we are too fast - if (inputLag > target * 10 + 30) - return (uint16_t) (rate - 1); - else if (inputLag < target * 10 - 30) - return (uint16_t) (rate + 1); - else - return rate; -} - -void MultiModuleSyncStatus::getRefreshString(char * statusText) -{ - if (!isValid()) { - return; - } - - char * tmp = statusText; -#if defined(DEBUG) - *tmp++ = 'L'; - tmp = strAppendUnsigned(tmp, inputLag, 5); - tmp = strAppend(tmp, "us R "); - tmp = strAppendUnsigned(tmp, (uint32_t) (adjustedRefreshRate / 1000), 5); - tmp = strAppend(tmp, "us"); -#else - tmp = strAppend(tmp, "Sync at "); - tmp = strAppendUnsigned(tmp, (uint32_t) (adjustedRefreshRate / 1000000)); - tmp = strAppend(tmp, " ms"); -#endif -} - void MultiModuleStatus::getStatusString(char * statusText) const { if (!isValid()) { diff --git a/radio/src/telemetry/multi.h b/radio/src/telemetry/multi.h index e9b9ada15..ca9d3fc79 100644 --- a/radio/src/telemetry/multi.h +++ b/radio/src/telemetry/multi.h @@ -94,28 +94,6 @@ void processMultiTelemetryData(uint8_t data, uint8_t module); #define MULTI_SCANNER_MAX_CHANNEL 249 -// This should be put into the Module definition if other modules gain this functionality -struct MultiModuleSyncStatus { - uint32_t adjustedRefreshRate = 9000 * 1000; // in ps - tmr10ms_t lastUpdate; - uint16_t refreshRate; - uint16_t inputLag; - uint8_t interval; - uint8_t target; - - inline bool isValid() const - { - return (get_tmr10ms() - lastUpdate < 100); - } - - void getRefreshString(char * refreshText); - const uint16_t getAdjustedRefreshRate(); - void calcAdjustedRefreshRate(uint16_t newRefreshRate, uint16_t newInputLag); -}; - -MultiModuleSyncStatus& getMultiSyncStatus(uint8_t module); - - struct MultiModuleStatus { uint8_t major; diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 70c4fdc74..21e1e6d9b 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "multi.h" #include "pulses/afhds3.h" +#include "mixer_scheduler.h" uint8_t telemetryStreaming = 0; uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1) @@ -374,3 +375,88 @@ OutputTelemetryBuffer outputTelemetryBuffer __DMA; #if defined(LUA) Fifo * luaInputTelemetryFifo = NULL; #endif + +#if defined(HARDWARE_INTERNAL_MODULE) + +static ModuleSyncStatus moduleSyncStatus[NUM_MODULES]; + +ModuleSyncStatus &getModuleSyncStatus(uint8_t moduleIdx) +{ + return moduleSyncStatus[moduleIdx]; +} + +#else + +static ModuleSyncStatus moduleSyncStatus; + +ModuleSyncStatus &getModuleSyncStatus(uint8_t moduleIdx) +{ + return moduleSyncStatus; +} + +#endif + +ModuleSyncStatus::ModuleSyncStatus() +{ + memset(this, 0, sizeof(ModuleSyncStatus)); +} + +void ModuleSyncStatus::update(uint16_t newRefreshRate, uint16_t newInputLag) +{ + if (!newRefreshRate) + return; + + if (newRefreshRate < MIN_REFRESH_RATE) + newRefreshRate = newRefreshRate * (MIN_REFRESH_RATE / (newRefreshRate + 1)); + else if (newRefreshRate > MAX_REFRESH_RATE) + newRefreshRate = MAX_REFRESH_RATE; + + refreshRate = newRefreshRate; + inputLag = newInputLag; + currentLag = newInputLag; + lastUpdate = get_tmr10ms(); +} + +uint16_t ModuleSyncStatus::getAdjustedRefreshRate() +{ + int16_t lag = currentLag - SAFE_SYNC_LAG; + int32_t newRefreshRate = refreshRate; + + if (lag == 0) { + return refreshRate; + } + + newRefreshRate += lag; + + if (newRefreshRate < MIN_REFRESH_RATE) { + newRefreshRate = MIN_REFRESH_RATE; + } + else if (newRefreshRate > MAX_REFRESH_RATE) { + newRefreshRate = MAX_REFRESH_RATE; + } + + TRACE("[SYNC] rate = %dus",newRefreshRate); + + currentLag -= newRefreshRate - refreshRate; + return (uint16_t)newRefreshRate; +} + +void ModuleSyncStatus::getRefreshString(char * statusText) +{ + if (!isValid()) { + return; + } + + char * tmp = statusText; +#if defined(DEBUG) + *tmp++ = 'L'; + tmp = strAppendUnsigned(tmp, inputLag, 5); + tmp = strAppend(tmp, "us R "); + tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000), 5); + tmp = strAppend(tmp, "us"); +#else + tmp = strAppend(tmp, "Sync at "); + tmp = strAppendUnsigned(tmp, (uint32_t) (refreshRate / 1000000)); + tmp = strAppend(tmp, " ms"); +#endif +} diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 6599db749..18f4f98a6 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -289,4 +289,35 @@ extern Fifo * luaInputTelemetryFifo; void processPXX2Frame(uint8_t module, const uint8_t *frame); +// Module pulse synchronization +#define SAFE_SYNC_LAG 800 /* us */ + +struct ModuleSyncStatus +{ + // feedback input: last received values + uint16_t refreshRate; // in us + int16_t inputLag; // in us + + tmr10ms_t lastUpdate; // in 10ms + int16_t currentLag; // in us + + inline bool isValid() { + // 2 seconds + return (get_tmr10ms() - lastUpdate < 200); + } + + // Set feedback from RF module + void update(uint16_t newRefreshRate, uint16_t newInputLag); + + // Get computed settings for scheduler + uint16_t getAdjustedRefreshRate(); + + // Status string for the UI + void getRefreshString(char* refreshText); + + ModuleSyncStatus(); +}; + +ModuleSyncStatus& getModuleSyncStatus(uint8_t moduleIdx); + #endif // _TELEMETRY_H_ From 63438b06d76735f7ae176767f8d563268773761e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 10:06:37 +0200 Subject: [PATCH 003/300] restored 250Hz default rate for crossfire at 400kbps --- radio/src/telemetry/crossfire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/telemetry/crossfire.h b/radio/src/telemetry/crossfire.h index 9940986f9..376b1fec4 100644 --- a/radio/src/telemetry/crossfire.h +++ b/radio/src/telemetry/crossfire.h @@ -107,7 +107,7 @@ const uint8_t CROSSFIRE_PERIODS[] = { #define CROSSFIRE_PERIOD (CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate]*1000) #else #define CROSSFIRE_BAUDRATE 400000 -#define CROSSFIRE_PERIOD 6666 /* us; 150 Hz */ +#define CROSSFIRE_PERIOD 4000 /* us; 250 Hz */ #endif #define CROSSFIRE_TELEM_MIRROR_BAUDRATE 115200 From 8247a05438bae4992a0cbbea6db3c5ae0b8c8de5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 10:41:20 +0200 Subject: [PATCH 004/300] debugging crsfshot... --- radio/src/pulses/pulses.cpp | 2 +- radio/src/telemetry/telemetry.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 1681157bc..d21c58853 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -522,7 +522,7 @@ bool setupPulsesInternalModule(uint8_t protocol) #endif default: - mixerSchedulerSetPeriod(INTERNAL_MODULE, 10000 /*us*/); // used for USB sim for example + //mixerSchedulerSetPeriod(INTERNAL_MODULE, 10000 /*us*/); // used for USB sim for example return false; } } diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 21e1e6d9b..44906b9d7 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -415,6 +415,8 @@ void ModuleSyncStatus::update(uint16_t newRefreshRate, uint16_t newInputLag) inputLag = newInputLag; currentLag = newInputLag; lastUpdate = get_tmr10ms(); + + TRACE("[SYNC] update rate = %dus; lag = %dus",refreshRate,currentLag); } uint16_t ModuleSyncStatus::getAdjustedRefreshRate() @@ -435,9 +437,9 @@ uint16_t ModuleSyncStatus::getAdjustedRefreshRate() newRefreshRate = MAX_REFRESH_RATE; } - TRACE("[SYNC] rate = %dus",newRefreshRate); - currentLag -= newRefreshRate - refreshRate; + TRACE("[SYNC] mod rate = %dus; lag = %dus",newRefreshRate,currentLag); + return (uint16_t)newRefreshRate; } From ef3b669d38c1742b95cc0327935fba244ff22e94 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 11:16:00 +0200 Subject: [PATCH 005/300] fixed Ghost sync feedback --- radio/src/telemetry/ghost.cpp | 13 +++++++++++++ radio/src/telemetry/ghost.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 098e9c990..2095a3710 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -132,6 +132,19 @@ void processGhostTelemetryFrame() uint8_t id = telemetryRxBuffer[2]; switch(id) { + case GHST_DL_OPENTX_SYNC: + { + uint32_t update_interval = getTelemetryValue_s32(3); + int32_t offset = getTelemetryValue_s32(7); + + // values are in units of 100ns + update_interval /= 10; + offset /= 10; + + getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset + SAFE_SYNC_LAG); + } + break; + case GHST_DL_LINK_STAT: { #if defined(BLUETOOTH) diff --git a/radio/src/telemetry/ghost.h b/radio/src/telemetry/ghost.h index 0a3195835..9cdd48a39 100755 --- a/radio/src/telemetry/ghost.h +++ b/radio/src/telemetry/ghost.h @@ -113,7 +113,7 @@ enum GhostTelemetryBaudrates }; #endif #define GHOST_BAUDRATE 400000 -#define GHOST_PERIOD 4 +#define GHOST_PERIOD 4000 /* us */ enum GhostLineFlags { From 92f27a853ae16c194ccc302bc6b2a4a03e5be748 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 12:07:09 +0200 Subject: [PATCH 006/300] moved sync pulse sending to *before* periodic mixer processing This should enable a more stable pulse sending to the module. --- radio/src/opentx.cpp | 13 +++++++++++++ radio/src/opentx.h | 1 + radio/src/tasks.cpp | 11 ++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 73b39c407..bebb5b317 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1426,6 +1426,19 @@ void doMixerCalculations() DEBUG_TIMER_START(debugTimerEvalMixes); evalMixes(tick10ms); DEBUG_TIMER_STOP(debugTimerEvalMixes); +} + +void doMixerPeriodicUpdates() +{ + static tmr10ms_t lastTMR = 0; + + tmr10ms_t tmr10ms = get_tmr10ms(); + + uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); + // handle tick10ms overrun + // correct overflow handling costs a lot of code; happens only each 11 min; + // therefore forget the exact calculation and use only 1 instead; good compromise + lastTMR = tmr10ms; DEBUG_TIMER_START(debugTimerMixes10ms); if (tick10ms) { diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 394a0734f..6d028ff06 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -483,6 +483,7 @@ extern uint32_t nextMixerTime[NUM_MODULES]; void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms); void evalMixes(uint8_t tick10ms); void doMixerCalculations(); +void doMixerPeriodicUpdates(); void checkTrims(); extern uint8_t currentBacklightBright; diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index e4c209edd..431ed7e52 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -168,12 +168,20 @@ TASK_FUNCTION(mixerTask) DEBUG_TIMER_START(debugTimerMixer); RTOS_LOCK_MUTEX(mixerMutex); + doMixerCalculations(); + + // TODO: fix runMask + sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE)); + + doMixerPeriodicUpdates(); + DEBUG_TIMER_START(debugTimerMixerCalcToUsage); DEBUG_TIMER_SAMPLE(debugTimerMixerIterval); RTOS_UNLOCK_MUTEX(mixerMutex); DEBUG_TIMER_STOP(debugTimerMixer); + #if defined(STM32) && !defined(SIMU) if (getSelectedUsbMode() == USB_JOYSTICK_MODE) { usbJoystickUpdate(); @@ -202,9 +210,6 @@ TASK_FUNCTION(mixerTask) // between protocols with multi-proto RF if (timeout) serialPrint("mix sched timeout!"); - - // TODO: fix runMask - sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE)); } } } From dd4c91ce4f86b6018ea1b8af400e591c7697f3f3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 12:17:05 +0200 Subject: [PATCH 007/300] removed SAFE_SYNC_LAG --- radio/src/telemetry/crossfire.cpp | 2 +- radio/src/telemetry/ghost.cpp | 2 +- radio/src/telemetry/telemetry.cpp | 2 +- radio/src/telemetry/telemetry.h | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/radio/src/telemetry/crossfire.cpp b/radio/src/telemetry/crossfire.cpp index 672dd56f0..173ea38f4 100644 --- a/radio/src/telemetry/crossfire.cpp +++ b/radio/src/telemetry/crossfire.cpp @@ -198,7 +198,7 @@ void processCrossfireTelemetryFrame() offset /= 10; TRACE("[XF] Rate: %d, Lag: %d", update_interval, offset); - getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset + SAFE_SYNC_LAG); + getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset); } } break; diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 2095a3710..1a3cef9d4 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -141,7 +141,7 @@ void processGhostTelemetryFrame() update_interval /= 10; offset /= 10; - getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset + SAFE_SYNC_LAG); + getModuleSyncStatus(EXTERNAL_MODULE).update(update_interval, offset); } break; diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 44906b9d7..03878eee6 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -421,7 +421,7 @@ void ModuleSyncStatus::update(uint16_t newRefreshRate, uint16_t newInputLag) uint16_t ModuleSyncStatus::getAdjustedRefreshRate() { - int16_t lag = currentLag - SAFE_SYNC_LAG; + int16_t lag = currentLag; int32_t newRefreshRate = refreshRate; if (lag == 0) { diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 18f4f98a6..28f66da2c 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -290,8 +290,6 @@ extern Fifo * luaInputTelemetryFifo; void processPXX2Frame(uint8_t module, const uint8_t *frame); // Module pulse synchronization -#define SAFE_SYNC_LAG 800 /* us */ - struct ModuleSyncStatus { // feedback input: last received values From 931743af44b8cc266691776d0c5813db7abdd104 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 15:44:20 +0200 Subject: [PATCH 008/300] init_intmodule_heartbeat --- radio/src/pulses/pulses.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index d21c58853..3fc1868ac 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -411,6 +411,7 @@ static void enablePulsesInternalModule(uint8_t protocol) intmodulePxx1PulsesStart(); #if defined(INTMODULE_HEARTBEAT) // use backup trigger (1 ms later) + init_intmodule_heartbeat(); mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000/*us*/); #else mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); @@ -423,6 +424,7 @@ static void enablePulsesInternalModule(uint8_t protocol) intmodulePxx1SerialStart(); #if defined(INTMODULE_HEARTBEAT) // use backup trigger (1 ms later) + init_intmodule_heartbeat(); mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD + 1000/*us*/); #else mixerSchedulerSetPeriod(INTERNAL_MODULE, INTMODULE_PXX1_SERIAL_PERIOD); @@ -437,6 +439,7 @@ static void enablePulsesInternalModule(uint8_t protocol) #if defined(INTMODULE_HEARTBEAT) // use backup trigger (1 ms later) + init_intmodule_heartbeat(); mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD + 1000/*us*/); #else mixerSchedulerSetPeriod(INTERNAL_MODULE, PXX2_PERIOD); From e00224fea192e133898d887c6c1cf7f325c935b1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 19:19:23 +0200 Subject: [PATCH 009/300] fix SBUS rate adjustment --- radio/src/pulses/pulses.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 3fc1868ac..f6bb81176 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -333,6 +333,9 @@ bool setupPulsesExternalModule(uint8_t protocol) #if defined(SBUS) case PROTOCOL_CHANNELS_SBUS: setupPulsesSbus(); + + // SBUS_PERIOD is not a constant! It can be set from UI + mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD); return true; #endif From e2fb0522cd8e473457676977b660afc1f00bda7e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 22:33:03 +0200 Subject: [PATCH 010/300] fixed pxx1 driver --- radio/src/pulses/pxx.h | 2 +- .../src/targets/taranis/extmodule_driver.cpp | 32 ++++++++++++------- radio/src/tasks.cpp | 2 ++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/radio/src/pulses/pxx.h b/radio/src/pulses/pxx.h index ae84d77b2..b9536d723 100644 --- a/radio/src/pulses/pxx.h +++ b/radio/src/pulses/pxx.h @@ -108,7 +108,7 @@ class PwmPxxBitTransport: public PulsesBuffer { void addTail() { // rest min value is 18000 - 200 * 48 = 8400 (4.2ms) - *(ptr - 1) += rest; + *(ptr - 1) = 60000;//+= rest; } }; diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 7a7927a41..b8139ad6a 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -98,7 +98,7 @@ void extmoduleSerialStart() GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; - EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS from 30MHz + EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2 MHz) EXTMODULE_TIMER->CCR3 = 0; EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | EXTMODULE_TIMER_OUTPUT_POLARITY; @@ -215,16 +215,17 @@ void extmodulePxx1PulsesStart() EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + + EXTMODULE_TIMER->CCR3 = 0; EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | EXTMODULE_TIMER_OUTPUT_POLARITY; // polarity, default low EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs EXTMODULE_TIMER->CCR1 = 18; EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high EXTMODULE_TIMER->EGR = 1; // Restart - EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; - EXTMODULE_TIMER->ARR = 45000; - EXTMODULE_TIMER->CCR2 = 40000; // The first frame will be sent in 20ms + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // 0 TOGGLE / 2 PWM1 + EXTMODULE_TIMER->ARR = 40000; EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_UDE | TIM_DIER_CC2IE; + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(EXTMODULE_TIMER_DMA_STREAM_IRQn); @@ -258,13 +259,23 @@ void extmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: - EXTMODULE_TIMER->CCR2 = extmodulePulsesData.pxx.getLast() - 4000; // 2mS in advance + + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + + // disable timer + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(extmodulePulsesData.pxx.getData()); EXTMODULE_TIMER_DMA_STREAM->NDTR = extmodulePulsesData.pxx.getSize(); EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = 1; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; #endif @@ -389,11 +400,10 @@ extern "C" void EXTMODULE_TIMER_DMA_STREAM_IRQHandler() DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); switch (moduleState[EXTERNAL_MODULE].protocol) { - case PROTOCOL_CHANNELS_PXX1_PULSES: - case PROTOCOL_CHANNELS_PPM: - EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt - break; + case PROTOCOL_CHANNELS_PPM: + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + break; } } diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 431ed7e52..8346b2ceb 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -91,6 +91,8 @@ bool isModuleSynchronous(uint8_t moduleIdx) #if defined(INTMODULE_USART) || defined(EXTMODULE_USART) case PROTOCOL_CHANNELS_PXX1_SERIAL: #endif + // case PROTOCOL_CHANNELS_PPM: + case PROTOCOL_CHANNELS_PXX1_PULSES: #if defined(DSM2) case PROTOCOL_CHANNELS_SBUS: case PROTOCOL_CHANNELS_DSM2_LP45: From 47f38dc5b79d35218047d52417704655a143d9ec Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 23:04:43 +0200 Subject: [PATCH 011/300] potential fix for internal PXX1 --- .../taranis/intmodule_pulses_driver.cpp | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/radio/src/targets/taranis/intmodule_pulses_driver.cpp b/radio/src/targets/taranis/intmodule_pulses_driver.cpp index e71768eac..7b8c40578 100644 --- a/radio/src/targets/taranis/intmodule_pulses_driver.cpp +++ b/radio/src/targets/taranis/intmodule_pulses_driver.cpp @@ -42,21 +42,31 @@ void intmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: { - uint32_t last = intmodulePulsesData.pxx.getLast(); - if (heartbeatCapture.valid) { - if (getTmr2MHz() - heartbeatCapture.timestamp > HEARBEAT_OFFSET) - last -= 21; - else - last += 19; - intmodulePulsesData.pxx.setLast(last); - } - INTMODULE_TIMER->CCR2 = last - 4000; // 2mS in advance + // uint32_t last = intmodulePulsesData.pxx.getLast(); + // if (heartbeatCapture.valid) { + // if (getTmr2MHz() - heartbeatCapture.timestamp > HEARBEAT_OFFSET) + // last -= 21; + // else + // last += 19; + // intmodulePulsesData.pxx.setLast(last); + // } + if (INTMODULE_DMA_STREAM->CR & DMA_SxCR_EN) + return; + + //disable timer + INTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + + //INTMODULE_TIMER->CCR2 = last - 4000; // 2mS in advance INTMODULE_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA INTMODULE_DMA_STREAM->CR |= INTMODULE_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; INTMODULE_DMA_STREAM->PAR = CONVERT_PTR_UINT(&INTMODULE_TIMER->ARR); INTMODULE_DMA_STREAM->M0AR = CONVERT_PTR_UINT(intmodulePulsesData.pxx.getData()); INTMODULE_DMA_STREAM->NDTR = intmodulePulsesData.pxx.getSize(); INTMODULE_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = 1; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; } #endif @@ -97,17 +107,16 @@ void intmodulePxx1PulsesStart() INTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; INTMODULE_TIMER->PSC = INTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) - INTMODULE_TIMER->ARR = 18000; + INTMODULE_TIMER->CCER = TIM_CCER_CC3E | TIM_CCER_CC3NE; INTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs INTMODULE_TIMER->CCR3 = 16; INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_0; // Force O/P high INTMODULE_TIMER->EGR = 1; // Restart - INTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; + INTMODULE_TIMER->ARR = 40000; INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - INTMODULE_TIMER->CCR2 = 16000; // The first frame will be sent in 20ms - INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + INTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update INTMODULE_TIMER->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(INTMODULE_DMA_STREAM_IRQn); @@ -157,8 +166,12 @@ extern "C" void INTMODULE_DMA_STREAM_IRQHandler() DMA_ClearITPendingBit(INTMODULE_DMA_STREAM, INTMODULE_DMA_FLAG_TC); - INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + switch (moduleState[INTERNAL_MODULE].protocol) { + case PROTOCOL_CHANNELS_PPM: + INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + break; + } } extern "C" void INTMODULE_TIMER_CC_IRQHandler() From 02c7d5004ab9e2f007efff05bba1a5ec9a5fbc6b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Sep 2020 23:04:43 +0200 Subject: [PATCH 012/300] fixed internal PXX1 re-enable for DMA request --- radio/src/targets/taranis/intmodule_pulses_driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/taranis/intmodule_pulses_driver.cpp b/radio/src/targets/taranis/intmodule_pulses_driver.cpp index 7b8c40578..9dad13fd1 100644 --- a/radio/src/targets/taranis/intmodule_pulses_driver.cpp +++ b/radio/src/targets/taranis/intmodule_pulses_driver.cpp @@ -65,8 +65,8 @@ void intmoduleSendNextFrame() INTMODULE_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA // re-init timer - EXTMODULE_TIMER->EGR = 1; - EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; + INTMODULE_TIMER->EGR = 1; + INTMODULE_TIMER->CR1 |= TIM_CR1_CEN; break; } #endif From 3f3747c0c75856046d7ede0b53412766a46a2cbc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Sep 2020 21:00:19 +0200 Subject: [PATCH 013/300] pxx1 internal module: add pre-load register enable (required for pwm mode) --- radio/src/targets/taranis/intmodule_pulses_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/taranis/intmodule_pulses_driver.cpp b/radio/src/targets/taranis/intmodule_pulses_driver.cpp index 9dad13fd1..588e1a448 100644 --- a/radio/src/targets/taranis/intmodule_pulses_driver.cpp +++ b/radio/src/targets/taranis/intmodule_pulses_driver.cpp @@ -113,7 +113,7 @@ void intmodulePxx1PulsesStart() INTMODULE_TIMER->CCR3 = 16; INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_0; // Force O/P high INTMODULE_TIMER->EGR = 1; // Restart - INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; + INTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3PE; INTMODULE_TIMER->ARR = 40000; INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag INTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update From 3c98a9907eeb12b80b80e0cdf589fcbcd43fb700 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Sep 2020 11:10:36 +0200 Subject: [PATCH 014/300] fixed extmodule serial polarity --- radio/src/targets/taranis/extmodule_driver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index b8139ad6a..4bc47698e 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -321,13 +321,13 @@ void extmoduleSendNextFrame() if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) return; + // disable timer + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + if (PROTOCOL_CHANNELS_SBUS == moduleState[EXTERNAL_MODULE].protocol) { EXTMODULE_TIMER->CCER = EXTMODULE_TIMER_OUTPUT_ENABLE | (GET_SBUS_POLARITY(EXTERNAL_MODULE) ? EXTMODULE_TIMER_OUTPUT_POLARITY : 0); // reverse polarity for Sbus if needed } - // disable timer - EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; - // send DMA request EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; From 87b63402fd2772285868de3f5fc6bc34261e86ae Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Sep 2020 13:19:59 +0200 Subject: [PATCH 015/300] set minimum period to 1750us This should allow 500Hz refresh rate + some headroom for sync. --- radio/src/mixer_scheduler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/mixer_scheduler.h b/radio/src/mixer_scheduler.h index ccded9a5e..601f3fcd7 100644 --- a/radio/src/mixer_scheduler.h +++ b/radio/src/mixer_scheduler.h @@ -23,7 +23,7 @@ #define MIXER_SCHEDULER_DEFAULT_PERIOD_US 4000u // 4ms -#define MIN_REFRESH_RATE 4000 /* us */ +#define MIN_REFRESH_RATE 1750 /* us */ #define MAX_REFRESH_RATE 25000 /* us */ #if !defined(SIMU) From 1c33efac9f6e68fd00989ec7e5cc8966a71c8969 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Sep 2020 21:59:48 +0200 Subject: [PATCH 016/300] fixed AFHDS3 taranis external module --- radio/src/pulses/afhds3.h | 13 +------------ radio/src/targets/taranis/extmodule_driver.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/radio/src/pulses/afhds3.h b/radio/src/pulses/afhds3.h index fab057854..5eef56a4b 100644 --- a/radio/src/pulses/afhds3.h +++ b/radio/src/pulses/afhds3.h @@ -94,9 +94,6 @@ struct Data void reset() { -#if !(defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO)) - total = 0; -#endif pulsesSize = 0; } #if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) @@ -118,7 +115,6 @@ struct Data return; } pulses[pulsesSize++] = v; - total += v; } void sendByte(uint8_t b) { @@ -149,14 +145,7 @@ struct Data //add remaining time of frame void flush() { - uint16_t diff = AFHDS3_FRAME_HALF_US - total; - pulses[pulsesSize - 1] += diff; - //ensure 2 ms break - if (pulses[pulsesSize - 1] < 4000) - { - pulses[pulsesSize - 1] = 4050; - } - total += diff; + pulses[pulsesSize - 1] = 60000; } const uint16_t* getData() diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index 4bc47698e..b856cb309 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -297,15 +297,21 @@ void extmoduleSendNextFrame() #if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) extmoduleSendBuffer(extmodulePulsesData.afhds3.getData(), extmodulePulsesData.afhds3.getSize()); #else + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + const uint16_t* dataPtr = extmodulePulsesData.afhds3.getData(); uint32_t dataSize = extmodulePulsesData.afhds3.getSize(); - EXTMODULE_TIMER->CCR2 = dataPtr[dataSize -1] - 4000; // 2mS in advance EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(dataPtr); EXTMODULE_TIMER_DMA_STREAM->NDTR = dataSize; EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = TIM_PSCReloadMode_Immediate; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; #endif } break; From d10b16d4c69c3099f84e9cdc238dd3b5a690c2ed Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 20 Dec 2020 14:14:21 +1100 Subject: [PATCH 017/300] Dynamic ui data models continued --- .../src/datamodels/rawitemdatamodels.cpp | 269 +++++++++++++++++- companion/src/datamodels/rawitemdatamodels.h | 86 +++++- .../src/firmwares/adjustmentreference.cpp | 7 +- companion/src/firmwares/adjustmentreference.h | 1 + companion/src/firmwares/curvereference.cpp | 6 + companion/src/firmwares/curvereference.h | 1 + .../src/firmwares/customfunctiondata.cpp | 97 +++++-- companion/src/firmwares/customfunctiondata.h | 26 +- companion/src/firmwares/modeldata.cpp | 39 +++ companion/src/firmwares/modeldata.h | 4 + companion/src/firmwares/rawsource.cpp | 32 +-- companion/src/firmwares/rawsource.h | 3 + companion/src/firmwares/sensordata.cpp | 38 +++ companion/src/firmwares/sensordata.h | 3 + 14 files changed, 557 insertions(+), 55 deletions(-) diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/rawitemdatamodels.cpp index d7c1b6682..3805b98d6 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/rawitemdatamodels.cpp @@ -22,6 +22,7 @@ #include "generalsettings.h" #include "eeprominterface.h" #include "modeldata.h" +#include "adjustmentreference.h" RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : AbstractRawItemDataModel(generalSettings, modelData, parent) @@ -187,28 +188,28 @@ void RawSwitchItemModel::update() CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : AbstractRawItemDataModel(generalSettings, modelData, parent) { + if (!modelData) + return; + const int count = getCurrentFirmware()->getCapability(NumCurves); for (int i = -count ; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); - int flags; - if (i < 0) - flags = DataGroups::NegativeGroup; - else if (i > 0) - flags = DataGroups::PositiveGroup; - else - flags = DataGroups::NoneGroup; + int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; + if (i == 0) + flags |= DataGroups::NoneGroup; modelItem->setData(flags, ItemFlagsRole); setDynamicItemData(modelItem, i); appendRow(modelItem); } } -void CurveItemModel::setDynamicItemData(QStandardItem * item, int index) const +void CurveItemModel::setDynamicItemData(QStandardItem * item, const int index) const { - item->setText(CurveReference(CurveReference::CURVE_REF_CUSTOM, index).toString(modelData, false)); - item->setData(true, IsAvailableRole); + CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, index); + item->setText(cr.toString(modelData, false)); + item->setData(cr.isAvailable(), IsAvailableRole); } void CurveItemModel::update() @@ -221,16 +222,228 @@ void CurveItemModel::update() emit dataUpdateComplete(); } + // -// CommonItemModels +// GVarReferenceItemModel +// + +GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + if (!modelData) + return; + + const int count = getCurrentFirmware()->getCapability(Gvars); + if (count > 0) { + addItems(-count); + addItems(count); + } +} + +void GVarReferenceItemModel::addItems(int count) +{ + int i = (count < 0 ? count : 1); + count = (i < 0 ? 0 : count + i); + for ( ; i < count; ++i) { + QStandardItem * modelItem = new QStandardItem(); + AdjustmentReference ar(AdjustmentReference::ADJUST_REF_GVAR, i); + modelItem->setData(ar.toValue(), ItemIdRole); + modelItem->setData(i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup, ItemFlagsRole); + setDynamicItemData(modelItem, ar); + appendRow(modelItem); + } +} + +void GVarReferenceItemModel::setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const +{ + item->setText(ar.toString(modelData, false)); + item->setData(ar.isAvailable(), IsAvailableRole); +} + +void GVarReferenceItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); + + emit dataUpdateComplete(); +} + + +// +// ThrottleSourceItemModel +// + +ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + if (!modelData) + return; + + for (int i = 0; i < modelData->thrTraceSrcCount(); i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + item->setText(modelData->thrTraceSrcToString(index)); + item->setData(modelData->isThrTraceSrcAvailable(generalSettings, index), IsAvailableRole); +} + +void ThrottleSourceItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// CustomFuncActionItemModel +// + +CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + for (int i = 0; i < AssignFunc::FuncCount; i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + modelItem->setData(CustomFunctionData::funcContext(i), ItemFlagsRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + item->setText(CustomFunctionData(AssignFunc(index)).funcToString(modelData)); + item->setData(CustomFunctionData::isFuncAvailable(index), IsAvailableRole); +} + +void CustomFuncActionItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// CustomFuncResetParamItemModel +// + +CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); + cfd.param = index; + item->setText(cfd.paramToString(modelData)); + item->setData(CustomFunctionData::isResetParamAvailable(modelData, index), IsAvailableRole); +} + +void CustomFuncResetParamItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// TelemetrySourceItemModel +// + +TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : + AbstractRawItemDataModel(generalSettings, modelData, parent) +{ + if (!modelData) + return; + + const int count = getCurrentFirmware()->getCapability(Sensors); + + for (int i = -count + 1; i < count; ++i) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, ItemIdRole); + int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; + if (i == 0) + flags |= DataGroups::NoneGroup; + modelItem->setData(flags, ItemFlagsRole); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +{ + item->setText(SensorData::sourceToString(modelData, index)); + item->setData(SensorData::isSourceAvailable(modelData, index), IsAvailableRole); +} + +void TelemetrySourceItemModel::update() +{ + emit dataAboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + + emit dataUpdateComplete(); +} + + +// +// CommonItemModels TODO rename to DynamicDataModels // CommonItemModels::CommonItemModels(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : QObject(parent) { m_rawSourceItemModel = new RawSourceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("raw source"), qobject_cast(m_rawSourceItemModel)); + m_rawSwitchItemModel = new RawSwitchItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("raw switch"), qobject_cast(m_rawSwitchItemModel)); + m_curveItemModel = new CurveItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("curve"), qobject_cast(m_curveItemModel)); + + m_gvarReferenceItemModel = new GVarReferenceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("gvar ref"), qobject_cast(m_gvarReferenceItemModel)); + + m_throttleSourceItemModel = new ThrottleSourceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("thr source"), qobject_cast(m_throttleSourceItemModel)); + + m_customFuncActionItemModel = new CustomFuncActionItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("cf action"), qobject_cast(m_customFuncActionItemModel)); + + m_customFuncResetParamItemModel = new CustomFuncResetParamItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("cf reset param"), qobject_cast(m_customFuncResetParamItemModel)); + + m_telemetrySourceItemModel = new TelemetrySourceItemModel(generalSettings, modelData, parent); + //dumpModelContents(QString("tele source"), qobject_cast(m_telemetrySourceItemModel)); } CommonItemModels::~CommonItemModels() @@ -241,24 +454,54 @@ void CommonItemModels::update(const RadioModelObjects radioModelObjects) { switch (radioModelObjects) { case RMO_CHANNELS: + m_throttleSourceItemModel->update(); + // no break + case RMO_TIMERS: + m_customFuncActionItemModel->update(); + // no break case RMO_INPUTS: case RMO_TELEMETRY_SENSORS: - case RMO_TIMERS: m_rawSourceItemModel->update(); + m_customFuncResetParamItemModel->update(); + m_telemetrySourceItemModel->update(); break; - case RMO_FLIGHT_MODES: + case RMO_GLOBAL_VARIABLES: + m_gvarReferenceItemModel->update(); + m_customFuncActionItemModel->update(); + // no break + case RMO_FLIGHT_MODES: case RMO_LOGICAL_SWITCHES: m_rawSourceItemModel->update(); m_rawSwitchItemModel->update(); break; + case RMO_CURVES: m_curveItemModel->update(); break; + case RMO_SCRIPTS: // no need to refresh break; + default: qDebug() << "Unknown RadioModelObject:" << radioModelObjects; } } + + +void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel) +{ + if (dataModel) { + qDebug() << "model:" << desc << "rows:" << dataModel->rowCount(); + for (int i = 0; i < dataModel->rowCount(); ++i) { + qDebug() << "row:" << i << "text:" << dataModel->item(i)->data(Qt::DisplayRole).toString() + << "id:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemIdRole).toInt() + << "avail:" << dataModel->item(i)->data(AbstractRawItemDataModel::IsAvailableRole).toBool() + << "flags:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemFlagsRole).toInt() + << "type:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemTypeRole).toInt(); + } + } + else + qDebug() << "model:" << desc << "did not cast"; +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/rawitemdatamodels.h index 48cb55fd2..fbcfe8be9 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/rawitemdatamodels.h @@ -28,6 +28,7 @@ class GeneralSettings; class ModelData; +class AdjustmentReference; class AbstractRawItemDataModel: public QStandardItemModel { @@ -102,7 +103,78 @@ class CurveItemModel: public AbstractRawItemDataModel void update() override; protected: - void setDynamicItemData(QStandardItem * item, int index) const; + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class GVarReferenceItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; + void addItems(int count); +}; + + +class ThrottleSourceItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class CustomFuncActionItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class CustomFuncResetParamItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; +}; + + +class TelemetrySourceItemModel: public AbstractRawItemDataModel +{ + Q_OBJECT + public: + explicit TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + + public slots: + void update() override; + + protected: + void setDynamicItemData(QStandardItem * item, const int index) const; }; @@ -130,11 +202,23 @@ class CommonItemModels: public QObject RawSourceItemModel * rawSourceItemModel() const { return m_rawSourceItemModel; } RawSwitchItemModel * rawSwitchItemModel() const { return m_rawSwitchItemModel; } CurveItemModel * curveItemModel() const { return m_curveItemModel; } + GVarReferenceItemModel * gvarItemModel() const { return m_gvarReferenceItemModel; } + ThrottleSourceItemModel * throttleSourceItemModel() const { return m_throttleSourceItemModel; } + CustomFuncActionItemModel * customFuncActionItemModel() const { return m_customFuncActionItemModel; } + CustomFuncResetParamItemModel * customFuncResetParamItemModel() const { return m_customFuncResetParamItemModel; } + TelemetrySourceItemModel * telemetrySourceItemModel() const { return m_telemetrySourceItemModel; } private: RawSourceItemModel *m_rawSourceItemModel; RawSwitchItemModel *m_rawSwitchItemModel; CurveItemModel *m_curveItemModel; + GVarReferenceItemModel *m_gvarReferenceItemModel; + ThrottleSourceItemModel *m_throttleSourceItemModel; + CustomFuncActionItemModel *m_customFuncActionItemModel; + CustomFuncResetParamItemModel *m_customFuncResetParamItemModel; + TelemetrySourceItemModel *m_telemetrySourceItemModel; }; +void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel); + #endif // RAWITEMDATAMODELS_H diff --git a/companion/src/firmwares/adjustmentreference.cpp b/companion/src/firmwares/adjustmentreference.cpp index 7181e15dd..ae487effa 100644 --- a/companion/src/firmwares/adjustmentreference.cpp +++ b/companion/src/firmwares/adjustmentreference.cpp @@ -19,7 +19,7 @@ */ #include "adjustmentreference.h" -#include "radiodata.h" // for ModelData +#include "modeldata.h" AdjustmentReference::AdjustmentReference(int value) { @@ -76,3 +76,8 @@ QString AdjustmentReference::toString(const ModelData * model, const bool sign) } return ret; } + +const bool AdjustmentReference::isAvailable() const +{ + return true; +} diff --git a/companion/src/firmwares/adjustmentreference.h b/companion/src/firmwares/adjustmentreference.h index 2a7b7e141..9377cd5e8 100644 --- a/companion/src/firmwares/adjustmentreference.h +++ b/companion/src/firmwares/adjustmentreference.h @@ -45,6 +45,7 @@ class AdjustmentReference { bool isSet() const { return type != ADJUST_REF_VALUE || value != 0; } bool isValid(const int value) const; QString toString(const ModelData * model = nullptr, const bool sign = false) const; + const bool isAvailable() const; inline const int toValue() const { diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 449dc0266..6ee2e38c5 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -63,6 +63,11 @@ const bool CurveReference::isValueNumber() const return (type == CURVE_REF_DIFF || type == CURVE_REF_EXPO) && AdjustmentReference(value).type == AdjustmentReference::ADJUST_REF_VALUE; } +const bool CurveReference::isAvailable() const +{ + return true; +} + // static int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) { @@ -101,6 +106,7 @@ QString CurveReference::functionToString(const int value) } /* + * CurveReferenceUIManager */ diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 0a1c7a180..018fb12aa 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -63,6 +63,7 @@ class CurveReference { const bool isValueNumber() const; const bool isValueReference() const { return !isValueNumber(); } const QString toString(const ModelData * model = nullptr, bool verbose = true) const; + const bool isAvailable() const; CurveRefType type; int value; diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index d6918e27c..d4942c60c 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -38,12 +38,12 @@ bool CustomFunctionData::isEmpty() const QString CustomFunctionData::nameToString(int index, bool globalContext) const { - return RadioData::getElementName((globalContext ? tr("GF") : tr("SF")), index+1, 0, true); + return RadioData::getElementName((globalContext ? tr("GF") : tr("SF")), index + 1, 0, true); } QString CustomFunctionData::funcToString(const ModelData * model) const { - if (func >= FuncOverrideCH1 && func <= FuncOverrideCH32) + if (func >= FuncOverrideCH1 && func <= FuncOverrideCHLast) return tr("Override %1").arg(RawSource(SOURCE_TYPE_CH, func).toString(model)); else if (func == FuncTrainer) return tr("Trainer Sticks"); @@ -65,8 +65,8 @@ QString CustomFunctionData::funcToString(const ModelData * model) const return tr("Haptic"); else if (func == FuncReset) return tr("Reset"); - else if (func >= FuncSetTimer1 && func <= FuncSetTimer3) - return tr("Set Timer %1").arg(func-FuncSetTimer1+1); + else if (func >= FuncSetTimer1 && func <= FuncSetTimerLast) + return tr("Set %1").arg(RawSource(SOURCE_TYPE_SPECIAL, SOURCE_TYPE_SPECIAL_TIMER1_IDX + func - FuncSetTimer1).toString(model)); else if (func == FuncVario) return tr("Vario"); else if (func == FuncPlayPrompt) @@ -90,7 +90,7 @@ QString CustomFunctionData::funcToString(const ModelData * model) const else if (func == FuncBackgroundMusicPause) return tr("Background Music Pause"); else if (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) - return tr("Adjust %1").arg(RawSource(SOURCE_TYPE_GVAR, func-FuncAdjustGV1).toString(model)); + return tr("Adjust %1").arg(RawSource(SOURCE_TYPE_GVAR, func - FuncAdjustGV1).toString(model)); else if (func == FuncSetFailsafe) return tr("Set Failsafe"); else if (func == FuncRangeCheckInternalModule) @@ -102,7 +102,7 @@ QString CustomFunctionData::funcToString(const ModelData * model) const else if (func == FuncBindExternalModule) return tr("Bind Ext. Module"); else { - return QString("???"); // Highlight unknown functions with output of question marks.(BTW should not happen that we do not know what a function is) + return QString(CPN_STR_UNKNOWN_ITEM); } } @@ -111,11 +111,16 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox int val = 0; Firmware * firmware = Firmware::getCurrentVariant(); - b->addItem(tr("Timer1"), val++); - b->addItem(tr("Timer2"), val++); - b->addItem( tr("Timer3"), val++); + for (int i = 0; i < CPN_MAX_TIMERS; i++, val++) { + if (i < firmware->getCapability(Timers)) { + RawSource item = RawSource(SOURCE_TYPE_SPECIAL, i + SOURCE_TYPE_SPECIAL_TIMER1_IDX); + b->addItem(item.toString(model), val); + } + } + b->addItem(tr("Flight"), val++); b->addItem(tr("Telemetry"), val++); + int reCount = firmware->getCapability(RotaryEncoders); if (reCount == 1) { b->addItem(tr("Rotary Encoder"), val++); @@ -124,6 +129,7 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox b->addItem(tr("REa"), val++); b->addItem(tr("REb"), val++); } + if (model) { for (int i = 0; i < firmware->getCapability(Sensors); ++i) { if (model->sensorData[i].isAvailable()) { @@ -132,6 +138,7 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox } } } + b->setCurrentIndex(b->findData(value)); } @@ -153,18 +160,18 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return QString("%1").arg(param); } else if (func == FuncLogs) { - return QString("%1").arg(param/10.0) + tr("s"); + return QString("%1").arg(param / 10.0) + tr("s"); } else if (func == FuncPlaySound) { CustomFunctionData::populatePlaySoundParams(qs); - if (param>=0 && param<(int)qs.count()) + if (param >= 0 && param < (int)qs.count()) return qs.at(param); else return tr("Inconsistent parameter"); } else if (func == FuncPlayHaptic) { CustomFunctionData::populateHapticParams(qs); - if (param>=0 && param<(int)qs.count()) + if (param >= 0 && param < (int)qs.count()) return qs.at(param); else return tr("Inconsistent parameter"); @@ -193,7 +200,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const else if (func >= FuncAdjustGV1 && func < FuncCount) { switch (adjustMode) { case FUNC_ADJUST_GVAR_CONSTANT: - return tr("Value ")+QString("%1").arg(param); + return tr("Value ") + QString("%1").arg(param); case FUNC_ADJUST_GVAR_SOURCE: case FUNC_ADJUST_GVAR_GVAR: return RawSource(param).toString(); @@ -218,16 +225,16 @@ QString CustomFunctionData::repeatToString() const } else { unsigned int step = 1; - return tr("repeat(%1s)").arg(step*repeatParam); + return tr("repeat(%1s)").arg(step * repeatParam); } } QString CustomFunctionData::enabledToString() const { - if ((func >= FuncOverrideCH1 && func <= FuncOverrideCH32) || + if ((func >= FuncOverrideCH1 && func <= FuncOverrideCHLast) || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) || (func == FuncReset) || - (func >= FuncSetTimer1 && func <= FuncSetTimer2) || + (func >= FuncSetTimer1 && func <= FuncSetTimerLast) || (func == FuncVolume) || (func == FuncBacklight) || (func <= FuncInstantTrim)) { @@ -238,6 +245,64 @@ QString CustomFunctionData::enabledToString() const return ""; } +// static +bool CustomFunctionData::isFuncAvailable(int index) +{ + Firmware * fw = getCurrentFirmware(); + + bool ret = (((index >= FuncOverrideCH1 && index <= FuncOverrideCHLast) && !fw->getCapability(SafetyChannelCustomFunction)) || + ((index == FuncVolume || index == FuncBackgroundMusic || index == FuncBackgroundMusicPause) && !fw->getCapability(HasVolume)) || + ((index == FuncPlayScript && !IS_HORUS_OR_TARANIS(fw->getBoard()))) || + ((index == FuncPlayHaptic) && !fw->getCapability(Haptic)) || + ((index == FuncPlayBoth) && !fw->getCapability(HasBeeper)) || + ((index == FuncLogs) && !fw->getCapability(HasSDLogs)) || + ((index >= FuncSetTimer1 && index <= FuncSetTimerLast) && index > FuncSetTimer1 + fw->getCapability(Timers)) || + ((index == FuncScreenshot) && !IS_HORUS_OR_TARANIS(fw->getBoard())) || + ((index >= FuncRangeCheckInternalModule && index <= FuncBindExternalModule) && !fw->getCapability(DangerousFunctions)) || + ((index >= FuncAdjustGV1 && index <= FuncAdjustGVLast) && !fw->getCapability(Gvars)) + ); + return !ret; +} + +// static +int CustomFunctionData::funcContext(int index) +{ + int ret = AllFunctionContexts; + + if ((index >= FuncOverrideCH1 && index <= FuncOverrideCHLast) || + (index >= FuncRangeCheckInternalModule && index <= FuncBindExternalModule) || + (index >= FuncAdjustGV1 && index <= FuncAdjustGVLast)) + ret &= ~GlobalFunctionsContext; + + return ret; +} +// static +int CustomFunctionData::resetParamCount(const ModelData * model) +{ + QComboBox cb; + CustomFunctionData::populateResetParams(model, &cb); + return cb.count(); +} + +// static +bool CustomFunctionData::isResetParamAvailable(const ModelData * model, int index) +{ + Firmware * firmware = getCurrentFirmware(); + + if (index < CPN_MAX_TIMERS) { + if (index < firmware->getCapability(Timers)) + return true; + else + return false; + } + else if (index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders)) + return true; + else if (model && index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders) + firmware->getCapability(Sensors)) + return model->sensorData[index - CPN_MAX_TIMERS - firmware->getCapability(RotaryEncoders)].isAvailable(); + + return false; +} + void CustomFunctionData::convert(RadioDataConversionState & cstate) { cstate.setComponent(tr("CFN"), 8); diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 93f9969e8..9e597441f 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -35,7 +35,8 @@ class RadioDataConversionState; enum AssignFunc { FuncOverrideCH1 = 0, - FuncOverrideCH32 = FuncOverrideCH1+CPN_MAX_CHNOUT-1, + FuncOverrideCHLast = FuncOverrideCH1 + CPN_MAX_CHNOUT - 1, + FuncOverrideCH32 = FuncOverrideCHLast, // TODO remove FuncTrainer, FuncTrainerRUD, FuncTrainerELE, @@ -47,8 +48,9 @@ enum AssignFunc { FuncPlayHaptic, FuncReset, FuncSetTimer1, - FuncSetTimer2, - FuncSetTimer3, + FuncSetTimer2, // TODO remove + FuncSetTimer3, // TODO remove + FuncSetTimerLast = FuncSetTimer1 + CPN_MAX_TIMERS - 1, FuncVario, FuncPlayPrompt, FuncPlayBoth, @@ -61,7 +63,7 @@ enum AssignFunc { FuncBackgroundMusic, FuncBackgroundMusicPause, FuncAdjustGV1, - FuncAdjustGVLast = FuncAdjustGV1+CPN_MAX_GVARS-1, + FuncAdjustGVLast = FuncAdjustGV1 + CPN_MAX_GVARS - 1, FuncSetFailsafe, FuncRangeCheckInternalModule, FuncRangeCheckExternalModule, @@ -83,7 +85,15 @@ class CustomFunctionData { Q_DECLARE_TR_FUNCTIONS(CustomFunctionData) public: - CustomFunctionData(AssignFunc func=FuncOverrideCH1) { clear(); this->func = func; } + enum CustomFunctionContext + { + GlobalFunctionsContext = 0x01, + SpecialFunctionsContext = 0x02, + + AllFunctionContexts = GlobalFunctionsContext | SpecialFunctionsContext + }; + + CustomFunctionData(AssignFunc func = FuncOverrideCH1) { clear(); this->func = func; } RawSwitch swtch; AssignFunc func; int param; @@ -95,7 +105,7 @@ class CustomFunctionData { void clear(); bool isEmpty() const; QString nameToString(int index, bool globalContext = false) const; - QString funcToString(const ModelData * model = NULL) const; + QString funcToString(const ModelData * model = nullptr) const; QString paramToString(const ModelData * model) const; QString repeatToString() const; QString enabledToString() const; @@ -103,6 +113,10 @@ class CustomFunctionData { static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); static void populatePlaySoundParams(QStringList & qs); static void populateHapticParams(QStringList & qs); + static bool isFuncAvailable(int index); + static int funcContext(int index); + static int resetParamCount(const ModelData * model); + static bool isResetParamAvailable(const ModelData * model, int index); void convert(RadioDataConversionState & cstate); diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 930df092c..7657e2798 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1439,3 +1439,42 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) updRefInfo.updcnt++; } } + +QString ModelData::thrTraceSrcToString() const +{ + return thrTraceSrcToString((int)thrTraceSrc); +} + +QString ModelData::thrTraceSrcToString(const int index) const +{ + Firmware * firmware = getCurrentFirmware(); + const Boards board = Boards(getCurrentBoard()); + const int pscnt = board.getCapability(Board::Pots) + board.getCapability(Board::Sliders); + + if (index == 0) + return tr("THR"); + else if (index <= pscnt) + return board.getAnalogInputName(index + board.getCapability(Board::Sticks) - 1); + else if (index <= pscnt + firmware->getCapability(Outputs)) + return RawSource(SOURCE_TYPE_CH, index - pscnt - 1).toString(this); + + return QString(CPN_STR_UNKNOWN_ITEM); +} + +int ModelData::thrTraceSrcCount() const +{ + const Boards board = Boards(getCurrentBoard()); + Firmware * firmware = getCurrentFirmware(); + + return 1 + board.getCapability(Board::Pots) + board.getCapability(Board::Sliders) + firmware->getCapability(Outputs); +} + +bool ModelData::isThrTraceSrcAvailable(const GeneralSettings * generalSettings, const int index) const +{ + const Boards board = Boards(getCurrentBoard()); + + if (index > 0 && index <= board.getCapability(Board::Pots) + board.getCapability(Board::Sliders)) + return RawSource(SOURCE_TYPE_STICK, index + board.getCapability(Board::Sticks) - 1).isAvailable(this, generalSettings, board.getBoardType()); + else + return true; +} diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 7eacdc26d..2b277568f 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -283,6 +283,10 @@ class ModelData { bool hasExpoChildren(const int index); bool hasExpoSiblings(const int index); void removeMix(const int idx); + QString thrTraceSrcToString() const; + QString thrTraceSrcToString(const int index) const; + int thrTraceSrcCount() const; + bool isThrTraceSrcAvailable(const GeneralSettings * generalSettings, const int index) const; protected: void removeGlobalVar(int & var); diff --git a/companion/src/firmwares/rawsource.cpp b/companion/src/firmwares/rawsource.cpp index 67a640c33..67ad743b6 100644 --- a/companion/src/firmwares/rawsource.cpp +++ b/companion/src/firmwares/rawsource.cpp @@ -143,14 +143,14 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con static const QString rotary[] = { tr("REa"), tr("REb") }; if (index<0) { - return tr("???"); + return QString(CPN_STR_UNKNOWN_ITEM); } QString result; int genAryIdx = 0; switch (type) { case SOURCE_TYPE_NONE: - return tr("----"); + return QString(CPN_STR_NONE_ITEM); case SOURCE_TYPE_VIRTUAL_INPUT: { @@ -161,7 +161,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con } case SOURCE_TYPE_LUA_OUTPUT: - return tr("LUA%1%2").arg(index/16+1).arg(QChar('a'+index%16)); + return tr("LUA%1%2").arg(index / 16 + 1).arg(QChar('a' + index % 16)); case SOURCE_TYPE_STICK: if (generalSettings) { @@ -193,10 +193,10 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return result; case SOURCE_TYPE_CUSTOM_SWITCH: - return RawSwitch(SWITCH_TYPE_VIRTUAL, index+1).toString(); + return RawSwitch(SWITCH_TYPE_VIRTUAL, index + 1).toString(); case SOURCE_TYPE_CYC: - return tr("CYC%1").arg(index+1); + return tr("CYC%1").arg(index + 1); case SOURCE_TYPE_PPM: return RadioData::getElementName(tr("TR", "as in Trainer"), index + 1); @@ -208,18 +208,15 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return LimitData().nameToString(index); case SOURCE_TYPE_SPECIAL: - result = CHECK_IN_ARRAY(special, index); - // TODO refactor timers into own source type - if (result.startsWith("Timer")) { - bool ok; - int n = result.right(1).toInt(&ok); - if (ok) { - if (model) - result = model->timers[n - 1].nameToString(n - 1); - else - result = TimerData().nameToString(n - 1); - } + if (index >= SOURCE_TYPE_SPECIAL_TIMER1_IDX && index <= SOURCE_TYPE_SPECIAL_TIMER1_IDX + CPN_MAX_TIMERS - 1) { + if (model) + result = model->timers[index - SOURCE_TYPE_SPECIAL_TIMER1_IDX].nameToString(index - SOURCE_TYPE_SPECIAL_TIMER1_IDX); + else + result = TimerData().nameToString(index - SOURCE_TYPE_SPECIAL_TIMER1_IDX); } + else + result = CHECK_IN_ARRAY(special, index); + return result; case SOURCE_TYPE_TELEMETRY: @@ -241,7 +238,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con return GVarData().nameToString(index); default: - return tr("???"); + return QString(CPN_STR_UNKNOWN_ITEM); } } @@ -409,4 +406,3 @@ QStringList RawSource::getSwitchList(Boards board) const } return ret; } - diff --git a/companion/src/firmwares/rawsource.h b/companion/src/firmwares/rawsource.h index 9d0b19450..a7d293173 100644 --- a/companion/src/firmwares/rawsource.h +++ b/companion/src/firmwares/rawsource.h @@ -178,6 +178,9 @@ enum RawSourceType { MAX_SOURCE_TYPE }; +constexpr int SOURCE_TYPE_STICK_THR_IDX { 3 }; // TODO is there a function to determine index? +constexpr int SOURCE_TYPE_SPECIAL_TIMER1_IDX { 2 }; // TODO temp const until Timers own source type + class RawSourceRange { Q_DECLARE_TR_FUNCTIONS(RawSourceRange) diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index a9b8d7640..2b01f2600 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -22,6 +22,7 @@ #include "radiodata.h" #include "modeldata.h" +#include "eeprominterface.h" void SensorData::updateUnit() { @@ -118,3 +119,40 @@ bool SensorData::isEmpty() const { return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); } + +// static +QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign) +{ + if (model) { + const QString prfx = sign ? index < 0 ? "-" : "" : ""; + + if (abs(index) > 0) { + const SensorData &sd = model->sensorData[abs(index) - 1]; + if (sd.type == SensorData::TELEM_TYPE_CUSTOM) + return QString("%1%2 (%3)").arg(prfx).arg(sd.label).arg(sd.getOrigin(model)); + else + return QString("%1%2").arg(prfx).arg(sd.label); + } + else + return CPN_STR_NONE_ITEM; + } + else + return ""; +} + +// static +bool SensorData::isSourceAvailable(const ModelData * model, const int index) +{ + if (model) { + Firmware * firmware = getCurrentFirmware(); + const int i = abs(index); + if (i <= firmware->getCapability(Sensors)) { + if (i > 0) + return model->sensorData[i - 1].isAvailable(); + else + return true; + } + } + + return false; +} diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index a2b993bcc..200f6e889 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -168,6 +168,9 @@ class SensorData { QString getOrigin(const ModelData* model) const; void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; + + static QString sourceToString(const ModelData * model, const int index, const bool sign = true); + static bool isSourceAvailable(const ModelData * model, const int index); }; #endif // SENSORDATA_H From afe406ec2dfe1a7109aecb3f025c1e8fc95314cd Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 20 Dec 2020 16:35:44 +0000 Subject: [PATCH 018/300] Reduce trim timer delay on simulator --- radio/src/targets/simu/opentxsimulator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index f7e4f80e9..180709484 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -222,7 +222,7 @@ void OpenTxSimulator::setTrim(unsigned int idx, int value) emit outputValueChange(OUTPUT_SRC_TRIM_VALUE, idx, 0); timer->deleteLater(); }); - timer->start(350); + timer->start(50); } } From 613ed785271c8b0e5cc1ef902bbb578bf441a20a Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 21 Dec 2020 23:29:30 +1100 Subject: [PATCH 019/300] Curve reference add ui data models and fix invalid memory crashes --- .../src/datamodels/rawitemdatamodels.cpp | 24 +++ companion/src/datamodels/rawitemdatamodels.h | 36 ++++- companion/src/firmwares/curvereference.cpp | 137 +++++++++--------- companion/src/firmwares/curvereference.h | 27 ++-- companion/src/modeledit/expodialog.cpp | 5 +- companion/src/modeledit/expodialog.h | 2 +- companion/src/modeledit/inputs.cpp | 10 +- companion/src/modeledit/inputs.h | 1 + companion/src/modeledit/mixerdialog.cpp | 6 +- companion/src/modeledit/mixerdialog.h | 3 +- companion/src/modeledit/mixes.cpp | 10 +- companion/src/modeledit/mixes.h | 1 + 12 files changed, 166 insertions(+), 96 deletions(-) diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/rawitemdatamodels.cpp index 3805b98d6..c421ff964 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/rawitemdatamodels.cpp @@ -505,3 +505,27 @@ void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel) else qDebug() << "model:" << desc << "did not cast"; } + +CurveRefTypeItemModel::CurveRefTypeItemModel(QObject * parent) : + AbstractStandardItemModel(parent) +{ + for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(CurveReference::typeToString((CurveReference::CurveRefType)i)); + modelItem->setData(i, ItemIdRole); + modelItem->setData(CurveReference::isTypeAvailable((CurveReference::CurveRefType)i), IsAvailableRole); + appendRow(modelItem); + } +} + +CurveRefFuncItemModel::CurveRefFuncItemModel(QObject * parent) : + AbstractStandardItemModel(parent) +{ + for (int i = 1; i <= CurveReference::functionCount(); i++) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(CurveReference::functionToString(i)); + modelItem->setData(i, ItemIdRole); + modelItem->setData(CurveReference::isFunctionAvailable(i), IsAvailableRole); + appendRow(modelItem); + } +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/rawitemdatamodels.h index fbcfe8be9..8c8a78cc4 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/rawitemdatamodels.h @@ -30,11 +30,20 @@ class GeneralSettings; class ModelData; class AdjustmentReference; -class AbstractRawItemDataModel: public QStandardItemModel +class AbstractStandardItemModel: public QStandardItemModel { Q_OBJECT public: - enum DataRoles { ItemIdRole = Qt::UserRole, ItemTypeRole, ItemFlagsRole, IsAvailableRole }; + explicit AbstractStandardItemModel(QObject * parent = nullptr) : + QStandardItemModel(parent) + {} + + enum DataRoles { + ItemIdRole = Qt::UserRole, + ItemTypeRole, + ItemFlagsRole, + IsAvailableRole + }; Q_ENUM(DataRoles) enum DataGroups { @@ -43,9 +52,14 @@ class AbstractRawItemDataModel: public QStandardItemModel PositiveGroup = 0x04 }; Q_ENUM(DataGroups) +}; +class AbstractRawItemDataModel: public AbstractStandardItemModel +{ + Q_OBJECT + public: explicit AbstractRawItemDataModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr) : - QStandardItemModel(parent), + AbstractStandardItemModel(parent), generalSettings(generalSettings), modelData(modelData) {} @@ -221,4 +235,20 @@ class CommonItemModels: public QObject void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel); + +class CurveRefTypeItemModel : public AbstractStandardItemModel +{ + Q_OBJECT + public: + explicit CurveRefTypeItemModel(QObject * parent = nullptr); +}; + +class CurveRefFuncItemModel : public AbstractStandardItemModel +{ + Q_OBJECT + public: + explicit CurveRefFuncItemModel(QObject * parent = nullptr); +}; + + #endif // RAWITEMDATAMODELS_H diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 6ee2e38c5..688b4188f 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -91,8 +91,6 @@ QString CurveReference::typeToString(const CurveRefType type) return strl.at(idx); } -constexpr int MAX_CURVE_REF_FUNC { 6 }; - // static QString CurveReference::functionToString(const int value) { @@ -105,25 +103,50 @@ QString CurveReference::functionToString(const int value) return strl.at(idx); } -/* +// static +bool CurveReference::isTypeAvailable(const CurveRefType type) +{ + bool ret = false; + Firmware * fw = getCurrentFirmware(); + switch(type) { + case CURVE_REF_DIFF: + if (fw->getCapability(HasInputDiff)) + ret = true; + break; + case CURVE_REF_EXPO: + if (fw->getCapability(HasMixerExpo)) + ret = true; + break; + case CURVE_REF_FUNC: + case CURVE_REF_CUSTOM: + ret = true; + break; + } + + return ret; +} + +// static +bool CurveReference::isFunctionAvailable(const int value) +{ + return true; +} + +// static +int CurveReference::functionCount() +{ + return 6; +} + + +/* * CurveReferenceUIManager */ -constexpr int CURVE_REF_UI_HIDE_DIFF { 0x01 }; -constexpr int CURVE_REF_UI_HIDE_EXPO { 0x02 }; -constexpr int CURVE_REF_UI_HIDE_NEGATIVE_CURVES { 0x04 }; - -// static -bool CurveReferenceUIManager::firsttime { true }; -int CurveReferenceUIManager::flags { 0 }; -bool CurveReferenceUIManager::hasCapabilityGvars { false }; -int CurveReferenceUIManager::numCurves { 0 }; -RawItemFilteredModel * CurveReferenceUIManager::curveItemModel { nullptr }; -QStandardItemModel * CurveReferenceUIManager::tempModel { nullptr }; - CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, QObject * parent) : + RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, + QObject * parent) : QObject(parent), curveTypeCB(nullptr), curveGVarCB(nullptr), @@ -131,14 +154,17 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, Curve curveValueCB(curveValueCB), curve(curve), model(model), - lock(false) + lock(false), + curveItemModel(curveItemModel), + gvarItemModel(gvarItemModel) { - init(curveItemModel); + init(parent); } CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, QObject * parent) : + RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, + QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), curveGVarCB(curveGVarCB), @@ -146,36 +172,25 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck curveValueCB(curveValueCB), curve(curve), model(model), - lock(false) + lock(false), + curveItemModel(curveItemModel), + gvarItemModel(gvarItemModel) { - init(curveItemModel); + init(parent); } CurveReferenceUIManager::~CurveReferenceUIManager() { - delete tempModel; } -void CurveReferenceUIManager::init(RawItemFilteredModel * curveModel) +void CurveReferenceUIManager::init(QObject * parent) { - tempModel = new QStandardItemModel(); - - if (firsttime) { - firsttime = false; - Firmware * fw = getCurrentFirmware(); - hasCapabilityGvars = fw->getCapability(Gvars); - numCurves = fw->getCapability(NumCurves); - curveItemModel = curveModel; - - if (!fw->getCapability(HasInputDiff)) - flags |= (CURVE_REF_UI_HIDE_DIFF | CURVE_REF_UI_HIDE_NEGATIVE_CURVES); - - if (!fw->getCapability(HasMixerExpo)) - flags |= CURVE_REF_UI_HIDE_EXPO; - } + hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { - populateTypeCB(curveTypeCB, curve); + typeItemModel = new RawItemFilteredModel(new CurveRefTypeItemModel(parent), parent); + curveTypeCB->setModel(typeItemModel); + curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curve.type)); connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); } @@ -188,9 +203,12 @@ void CurveReferenceUIManager::init(RawItemFilteredModel * curveModel) connect(curveValueSB, SIGNAL(editingFinished()), this, SLOT(valueSBChanged())); } - curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); - curveValueCB->setMaxVisibleItems(10); - connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); + if (curveValueCB) { + funcItemModel = new RawItemFilteredModel(new CurveRefFuncItemModel(parent), parent); + curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); + curveValueCB->setMaxVisibleItems(10); + connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); + } update(); } @@ -231,7 +249,7 @@ void CurveReferenceUIManager::update() curveValueSB->setVisible(widgetsMask & CURVE_REF_UI_VALUE_SHOW); if(curveValueCB) { if (curve.isValueReference()) - populateValueCB(curveValueCB, curve, &model); + populateValueCB(curveValueCB); curveValueCB->setVisible(widgetsMask & CURVE_REF_UI_REF_SHOW); } @@ -271,39 +289,16 @@ void CurveReferenceUIManager::valueCBChanged() } } -// static -void CurveReferenceUIManager::populateTypeCB(QComboBox * cb, const CurveReference & curveRef) +void CurveReferenceUIManager::populateValueCB(QComboBox * cb) { if (cb) { - cb->clear(); - for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { - if ((curveRef.type == CurveReference::CURVE_REF_DIFF && !(flags & CURVE_REF_UI_HIDE_DIFF)) || - (curveRef.type == CurveReference::CURVE_REF_EXPO && !(flags & CURVE_REF_UI_HIDE_EXPO)) || - (curveRef.type != CurveReference::CURVE_REF_DIFF && curveRef.type != CurveReference::CURVE_REF_EXPO)) - cb->addItem(CurveReference::typeToString((CurveReference::CurveRefType)i), i); - } - - cb->setCurrentIndex(cb->findData((int)curveRef.type)); - } -} - -// static -void CurveReferenceUIManager::populateValueCB(QComboBox * cb, const CurveReference & curveRef, const ModelData * model) -{ - if (cb) { - cb->setModel(tempModel); // do not want to clear/alter the shared curves model and set to nullptr is invalid - - switch (curveRef.type) { + switch (curve.type) { case CurveReference::CURVE_REF_DIFF: case CurveReference::CURVE_REF_EXPO: - cb->clear(); - Helpers::populateGVCB(*cb, curveRef.value, *model); + cb->setModel(gvarItemModel); break; case CurveReference::CURVE_REF_FUNC: - cb->clear(); - for (int i = 1; i <= MAX_CURVE_REF_FUNC; i++) { - cb->addItem(CurveReference::functionToString(i), i); - } + cb->setModel(funcItemModel); break; case CurveReference::CURVE_REF_CUSTOM: cb->setModel(curveItemModel); @@ -312,6 +307,6 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb, const CurveReferen break; } - cb->setCurrentIndex(cb->findData(curveRef.value)); + cb->setCurrentIndex(cb->findData(curve.value)); } } diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 018fb12aa..119aaf597 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -79,6 +79,9 @@ class CurveReference { static int getDefaultValue(const CurveRefType type, const bool isGVar = false); static QString typeToString(const CurveRefType type); static QString functionToString(const int value); + static bool isTypeAvailable(const CurveRefType type); + static bool isFunctionAvailable(const int value); + static int functionCount(); }; class CurveReferenceUIManager : public QObject { @@ -87,18 +90,18 @@ class CurveReferenceUIManager : public QObject { public: CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, QObject * parent = nullptr); + RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, RawItemFilteredModel * curveItemModel, QObject * parent = nullptr); + CurveReference & curve, const ModelData & model, RawItemFilteredModel * curveItemModel, + RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); virtual ~CurveReferenceUIManager(); - void init(RawItemFilteredModel * curveModel); - void update(); protected slots: void gvarCBChanged(int); void typeChanged(int); void valueSBChanged(); void valueCBChanged(); + void update(); protected: QComboBox *curveTypeCB; @@ -108,16 +111,16 @@ class CurveReferenceUIManager : public QObject { CurveReference & curve; const ModelData & model; bool lock; + bool hasCapabilityGvars; + RawItemFilteredModel * curveItemModel; + RawItemFilteredModel * gvarItemModel; + RawItemFilteredModel * typeItemModel; + RawItemFilteredModel * funcItemModel; - static bool firsttime; - static int flags; - static bool hasCapabilityGvars; - static int numCurves; - static RawItemFilteredModel * curveItemModel; - static QStandardItemModel * tempModel; + void populateValueCB(QComboBox * cb); - static void populateTypeCB(QComboBox * cb, const CurveReference & curveRef); - static void populateValueCB(QComboBox * cb, const CurveReference & curveRef, const ModelData * model = nullptr); + private: + void init(QObject * parent); }; #endif // CURVEREFERENCE_H diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index afebbeb27..217f787d2 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -25,7 +25,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings, Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceModel, - RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel) : + RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel) : QDialog(parent), ui(new Ui::ExpoDialog), model(model), @@ -50,7 +50,8 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, curveItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, curveItemModel, + gvarItemModel, this); ui->switchesCB->setModel(rawSwitchModel); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(ed->swtch.toValue())); diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index b77d55852..0b17900df 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -38,7 +38,7 @@ class ExpoDialog : public QDialog { public: ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expodata, GeneralSettings & generalSettings, Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceItemModel, - RawItemFilteredModel * rawSwitchItemModel, RawItemFilteredModel * curveItemModel); + RawItemFilteredModel * rawSwitchItemModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel); ~ExpoDialog(); protected: diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index ee2347aee..5e50de71c 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -37,10 +37,15 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), RawItemFilteredModel::AllFilter, this); + const int filter = firmware->getCapability(HasInputDiff) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; + curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); + gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); + connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); + connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); + inputsCount = firmware->getCapability(VirtualInputs); if (inputsCount == 0) inputsCount = CPN_MAX_STICKS; @@ -194,7 +199,8 @@ void InputsPanel::gm_openExpo(int index) if (firmware->getCapability(VirtualInputs)) inputName = model->inputNames[ed.chn]; - ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, curveFilteredModel); + ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, + curveFilteredModel, gvarFilteredModel); if (dlg->exec()) { model->expoData[index] = ed; if (firmware->getCapability(VirtualInputs)) diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index ea1243213..3bbfceb7b 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -76,6 +76,7 @@ class InputsPanel : public ModelPanel RawItemFilteredModel *rawSourceFilteredModel; RawItemFilteredModel *rawSwitchFilteredModel; RawItemFilteredModel *curveFilteredModel; + RawItemFilteredModel *gvarFilteredModel; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 00d2badc8..102d2d2a9 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -25,7 +25,8 @@ #include "helpers.h" MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel) : + RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, + RawItemFilteredModel * gvarItemModel) : QDialog(parent), ui(new Ui::MixerDialog), model(model), @@ -52,7 +53,8 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, curveItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, curveItemModel, + gvarItemModel, this); ui->MixDR_CB->setChecked(md->noExpo == 0); diff --git a/companion/src/modeledit/mixerdialog.h b/companion/src/modeledit/mixerdialog.h index 6d2131de2..d036832d9 100644 --- a/companion/src/modeledit/mixerdialog.h +++ b/companion/src/modeledit/mixerdialog.h @@ -36,7 +36,8 @@ class MixerDialog : public QDialog { Q_OBJECT public: MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel); + RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, + RawItemFilteredModel * gvarItemModel); ~MixerDialog(); protected: diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index f02a57417..01b4d7f64 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -37,10 +37,15 @@ MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & gen connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), RawItemFilteredModel::AllFilter, this); + const int filter = firmware->getCapability(HasMixerExpo) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; + curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); + gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); + connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); + connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); + QGridLayout * mixesLayout = new QGridLayout(this); mixersListWidget = new MixersListWidget(this, false); // TODO enum @@ -189,7 +194,8 @@ void MixesPanel::gm_openMix(int index) MixData mixd(model->mixData[index]); - MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, curveFilteredModel); + MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, + curveFilteredModel, gvarFilteredModel); if(dlg->exec()) { model->mixData[index] = mixd; emit modified(); diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 014786b74..154eda9b4 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -72,6 +72,7 @@ class MixesPanel : public ModelPanel RawItemFilteredModel *rawSourceFilteredModel; RawItemFilteredModel *rawSwitchFilteredModel; RawItemFilteredModel *curveFilteredModel; + RawItemFilteredModel *gvarFilteredModel; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); From 860b50faae0ddc1f603a8a542f21e27659d6294b Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 21 Dec 2020 19:28:05 +0100 Subject: [PATCH 020/300] Improve build files --- tools/boards.py | 69 +++++++++++++++++++++++++++++++++++++++++++++ tools/build-imrc.py | 65 ++++++++++++++++++++++++++++++++++++++++++ tools/build-tbs.py | 57 ++----------------------------------- 3 files changed, 136 insertions(+), 55 deletions(-) create mode 100755 tools/boards.py create mode 100755 tools/build-imrc.py diff --git a/tools/boards.py b/tools/boards.py new file mode 100755 index 000000000..72e367cb3 --- /dev/null +++ b/tools/boards.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 + +boards = { + "XLITE": { + "PCB": "XLITE", + }, + "XLITES": { + "PCB": "XLITES", + }, + "X9LITE": { + "PCB": "X9LITE", + }, + "X9LITES": { + "PCB": "X9LITES", + }, + "X9D": { + "PCB": "X9D", + }, + "X9D+": { + "PCB": "X9D+", + }, + "X9D+2019": { + "PCB": "X9D+", + "PCBREV": "2019", + }, + "X9E": { + "PCB": "X9E", + }, + "X7": { + "PCB": "X7", + }, + "X7ACCESS": { + "PCB": "X7", + "PCBREV": "ACCESS", + }, + "X10": { + "PCB": "X10", + }, + "X10EXPRESS": { + "PCB": "X10", + "PCBREV": "EXPRESS", + }, + "X12S": { + "PCB": "X12S", + }, + "T16": { + "PCB": "X10", + "PCBREV": "T16", + "INTERNAL_MODULE_MULTI": "YES" + }, + "T18": { + "PCB": "X10", + "PCBREV": "T18", + "INTERNAL_MODULE_MULTI": "YES" + }, + "TX16S": { + "PCB": "X10", + "PCBREV": "TX16S", + "INTERNAL_MODULE_MULTI": "YES" + }, + "T12": { + "PCB": "X7", + "PCBREV": "T12", + }, + "TX12": { + "PCB": "X7", + "PCBREV": "TX12", + }, +} diff --git a/tools/build-imrc.py b/tools/build-imrc.py new file mode 100755 index 000000000..b739a3fc6 --- /dev/null +++ b/tools/build-imrc.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +import argparse +import datetime +import os +from builtins import NotADirectoryError +import shutil +import tempfile + +from boards import * + +translations = [ + "EN" +] + + +def timestamp(): + return datetime.datetime.now().strftime("%Y%m%d") + + +def build(board, translation, srcdir): + cmake_options = " ".join(["-D%s=%s" % (key, value) for key, value in boards[board].items()]) + cwd = os.getcwd() + if not os.path.exists("output"): + os.mkdir("output") + path = tempfile.mkdtemp() + os.chdir(path) + command = "cmake %s -DTRANSLATIONS=%s -DIMRC_RELEASE=YES -DGHOST=YES -DTEST_BUILD_WARNING=YES %s" % (cmake_options, translation, srcdir) + print(command) + os.system(command) + os.system("make firmware -j16") + os.chdir(cwd) + index = 0 + while 1: + suffix = "" if index == 0 else "_%d" % index + filename = "output/ghost_firm_%s_%s_%s%s.bin" % (board.lower(), translation.lower(), timestamp(), suffix) + if not os.path.exists(filename): + shutil.copy("%s/firmware.bin" % path, filename) + break + index += 1 + shutil.rmtree(path) + + +def dir_path(string): + if os.path.isdir(string): + return string + else: + raise NotADirectoryError(string) + + +def main(): + parser = argparse.ArgumentParser(description="Build Ghost firmware") + parser.add_argument("-b", "--boards", action="append", help="Destination boards", required=True) + parser.add_argument("-t", "--translations", action="append", help="Translations", required=True) + parser.add_argument("srcdir", type=dir_path) + + args = parser.parse_args() + + for board in (boards.keys() if "ALL" in args.boards else args.boards): + for translation in (translations if "ALL" in args.translations else args.translations): + build(board, translation, args.srcdir) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/build-tbs.py b/tools/build-tbs.py index 3c5cb2d1c..a7a0bfc23 100755 --- a/tools/build-tbs.py +++ b/tools/build-tbs.py @@ -7,60 +7,7 @@ from builtins import NotADirectoryError import shutil import tempfile - -boards = { - "XLITE": { - "PCB": "XLITE", - }, - "XLITES": { - "PCB": "XLITES", - }, - "X9LITE": { - "PCB": "X9LITE", - }, - "X9LITES": { - "PCB": "X9LITES", - }, - "X9D": { - "PCB": "X9D+", - }, - "X9D+": { - "PCB": "X9D+", - }, - "X9D+2019": { - "PCB": "X9D+", - "PCBREV": "2019", - }, - "X9E": { - "PCB": "X9E", - }, - "X7": { - "PCB": "X7", - }, - "X7ACCESS": { - "PCB": "X7", - "PCBREV": "ACCESS", - }, - "X10": { - "PCB": "X10", - }, - "X10EXPRESS": { - "PCB": "X10", - "PCBREV": "EXPRESS", - }, - "X12S": { - "PCB": "X12S", - }, - "T16": { - "PCB": "X10", - "PCBREV": "T16", - "INTERNAL_MODULE_MULTI": "YES" - }, - "T12": { - "PCB": "X7", - "PCBREV": "T12", - }, -} +from boards import * translations = [ "EN" @@ -86,7 +33,7 @@ def build(board, translation, srcdir): index = 0 while 1: suffix = "" if index == 0 else "_%d" % index - filename = "output/firmware_%s_%s_%s%s.bin" % (board.lower(), translation.lower(), timestamp(), suffix) + filename = "output/tbs_firm_%s_%s_%s%s.bin" % (board.lower(), translation.lower(), timestamp(), suffix) if not os.path.exists(filename): shutil.copy("%s/firmware.bin" % path, filename) break From 2e8e5d9c55f28a99946233ba6cccd6d5a39a7c42 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Dec 2020 08:36:23 +0100 Subject: [PATCH 021/300] Undo popup size change --- radio/src/gui/128x64/popups.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/popups.h b/radio/src/gui/128x64/popups.h index 9400d69f7..e5d483e0b 100644 --- a/radio/src/gui/128x64/popups.h +++ b/radio/src/gui/128x64/popups.h @@ -23,16 +23,16 @@ #include -#define MESSAGEBOX_X 1 +#define MESSAGEBOX_X 10 #define MESSAGEBOX_Y 16 -#define MESSAGEBOX_W (LCD_W - 2) +#define MESSAGEBOX_W (LCD_W - 19) #define MENU_X MESSAGEBOX_X #define MENU_Y MESSAGEBOX_Y #define MENU_W MESSAGEBOX_W #define WARNING_LINE_LEN 20 -#define WARNING_LINE_X 3 +#define WARNING_LINE_X 16 #define WARNING_LINE_Y 3*FH #define POPUP_MENU_MAX_LINES 12 From fa8f52281cdfd05a6acd36f1344b4c0c45f91df5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Dec 2020 08:37:14 +0100 Subject: [PATCH 022/300] Improve script errors output for small lcds --- radio/src/lua/interface.cpp | 22 +++++++++++++++++++--- 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 +- 12 files changed, 30 insertions(+), 14 deletions(-) diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index a0cef2c1d..81ac4472a 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -745,11 +745,23 @@ void displayLuaError(const char * title) if (lua_warning_info[0]) { char * split = strstr(lua_warning_info, ": "); if (split) { - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH+3, lua_warning_info, split-lua_warning_info, SMLSIZE); - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+2*FH+2, split+2, lua_warning_info+LUA_WARNING_INFO_LEN-split, SMLSIZE); +#if LCD_W == 128 + if (strlen(split + 2) <= 20) { + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH + 2, split + 2, strlen(split + 2), SMLSIZE); + } + else { + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH, lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH, split + 2, 20, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 3 * FH, split + 22, strlen(split + 22), SMLSIZE); + } +#else + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH + 2, split + 2, lua_warning_info + LUA_WARNING_INFO_LEN - split, SMLSIZE); +#endif } else { - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH+3, lua_warning_info, 40, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, lua_warning_info, 40, SMLSIZE); } } } @@ -787,7 +799,11 @@ void luaError(lua_State * L, uint8_t error, bool acknowledge) #if defined(SIMU) if (!strncmp(msg, ".", 2)) msg += 1; #endif +#if LCD_W == 128 + msg = strrchr(msg, '/') + 1; +#else if (!strncmp(msg, "/SCRIPTS/", 9)) msg += 9; +#endif strncpy(lua_warning_info, msg, LUA_WARNING_INFO_LEN); lua_warning_info[LUA_WARNING_INFO_LEN] = '\0'; } diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 2f42f7edd..eed7492d1 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -780,7 +780,7 @@ #define TR_NO_MODELS_ON_SD "žádný model" BREAKSPACE "na SD" #define TR_NO_BITMAPS_ON_SD "žádné obrázky" BREAKSPACE "na SD" #define TR_NO_SCRIPTS_ON_SD "žádný skript" BREAKSPACE "na SD" -#define TR_SCRIPT_SYNTAX_ERROR "Syntaktická chyba skriptu" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntaktická chyba", "Syntaktická chyba skriptu") #define TR_SCRIPT_PANIC "Script zmaten" #define TR_SCRIPT_KILLED "Script ukončen" #define TR_SCRIPT_ERROR "Neznámá chyba" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 1e85008b0..d927fadda 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -782,7 +782,7 @@ #define TR_NO_MODELS_ON_SD "Kein Modelle" BREAKSPACE "auf SD" #define TR_NO_BITMAPS_ON_SD "Keine Bitmaps" BREAKSPACE "auf SD" #define TR_NO_SCRIPTS_ON_SD "Keine Skripte" BREAKSPACE "auf SD" -#define TR_SCRIPT_SYNTAX_ERROR "Skript Syntaxfehler" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntaxfehler", "Skript Syntaxfehler") #define TR_SCRIPT_PANIC "Skript Panik" #define TR_SCRIPT_KILLED "Skript beendet" #define TR_SCRIPT_ERROR "Unbekannter Fehler" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ad49d6997..171dce2c4 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -786,7 +786,7 @@ #define TR_NO_MODELS_ON_SD "No models" BREAKSPACE "on SD" #define TR_NO_BITMAPS_ON_SD "No bitmaps" BREAKSPACE "on SD" #define TR_NO_SCRIPTS_ON_SD "No scripts" BREAKSPACE "on SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 72eb11dbc..279b4e90d 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -783,7 +783,7 @@ #define TR_NO_MODELS_ON_SD "Sin modelos en SD" #define TR_NO_BITMAPS_ON_SD "Sin imágenes en SD" #define TR_NO_SCRIPTS_ON_SD "No scripts en SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Error desconocido" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index e18b51102..23bdc897c 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -799,7 +799,7 @@ #define TR_NO_MODELS_ON_SD "No Models on SD" #define TR_NO_BITMAPS_ON_SD "No Bitmaps on SD" #define TR_NO_SCRIPTS_ON_SD "No Scripts on SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index ffaeb38c6..0cf289578 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -802,7 +802,7 @@ #define TR_NO_MODELS_ON_SD "Aucun modèle SD" #define TR_NO_BITMAPS_ON_SD "Aucun Bitmap SD" #define TR_NO_SCRIPTS_ON_SD "Aucun Script SD" -#define TR_SCRIPT_SYNTAX_ERROR "Erreur syntaxe script" +#define TR_SCRIPT_SYNTAX_ERROR TR("Erreur syntaxe", "Erreur syntaxe script") #define TR_SCRIPT_PANIC "Script bloqué" #define TR_SCRIPT_KILLED "Script interrompu" #define TR_SCRIPT_ERROR "Erreur inconnue" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c1e8c0be0..dc49ecef3 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -801,7 +801,7 @@ #define TR_NO_MODELS_ON_SD "No Model." BREAKSPACE "su SD" #define TR_NO_BITMAPS_ON_SD "No Immag." BREAKSPACE "su SD" #define TR_NO_SCRIPTS_ON_SD "No Scripts" BREAKSPACE "su SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script errore sintassi" +#define TR_SCRIPT_SYNTAX_ERROR TR("Errore sintassi", "Script errore sintassi") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script fermato" #define TR_SCRIPT_ERROR "Errore sconosciuto" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index fe5abbfe3..b39271755 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -787,7 +787,7 @@ #define TR_NO_MODELS_ON_SD "Geen Modellen" BREAKSPACE "op SD" #define TR_NO_BITMAPS_ON_SD "Geen Bitmaps" BREAKSPACE "op SD" #define TR_NO_SCRIPTS_ON_SD "Geen Scripts" BREAKSPACE "op SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8212ce84d..4eb1a5139 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -800,7 +800,7 @@ #define TR_NO_MODELS_ON_SD "Brak modelu na SD" #define TR_NO_BITMAPS_ON_SD "Brak obrazków na SD" #define TR_NO_SCRIPTS_ON_SD "Brak skryptów na SD" -#define TR_SCRIPT_SYNTAX_ERROR "Skrypt:syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Skrypt:syntax error") #define TR_SCRIPT_PANIC "Skrypt:panic" #define TR_SCRIPT_KILLED "Skrypt:killed" #define TR_SCRIPT_ERROR "Nieznany błąd" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 412e3698a..8575e08bc 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -801,7 +801,7 @@ #define TR_NO_MODELS_ON_SD "Sem Modelo no SD" #define TR_NO_BITMAPS_ON_SD "No Bitmaps on SD" #define TR_NO_SCRIPTS_ON_SD "No Scripts on SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 57cf99ba6..c1877b88a 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -800,7 +800,7 @@ #define TR_NO_MODELS_ON_SD "Ingen modell i SD" #define TR_NO_BITMAPS_ON_SD "Ikoner saknas på SD" #define TR_NO_SCRIPTS_ON_SD "Programkod saknas på SD" -#define TR_SCRIPT_SYNTAX_ERROR "Script syntax error" +#define TR_SCRIPT_SYNTAX_ERROR TR("Syntax error", "Script syntax error") #define TR_SCRIPT_PANIC "Script panic" #define TR_SCRIPT_KILLED "Script killed" #define TR_SCRIPT_ERROR "Unknown error" From e9c9fcda14a08356db7369e452e863d8bbeb9b19 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 22 Dec 2020 08:53:10 +0100 Subject: [PATCH 023/300] Improve build files --- tools/build-tbs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build-tbs.py b/tools/build-tbs.py index a7a0bfc23..729855df0 100755 --- a/tools/build-tbs.py +++ b/tools/build-tbs.py @@ -28,7 +28,7 @@ def build(board, translation, srcdir): command = "cmake %s -DTRANSLATIONS=%s -DTBS_RELEASE=YES -DTEST_BUILD_WARNING=YES %s" % (cmake_options, translation, srcdir) print(command) os.system(command) - os.system("make firmware -j6") + os.system("make firmware -j16") os.chdir(cwd) index = 0 while 1: From d188b50a06f3e569d90403c3d2a39e53f8c052b0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 20 Dec 2020 09:43:29 +0100 Subject: [PATCH 024/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index da2a9413c..aa39c5bd1 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2052,3 +2052,5 @@ Robert Rubio Dennis G Miller Leonardo Garcia Osuna Antonio Carrasco Pastor +Musa Pinar +Jens Moder From 78b86e61d5eb38972130260408de82fc76594334 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 20 Dec 2020 19:28:57 +0100 Subject: [PATCH 025/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index aa39c5bd1..8d764caac 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2054,3 +2054,4 @@ Leonardo Garcia Osuna Antonio Carrasco Pastor Musa Pinar Jens Moder +Josef de Beer From f3ffdbff0a76365ed1c310e4ac51abd48ffdfad4 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 21 Dec 2020 12:14:18 +0100 Subject: [PATCH 026/300] Fix hardcoded string --- radio/src/gui/480x272/view_about.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/480x272/view_about.cpp b/radio/src/gui/480x272/view_about.cpp index 3f5e14660..ea3561188 100644 --- a/radio/src/gui/480x272/view_about.cpp +++ b/radio/src/gui/480x272/view_about.cpp @@ -128,7 +128,7 @@ bool menuAboutView(event_t event) break; case ABOUT_HARDWARE: - lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, "FrSky", INVERS); + lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_HARDWARE_1, INVERS); lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_HARDWARE_2); lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_HARDWARE_3); break; From d712a17f62f9e337cef178da5fba1449995cf6e1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 22 Dec 2020 10:07:37 +0100 Subject: [PATCH 027/300] Handle more module combination --- radio/src/mixer_scheduler.cpp | 7 +++++-- radio/src/tasks.cpp | 9 ++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/radio/src/mixer_scheduler.cpp b/radio/src/mixer_scheduler.cpp index b29c821c8..82dfb7086 100644 --- a/radio/src/mixer_scheduler.cpp +++ b/radio/src/mixer_scheduler.cpp @@ -37,13 +37,16 @@ static MixerSchedule mixerSchedules[NUM_MODULES]; uint16_t getMixerSchedulerPeriod() { +#if defined(INTERNAL_MODULE) if (mixerSchedules[INTERNAL_MODULE].period) { return mixerSchedules[INTERNAL_MODULE].period; } - else if (mixerSchedules[EXTERNAL_MODULE].period) { +#endif +#if defined(EXTERNAL_MODULE) + if (mixerSchedules[EXTERNAL_MODULE].period) { return mixerSchedules[EXTERNAL_MODULE].period; } - +#endif return MIXER_SCHEDULER_DEFAULT_PERIOD_US; } diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 8346b2ceb..f85c6a6b2 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -174,7 +174,14 @@ TASK_FUNCTION(mixerTask) doMixerCalculations(); // TODO: fix runMask - sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE)); + uint8_t runMask = 0; +#if defined(INTERNAL_MODULE) + runMask != (1 << INTERNAL_MODULE) +#endif +#if defined(EXTERNAL_MODULE) + runMask != (1 << EXTERNAL_MODULE) +#endif + sendSynchronousPulses(runMask); doMixerPeriodicUpdates(); From 803701bfb26d62356a1e125b02eec8f325508661 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 20 Dec 2020 09:43:29 +0100 Subject: [PATCH 028/300] Update CREDITS.txt --- CREDITS.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CREDITS.txt b/CREDITS.txt index 8d764caac..aa39c5bd1 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2054,4 +2054,3 @@ Leonardo Garcia Osuna Antonio Carrasco Pastor Musa Pinar Jens Moder -Josef de Beer From a6617d621fb141944a808f0745d0163790c35b71 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 20 Dec 2020 19:28:57 +0100 Subject: [PATCH 029/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index aa39c5bd1..8d764caac 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2054,3 +2054,4 @@ Leonardo Garcia Osuna Antonio Carrasco Pastor Musa Pinar Jens Moder +Josef de Beer From 70c1d77648faf107aceb3a063e5c442aaa494ee0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 23 Dec 2020 20:24:20 +0100 Subject: [PATCH 030/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8d764caac..10e2bb105 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2055,3 +2055,4 @@ Antonio Carrasco Pastor Musa Pinar Jens Moder Josef de Beer +Chad Mofford From e64c292d73056a98765dbfc7f7b90eaf58ef7048 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Dec 2020 18:55:26 +0100 Subject: [PATCH 031/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 10e2bb105..174d95b34 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2056,3 +2056,4 @@ Musa Pinar Jens Moder Josef de Beer Chad Mofford +Pim Pouw From 6daeb7477f3974200f24470752dacec935d640de Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 28 Dec 2020 11:31:11 +0100 Subject: [PATCH 032/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 174d95b34..e229ec8e1 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2057,3 +2057,4 @@ Jens Moder Josef de Beer Chad Mofford Pim Pouw +Kenth Jonsson From 749e8c547d9d5c6b1112590d5ed2385194059cc1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 29 Dec 2020 16:07:10 +0100 Subject: [PATCH 033/300] Fix multi telemetry not working when CRSF or Ghost had been selected --- radio/src/telemetry/telemetry.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 03878eee6..5c5b1181c 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -291,6 +291,7 @@ void telemetryInit(uint8_t protocol) #if defined(LUA) outputTelemetryBuffer.reset(); #endif + telemetryPortSetDirectionInput(); } else if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM) { // Spektrum's own small race RX (SPM4648) uses 125000 8N1, use the same since there is no real standard From d583f7536c2bc11d41399f0f46f3bdb07e67bf39 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 29 Dec 2020 16:31:13 +0100 Subject: [PATCH 034/300] Fix compilation --- radio/src/targets/sky9x/board.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/targets/sky9x/board.h b/radio/src/targets/sky9x/board.h index 2d098a45f..0a6aef20d 100644 --- a/radio/src/targets/sky9x/board.h +++ b/radio/src/targets/sky9x/board.h @@ -398,6 +398,9 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode); inline void telemetryPortSetDirectionOutput() { } +inline void telemetryPortSetDirectionInput() +{ +} uint32_t telemetryTransmitPending(); void telemetryTransmitBuffer(const uint8_t * buffer, uint32_t size); void rxPdcUsart( void (*pChProcess)(uint8_t x) ); From 0d281cf5dde3be01ae093a19b288cd78c218052d Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 31 Dec 2020 17:15:35 +1100 Subject: [PATCH 035/300] Major refactoring --- companion/src/datamodels/itemmodelfactory.cpp | 223 ++++++++ companion/src/datamodels/itemmodelfactory.h | 75 +++ .../src/datamodels/rawitemdatamodels.cpp | 509 ++++++++++-------- companion/src/datamodels/rawitemdatamodels.h | 268 +++++---- .../src/datamodels/rawitemfilteredmodel.cpp | 144 ++++- .../src/datamodels/rawitemfilteredmodel.h | 81 ++- companion/src/firmwares/curvereference.cpp | 50 +- companion/src/firmwares/curvereference.h | 28 +- companion/src/generaledit/generaledit.cpp | 9 +- companion/src/generaledit/generaledit.h | 4 +- companion/src/modeledit/channels.cpp | 15 +- companion/src/modeledit/channels.h | 16 +- companion/src/modeledit/curves.cpp | 7 +- companion/src/modeledit/curves.h | 6 +- companion/src/modeledit/customfunctions.cpp | 31 +- companion/src/modeledit/customfunctions.h | 21 +- companion/src/modeledit/expodialog.cpp | 13 +- companion/src/modeledit/expodialog.h | 9 +- companion/src/modeledit/flightmodes.cpp | 32 +- companion/src/modeledit/flightmodes.h | 19 +- companion/src/modeledit/heli.cpp | 14 +- companion/src/modeledit/heli.h | 15 +- companion/src/modeledit/inputs.cpp | 40 +- companion/src/modeledit/inputs.h | 22 +- companion/src/modeledit/logicalswitches.cpp | 22 +- companion/src/modeledit/logicalswitches.h | 16 +- companion/src/modeledit/mixerdialog.cpp | 13 +- companion/src/modeledit/mixerdialog.h | 6 +- companion/src/modeledit/mixes.cpp | 40 +- companion/src/modeledit/mixes.h | 22 +- companion/src/modeledit/modeledit.cpp | 34 +- companion/src/modeledit/modeledit.h | 10 +- companion/src/modeledit/setup.cpp | 31 +- companion/src/modeledit/setup.h | 20 +- companion/src/modeledit/telemetry.cpp | 329 ++++++----- companion/src/modeledit/telemetry.h | 54 +- companion/src/shared/autocombobox.h | 63 ++- 37 files changed, 1512 insertions(+), 799 deletions(-) create mode 100644 companion/src/datamodels/itemmodelfactory.cpp create mode 100644 companion/src/datamodels/itemmodelfactory.h diff --git a/companion/src/datamodels/itemmodelfactory.cpp b/companion/src/datamodels/itemmodelfactory.cpp new file mode 100644 index 000000000..892345568 --- /dev/null +++ b/companion/src/datamodels/itemmodelfactory.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "itemmodelfactory.h" + +#include "rawitemdatamodels.h" +#include "rawitemfilteredmodel.h" + +#include "eeprominterface.h" +#include "generalsettings.h" +#include "modeldata.h" + +ItemModelFactory::ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : + generalSettings(generalSettings), + modelData(modelData) +{ + firmware = getCurrentFirmware(); + board = new Boards(getCurrentBoard()); + boardType = getCurrentBoard(); +} + +ItemModelFactory::~ItemModelFactory() +{ + unregisterItemModels(); + delete board; +} + +void ItemModelFactory::addItemModel(const AbstractItemModel::ItemModelId id) +{ + switch (id) { + case AbstractItemModel::RawSourceId: + registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::RawSwitchId: + registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveId: + registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::GVarRefId: + registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::ThrSourceId: + registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncActionId: + registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncResetParamId: + registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::TeleSourceId: + registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefTypeId: + registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefFuncId: + registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + default: + qDebug() << "Error: unknown item model: id"; + break; + } +} + +void ItemModelFactory::registerItemModel(QAbstractItemModel * itemModel) +{ + if (itemModel && !getItemModel(itemModel->getId())) { + registeredItemModels.push_back(itemModel); + } +} + +void ItemModelFactory::unregisterItemModels() +{ + foreach (QAbstractItemModel *itemModel, registeredItemModels) { + delete itemModel; + } +} + +void ItemModelFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) +{ + QAbstractItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +QAbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +void ItemModelFactory::update(const AbstractItemModel::UpdateTrigger trigger) +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + itemModel->update(trigger); + } +} + +void ItemModelFactory::dumpAllItemModelContents() const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + AbstractItemModel::dumpItemModelContents(itemModel); + } +} + +// +// FilteredItemModelsFactory +// + +FilteredItemModelFactory::FilteredItemModelFactory() +{ +} + +FilteredItemModelFactory::~FilteredItemModelFactory() +{ + unregisterItemModels(); +} + +int FilteredItemModelFactory::registerItemModel(FilteredItemModel * itemModel, QString name) +{ + if (itemModel) { + if (isItemModelRegistered(name)) + qDebug() << "Warning: item model already registered with name:" << name; + const int id = registeredItemModels.count(); + itemModel->setId(id); + itemModel->setName(name); + registeredItemModels.push_back(itemModel); + return id; + } + return 0; +} + +void FilteredItemModelFactory::unregisterItemModels() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + delete itemModel; + } +} + +void FilteredItemModelFactory::unregisterItemModel(const int id) +{ + FilteredItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +bool FilteredItemModelFactory::isItemModelRegistered(const int id) const +{ + return getItemModel(id); +} + +bool FilteredItemModelFactory::isItemModelRegistered(const QString name) const +{ + return getItemModel(name); +} + +FilteredItemModel * FilteredItemModelFactory::getItemModel(const int id) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +FilteredItemModel * FilteredItemModelFactory::getItemModel(const QString name) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getName() == name) + return itemModel; + } + + return nullptr; +} + +void FilteredItemModelFactory::update() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + itemModel->update(); + } +} + +void FilteredItemModelFactory::dumpItemModelContents(const int id) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(id)); +} + +void FilteredItemModelFactory::dumpItemModelContents(const QString name) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(name)); +} + +void FilteredItemModelFactory::dumpAllItemModelContents() const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + FilteredItemModel::dumpItemModelContents(itemModel); + } +} + diff --git a/companion/src/datamodels/itemmodelfactory.h b/companion/src/datamodels/itemmodelfactory.h new file mode 100644 index 000000000..01b7626b4 --- /dev/null +++ b/companion/src/datamodels/itemmodelfactory.h @@ -0,0 +1,75 @@ +/* + * 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 ITEMMODELFACTORY_H +#define ITEMMODELFACTORY_H + +class GeneralSettings; +class ModelData; +class AbstractItemModel; +class FilteredItemModel; + +class ItemModelFactory +{ + public: + ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); + virtual ~ItemModelFactory(); + + void addItemModel(const AbstractItemModel::ItemModelId id); + void registerItemModel(AbstractItemModel * itemModel); + void unregisterItemModels(); + void unregisterItemModel(const AbstractItemModel::ItemModelId id); + AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; + void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); + void dumpAllItemModelContents() const; + + protected: + const GeneralSettings * generalSettings; + const ModelData * modelData; + Firmware * firmware; + Boards * board; + Board::Type boardType; + QVector registeredItemModels; +}; + +class FilteredItemModelsFactory +{ + public: + FilteredItemModelsFactory(); + virtual ~FilteredItemModelsFactory(); + + int registerItemModel(FilteredItemModel * itemModel, QString name); + void unregisterItemModels(); + void unregisterItemModel(const int id); + bool isItemModelRegistered(const int id) const; + bool isItemModelRegistered(const QString name) const; + FilteredItemModel * getItemModel(const int id) const; + FilteredItemModel * getItemModel(const QString name) const; + void update(); + + void dumpItemModelContents(const int id) const; + void dumpItemModelContents(const QString name) const; + void dumpAllItemModelContents() const; + + protected: + QVector registeredItemModels; +}; + +#endif // ITEMMODELFACTORY_H diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/rawitemdatamodels.cpp index c421ff964..59b1542d5 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/rawitemdatamodels.cpp @@ -24,35 +24,57 @@ #include "modeldata.h" #include "adjustmentreference.h" -RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +// static +void AbstractItemModel::dumpItemModelContents(AbstractItemModel * itemModel) { - const Boards board = Boards(getCurrentBoard()); - Firmware * fw = getCurrentFirmware(); + if (itemModel) { + qDebug() << "modelid:" << itemModel->getId(); + for (int i = 0; i < itemModel->rowCount(); ++i) { + qDebug() << "row:" << i + << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + } + } + else + qDebug() << "Error: model did not cast"; +} + +// +// RawSourceItemModel +// + +RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) +{ + setId(RawSourceId); + setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); addItems(SOURCE_TYPE_NONE, RawSource::NoneGroup, 1); - for (int i = 0; i < fw->getCapability(LuaScripts); i++) - addItems(SOURCE_TYPE_LUA_OUTPUT, RawSource::ScriptsGroup, fw->getCapability(LuaOutputsPerScript), i * 16); - addItems(SOURCE_TYPE_VIRTUAL_INPUT, RawSource::InputsGroup, fw->getCapability(VirtualInputs)); - addItems(SOURCE_TYPE_STICK, RawSource::SourcesGroup, board.getCapability(Board::MaxAnalogs)); - addItems(SOURCE_TYPE_ROTARY_ENCODER, RawSource::SourcesGroup, fw->getCapability(RotaryEncoders)); - addItems(SOURCE_TYPE_TRIM, RawSource::TrimsGroup, board.getCapability(Board::NumTrims)); + for (int i = 0; i < firmware->getCapability(LuaScripts); i++) + addItems(SOURCE_TYPE_LUA_OUTPUT, RawSource::ScriptsGroup, firmware->getCapability(LuaOutputsPerScript), i * 16); + addItems(SOURCE_TYPE_VIRTUAL_INPUT, RawSource::InputsGroup, firmware->getCapability(VirtualInputs)); + addItems(SOURCE_TYPE_STICK, RawSource::SourcesGroup, board->getCapability(Board::MaxAnalogs)); + addItems(SOURCE_TYPE_ROTARY_ENCODER, RawSource::SourcesGroup, firmware->getCapability(RotaryEncoders)); + addItems(SOURCE_TYPE_TRIM, RawSource::TrimsGroup, board->getCapability(Board::NumTrims)); addItems(SOURCE_TYPE_MAX, RawSource::SourcesGroup, 1); - addItems(SOURCE_TYPE_SWITCH, RawSource::SwitchesGroup, board.getCapability(Board::Switches)); - addItems(SOURCE_TYPE_CUSTOM_SWITCH, RawSource::SwitchesGroup, fw->getCapability(LogicalSwitches)); + addItems(SOURCE_TYPE_SWITCH, RawSource::SwitchesGroup, board->getCapability(Board::Switches)); + addItems(SOURCE_TYPE_CUSTOM_SWITCH, RawSource::SwitchesGroup, firmware->getCapability(LogicalSwitches)); addItems(SOURCE_TYPE_CYC, RawSource::SourcesGroup, CPN_MAX_CYC); - addItems(SOURCE_TYPE_PPM, RawSource::SourcesGroup, fw->getCapability(TrainerInputs)); - addItems(SOURCE_TYPE_CH, RawSource::SourcesGroup, fw->getCapability(Outputs)); + addItems(SOURCE_TYPE_PPM, RawSource::SourcesGroup, firmware->getCapability(TrainerInputs)); + addItems(SOURCE_TYPE_CH, RawSource::SourcesGroup, firmware->getCapability(Outputs)); addItems(SOURCE_TYPE_SPECIAL, RawSource::TelemGroup, 5); - addItems(SOURCE_TYPE_TELEMETRY, RawSource::TelemGroup, fw->getCapability(Sensors) * 3); - addItems(SOURCE_TYPE_GVAR, RawSource::GVarsGroup, fw->getCapability(Gvars)); + addItems(SOURCE_TYPE_TELEMETRY, RawSource::TelemGroup, firmware->getCapability(Sensors) * 3); + addItems(SOURCE_TYPE_GVAR, RawSource::GVarsGroup, firmware->getCapability(Gvars)); } void RawSourceItemModel::setDynamicItemData(QStandardItem * item, const RawSource & src) const { - Board::Type board = getCurrentBoard(); - item->setText(src.toString(modelData, generalSettings, board)); - item->setData(src.isAvailable(modelData, generalSettings, board), IsAvailableRole); + item->setText(src.toString(modelData, generalSettings, boardType)); + item->setData(src.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); } void RawSourceItemModel::addItems(const RawSourceType & type, const int group, const int count, const int start) @@ -68,49 +90,51 @@ void RawSourceItemModel::addItems(const RawSourceType & type, const int group, c } } -void RawSourceItemModel::update() +void RawSourceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSource(item(i)->data(ItemIdRole).toInt())); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), RawSource(item(i)->data(ItemIdRole).toInt())); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // RawSwitchItemModel // -RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - Boards board = Boards(getCurrentBoard()); - Firmware * fw = getCurrentFirmware(); + setId(RawSwitchId); + setUpdateMask(FlightModesUpdated | LogicalSwitchesUpdated | TeleSensorsUpdated); // Descending switch direction: NOT (!) switches addItems(SWITCH_TYPE_ACT, -1); - addItems(SWITCH_TYPE_SENSOR, -fw->getCapability(Sensors)); + addItems(SWITCH_TYPE_SENSOR, -firmware->getCapability(Sensors)); addItems(SWITCH_TYPE_TELEMETRY, -1); - addItems(SWITCH_TYPE_FLIGHT_MODE, -fw->getCapability(FlightModes)); - addItems(SWITCH_TYPE_VIRTUAL, -fw->getCapability(LogicalSwitches)); - addItems(SWITCH_TYPE_ROTARY_ENCODER, -fw->getCapability(RotaryEncoders)); - addItems(SWITCH_TYPE_TRIM, -board.getCapability(Board::NumTrimSwitches)); - addItems(SWITCH_TYPE_MULTIPOS_POT, -(board.getCapability(Board::MultiposPots) * board.getCapability(Board::MultiposPotsPositions))); - addItems(SWITCH_TYPE_SWITCH, -board.getCapability(Board::SwitchPositions)); + addItems(SWITCH_TYPE_FLIGHT_MODE, -firmware->getCapability(FlightModes)); + addItems(SWITCH_TYPE_VIRTUAL, -firmware->getCapability(LogicalSwitches)); + addItems(SWITCH_TYPE_ROTARY_ENCODER, -firmware->getCapability(RotaryEncoders)); + addItems(SWITCH_TYPE_TRIM, -board->getCapability(Board::NumTrimSwitches)); + addItems(SWITCH_TYPE_MULTIPOS_POT, -(board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions))); + addItems(SWITCH_TYPE_SWITCH, -board->getCapability(Board::SwitchPositions)); // Ascending switch direction (including zero) addItems(SWITCH_TYPE_TIMER_MODE, 5); addItems(SWITCH_TYPE_NONE, 1); - addItems(SWITCH_TYPE_SWITCH, board.getCapability(Board::SwitchPositions)); - addItems(SWITCH_TYPE_MULTIPOS_POT, board.getCapability(Board::MultiposPots) * board.getCapability(Board::MultiposPotsPositions)); - addItems(SWITCH_TYPE_TRIM, board.getCapability(Board::NumTrimSwitches)); - addItems(SWITCH_TYPE_ROTARY_ENCODER, fw->getCapability(RotaryEncoders)); - addItems(SWITCH_TYPE_VIRTUAL, fw->getCapability(LogicalSwitches)); - addItems(SWITCH_TYPE_FLIGHT_MODE, fw->getCapability(FlightModes)); + addItems(SWITCH_TYPE_SWITCH, board->getCapability(Board::SwitchPositions)); + addItems(SWITCH_TYPE_MULTIPOS_POT, board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions)); + addItems(SWITCH_TYPE_TRIM, board->getCapability(Board::NumTrimSwitches)); + addItems(SWITCH_TYPE_ROTARY_ENCODER, firmware->getCapability(RotaryEncoders)); + addItems(SWITCH_TYPE_VIRTUAL, firmware->getCapability(LogicalSwitches)); + addItems(SWITCH_TYPE_FLIGHT_MODE, firmware->getCapability(FlightModes)); addItems(SWITCH_TYPE_TELEMETRY, 1); - addItems(SWITCH_TYPE_SENSOR, fw->getCapability(Sensors)); + addItems(SWITCH_TYPE_SENSOR, firmware->getCapability(Sensors)); addItems(SWITCH_TYPE_ON, 1); addItems(SWITCH_TYPE_ONE, 1); addItems(SWITCH_TYPE_ACT, 1); @@ -118,9 +142,8 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett void RawSwitchItemModel::setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const { - const Board::Type board = getCurrentBoard(); - item->setText(rsw.toString(board, generalSettings, modelData)); - item->setData(rsw.isAvailable(modelData, generalSettings, board), IsAvailableRole); + item->setText(rsw.toString(boardType, generalSettings, modelData)); + item->setData(rsw.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); } void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) @@ -171,27 +194,34 @@ void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) } } -void RawSwitchItemModel::update() +void RawSwitchItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSwitch(item(i)->data(ItemIdRole).toInt())); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), RawSwitch(item(i)->data(ItemIdRole).toInt())); - emit dataUpdateComplete(); + emit updateComplete(); + } } // // CurveItemModel // -CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CurveId); + if (!modelData) return; - const int count = getCurrentFirmware()->getCapability(NumCurves); + setUpdateMask(CurvesUpdated); + + const int count = firmware->getCapability(NumCurves); for (int i = -count ; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); @@ -205,35 +235,42 @@ CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, co } } -void CurveItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void CurveItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, index); + CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, value); item->setText(cr.toString(modelData, false)); item->setData(cr.isAvailable(), IsAvailableRole); } -void CurveItemModel::update() +void CurveItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // GVarReferenceItemModel // -GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(GVarRefId); + if (!modelData) return; - const int count = getCurrentFirmware()->getCapability(Gvars); + setUpdateMask(GVarsUpdated | FlightModesUpdated | LogicalSwitchesUpdated); + + const int count = firmware->getCapability(Gvars); + if (count > 0) { addItems(-count); addItems(count); @@ -260,27 +297,33 @@ void GVarReferenceItemModel::setDynamicItemData(QStandardItem * item, const Adju item->setData(ar.isAvailable(), IsAvailableRole); } -void GVarReferenceItemModel::update() +void GVarReferenceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // ThrottleSourceItemModel // -ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(ThrSourceId); + if (!modelData) return; + setUpdateMask(TimersUpdated | InputsUpdated | TeleSensorsUpdated); + for (int i = 0; i < modelData->thrTraceSrcCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); @@ -289,30 +332,35 @@ ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const g } } -void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(modelData->thrTraceSrcToString(index)); - item->setData(modelData->isThrTraceSrcAvailable(generalSettings, index), IsAvailableRole); + item->setText(modelData->thrTraceSrcToString(value)); + item->setData(modelData->isThrTraceSrcAvailable(generalSettings, value), IsAvailableRole); } -void ThrottleSourceItemModel::update() +void ThrottleSourceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // CustomFuncActionItemModel // -CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CustomFuncActionId); + setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); + for (int i = 0; i < AssignFunc::FuncCount; i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); @@ -322,30 +370,35 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con } } -void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(CustomFunctionData(AssignFunc(index)).funcToString(modelData)); - item->setData(CustomFunctionData::isFuncAvailable(index), IsAvailableRole); + item->setText(CustomFunctionData(AssignFunc(value)).funcToString(modelData)); + item->setData(CustomFunctionData::isFuncAvailable(value), IsAvailableRole); } -void CustomFuncActionItemModel::update() +void CustomFuncActionItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // CustomFuncResetParamItemModel // -CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CustomFuncResetParamId); + setUpdateMask(TeleSensorsUpdated); + for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); @@ -354,38 +407,43 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin } } -void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int value) const { CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); - cfd.param = index; + cfd.param = value; item->setText(cfd.paramToString(modelData)); - item->setData(CustomFunctionData::isResetParamAvailable(modelData, index), IsAvailableRole); + item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IsAvailableRole); } -void CustomFuncResetParamItemModel::update() +void CustomFuncResetParamItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + for (int i = 0; i < rowCount(); ++i) + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - emit dataUpdateComplete(); + emit updateComplete(); + } } - // // TelemetrySourceItemModel // -TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - AbstractRawItemDataModel(generalSettings, modelData, parent) +TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(TeleSourceId); + if (!modelData) return; - const int count = getCurrentFirmware()->getCapability(Sensors); + setUpdateMask(TeleSensorsUpdated); + const int count = firmware->getCapability(Sensors); - for (int i = -count + 1; i < count; ++i) { + for (int i = -count; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, ItemIdRole); int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; @@ -397,118 +455,35 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const } } -void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int index) const +void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(SensorData::sourceToString(modelData, index)); - item->setData(SensorData::isSourceAvailable(modelData, index), IsAvailableRole); + item->setText(SensorData::sourceToString(modelData, value)); + item->setData(SensorData::isSourceAvailable(modelData, value), IsAvailableRole); } -void TelemetrySourceItemModel::update() +void TelemetrySourceItemModel::update(const UpdateTrigger trigger) { - emit dataAboutToBeUpdated(); + if (doUpdate(trigger)) { + emit aboutToBeUpdated(); - for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); - - emit dataUpdateComplete(); -} - - -// -// CommonItemModels TODO rename to DynamicDataModels -// - -CommonItemModels::CommonItemModels(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent) : - QObject(parent) -{ - m_rawSourceItemModel = new RawSourceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("raw source"), qobject_cast(m_rawSourceItemModel)); - - m_rawSwitchItemModel = new RawSwitchItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("raw switch"), qobject_cast(m_rawSwitchItemModel)); - - m_curveItemModel = new CurveItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("curve"), qobject_cast(m_curveItemModel)); - - m_gvarReferenceItemModel = new GVarReferenceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("gvar ref"), qobject_cast(m_gvarReferenceItemModel)); - - m_throttleSourceItemModel = new ThrottleSourceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("thr source"), qobject_cast(m_throttleSourceItemModel)); - - m_customFuncActionItemModel = new CustomFuncActionItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("cf action"), qobject_cast(m_customFuncActionItemModel)); - - m_customFuncResetParamItemModel = new CustomFuncResetParamItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("cf reset param"), qobject_cast(m_customFuncResetParamItemModel)); - - m_telemetrySourceItemModel = new TelemetrySourceItemModel(generalSettings, modelData, parent); - //dumpModelContents(QString("tele source"), qobject_cast(m_telemetrySourceItemModel)); -} - -CommonItemModels::~CommonItemModels() -{ -} - -void CommonItemModels::update(const RadioModelObjects radioModelObjects) -{ - switch (radioModelObjects) { - case RMO_CHANNELS: - m_throttleSourceItemModel->update(); - // no break - case RMO_TIMERS: - m_customFuncActionItemModel->update(); - // no break - case RMO_INPUTS: - case RMO_TELEMETRY_SENSORS: - m_rawSourceItemModel->update(); - m_customFuncResetParamItemModel->update(); - m_telemetrySourceItemModel->update(); - break; - - case RMO_GLOBAL_VARIABLES: - m_gvarReferenceItemModel->update(); - m_customFuncActionItemModel->update(); - // no break - case RMO_FLIGHT_MODES: - case RMO_LOGICAL_SWITCHES: - m_rawSourceItemModel->update(); - m_rawSwitchItemModel->update(); - break; - - case RMO_CURVES: - m_curveItemModel->update(); - break; - - case RMO_SCRIPTS: - // no need to refresh - break; - - default: - qDebug() << "Unknown RadioModelObject:" << radioModelObjects; - } -} - - -void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel) -{ - if (dataModel) { - qDebug() << "model:" << desc << "rows:" << dataModel->rowCount(); - for (int i = 0; i < dataModel->rowCount(); ++i) { - qDebug() << "row:" << i << "text:" << dataModel->item(i)->data(Qt::DisplayRole).toString() - << "id:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemIdRole).toInt() - << "avail:" << dataModel->item(i)->data(AbstractRawItemDataModel::IsAvailableRole).toBool() - << "flags:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemFlagsRole).toInt() - << "type:" << dataModel->item(i)->data(AbstractRawItemDataModel::ItemTypeRole).toInt(); + for (int i = 0; i < rowCount(); ++i) { + setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); } + + emit updateComplete(); } - else - qDebug() << "model:" << desc << "did not cast"; } -CurveRefTypeItemModel::CurveRefTypeItemModel(QObject * parent) : - AbstractStandardItemModel(parent) +// +// CurveRefTypeItemModel +// + +CurveRefTypeItemModel::CurveRefTypeItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CurveRefTypeId); + for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::typeToString((CurveReference::CurveRefType)i)); @@ -518,9 +493,16 @@ CurveRefTypeItemModel::CurveRefTypeItemModel(QObject * parent) : } } -CurveRefFuncItemModel::CurveRefFuncItemModel(QObject * parent) : - AbstractStandardItemModel(parent) +// +// CurveRefFuncItemModel +// + +CurveRefFuncItemModel::CurveRefFuncItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { + setId(CurveRefFuncId); + for (int i = 1; i <= CurveReference::functionCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::functionToString(i)); @@ -529,3 +511,106 @@ CurveRefFuncItemModel::CurveRefFuncItemModel(QObject * parent) : appendRow(modelItem); } } + +// +// ItemModelsFactory +// + +ItemModelsFactory::ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : + generalSettings(generalSettings), + modelData(modelData) +{ + firmware = getCurrentFirmware(); + board = new Boards(getCurrentBoard()); + boardType = getCurrentBoard(); +} + +ItemModelsFactory::~ItemModelsFactory() +{ + unregisterItemModels(); + delete board; +} + +void ItemModelsFactory::addItemModel(const AbstractItemModel::ItemModelId id) +{ + switch (id) { + case AbstractItemModel::RawSourceId: + registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::RawSwitchId: + registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveId: + registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::GVarRefId: + registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::ThrSourceId: + registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncActionId: + registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CustomFuncResetParamId: + registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::TeleSourceId: + registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefTypeId: + registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + case AbstractItemModel::CurveRefFuncId: + registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); + break; + default: + qDebug() << "Error: unknown item model: id"; + break; + } +} + +void ItemModelsFactory::registerItemModel(AbstractItemModel * itemModel) +{ + if (itemModel && !getItemModel(itemModel->getId())) { + registeredItemModels.push_back(itemModel); + } +} + +void ItemModelsFactory::unregisterItemModels() +{ + foreach (AbstractItemModel *itemModel, registeredItemModels) { + delete itemModel; + } +} + +void ItemModelsFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) +{ + AbstractItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +AbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +void ItemModelsFactory::update(const AbstractItemModel::UpdateTrigger trigger) +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + itemModel->update(trigger); + } +} + +void ItemModelsFactory::dumpAllItemModelContents() const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + AbstractItemModel::dumpItemModelContents(itemModel); + } +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/rawitemdatamodels.h index 8c8a78cc4..89f24acdc 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/rawitemdatamodels.h @@ -30,13 +30,24 @@ class GeneralSettings; class ModelData; class AdjustmentReference; -class AbstractStandardItemModel: public QStandardItemModel +class AbstractItemModel: public QStandardItemModel { Q_OBJECT public: - explicit AbstractStandardItemModel(QObject * parent = nullptr) : - QStandardItemModel(parent) - {} + enum ItemModelId { + UnknownId, + RawSourceId, + RawSwitchId, + CurveId, + GVarRefId, + ThrSourceId, + CustomFuncActionId, + CustomFuncResetParamId, + TeleSourceId, + CurveRefTypeId, + CurveRefFuncId + }; + Q_ENUM(ItemModelId) enum DataRoles { ItemIdRole = Qt::UserRole, @@ -52,203 +63,254 @@ class AbstractStandardItemModel: public QStandardItemModel PositiveGroup = 0x04 }; Q_ENUM(DataGroups) -}; -class AbstractRawItemDataModel: public AbstractStandardItemModel -{ - Q_OBJECT - public: - explicit AbstractRawItemDataModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr) : - AbstractStandardItemModel(parent), + enum UpdateTrigger { + SystemRefresh = 1 << 0, + ChannelsUpdated = 1 << 1, + CurvesUpdated = 1 << 2, + FlightModesUpdated = 1 << 3, + GVarsUpdated = 1 << 4, + InputsUpdated = 1 << 5, + LogicalSwitchesUpdated = 1 << 6, + ScriptsUpdated = 1 << 7, + TeleSensorsUpdated = 1 << 8, + TimersUpdated = 1 << 9, + AllTriggers = SystemRefresh | ChannelsUpdated | CurvesUpdated | FlightModesUpdated | GVarsUpdated | InputsUpdated | + LogicalSwitchesUpdated | ScriptsUpdated | TeleSensorsUpdated | TimersUpdated, + NoTriggers = 0 + }; + Q_ENUM(UpdateTrigger) + + explicit AbstractItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + QStandardItemModel(nullptr), generalSettings(generalSettings), - modelData(modelData) + modelData(modelData), + firmware(firmware), + board(board), + boardType(boardType) {} - public slots: - virtual void update() = 0; + virtual ~AbstractItemModel() {}; - signals: - void dataAboutToBeUpdated(); - void dataUpdateComplete(); + void setId(ItemModelId id) { m_id = id; } + ItemModelId getId () const { return m_id; } + + void setUpdateMask(const int mask) { m_updateMask = mask; } + int getUpdateMask() const { return m_updateMask; } + inline bool doUpdate(const UpdateTrigger trigger) const { return m_updateMask & (int)trigger; } + + AbstractItemModel * getItemModel(const ItemModelId id) const; + + static void dumpItemModelContents(AbstractItemModel * itemModel); + + public slots: + virtual void update(const UpdateTrigger trigger = SystemRefresh) = 0; protected: const GeneralSettings * generalSettings; const ModelData * modelData; + Firmware * firmware; + const Boards * board; + const Board::Type boardType; + + private: + ItemModelId m_id = UnknownId; + int m_updateMask = 0; }; - -class RawSourceItemModel: public AbstractRawItemDataModel +class AbstractStaticItemModel: public AbstractItemModel { Q_OBJECT public: - explicit RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit AbstractStaticItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractItemModel(generalSettings, modelData, firmware, board, boardType) {} + virtual ~AbstractStaticItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override final {} +}; + +class AbstractDynamicItemModel: public AbstractItemModel +{ + Q_OBJECT + public: + explicit AbstractDynamicItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractItemModel(generalSettings, modelData, firmware, board, boardType) {} + virtual ~AbstractDynamicItemModel() {}; + + public slots: + virtual void update(const UpdateTrigger trigger = SystemRefresh) {} + + signals: + void aboutToBeUpdated(); + void updateComplete(); +}; + +class RawSourceItemModel: public AbstractDynamicItemModel +{ + Q_OBJECT + public: + explicit RawSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~RawSourceItemModel() {}; + + public slots: + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const RawSource & src) const; + virtual void setDynamicItemData(QStandardItem * item, const RawSource & src) const; void addItems(const RawSourceType & type, const int group, const int count, const int start = 0); }; - -class RawSwitchItemModel: public AbstractRawItemDataModel +class RawSwitchItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit RawSwitchItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~RawSwitchItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const; + virtual void setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const; void addItems(const RawSwitchType & type, int count); }; - -class CurveItemModel: public AbstractRawItemDataModel +class CurveItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit CurveItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CurveItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class GVarReferenceItemModel: public AbstractRawItemDataModel +class GVarReferenceItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit GVarReferenceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~GVarReferenceItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; + virtual void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; void addItems(int count); }; - -class ThrottleSourceItemModel: public AbstractRawItemDataModel +class ThrottleSourceItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit ThrottleSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~ThrottleSourceItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class CustomFuncActionItemModel: public AbstractRawItemDataModel +class CustomFuncActionItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit CustomFuncActionItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CustomFuncActionItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class CustomFuncResetParamItemModel: public AbstractRawItemDataModel +class CustomFuncResetParamItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit CustomFuncResetParamItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CustomFuncResetParamItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class TelemetrySourceItemModel: public AbstractRawItemDataModel +class TelemetrySourceItemModel: public AbstractDynamicItemModel { Q_OBJECT public: - explicit TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); + explicit TelemetrySourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~TelemetrySourceItemModel() {}; public slots: - void update() override; + virtual void update(const UpdateTrigger trigger = SystemRefresh) override; protected: - void setDynamicItemData(QStandardItem * item, const int index) const; + virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; - -class CommonItemModels: public QObject +class CurveRefTypeItemModel : public AbstractStaticItemModel { Q_OBJECT public: - enum RadioModelObjects { - RMO_CHANNELS, - RMO_CURVES, - RMO_FLIGHT_MODES, - RMO_GLOBAL_VARIABLES, - RMO_INPUTS, - RMO_LOGICAL_SWITCHES, - RMO_SCRIPTS, - RMO_TELEMETRY_SENSORS, - RMO_TIMERS - }; - Q_ENUM(RadioModelObjects) - - explicit CommonItemModels(const GeneralSettings * const generalSettings, const ModelData * const modelData, QObject * parent = nullptr); - ~CommonItemModels(); - - void update(const RadioModelObjects radioModelObjects); - RawSourceItemModel * rawSourceItemModel() const { return m_rawSourceItemModel; } - RawSwitchItemModel * rawSwitchItemModel() const { return m_rawSwitchItemModel; } - CurveItemModel * curveItemModel() const { return m_curveItemModel; } - GVarReferenceItemModel * gvarItemModel() const { return m_gvarReferenceItemModel; } - ThrottleSourceItemModel * throttleSourceItemModel() const { return m_throttleSourceItemModel; } - CustomFuncActionItemModel * customFuncActionItemModel() const { return m_customFuncActionItemModel; } - CustomFuncResetParamItemModel * customFuncResetParamItemModel() const { return m_customFuncResetParamItemModel; } - TelemetrySourceItemModel * telemetrySourceItemModel() const { return m_telemetrySourceItemModel; } - - private: - RawSourceItemModel *m_rawSourceItemModel; - RawSwitchItemModel *m_rawSwitchItemModel; - CurveItemModel *m_curveItemModel; - GVarReferenceItemModel *m_gvarReferenceItemModel; - ThrottleSourceItemModel *m_throttleSourceItemModel; - CustomFuncActionItemModel *m_customFuncActionItemModel; - CustomFuncResetParamItemModel *m_customFuncResetParamItemModel; - TelemetrySourceItemModel *m_telemetrySourceItemModel; + explicit CurveRefTypeItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CurveRefTypeItemModel() {}; }; -void dumpModelContents(QString desc, AbstractRawItemDataModel * dataModel); - - -class CurveRefTypeItemModel : public AbstractStandardItemModel +class CurveRefFuncItemModel : public AbstractStaticItemModel { Q_OBJECT public: - explicit CurveRefTypeItemModel(QObject * parent = nullptr); + explicit CurveRefFuncItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~CurveRefFuncItemModel() {}; }; -class CurveRefFuncItemModel : public AbstractStandardItemModel +class ItemModelsFactory { - Q_OBJECT public: - explicit CurveRefFuncItemModel(QObject * parent = nullptr); -}; + ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); + virtual ~ItemModelsFactory(); + void addItemModel(const AbstractItemModel::ItemModelId id); + void registerItemModel(AbstractItemModel * itemModel); + void unregisterItemModels(); + void unregisterItemModel(const AbstractItemModel::ItemModelId id); + AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; + void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); + void dumpAllItemModelContents() const; + + protected: + const GeneralSettings * generalSettings; + const ModelData * modelData; + Firmware * firmware; + Boards * board; + Board::Type boardType; + QVector registeredItemModels; +}; #endif // RAWITEMDATAMODELS_H diff --git a/companion/src/datamodels/rawitemfilteredmodel.cpp b/companion/src/datamodels/rawitemfilteredmodel.cpp index abe9ca08e..1cddff04d 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.cpp +++ b/companion/src/datamodels/rawitemfilteredmodel.cpp @@ -20,24 +20,24 @@ #include "rawitemfilteredmodel.h" -RawItemFilteredModel::RawItemFilteredModel(QAbstractItemModel * sourceModel, int flags, QObject * parent) : - QSortFilterProxyModel(parent), +FilteredItemModel::FilteredItemModel(AbstractItemModel * sourceModel, int flags) : + QSortFilterProxyModel(nullptr), filterFlags(0) { - setFilterRole(AbstractRawItemDataModel::IsAvailableRole); + setFilterRole(AbstractItemModel::IsAvailableRole); setFilterKeyColumn(0); setFilterFlags(flags); setDynamicSortFilter(true); setSourceModel(sourceModel); - AbstractRawItemDataModel * itemModel = qobject_cast(sourceModel); + AbstractDynamicItemModel * itemModel = qobject_cast(sourceModel); if (itemModel) { - connect(itemModel, &AbstractRawItemDataModel::dataAboutToBeUpdated, this, &RawItemFilteredModel::onDataAboutToBeUpdated); - connect(itemModel, &AbstractRawItemDataModel::dataUpdateComplete, this, &RawItemFilteredModel::onDataUpdateComplete); + connect(itemModel, &AbstractDynamicItemModel::aboutToBeUpdated, this, &FilteredItemModel::onAboutToBeUpdated); + connect(itemModel, &AbstractDynamicItemModel::updateComplete, this, &FilteredItemModel::onUpdateComplete); } } -void RawItemFilteredModel::setFilterFlags(int flags) +void FilteredItemModel::setFilterFlags(int flags) { if (filterFlags != flags) { filterFlags = flags; @@ -45,7 +45,7 @@ void RawItemFilteredModel::setFilterFlags(int flags) } } -bool RawItemFilteredModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const +bool FilteredItemModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const { const QModelIndex & srcIdx = sourceModel()->index(sourceRow, 0, sourceParent); if (!srcIdx.isValid() || !sourceModel()->data(srcIdx, filterRole()).toBool()) @@ -55,23 +55,137 @@ bool RawItemFilteredModel::filterAcceptsRow(int sourceRow, const QModelIndex & s return true; bool ok; - const int flags = sourceModel()->data(srcIdx, AbstractRawItemDataModel::ItemFlagsRole).toInt(&ok); + const int flags = sourceModel()->data(srcIdx, AbstractItemModel::ItemFlagsRole).toInt(&ok); return (ok && (!flags || (filterFlags & flags))); } -void RawItemFilteredModel::update() const +void FilteredItemModel::update() const { - AbstractRawItemDataModel * itemModel = qobject_cast(sourceModel()); + AbstractDynamicItemModel * itemModel = qobject_cast(sourceModel()); if (itemModel) itemModel->update(); } -void RawItemFilteredModel::onDataAboutToBeUpdated() +void FilteredItemModel::onAboutToBeUpdated() { - emit dataAboutToBeUpdated(); + emit aboutToBeUpdated(); } -void RawItemFilteredModel::onDataUpdateComplete() +void FilteredItemModel::onUpdateComplete() { - emit dataUpdateComplete(); + emit updateComplete(); +} + +// static +void FilteredItemModel::dumpItemModelContents(FilteredItemModel * itemModel) +{ + if (itemModel) { + qDebug() << "id:" << itemModel->getId() << "name:" << itemModel->getName(); + + for (int i = 0; i < itemModel->rowCount(); ++i) { + qDebug() << "row:" << i + << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + } + } + else + qDebug() << "Error: model did not cast"; +} + +// +// FilteredItemModelsFactory +// + +FilteredItemModelsFactory::FilteredItemModelsFactory() +{ +} + +FilteredItemModelsFactory::~FilteredItemModelsFactory() +{ + unregisterItemModels(); +} + +int FilteredItemModelsFactory::registerItemModel(FilteredItemModel * itemModel, QString name) +{ + if (itemModel) { + if (isItemModelRegistered(name)) + qDebug() << "Warning: item model already registered with name:" << name; + const int id = registeredItemModels.count(); + itemModel->setId(id); + itemModel->setName(name); + registeredItemModels.push_back(itemModel); + return id; + } + return 0; +} + +void FilteredItemModelsFactory::unregisterItemModels() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + delete itemModel; + } +} + +void FilteredItemModelsFactory::unregisterItemModel(const int id) +{ + FilteredItemModel * itemModel = getItemModel(id); + if (itemModel) + delete itemModel; +} + +bool FilteredItemModelsFactory::isItemModelRegistered(const int id) const +{ + return getItemModel(id); +} + +bool FilteredItemModelsFactory::isItemModelRegistered(const QString name) const +{ + return getItemModel(name); +} + +FilteredItemModel * FilteredItemModelsFactory::getItemModel(const int id) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getId() == id) + return itemModel; + } + + return nullptr; +} + +FilteredItemModel * FilteredItemModelsFactory::getItemModel(const QString name) const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + if (itemModel->getName() == name) + return itemModel; + } + + return nullptr; +} + +void FilteredItemModelsFactory::update() +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + itemModel->update(); + } +} + +void FilteredItemModelsFactory::dumpItemModelContents(const int id) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(id)); +} + +void FilteredItemModelsFactory::dumpItemModelContents(const QString name) const +{ + FilteredItemModel::dumpItemModelContents(getItemModel(name)); +} + +void FilteredItemModelsFactory::dumpAllItemModelContents() const +{ + foreach (FilteredItemModel * itemModel, registeredItemModels) { + FilteredItemModel::dumpItemModelContents(itemModel); + } } diff --git a/companion/src/datamodels/rawitemfilteredmodel.h b/companion/src/datamodels/rawitemfilteredmodel.h index 3274460f7..08bc65993 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.h +++ b/companion/src/datamodels/rawitemfilteredmodel.h @@ -23,41 +23,90 @@ #include "rawitemdatamodels.h" +#include #include +#include -class GeneralSettings; -class ModelData; - -class RawItemFilteredModel: public QSortFilterProxyModel +class FilteredItemModel: public QSortFilterProxyModel { Q_OBJECT public: enum DataFilters { - AllFilter = AbstractRawItemDataModel::NegativeGroup | AbstractRawItemDataModel::NoneGroup | AbstractRawItemDataModel::PositiveGroup, - AllExcludeNoneFilter = AllFilter &~ AbstractRawItemDataModel::NoneGroup, - NegativeFilter = AbstractRawItemDataModel::NegativeGroup | AbstractRawItemDataModel::NoneGroup, - NegativeExcludeNoneFilter = AbstractRawItemDataModel::NegativeGroup, - PositiveFilter = AbstractRawItemDataModel::PositiveGroup | AbstractRawItemDataModel::NoneGroup, - PositiveExcludeNoneFilter = AbstractRawItemDataModel::PositiveGroup + AllFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup | AbstractItemModel::PositiveGroup, + AllExcludeNoneFilter = AllFilter &~ AbstractItemModel::NoneGroup, + NegativeFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup, + NegativeExcludeNoneFilter = AbstractItemModel::NegativeGroup, + PositiveFilter = AbstractItemModel::PositiveGroup | AbstractItemModel::NoneGroup, + PositiveExcludeNoneFilter = AbstractItemModel::PositiveGroup }; Q_ENUM(DataFilters) - explicit RawItemFilteredModel(QAbstractItemModel * sourceModel, int flags, QObject * parent = nullptr); - explicit RawItemFilteredModel(QAbstractItemModel * sourceModel, QObject * parent = nullptr) : RawItemFilteredModel(sourceModel, 0, parent) {} + explicit FilteredItemModel(AbstractItemModel * sourceModel, int flags); + explicit FilteredItemModel(AbstractItemModel * sourceModel) : + FilteredItemModel(sourceModel, 0) {} + virtual ~FilteredItemModel() {}; + + void setId(int id) { m_id = id; } + int getId() const { return m_id; }; + void setName(QString name) { m_name = name; } + QString getName() const { return m_name; } + + static void dumpItemModelContents(FilteredItemModel * itemModel); public slots: void setFilterFlags(int flags); void update() const; - void onDataAboutToBeUpdated(); - void onDataUpdateComplete(); + void onAboutToBeUpdated(); + void onUpdateComplete(); signals: - void dataAboutToBeUpdated(); - void dataUpdateComplete(); + void aboutToBeUpdated(); + void updateComplete(); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const override; int filterFlags; + + private: + int m_id = 0; + QString m_name = ""; }; + +class FilteredItemModelsFactory +{ + public: + FilteredItemModelsFactory(); + virtual ~FilteredItemModelsFactory(); + + int registerItemModel(FilteredItemModel * itemModel, QString name); + void unregisterItemModels(); + void unregisterItemModel(const int id); + bool isItemModelRegistered(const int id) const; + bool isItemModelRegistered(const QString name) const; + FilteredItemModel * getItemModel(const int id) const; + FilteredItemModel * getItemModel(const QString name) const; + void update(); + + void dumpItemModelContents(const int id) const; + void dumpItemModelContents(const QString name) const; + void dumpAllItemModelContents() const; + + protected: + QVector registeredItemModels; +}; + + +#define FILTEREDITEMMODELCONNECT(fltrmodel) \ + connect(fltrmodel, SIGNAL(aboutToBeUpdated()), this, SLOT(onItemModelAboutToBeUpdated())); \ + connect(fltrmodel, SIGNAL(updateComplete()), this, SLOT(onItemModelUpdateComplete())); + +#define FILTEREDITEMMODEL(fltrmodel, class, modelid, flags) \ + fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid), flags); \ + FILTEREDITEMMODELCONNECT(fltrmodel) + +#define FILTEREDITEMMODELNOFLAGS(fltrmodel, class, modelid) \ + fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid)); \ + FILTEREDITEMMODELCONNECT(fltrmodel) + #endif // RAWITEMFILTEREDMODEL_H diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 688b4188f..4d961630a 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -144,37 +144,41 @@ int CurveReference::functionCount() * CurveReferenceUIManager */ -CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, - QObject * parent) : +CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, + FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, + FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : QObject(parent), curveTypeCB(nullptr), curveGVarCB(nullptr), curveValueSB(nullptr), curveValueCB(curveValueCB), - curve(curve), + curveRef(curveRef), model(model), lock(false), curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel) + gvarItemModel(gvarItemModel), + typeItemModel(typeItemModel), + funcItemModel(funcItemModel) { init(parent); } CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, - QComboBox * curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, - QObject * parent) : + QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, + FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, + FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), curveGVarCB(curveGVarCB), curveValueSB(curveValueSB), curveValueCB(curveValueCB), - curve(curve), + curveRef(curveRef), model(model), lock(false), curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel) + gvarItemModel(gvarItemModel), + typeItemModel(typeItemModel), + funcItemModel(funcItemModel) { init(parent); } @@ -188,9 +192,8 @@ void CurveReferenceUIManager::init(QObject * parent) hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { - typeItemModel = new RawItemFilteredModel(new CurveRefTypeItemModel(parent), parent); curveTypeCB->setModel(typeItemModel); - curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curve.type)); + curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curveRef.type)); connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); } @@ -204,7 +207,6 @@ void CurveReferenceUIManager::init(QObject * parent) } if (curveValueCB) { - funcItemModel = new RawItemFilteredModel(new CurveRefFuncItemModel(parent), parent); curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); curveValueCB->setMaxVisibleItems(10); connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); @@ -222,12 +224,12 @@ void CurveReferenceUIManager::update() lock = true; int widgetsMask = 0; - if (curve.type == CurveReference::CURVE_REF_DIFF || curve.type == CurveReference::CURVE_REF_EXPO) { + if (curveRef.type == CurveReference::CURVE_REF_DIFF || curveRef.type == CurveReference::CURVE_REF_EXPO) { if (hasCapabilityGvars) widgetsMask |= CURVE_REF_UI_GVAR_SHOW; - if (curve.isValueNumber()) { + if (curveRef.isValueNumber()) { curveGVarCB->setChecked(false); - curveValueSB->setValue(curve.value); + curveValueSB->setValue(curveRef.value); widgetsMask |= CURVE_REF_UI_VALUE_SHOW; } else { @@ -240,7 +242,7 @@ void CurveReferenceUIManager::update() } if(curveTypeCB) { - curveTypeCB->setCurrentIndex(curveTypeCB->findData(curve.type)); + curveTypeCB->setCurrentIndex(curveTypeCB->findData(curveRef.type)); curveTypeCB->show(); } if(curveGVarCB) @@ -248,7 +250,7 @@ void CurveReferenceUIManager::update() if(curveValueSB) curveValueSB->setVisible(widgetsMask & CURVE_REF_UI_VALUE_SHOW); if(curveValueCB) { - if (curve.isValueReference()) + if (curveRef.isValueReference()) populateValueCB(curveValueCB); curveValueCB->setVisible(widgetsMask & CURVE_REF_UI_REF_SHOW); } @@ -259,7 +261,7 @@ void CurveReferenceUIManager::update() void CurveReferenceUIManager::gvarCBChanged(int state) { if (!lock) { - curve.value = CurveReference::getDefaultValue(curve.type, state); + curveRef.value = CurveReference::getDefaultValue(curveRef.type, state); update(); } } @@ -268,7 +270,7 @@ void CurveReferenceUIManager::typeChanged(int value) { if (!lock) { CurveReference::CurveRefType type = (CurveReference::CurveRefType)curveTypeCB->itemData(curveTypeCB->currentIndex()).toInt(); - curve = CurveReference(type, CurveReference::getDefaultValue(type)); + curveRef = CurveReference(type, CurveReference::getDefaultValue(type)); update(); } } @@ -276,7 +278,7 @@ void CurveReferenceUIManager::typeChanged(int value) void CurveReferenceUIManager::valueSBChanged() { if (!lock) { - curve.value = curveValueSB->value(); + curveRef.value = curveValueSB->value(); update(); } } @@ -284,7 +286,7 @@ void CurveReferenceUIManager::valueSBChanged() void CurveReferenceUIManager::valueCBChanged() { if (!lock) { - curve.value = curveValueCB->itemData(curveValueCB->currentIndex()).toInt(); + curveRef.value = curveValueCB->itemData(curveValueCB->currentIndex()).toInt(); update(); } } @@ -292,7 +294,7 @@ void CurveReferenceUIManager::valueCBChanged() void CurveReferenceUIManager::populateValueCB(QComboBox * cb) { if (cb) { - switch (curve.type) { + switch (curveRef.type) { case CurveReference::CURVE_REF_DIFF: case CurveReference::CURVE_REF_EXPO: cb->setModel(gvarItemModel); @@ -307,6 +309,6 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb) break; } - cb->setCurrentIndex(cb->findData(curve.value)); + cb->setCurrentIndex(cb->findData(curveRef.value)); } } diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 119aaf597..3d61e3cb5 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -29,7 +29,7 @@ class ModelData; -class RawItemFilteredModel; +class FilteredItemModel; class CurveReference { @@ -90,10 +90,14 @@ class CurveReferenceUIManager : public QObject { public: CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, - RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); + FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, + FilteredItemModel * funcItemModel, QObject * parent = nullptr); + CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, RawItemFilteredModel * curveItemModel, - RawItemFilteredModel * gvarItemModel, QObject * parent = nullptr); + CurveReference & curve, const ModelData & model, FilteredItemModel * curveItemModel, + FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, + FilteredItemModel * funcItemModel, QObject * parent = nullptr); + virtual ~CurveReferenceUIManager(); protected slots: @@ -103,24 +107,22 @@ class CurveReferenceUIManager : public QObject { void valueCBChanged(); void update(); - protected: + private: QComboBox *curveTypeCB; QCheckBox *curveGVarCB; QSpinBox *curveValueSB; QComboBox *curveValueCB; - CurveReference & curve; + CurveReference & curveRef; const ModelData & model; bool lock; bool hasCapabilityGvars; - RawItemFilteredModel * curveItemModel; - RawItemFilteredModel * gvarItemModel; - RawItemFilteredModel * typeItemModel; - RawItemFilteredModel * funcItemModel; + FilteredItemModel * curveItemModel; + FilteredItemModel * gvarItemModel; + FilteredItemModel * typeItemModel; + FilteredItemModel * funcItemModel; - void populateValueCB(QComboBox * cb); - - private: void init(QObject * parent); + void populateValueCB(QComboBox * cb); }; #endif // CURVEREFERENCE_H diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index bff9d3f83..c2a38b61d 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -56,10 +56,14 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } } - commonItemModels = new CommonItemModels(&generalSettings, nullptr, this); + sharedItemModels = new ItemModelsFactory(&generalSettings, nullptr); + sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); + sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); - addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, commonItemModels), tr("Global Functions")); + addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, sharedItemModels), tr("Global Functions")); addTab(new TrainerPanel(this, generalSettings, firmware), tr("Trainer")); addTab(new HardwarePanel(this, generalSettings, firmware), tr("Hardware")); addTab(new CalibrationPanel(this, generalSettings, firmware), tr("Calibration")); @@ -70,6 +74,7 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir GeneralEdit::~GeneralEdit() { delete ui; + delete sharedItemModels; } void GeneralEdit::closeEvent(QCloseEvent *event) diff --git a/companion/src/generaledit/generaledit.h b/companion/src/generaledit/generaledit.h index 3f73a9e01..b4a2ea3cc 100644 --- a/companion/src/generaledit/generaledit.h +++ b/companion/src/generaledit/generaledit.h @@ -25,7 +25,7 @@ #include "eeprominterface.h" #include "genericpanel.h" -class CommonItemModels; +class ItemModelsFactory; namespace Ui { class GeneralEdit; @@ -73,7 +73,7 @@ class GeneralEdit : public QDialog QVector panels; void addTab(GenericPanel *panel, QString text); void closeEvent(QCloseEvent *event); - CommonItemModels *commonItemModels; + ItemModelsFactory *sharedItemModels; }; #endif // _GENERALEDIT_H_ diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index d2818368e..cd83675dc 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -96,16 +96,14 @@ void LimitsGroup::updateMinMax(int max) } } } -ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { chnCapability = firmware->getCapability(Outputs); int channelNameMaxLen = firmware->getCapability(ChannelsName); - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), RawItemFilteredModel::AllFilter, this); - connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &ChannelsPanel::onModelDataAboutToBeUpdated); - connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &ChannelsPanel::onModelDataUpdateComplete); + FILTEREDITEMMODELNOFLAGS(curveFilteredModel, ChannelsPanel, CurveId) QStringList headerLabels; headerLabels << "#"; @@ -214,6 +212,7 @@ ChannelsPanel::~ChannelsPanel() delete centerSB[i]; delete symlimitsChk[i]; } + delete curveFilteredModel; } void ChannelsPanel::symlimitsEdited() @@ -474,12 +473,12 @@ void ChannelsPanel::swapData(int idx1, int idx2) } } -void ChannelsPanel::onModelDataAboutToBeUpdated() +void ChannelsPanel::onItemModelAboutToBeUpdated() { lock = true; } -void ChannelsPanel::onModelDataUpdateComplete() +void ChannelsPanel::onItemModelUpdateComplete() { update(); lock = false; @@ -487,5 +486,5 @@ void ChannelsPanel::onModelDataUpdateComplete() void ChannelsPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_CHANNELS); + sharedItemModels->update(AbstractItemModel::ChannelsUpdated); } diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index a481feec2..7e2d3f3fa 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -26,8 +26,8 @@ #include -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel"; @@ -57,8 +57,8 @@ class ChannelsPanel : public ModelPanel Q_OBJECT public: - ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); - ~ChannelsPanel(); + ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + virtual ~ChannelsPanel(); public slots: void refreshExtendedLimits(); @@ -81,8 +81,8 @@ class ChannelsPanel : public ModelPanel void cmClear(bool prompt = true); void cmClearAll(); void onCustomContextMenuRequested(QPoint pos); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: bool hasClipboardData(QByteArray * data = nullptr) const; @@ -100,8 +100,8 @@ class ChannelsPanel : public ModelPanel QCheckBox *symlimitsChk[CPN_MAX_CHNOUT]; int selectedIndex; int chnCapability; - CommonItemModels * commonItemModels; - RawItemFilteredModel *curveFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *curveFilteredModel; void updateItemModels(); }; diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 9e60f30c3..34cda04f9 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -109,11 +109,12 @@ float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax return y; } -CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Curves), currentCurve(0), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { ui->setupUi(this); @@ -812,7 +813,7 @@ void CurvesPanel::swapData(int idx1, int idx2) void CurvesPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_CURVES); + sharedItemModels->update(AbstractItemModel::CurvesUpdated); } CustomScene::CustomScene(QGraphicsView * view) : diff --git a/companion/src/modeledit/curves.h b/companion/src/modeledit/curves.h index 8df3a8528..46ac63a01 100644 --- a/companion/src/modeledit/curves.h +++ b/companion/src/modeledit/curves.h @@ -26,7 +26,7 @@ #include #include -class CommonItemModels; +class ItemModelsFactory; constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve"; @@ -61,7 +61,7 @@ class CurvesPanel : public ModelPanel Q_OBJECT public: - CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~CurvesPanel(); virtual void update(); @@ -121,7 +121,7 @@ class CurvesPanel : public ModelPanel bool moveDownAllowed() const; bool moveUpAllowed() const; void swapData(int idx1, int idx2); - CommonItemModels * commonItemModels; + ItemModelsFactory * sharedItemModels; void updateItemModels(); }; diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index c3ee895b7..94949064b 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -66,10 +66,10 @@ void RepeatComboBox::update() setCurrentIndex(value); } -CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): GenericPanel(parent, model, generalSettings, firmware), functions(model ? model->customFn : generalSettings.customFn), - commonItemModels(commonItemModels), mediaPlayerCurrent(-1), mediaPlayer(nullptr), modelsUpdateCnt(0) @@ -77,21 +77,10 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, lock = true; fswCapability = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions); - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); - - rawSourceAllModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), this); - connect(rawSourceAllModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceAllModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); - - rawSourceInputsModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), RawSource::InputSourceGroups, this); - connect(rawSourceInputsModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceInputsModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); - - rawSourceGVarsModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), RawSource::GVarsGroup, this); - connect(rawSourceGVarsModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &CustomFunctionsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceGVarsModel, &RawItemFilteredModel::dataUpdateComplete, this, &CustomFunctionsPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSwitchFilteredModel, CustomFunctionsPanel, RawSwitchId, model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext) + FILTEREDITEMMODELNOFLAGS(rawSourceAllModel, CustomFunctionsPanel, RawSourceId) + FILTEREDITEMMODEL(rawSourceInputsModel, CustomFunctionsPanel, RawSourceId, RawSource::InputSourceGroups) + FILTEREDITEMMODEL(rawSourceGVarsModel, CustomFunctionsPanel, RawSourceId, RawSource::GVarsGroup) if (!firmware->getCapability(VoicesAsNumbers)) { tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength)); @@ -253,6 +242,10 @@ CustomFunctionsPanel::~CustomFunctionsPanel() { if (mediaPlayer) stopSound(mediaPlayerCurrent); + delete rawSwitchFilteredModel; + delete rawSourceAllModel; + delete rawSourceInputsModel; + delete rawSourceGVarsModel; } void CustomFunctionsPanel::onMediaPlayerStateChanged(QMediaPlayer::State state) @@ -827,13 +820,13 @@ bool CustomFunctionsPanel::moveUpAllowed() const return selectedIndex > 0; } -void CustomFunctionsPanel::onModelDataAboutToBeUpdated() +void CustomFunctionsPanel::onItemModelAboutToBeUpdated() { lock = true; modelsUpdateCnt++; } -void CustomFunctionsPanel::onModelDataUpdateComplete() +void CustomFunctionsPanel::onItemModelUpdateComplete() { modelsUpdateCnt--; if (modelsUpdateCnt < 1) { diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index 9b3e3d238..ef8e68b5f 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -26,8 +26,8 @@ #include -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; class TimerEdit; constexpr char MIMETYPE_CUSTOM_FUNCTION[] = "application/x-companion-custom-function"; @@ -55,8 +55,8 @@ class CustomFunctionsPanel : public GenericPanel Q_OBJECT public: - CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); - ~CustomFunctionsPanel(); + CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + virtual ~CustomFunctionsPanel(); virtual void update(); @@ -83,8 +83,8 @@ class CustomFunctionsPanel : public GenericPanel void cmInsert(); void cmClear(bool prompt = true); void cmClearAll(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: void populateFuncCB(QComboBox *b, unsigned int value); @@ -96,11 +96,10 @@ class CustomFunctionsPanel : public GenericPanel bool moveUpAllowed() const; void swapData(int idx1, int idx2); void resetCBsAndRefresh(int idx); - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSwitchFilteredModel; - RawItemFilteredModel * rawSourceAllModel; - RawItemFilteredModel * rawSourceInputsModel; - RawItemFilteredModel * rawSourceGVarsModel; + FilteredItemModel * rawSwitchFilteredModel; + FilteredItemModel * rawSourceAllModel; + FilteredItemModel * rawSourceInputsModel; + FilteredItemModel * rawSourceGVarsModel; QSet tracksSet; QSet scriptsSet; diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 217f787d2..1bf22c68e 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -24,8 +24,9 @@ #include "helpers.h" ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceModel, - RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel) : + Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, + FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, + FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : QDialog(parent), ui(new Ui::ExpoDialog), model(model), @@ -50,10 +51,10 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, curveItemModel, - gvarItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); - ui->switchesCB->setModel(rawSwitchModel); + ui->switchesCB->setModel(rawSwitchFilteredModel); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(ed->swtch.toValue())); ui->sideCB->setCurrentIndex(ed->mode - 1); @@ -84,7 +85,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G if (firmware->getCapability(VirtualInputs)) { ui->inputName->setMaxLength(firmware->getCapability(InputsLength)); - ui->sourceCB->setModel(rawSourceModel); + ui->sourceCB->setModel(rawSourceFilteredModel); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(ed->srcRaw.toValue())); ui->inputName->setValidator(new QRegExpValidator(rx, this)); ui->inputName->setText(inputName); diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index 0b17900df..427fe9572 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -26,7 +26,7 @@ #include "modelprinter.h" class GVarGroup; -class RawItemFilteredModel; +class FilteredItemModel; class CurveReferenceUIManager; namespace Ui { @@ -37,9 +37,10 @@ class ExpoDialog : public QDialog { Q_OBJECT public: ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expodata, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, RawItemFilteredModel * rawSourceItemModel, - RawItemFilteredModel * rawSwitchItemModel, RawItemFilteredModel * curveItemModel, RawItemFilteredModel * gvarItemModel); - ~ExpoDialog(); + Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, + FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, + FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); + virtual ~ExpoDialog(); protected: void updateScale(); diff --git a/companion/src/modeledit/flightmodes.cpp b/companion/src/modeledit/flightmodes.cpp index 32188feea..a60c5f09b 100644 --- a/companion/src/modeledit/flightmodes.cpp +++ b/companion/src/modeledit/flightmodes.cpp @@ -24,15 +24,16 @@ #include "helpers.h" #include "customdebug.h" -FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseIdx, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSwitchFilteredModel): +FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseIdx, GeneralSettings & generalSettings, Firmware * firmware, + FilteredItemModel * rawSwitchFilteredModel): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::FlightMode), phaseIdx(phaseIdx), phase(model.flightModeData[phaseIdx]) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &FlightModePanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &FlightModePanel::onModelDataUpdateComplete); + connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModePanel::onItemModelAboutToBeUpdated); + connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &FlightModePanel::onItemModelUpdateComplete); ui->labelName->setContextMenuPolicy(Qt::CustomContextMenu); ui->labelName->setToolTip(tr("Popup menu available")); @@ -1361,12 +1362,12 @@ void FlightModePanel::gvSwapData(int idx1, int idx2) } } -void FlightModePanel::onModelDataAboutToBeUpdated() +void FlightModePanel::onItemModelAboutToBeUpdated() { lock = true; } -void FlightModePanel::onModelDataUpdateComplete() +void FlightModePanel::onItemModelUpdateComplete() { update(); lock = false; @@ -1374,11 +1375,13 @@ void FlightModePanel::onModelDataUpdateComplete() /**********************************************************/ -FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::MixesContext, this); + FILTEREDITEMMODEL(rawSwitchFilteredModel, FlightModesPanel, RawSwitchId, RawSwitch::MixesContext) + modesCount = firmware->getCapability(FlightModes); QGridLayout * gridLayout = new QGridLayout(this); @@ -1403,6 +1406,7 @@ FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralS FlightModesPanel::~FlightModesPanel() { + delete rawSwitchFilteredModel; } QString FlightModesPanel::getTabName(int index) @@ -1431,10 +1435,18 @@ void FlightModesPanel::update() emit updated(); } +void FlightModesPanel::onItemModelAboutToBeUpdated() +{ +} + +void FlightModesPanel::onItemModelUpdateComplete() +{ +} + void FlightModesPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_FLIGHT_MODES); - commonItemModels->update(CommonItemModels::RMO_GLOBAL_VARIABLES); + sharedItemModels->update(AbstractItemModel::FlightModesUpdated); + sharedItemModels->update(AbstractItemModel::GVarsUpdated); } void FlightModesPanel::onTabIndexChanged(int index) diff --git a/companion/src/modeledit/flightmodes.h b/companion/src/modeledit/flightmodes.h index ca3619c6c..e74dec8bb 100644 --- a/companion/src/modeledit/flightmodes.h +++ b/companion/src/modeledit/flightmodes.h @@ -24,8 +24,8 @@ #include "modeledit.h" #include "eeprominterface.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; constexpr char MIMETYPE_FLIGHTMODE[] = "application/x-companion-flightmode"; constexpr char MIMETYPE_GVAR_PARAMS[] = "application/x-companion-gvar-params"; @@ -41,7 +41,8 @@ class FlightModePanel : public ModelPanel Q_OBJECT public: - FlightModePanel(QWidget *parent, ModelData &model, int modeIdx, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSwitchFilteredModel); + FlightModePanel(QWidget *parent, ModelData &model, int modeIdx, GeneralSettings & generalSettings, Firmware * firmware, + FilteredItemModel * rawSwitchFilteredModel); virtual ~FlightModePanel(); virtual void update(); @@ -90,8 +91,8 @@ class FlightModePanel : public ModelPanel void gvCmPaste(); void gvCmMoveDown(); void gvCmMoveUp(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::FlightMode *ui; @@ -145,7 +146,7 @@ class FlightModesPanel : public ModelPanel Q_OBJECT public: - FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~FlightModesPanel(); public slots: @@ -157,14 +158,16 @@ class FlightModesPanel : public ModelPanel private slots: void onPhaseNameChanged(); void onTabIndexChanged(int index); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: QString getTabName(int index); int modesCount; QTabWidget *tabWidget; - CommonItemModels *commonItemModels; - RawItemFilteredModel *rawSwitchFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *rawSwitchFilteredModel; QVector panels; void updateItemModels(); }; diff --git a/companion/src/modeledit/heli.cpp b/companion/src/modeledit/heli.cpp index 1b8607799..a1142468e 100644 --- a/companion/src/modeledit/heli.cpp +++ b/companion/src/modeledit/heli.cpp @@ -23,16 +23,13 @@ #include "helpers.h" #include "rawitemfilteredmodel.h" -HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - ui(new Ui::Heli), - commonItemModels(commonItemModels) + ui(new Ui::Heli) { ui->setupUi(this); - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), RawSource::InputSourceGroups, this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &HeliPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &HeliPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, HeliPanel, RawSourceId, RawSource::InputSourceGroups) connect(ui->swashType, SIGNAL(currentIndexChanged(int)), this, SLOT(edited())); connect(ui->swashRingVal, SIGNAL(editingFinished()), this, SLOT(edited())); @@ -71,6 +68,7 @@ HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & gener HeliPanel::~HeliPanel() { delete ui; + delete rawSourceFilteredModel; } void HeliPanel::update() @@ -118,12 +116,12 @@ void HeliPanel::edited() } } -void HeliPanel::onModelDataAboutToBeUpdated() +void HeliPanel::onItemModelAboutToBeUpdated() { lock = true; } -void HeliPanel::onModelDataUpdateComplete() +void HeliPanel::onItemModelUpdateComplete() { update(); lock = false; diff --git a/companion/src/modeledit/heli.h b/companion/src/modeledit/heli.h index ee9a3ac36..2238a67f1 100644 --- a/companion/src/modeledit/heli.h +++ b/companion/src/modeledit/heli.h @@ -23,8 +23,8 @@ #include "modeledit.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; namespace Ui { class Heli; @@ -35,19 +35,18 @@ class HeliPanel : public ModelPanel Q_OBJECT public: - HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); - ~HeliPanel(); + HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + virtual ~HeliPanel(); void update(); private slots: void edited(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::Heli *ui; - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSourceFilteredModel; + FilteredItemModel * rawSourceFilteredModel; }; #endif // _HELI_H_ diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index 5e50de71c..ae25ae451 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -23,28 +23,18 @@ #include "helpers.h" #include "rawitemfilteredmodel.h" -InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), expoInserted(false), modelPrinter(firmware, generalSettings, model), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), (RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup), this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::MixesContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - - const int filter = firmware->getCapability(HasInputDiff) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); - connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); - - gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); - connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &InputsPanel::onModelDataAboutToBeUpdated); - connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &InputsPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, InputsPanel, RawSourceId,(RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)) + FILTEREDITEMMODEL(rawSwitchFilteredModel, InputsPanel, RawSwitchId, RawSwitch::MixesContext) + FILTEREDITEMMODEL(curveFilteredModel, InputsPanel, CurveId, firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter) + FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, InputsPanel, GVarRefId) + FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, InputsPanel, CurveRefTypeId) + FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, InputsPanel, CurveRefFuncId) inputsCount = firmware->getCapability(VirtualInputs); if (inputsCount == 0) @@ -84,6 +74,12 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g InputsPanel::~InputsPanel() { + delete rawSourceFilteredModel; + delete rawSwitchFilteredModel; + delete curveFilteredModel; + delete gvarFilteredModel; + delete crTypeFilteredModel; + delete crFuncFilteredModel; } void InputsPanel::update() @@ -200,7 +196,7 @@ void InputsPanel::gm_openExpo(int index) inputName = model->inputNames[ed.chn]; ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel); + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); if (dlg->exec()) { model->expoData[index] = ed; if (firmware->getCapability(VirtualInputs)) @@ -736,12 +732,12 @@ int InputsPanel::getInputIndexFromSelected() return idx; } -void InputsPanel::onModelDataAboutToBeUpdated() +void InputsPanel::onItemModelAboutToBeUpdated() { lock = true; } -void InputsPanel::onModelDataUpdateComplete() +void InputsPanel::onItemModelUpdateComplete() { update(); lock = false; @@ -749,5 +745,5 @@ void InputsPanel::onModelDataUpdateComplete() void InputsPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_INPUTS); + sharedItemModels->update(AbstractItemModel::InputsUpdated); } diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index 3bbfceb7b..8b2c6bcef 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -25,8 +25,8 @@ #include "mixerslistwidget.h" #include "modelprinter.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; constexpr char MIMETYPE_EXPO[] = "application/x-companion-expo"; @@ -35,7 +35,7 @@ class InputsPanel : public ModelPanel Q_OBJECT public: - InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~InputsPanel(); virtual void update(); @@ -62,8 +62,8 @@ class InputsPanel : public ModelPanel void cmInputInsert(); void cmInputMoveDown(); void cmInputMoveUp(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: bool expoInserted; @@ -72,11 +72,13 @@ class InputsPanel : public ModelPanel ModelPrinter modelPrinter; int selectedIdx; int inputIdx; - CommonItemModels * commonItemModels; - RawItemFilteredModel *rawSourceFilteredModel; - RawItemFilteredModel *rawSwitchFilteredModel; - RawItemFilteredModel *curveFilteredModel; - RawItemFilteredModel *gvarFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *rawSourceFilteredModel; + FilteredItemModel *rawSwitchFilteredModel; + FilteredItemModel *curveFilteredModel; + FilteredItemModel *gvarFilteredModel; + FilteredItemModel *crTypeFilteredModel; + FilteredItemModel *crFuncFilteredModel; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index cacd5c6fd..dc4755f53 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -24,20 +24,16 @@ #include -LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - commonItemModels(commonItemModels), + sharedItemModels(sharedItemModels), selectedIndex(0), modelsUpdateCnt(0) { - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::LogicalSwitchesContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &LogicalSwitchesPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &LogicalSwitchesPanel::onModelDataUpdateComplete); - + FILTEREDITEMMODEL(rawSwitchFilteredModel, LogicalSwitchesPanel, RawSwitchId, RawSwitch::LogicalSwitchesContext) const int srcGroups = firmware->getCapability(GvarsInCS) ? 0 : (RawSource::AllSourceGroups & ~RawSource::GVarsGroup); - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), srcGroups, this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &LogicalSwitchesPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &LogicalSwitchesPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, LogicalSwitchesPanel, RawSourceId, srcGroups) lsCapability = firmware->getCapability(LogicalSwitches); lsCapabilityExt = firmware->getCapability(LogicalSwitchesExt); @@ -161,6 +157,8 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, LogicalSwitchesPanel::~LogicalSwitchesPanel() { + delete rawSourceFilteredModel; + delete rawSwitchFilteredModel; } void LogicalSwitchesPanel::onFunctionChanged() @@ -644,16 +642,16 @@ void LogicalSwitchesPanel::swapData(int idx1, int idx2) void LogicalSwitchesPanel::updateItemModels() { lock = true; - commonItemModels->update(CommonItemModels::RMO_LOGICAL_SWITCHES); + sharedItemModels->update(AbstractItemModel::LogicalSwitchesUpdated); } -void LogicalSwitchesPanel::onModelDataAboutToBeUpdated() +void LogicalSwitchesPanel::onItemModelAboutToBeUpdated() { lock = true; modelsUpdateCnt++; } -void LogicalSwitchesPanel::onModelDataUpdateComplete() +void LogicalSwitchesPanel::onItemModelUpdateComplete() { modelsUpdateCnt--; if (modelsUpdateCnt < 1) { diff --git a/companion/src/modeledit/logicalswitches.h b/companion/src/modeledit/logicalswitches.h index 87a360408..05b683b49 100644 --- a/companion/src/modeledit/logicalswitches.h +++ b/companion/src/modeledit/logicalswitches.h @@ -24,8 +24,8 @@ #include "modeledit.h" #include "radiodata.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; class TimerEdit; constexpr char MIMETYPE_LOGICAL_SWITCH[] = "application/x-companion-logical-switch"; @@ -35,7 +35,7 @@ class LogicalSwitchesPanel : public ModelPanel Q_OBJECT public: - LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~LogicalSwitchesPanel(); virtual void update(); @@ -60,8 +60,8 @@ class LogicalSwitchesPanel : public ModelPanel void cmInsert(); void cmClear(bool prompt = true); void cmClearAll(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: QComboBox * cbFunction[CPN_MAX_LOGICAL_SWITCHES]; @@ -74,9 +74,9 @@ class LogicalSwitchesPanel : public ModelPanel QDoubleSpinBox * dsbDelay[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource1[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource2[CPN_MAX_LOGICAL_SWITCHES]; - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSwitchFilteredModel; - RawItemFilteredModel * rawSourceFilteredModel; + ItemModelsFactory * sharedItemModels; + FilteredItemModel * rawSwitchFilteredModel; + FilteredItemModel * rawSourceFilteredModel; int selectedIndex; void populateFunctionCB(QComboBox *b); void updateTimerParam(QDoubleSpinBox *sb, int timer, double minimum=0); diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 102d2d2a9..819649698 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -25,8 +25,9 @@ #include "helpers.h" MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, - RawItemFilteredModel * gvarItemModel) : + FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, + FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, + FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : QDialog(parent), ui(new Ui::MixerDialog), model(model), @@ -46,15 +47,15 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, this->setWindowTitle(tr("DEST -> %1").arg(RawSource(SOURCE_TYPE_CH, md->destCh - 1).toString(&model, &generalSettings))); - ui->sourceCB->setModel(rawSourceModel); + ui->sourceCB->setModel(rawSourceFilteredModel); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); int limit = firmware->getCapability(OffsetWeight); gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, curveItemModel, - gvarItemModel, this); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); ui->MixDR_CB->setChecked(md->noExpo == 0); @@ -106,7 +107,7 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, } } - ui->switchesCB->setModel(rawSwitchModel); + ui->switchesCB->setModel(rawSwitchFilteredModel); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(md->swtch.toValue())); ui->warningCB->setCurrentIndex(md->mixWarn); ui->mltpxCB->setCurrentIndex(md->mltpx); diff --git a/companion/src/modeledit/mixerdialog.h b/companion/src/modeledit/mixerdialog.h index d036832d9..1beb3b220 100644 --- a/companion/src/modeledit/mixerdialog.h +++ b/companion/src/modeledit/mixerdialog.h @@ -25,7 +25,7 @@ #include "eeprominterface.h" class GVarGroup; -class RawItemFilteredModel; +class FilteredItemModel; class CurveReferenceUIManager; namespace Ui { @@ -36,8 +36,8 @@ class MixerDialog : public QDialog { Q_OBJECT public: MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, GeneralSettings & generalSettings, Firmware * firmware, - RawItemFilteredModel * rawSourceModel, RawItemFilteredModel * rawSwitchModel, RawItemFilteredModel * curveItemModel, - RawItemFilteredModel * gvarItemModel); + FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, + FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); ~MixerDialog(); protected: diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index 01b4d7f64..5bcc69d30 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -22,29 +22,19 @@ #include "helpers.h" #include "rawitemfilteredmodel.h" -MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,CommonItemModels * commonItemModels): +MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), mixInserted(false), highlightedSource(0), modelPrinter(firmware, generalSettings, model), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), ((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup), this); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(rawSourceFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::MixesContext, this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - - const int filter = firmware->getCapability(HasMixerExpo) ? RawItemFilteredModel::AllFilter : RawItemFilteredModel::PositiveFilter; - curveFilteredModel = new RawItemFilteredModel(commonItemModels->curveItemModel(), filter, this); - connect(curveFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(curveFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); - - gvarFilteredModel = new RawItemFilteredModel(commonItemModels->gvarItemModel(), RawItemFilteredModel::AllFilter, this); - connect(gvarFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &MixesPanel::onModelDataAboutToBeUpdated); - connect(gvarFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &MixesPanel::onModelDataUpdateComplete); + FILTEREDITEMMODEL(rawSourceFilteredModel, MixesPanel, RawSourceId,((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)) + FILTEREDITEMMODEL(rawSwitchFilteredModel, MixesPanel, RawSwitchId, RawSwitch::MixesContext) + FILTEREDITEMMODEL(curveFilteredModel, MixesPanel, CurveId, firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter) + FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, MixesPanel, GVarRefId) + FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, MixesPanel, CurveRefTypeId) + FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, MixesPanel, CurveRefFuncId) QGridLayout * mixesLayout = new QGridLayout(this); @@ -79,6 +69,12 @@ MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & gen MixesPanel::~MixesPanel() { + delete rawSourceFilteredModel; + delete rawSwitchFilteredModel; + delete curveFilteredModel; + delete gvarFilteredModel; + delete crTypeFilteredModel; + delete crFuncFilteredModel; } void MixesPanel::update() @@ -194,8 +190,8 @@ void MixesPanel::gm_openMix(int index) MixData mixd(model->mixData[index]); - MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel); + MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, + curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); if(dlg->exec()) { model->mixData[index] = mixd; emit modified(); @@ -551,12 +547,12 @@ void MixesPanel::clearMixes() } } -void MixesPanel::onModelDataAboutToBeUpdated() +void MixesPanel::onItemModelAboutToBeUpdated() { lock = true; } -void MixesPanel::onModelDataUpdateComplete() +void MixesPanel::onItemModelUpdateComplete() { update(); lock = false; diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 154eda9b4..211632a2e 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -26,15 +26,15 @@ #include "mixerdialog.h" #include "modelprinter.h" -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; class MixesPanel : public ModelPanel { Q_OBJECT public: - MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~MixesPanel(); virtual void update(); @@ -60,19 +60,21 @@ class MixesPanel : public ModelPanel void mimeMixerDropped(int index, const QMimeData *data, Qt::DropAction action); void pasteMixerMimeData(const QMimeData * mimeData, int destIdx); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: MixersListWidget * mixersListWidget; bool mixInserted; unsigned int highlightedSource; ModelPrinter modelPrinter; - CommonItemModels * commonItemModels; - RawItemFilteredModel *rawSourceFilteredModel; - RawItemFilteredModel *rawSwitchFilteredModel; - RawItemFilteredModel *curveFilteredModel; - RawItemFilteredModel *gvarFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModel *rawSourceFilteredModel; + FilteredItemModel *rawSwitchFilteredModel; + FilteredItemModel *curveFilteredModel; + FilteredItemModel *gvarFilteredModel; + FilteredItemModel *crTypeFilteredModel; + FilteredItemModel *crFuncFilteredModel; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 41989ba53..569b64e33 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -53,42 +53,53 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw GeneralSettings &generalSettings = radioData.generalSettings; ModelData &model = radioData.models[modelId]; - commonItemModels = new CommonItemModels(&generalSettings, &model, this); + sharedItemModels = new ItemModelsFactory(&generalSettings, &model); + sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); + sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); + sharedItemModels->addItemModel(AbstractItemModel::CurveId); + sharedItemModels->addItemModel(AbstractItemModel::GVarRefId); + sharedItemModels->addItemModel(AbstractItemModel::ThrSourceId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); + sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); + sharedItemModels->addItemModel(AbstractItemModel::TeleSourceId); + sharedItemModels->addItemModel(AbstractItemModel::CurveRefTypeId); + sharedItemModels->addItemModel(AbstractItemModel::CurveRefFuncId); + s1.report("Init"); - SetupPanel * setupPanel = new SetupPanel(this, model, generalSettings, firmware, commonItemModels); + SetupPanel * setupPanel = new SetupPanel(this, model, generalSettings, firmware, sharedItemModels); addTab(setupPanel, tr("Setup")); s1.report("Setup"); if (firmware->getCapability(Heli)) { - addTab(new HeliPanel(this, model, generalSettings, firmware, commonItemModels), tr("Heli")); + addTab(new HeliPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Heli")); s1.report("Heli"); } - addTab(new FlightModesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Flight Modes")); + addTab(new FlightModesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Flight Modes")); s1.report("Flight Modes"); - addTab(new InputsPanel(this, model, generalSettings, firmware, commonItemModels), tr("Inputs")); + addTab(new InputsPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Inputs")); s1.report("Inputs"); - addTab(new MixesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Mixes")); + addTab(new MixesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Mixes")); s1.report("Mixes"); - ChannelsPanel * channelsPanel = new ChannelsPanel(this, model, generalSettings, firmware, commonItemModels); + ChannelsPanel * channelsPanel = new ChannelsPanel(this, model, generalSettings, firmware, sharedItemModels); addTab(channelsPanel, tr("Outputs")); s1.report("Outputs"); - addTab(new CurvesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Curves")); + addTab(new CurvesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Curves")); s1.report("Curves"); - addTab(new LogicalSwitchesPanel(this, model, generalSettings, firmware, commonItemModels), tr("Logical Switches")); + addTab(new LogicalSwitchesPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Logical Switches")); s1.report("Logical Switches"); - addTab(new CustomFunctionsPanel(this, &model, generalSettings, firmware, commonItemModels), tr("Special Functions")); + addTab(new CustomFunctionsPanel(this, &model, generalSettings, firmware, sharedItemModels), tr("Special Functions")); s1.report("Special Functions"); if (firmware->getCapability(Telemetry)) { - addTab(new TelemetryPanel(this, model, generalSettings, firmware, commonItemModels), tr("Telemetry")); + addTab(new TelemetryPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Telemetry")); s1.report("Telemetry"); } @@ -104,6 +115,7 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw ModelEdit::~ModelEdit() { delete ui; + delete sharedItemModels; } void ModelEdit::closeEvent(QCloseEvent *event) diff --git a/companion/src/modeledit/modeledit.h b/companion/src/modeledit/modeledit.h index 9095101bf..99337f862 100644 --- a/companion/src/modeledit/modeledit.h +++ b/companion/src/modeledit/modeledit.h @@ -25,7 +25,7 @@ #include "genericpanel.h" class RadioData; -class CommonItemModels; +class ItemModelsFactory; namespace Ui { class ModelEdit; @@ -60,12 +60,12 @@ class ModelEdit : public QDialog private: Ui::ModelEdit *ui; int modelId; - RadioData & radioData; - Firmware * firmware; + RadioData &radioData; + Firmware *firmware; QVector panels; - CommonItemModels * commonItemModels; + ItemModelsFactory *sharedItemModels; - void addTab(GenericPanel *panel, QString text); + void addTab(GenericPanel * panel, QString text); void launchSimulation(); }; diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index e4f67178d..d3f58322e 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -31,14 +31,15 @@ #include -TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, QWidget * prevFocus, RawItemFilteredModel * rawSwitchFilteredModel): +TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget * prevFocus, FilteredItemModel * rawSwitchFilteredModel): ModelPanel(parent, model, generalSettings, firmware), timer(timer), ui(new Ui::Timer) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &TimerPanel::onModelDataAboutToBeUpdated); - connect(rawSwitchFilteredModel, &RawItemFilteredModel::dataUpdateComplete, this, &TimerPanel::onModelDataUpdateComplete); + connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); + connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &TimerPanel::onItemModelUpdateComplete); lock = true; @@ -172,12 +173,12 @@ void TimerPanel::on_name_editingFinished() } } -void TimerPanel::onModelDataAboutToBeUpdated() +void TimerPanel::onItemModelAboutToBeUpdated() { lock = true; } -void TimerPanel::onModelDataUpdateComplete() +void TimerPanel::onItemModelUpdateComplete() { update(); lock = false; @@ -991,13 +992,15 @@ void ModulePanel::onClearAccessRxClicked() /******************************************************************************/ -SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Setup), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels) { ui->setupUi(this); - rawSwitchFilteredModel = new RawItemFilteredModel(commonItemModels->rawSwitchItemModel(), RawSwitch::TimersContext, this); + FILTEREDITEMMODEL(rawSwitchFilteredModel, SetupPanel, RawSwitchId, RawSwitch::TimersContext) + FILTEREDITEMMODELNOFLAGS(thrSourceFilteredModel, SetupPanel, ThrSourceId) Board::Type board = firmware->getBoard(); @@ -1248,6 +1251,8 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge SetupPanel::~SetupPanel() { delete ui; + delete rawSwitchFilteredModel; + delete thrSourceFilteredModel; } void SetupPanel::on_extendedLimits_toggled(bool checked) @@ -1750,7 +1755,15 @@ void SetupPanel::onTimerNameChanged() updateItemModels(); } +void SetupPanel::onItemModelAboutToBeUpdated() +{ +} + +void SetupPanel::onItemModelUpdateComplete() +{ +} + void SetupPanel::updateItemModels() { - commonItemModels->update(CommonItemModels::RMO_TIMERS); + sharedItemModels->update(AbstractItemModel::TimersUpdated); } diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index c56e63ea1..a4b7e7f81 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -26,8 +26,8 @@ constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; -class CommonItemModels; -class RawItemFilteredModel; +class ItemModelsFactory; +class FilteredItemModel; namespace Ui { class Setup; @@ -40,7 +40,8 @@ class TimerPanel : public ModelPanel Q_OBJECT public: - TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, QWidget *prevFocus, RawItemFilteredModel * switchModel); + TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget *prevFocus, FilteredItemModel * switchModel); virtual ~TimerPanel(); virtual void update(); @@ -51,8 +52,8 @@ class TimerPanel : public ModelPanel void on_value_editingFinished(); void on_minuteBeep_toggled(bool checked); void on_name_editingFinished(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); signals: void nameChanged(); @@ -131,7 +132,7 @@ class SetupPanel : public ModelPanel Q_OBJECT public: - SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); virtual ~SetupPanel(); virtual void update(); @@ -170,6 +171,8 @@ class SetupPanel : public ModelPanel void cmTimerMoveDown(); void cmTimerMoveUp(); void onTimerNameChanged(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::Setup *ui; @@ -191,8 +194,9 @@ class SetupPanel : public ModelPanel bool moveTimerDownAllowed() const; bool moveTimerUpAllowed() const; void swapTimerData(int idx1, int idx2); - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSwitchFilteredModel; + ItemModelsFactory * sharedItemModels; + FilteredItemModel * rawSwitchFilteredModel; + FilteredItemModel * thrSourceFilteredModel; void updateItemModels(); }; diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index e980c3943..3e1a5c49b 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -24,24 +24,31 @@ #include "ui_telemetry_sensor.h" #include "helpers.h" #include "appdata.h" -#include "rawitemfilteredmodel.h" #include -TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSourceModel): +constexpr char FIM_NAME_RAWSOURCE[] {"Raw Source"}; +constexpr char FIM_NAME_TELEALLSRC[] {"Tele All Source"}; +constexpr char FIM_NAME_TELEPOSSRC[] {"Tele Pos Source"}; + +TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, + Firmware * firmware, const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetryCustomScreen), - screen(screen) + screen(screen), + modelsUpdateCnt(0), + parentLock(parentLock) { ui->setupUi(this); - connect(rawSourceModel, &RawItemFilteredModel::dataAboutToBeUpdated, this, &TelemetryCustomScreen::onModelDataAboutToBeUpdated); - connect(rawSourceModel, &RawItemFilteredModel::dataUpdateComplete, this, &TelemetryCustomScreen::onModelDataUpdateComplete); + + FilteredItemModel * rawSourceFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_RAWSOURCE); + connectItemModelEvents(rawSourceFilteredModel); for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) { for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) { fieldsCB[l][c] = new QComboBox(this); fieldsCB[l][c]->setProperty("index", c + (l << 8)); - fieldsCB[l][c]->setModel(rawSourceModel); + fieldsCB[l][c]->setModel(rawSourceFilteredModel); ui->screenNumsLayout->addWidget(fieldsCB[l][c], l, c, 1, 1); connect(fieldsCB[l][c], SIGNAL(activated(int)), this, SLOT(customFieldChanged(int))); } @@ -50,7 +57,7 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) { barsCB[l] = new QComboBox(this); barsCB[l]->setProperty("index", l); - barsCB[l]->setModel(rawSourceModel); + barsCB[l]->setModel(rawSourceFilteredModel); connect(barsCB[l], SIGNAL(activated(int)), this, SLOT(barSourceChanged(int))); ui->screenBarsLayout->addWidget(barsCB[l], l, 0, 1, 1); @@ -203,7 +210,7 @@ void TelemetryCustomScreen::updateBar(int line) void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { memset(reinterpret_cast(&screen.body), 0, sizeof(screen.body)); update(); emit modified(); @@ -212,7 +219,7 @@ void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index) void TelemetryCustomScreen::scriptNameEdited() { - if (!lock) { + if (!isLocked()) { lock = true; Helpers::getFileComboBoxValue(ui->scriptName, screen.body.script.filename, 8); emit modified(); @@ -222,7 +229,7 @@ void TelemetryCustomScreen::scriptNameEdited() void TelemetryCustomScreen::customFieldChanged(int value) { - if (lock || !sender() || !qobject_cast(sender())) + if (isLocked() || !sender() || !qobject_cast(sender())) return; bool ok; @@ -238,7 +245,7 @@ void TelemetryCustomScreen::customFieldChanged(int value) void TelemetryCustomScreen::barSourceChanged(int value) { - if (lock || !sender() || !qobject_cast(sender())) + if (isLocked() || !sender() || !qobject_cast(sender())) return; bool ok; @@ -256,7 +263,7 @@ void TelemetryCustomScreen::barSourceChanged(int value) void TelemetryCustomScreen::barMinChanged(double value) { - if (!lock) { + if (!isLocked()) { int line = sender()->property("index").toInt(); screen.body.bars[line].barMin = round(value / minSB[line]->singleStep()); // TODO set min (maxSB) @@ -266,7 +273,7 @@ void TelemetryCustomScreen::barMinChanged(double value) void TelemetryCustomScreen::barMaxChanged(double value) { - if (!lock) { + if (!isLocked()) { int line = sender()->property("index").toInt(); screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep()); // TODO set max (minSB) @@ -276,7 +283,7 @@ void TelemetryCustomScreen::barMaxChanged(double value) void TelemetryCustomScreen::barTimeChanged() { - if (!lock) { + if (!isLocked()) { int line = sender()->property("index").toInt(); int & valRef = (sender()->property("type").toString() == "min" ? screen.body.bars[line].barMin : screen.body.bars[line].barMax); TimerEdit * te = qobject_cast(sender()); @@ -289,29 +296,49 @@ void TelemetryCustomScreen::barTimeChanged() } } -void TelemetryCustomScreen::onModelDataAboutToBeUpdated() +void TelemetryCustomScreen::connectItemModelEvents(const FilteredItemModel * itemModel) { - lock = true; + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryCustomScreen::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryCustomScreen::onItemModelUpdateComplete); } -void TelemetryCustomScreen::onModelDataUpdateComplete() +void TelemetryCustomScreen::onItemModelAboutToBeUpdated() { - update(); - lock = false; + lock = true; + modelsUpdateCnt++; +} + +void TelemetryCustomScreen::onItemModelUpdateComplete() +{ + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + // leave updating to parent + lock = false; + } } /******************************************************/ -TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): +TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, + GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, + FilteredItemModelsFactory * localFilteredItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), sensor(sensor), - lock(false), + //lock(false), sensorIndex(sensorIndex), selectedIndex(0), - sensorCapability(sensorCapability) + sensorCapability(sensorCapability), + modelsUpdateCnt(0), + parentLock(parentLock) { ui->setupUi(this); + + FilteredItemModel * teleAllSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEALLSRC); + connectItemModelEvents(teleAllSrcFilteredModel); + FilteredItemModel * telePosSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC); + connectItemModelEvents(telePosSrcFilteredModel); + ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1)); ui->numLabel->setProperty("index", sensorIndex); ui->numLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -333,9 +360,13 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->logs->setField(sensor.logs, this); ui->persistent->setField(sensor.persistent, this); ui->onlyPositive->setField(sensor.onlyPositive, this); + ui->gpsSensor->setModel(telePosSrcFilteredModel); ui->gpsSensor->setField(sensor.gps, this); + ui->altSensor->setModel(telePosSrcFilteredModel); ui->altSensor->setField(sensor.alt, this); + ui->ampsSensor->setModel(telePosSrcFilteredModel); ui->ampsSensor->setField(sensor.amps, this); + ui->cellsSensor->setModel(telePosSrcFilteredModel); ui->cellsSensor->setField(sensor.source, this); ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); for (int i = SensorData::TELEM_CELL_INDEX_LOWEST + 1; i < SensorData::TELEM_CELL_INDEX_HIGHEST; i++) @@ -343,9 +374,13 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); ui->cellsIndex->setField(sensor.index); + ui->source1->setModel(teleAllSrcFilteredModel); ui->source1->setField(sensor.sources[0], this); + ui->source2->setModel(teleAllSrcFilteredModel); ui->source2->setField(sensor.sources[1], this); + ui->source3->setModel(teleAllSrcFilteredModel); ui->source3->setField(sensor.sources[2], this); + ui->source4->setModel(teleAllSrcFilteredModel); ui->source4->setField(sensor.sources[3], this); ui->prec->setField(sensor.prec, 0, 2, false, "", this); update(); @@ -404,14 +439,10 @@ void TelemetrySensorPanel::update() sources12FieldsDisplayed = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY); sources34FieldsDisplayed = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY); totalizeFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE); - updateSourcesComboBox(ui->source1, true); - updateSourcesComboBox(ui->source2, true); - updateSourcesComboBox(ui->source3, true); - updateSourcesComboBox(ui->source4, true); - updateSourcesComboBox(ui->gpsSensor, false); - updateSourcesComboBox(ui->altSensor, false); - updateSourcesComboBox(ui->ampsSensor, false); - updateSourcesComboBox(ui->cellsSensor, false); + ui->source1->updateValue(); + ui->source2->updateValue(); + ui->source3->updateValue(); + ui->source4->updateValue(); } else { ui->idLabel->show(); @@ -483,40 +514,9 @@ void TelemetrySensorPanel::update() lock = false; } -void populateTelemetrySourcesComboBox(AutoComboBox * cb, const ModelData * model, bool negative) -{ - cb->clear(); - if (negative) { - for (int i = -CPN_MAX_SENSORS; i < 0; ++i) { - const SensorData& sensor = model->sensorData[-i - 1]; - if (sensor.isAvailable()) { - if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) - cb->addItem(QString("-%1 (%2)").arg(sensor.label, sensor.getOrigin(model)), i); - else - cb->addItem(QString("-%1").arg(sensor.label), i); - } - } - } - cb->addItem("---", 0); - for (unsigned i = 1; i <= CPN_MAX_SENSORS; ++i) { - const SensorData& sensor = model->sensorData[i-1]; - if (sensor.isAvailable()) { - if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) - cb->addItem(QString("%1 (%2)").arg(sensor.label, sensor.getOrigin(model)), i); - else - cb->addItem(QString("%1").arg(sensor.label), i); - } - } -} - -void TelemetrySensorPanel::updateSourcesComboBox(AutoComboBox * cb, bool negative) -{ - populateTelemetrySourcesComboBox(cb, model, negative); -} - void TelemetrySensorPanel::on_name_editingFinished() { - if (!lock) { + if (!isLocked() && (sensor.label != ui->name->text().toLatin1())) { strcpy(sensor.label, ui->name->text().toLatin1()); emit dataModified(); } @@ -524,7 +524,7 @@ void TelemetrySensorPanel::on_name_editingFinished() void TelemetrySensorPanel::on_type_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { sensor.type = index; update(); emit modified(); @@ -533,7 +533,7 @@ void TelemetrySensorPanel::on_type_currentIndexChanged(int index) void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { sensor.formula = index; if (sensor.formula == SensorData::TELEM_FORMULA_CELL) { sensor.prec = 2; @@ -553,7 +553,7 @@ void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { sensor.unit = index; if (sensor.unit == SensorData::UNIT_FAHRENHEIT) { sensor.prec = 0; @@ -565,7 +565,7 @@ void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) void TelemetrySensorPanel::on_prec_valueChanged() { - if (!lock) { + if (!isLocked()) { update(); } } @@ -686,15 +686,52 @@ void TelemetrySensorPanel::cmMoveDown() emit moveDownSensor(selectedIndex); } +void TelemetrySensorPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetrySensorPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetrySensorPanel::onItemModelUpdateComplete); +} + +void TelemetrySensorPanel::onItemModelAboutToBeUpdated() +{ + lock = true; + modelsUpdateCnt++; +} + +void TelemetrySensorPanel::onItemModelUpdateComplete() +{ + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + // leave updating to parent + lock = false; + } +} + /******************************************************/ -TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels): +TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Telemetry), - commonItemModels(commonItemModels) + sharedItemModels(sharedItemModels), + modelsUpdateCnt(0) { ui->setupUi(this); - rawSourceFilteredModel = new RawItemFilteredModel(commonItemModels->rawSourceItemModel(), this); + + localFilteredItemModels = new FilteredItemModelsFactory(); + int newid; + + newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::RawSourceId)), + FIM_NAME_RAWSOURCE); + connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + + newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId)), + FIM_NAME_TELEALLSRC); + connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + + newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId), + FilteredItemModel::PositiveFilter), FIM_NAME_TELEPOSSRC); + connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); sensorCapability = firmware->getCapability(Sensors); if (sensorCapability > CPN_MAX_SENSORS) // TODO should be role of getCapability @@ -704,13 +741,15 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin model.frsky.usrProto = 1; } + ui->varioSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->varioSource->setField(model.frsky.varioSource, this); ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->A1GB->hide(); ui->A2GB->hide(); for (int i = 0; i < sensorCapability; ++i) { - TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, firmware); + TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, + firmware, lock, localFilteredItemModels); ui->sensorsLayout->addWidget(panel); sensorPanels[i] = panel; connect(panel, SIGNAL(dataModified()), this, SLOT(on_dataModifiedSensor())); @@ -723,7 +762,9 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin } if (IS_TARANIS_X9(firmware->getBoard())) { + ui->voltsSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->voltsSource->setField(model.frsky.voltsSource, this); + ui->altitudeSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->altitudeSource->setField(model.frsky.altitudeSource, this); } else { @@ -731,7 +772,8 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin } for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { - TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, rawSourceFilteredModel); + TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock, + localFilteredItemModels); ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1)); telemetryCustomScreens[i] = tab; connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified); @@ -745,10 +787,13 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin TelemetryPanel::~TelemetryPanel() { delete ui; + delete localFilteredItemModels; } void TelemetryPanel::update() { + lock = true; + if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { if (model->moduleData[0].protocol == PULSES_OFF && model->moduleData[1].protocol == PULSES_PPM) { ui->telemetryProtocol->setEnabled(true); @@ -758,10 +803,10 @@ void TelemetryPanel::update() ui->telemetryProtocol->setCurrentIndex(0); } - populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); - populateTelemetrySourcesComboBox(ui->voltsSource, model, false); - populateTelemetrySourcesComboBox(ui->altitudeSource, model, false); - populateTelemetrySourcesComboBox(ui->varioSource, model, false); + ui->rssiSourceCB->updateValue(); + ui->voltsSource->updateValue(); + ui->altitudeSource->updateValue(); + ui->varioSource->updateValue(); } for (int i = 0; i < sensorCapability; ++i) { @@ -771,6 +816,8 @@ void TelemetryPanel::update() for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { telemetryCustomScreens[i]->update(); } + + lock = false; } void TelemetryPanel::setup() @@ -792,9 +839,9 @@ void TelemetryPanel::setup() ui->rssiSourceLabel->show(); ui->rssiSourceLabel->setText(tr("Source")); + ui->rssiSourceCB->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); ui->rssiSourceCB->setField(model->rssiSource, this); ui->rssiSourceCB->show(); - populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); ui->rssiAlarmWarningCB->hide(); ui->rssiAlarmCriticalCB->hide(); @@ -842,44 +889,9 @@ void TelemetryPanel::setup() lock = false; } -void TelemetryPanel::populateVarioSource() -{ - AutoComboBox * cb = ui->varioSource; - cb->setField(model->frsky.varioSource, this); - cb->addItem(tr("Alti"), TELEMETRY_VARIO_SOURCE_ALTI); - cb->addItem(tr("Alti+"), TELEMETRY_VARIO_SOURCE_ALTI_PLUS); - cb->addItem(tr("VSpeed"), TELEMETRY_VARIO_SOURCE_VSPEED); - cb->addItem(tr("A1"), TELEMETRY_VARIO_SOURCE_A1); - cb->addItem(tr("A2"), TELEMETRY_VARIO_SOURCE_A2); -} - -void TelemetryPanel::populateVoltsSource() -{ - AutoComboBox * cb = ui->frskyVoltCB; - cb->setField(model->frsky.voltsSource, this); - cb->addItem(tr("A1"), TELEMETRY_VOLTS_SOURCE_A1); - cb->addItem(tr("A2"), TELEMETRY_VOLTS_SOURCE_A2); - cb->addItem(tr("A3"), TELEMETRY_VOLTS_SOURCE_A3); - cb->addItem(tr("A4"), TELEMETRY_VOLTS_SOURCE_A4); - cb->addItem(tr("FAS"), TELEMETRY_VOLTS_SOURCE_FAS); - cb->addItem(tr("Cells"), TELEMETRY_VOLTS_SOURCE_CELLS); -} - -void TelemetryPanel::populateCurrentSource() -{ - AutoComboBox * cb = ui->frskyCurrentCB; - cb->setField(model->frsky.currentSource, this); - cb->addItem(tr("---"), TELEMETRY_CURRENT_SOURCE_NONE); - cb->addItem(tr("A1"), TELEMETRY_CURRENT_SOURCE_A1); - cb->addItem(tr("A2"), TELEMETRY_CURRENT_SOURCE_A2); - cb->addItem(tr("A3"), TELEMETRY_CURRENT_SOURCE_A3); - cb->addItem(tr("A4"), TELEMETRY_CURRENT_SOURCE_A4); - cb->addItem(tr("FAS"), TELEMETRY_CURRENT_SOURCE_FAS); -} - void TelemetryPanel::on_telemetryProtocol_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { model->telemetryProtocol = index; emit modified(); } @@ -892,7 +904,7 @@ void TelemetryPanel::onModified() void TelemetryPanel::on_bladesCount_editingFinished() { - if (!lock) { + if (!isLocked()) { model->frsky.blades = ui->bladesCount->value(); emit modified(); } @@ -900,7 +912,7 @@ void TelemetryPanel::on_bladesCount_editingFinished() void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index) { - if (!lock) { + if (!isLocked()) { model->frsky.usrProto = index; for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) telemetryCustomScreens[i]->update(); @@ -910,31 +922,39 @@ void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index) void TelemetryPanel::on_rssiAlarmWarningSB_editingFinished() { - model->rssiAlarms.warning= ui->rssiAlarmWarningSB->value(); - emit modified(); + if (!isLocked()) { + model->rssiAlarms.warning= ui->rssiAlarmWarningSB->value(); + emit modified(); + } } void TelemetryPanel::on_rssiAlarmCriticalSB_editingFinished() { - model->rssiAlarms.critical = ui->rssiAlarmCriticalSB->value(); - emit modified(); + if (!isLocked()) { + model->rssiAlarms.critical = ui->rssiAlarmCriticalSB->value(); + emit modified(); + } } void TelemetryPanel::on_varioLimitMin_DSB_editingFinished() { - model->frsky.varioMin = round(ui->varioLimitMin_DSB->value() + 10); - emit modified(); + if (!isLocked()) { + model->frsky.varioMin = round(ui->varioLimitMin_DSB->value() + 10); + emit modified(); + } } void TelemetryPanel::on_varioLimitMax_DSB_editingFinished() { - model->frsky.varioMax = round(ui->varioLimitMax_DSB->value() - 10); - emit modified(); + if (!isLocked()) { + model->frsky.varioMax = round(ui->varioLimitMax_DSB->value() - 10); + emit modified(); + } } void TelemetryPanel::on_varioLimitCenterMin_DSB_editingFinished() { - if (!lock) { + if (!isLocked()) { if (ui->varioLimitCenterMin_DSB->value() > ui->varioLimitCenterMax_DSB->value()) { ui->varioLimitCenterMax_DSB->setValue(ui->varioLimitCenterMin_DSB->value()); } @@ -945,7 +965,7 @@ void TelemetryPanel::on_varioLimitCenterMin_DSB_editingFinished() void TelemetryPanel::on_varioLimitCenterMax_DSB_editingFinished() { - if (!lock) { + if (!isLocked()) { if (ui->varioLimitCenterMin_DSB->value() > ui->varioLimitCenterMax_DSB->value()) { ui->varioLimitCenterMax_DSB->setValue(ui->varioLimitCenterMin_DSB->value()); } @@ -956,21 +976,27 @@ void TelemetryPanel::on_varioLimitCenterMax_DSB_editingFinished() void TelemetryPanel::on_fasOffset_DSB_editingFinished() { - model->frsky.fasOffset = ui->fasOffset_DSB->value() * 10; - emit modified(); + if (!isLocked()) { + model->frsky.fasOffset = ui->fasOffset_DSB->value() * 10; + emit modified(); + } } void TelemetryPanel::on_mahCount_SB_editingFinished() { - model->frsky.storedMah = ui->mahCount_SB->value(); - emit modified(); + if (!isLocked()) { + model->frsky.storedMah = ui->mahCount_SB->value(); + emit modified(); + } } void TelemetryPanel::on_mahCount_ChkB_toggled(bool checked) { - model->frsky.mAhPersistent = checked; - ui->mahCount_SB->setDisabled(!checked); - emit modified(); + if (!isLocked()) { + model->frsky.mAhPersistent = checked; + ui->mahCount_SB->setDisabled(!checked); + emit modified(); + } } void TelemetryPanel::on_clearAllSensors() @@ -980,9 +1006,7 @@ void TelemetryPanel::on_clearAllSensors() model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_CLEAR, i); } - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } void TelemetryPanel::on_insertSensor(int selectedIndex) @@ -990,10 +1014,7 @@ void TelemetryPanel::on_insertSensor(int selectedIndex) memmove(&model->sensorData[selectedIndex + 1], &model->sensorData[selectedIndex], (CPN_MAX_SENSORS - (selectedIndex + 1)) * sizeof(SensorData)); model->sensorData[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1); - - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } void TelemetryPanel::on_deleteSensor(int selectedIndex) @@ -1004,10 +1025,7 @@ void TelemetryPanel::on_deleteSensor(int selectedIndex) memmove(&model->sensorData[selectedIndex], &model->sensorData[selectedIndex + 1], (CPN_MAX_SENSORS - (selectedIndex + 1)) * sizeof(SensorData)); model->sensorData[CPN_MAX_SENSORS - 1].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1); - - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } void TelemetryPanel::on_moveUpSensor(int selectedIndex) @@ -1029,20 +1047,33 @@ void TelemetryPanel::swapData(int idx1, int idx2) memcpy(sd2, sd1, sizeof(SensorData)); memcpy(sd1, &sdtmp, sizeof(SensorData)); model->updateAllReferences(ModelData::REF_UPD_TYPE_SENSOR, ModelData::REF_UPD_ACT_SWAP, idx1, idx2); - updateItemModels(); - update(); - emit modified(); + on_dataModifiedSensor(); } } void TelemetryPanel::on_dataModifiedSensor() { - updateItemModels(); - update(); + sharedItemModels->update(AbstractItemModel::TeleSensorsUpdated); emit modified(); } -void TelemetryPanel::updateItemModels() +void TelemetryPanel::connectItemModelEvents(const FilteredItemModel * itemModel) { - commonItemModels->update(CommonItemModels::RMO_TELEMETRY_SENSORS); + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryPanel::onItemModelUpdateComplete); +} + +void TelemetryPanel::onItemModelAboutToBeUpdated() +{ + lock = true; + modelsUpdateCnt++; +} + +void TelemetryPanel::onItemModelUpdateComplete() +{ + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 9719c7df6..51273d867 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -23,12 +23,11 @@ #include "modeledit.h" #include "eeprominterface.h" +#include "rawitemfilteredmodel.h" -constexpr char MIMETYPE_TELE_SENSOR[] = "application/x-companion-tele-sensor"; +constexpr char MIMETYPE_TELE_SENSOR[] {"application/x-companion-tele-sensor"}; class AutoComboBox; -class CommonItemModels; -class RawItemFilteredModel; class TimerEdit; namespace Ui { @@ -42,7 +41,8 @@ class TelemetryCustomScreen: public ModelPanel Q_OBJECT public: - TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, RawItemFilteredModel * rawSourceModel); + TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, + const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels); ~TelemetryCustomScreen(); void update(); @@ -54,8 +54,8 @@ class TelemetryCustomScreen: public ModelPanel void barMinChanged(double value); void barMaxChanged(double value); void barTimeChanged(); - void onModelDataAboutToBeUpdated(); - void onModelDataUpdateComplete(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: void updateBar(int line); @@ -67,6 +67,11 @@ class TelemetryCustomScreen: public ModelPanel QDoubleSpinBox * maxSB[4]; TimerEdit * minTime[4]; TimerEdit * maxTime[4]; + int modelsUpdateCnt; + const bool &parentLock; + + inline bool isLocked() { return lock | parentLock; } + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class TelemetrySensorPanel: public ModelPanel @@ -74,7 +79,9 @@ class TelemetrySensorPanel: public ModelPanel Q_OBJECT public: - TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); + TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, + GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, + FilteredItemModelsFactory * localFilteredItemModels); ~TelemetrySensorPanel(); void update(); @@ -86,7 +93,6 @@ class TelemetrySensorPanel: public ModelPanel void moveUpSensor(int index); void moveDownSensor(int index); - protected slots: void on_name_editingFinished(); void on_type_currentIndexChanged(int index); @@ -104,20 +110,24 @@ class TelemetrySensorPanel: public ModelPanel void cmDelete(); void cmMoveUp(); void cmMoveDown(); - - protected: - void updateSourcesComboBox(AutoComboBox * cb, bool negative); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::TelemetrySensor * ui; SensorData & sensor; - bool lock = false; + //bool lock = false; int sensorIndex = 0; int selectedIndex = 0; int sensorCapability; bool insertAllowed() const; bool moveDownAllowed() const; bool moveUpAllowed() const; + int modelsUpdateCnt; + const bool &parentLock; + + inline bool isLocked() { return lock | parentLock; } + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class TelemetryPanel : public ModelPanel @@ -125,7 +135,8 @@ class TelemetryPanel : public ModelPanel Q_OBJECT public: - TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CommonItemModels * commonItemModels); + TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + ItemModelsFactory * sharedItemModels); virtual ~TelemetryPanel(); virtual void update(); @@ -152,22 +163,23 @@ class TelemetryPanel : public ModelPanel void on_moveUpSensor(int index); void on_moveDownSensor(int index); void on_dataModifiedSensor(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: Ui::Telemetry *ui; - TelemetryCustomScreen * telemetryCustomScreens[4]; - TelemetrySensorPanel * sensorPanels[CPN_MAX_SENSORS]; + TelemetryCustomScreen *telemetryCustomScreens[4]; + TelemetrySensorPanel *sensorPanels[CPN_MAX_SENSORS]; int sensorCapability; - CommonItemModels * commonItemModels; - RawItemFilteredModel * rawSourceFilteredModel; + ItemModelsFactory *sharedItemModels; + FilteredItemModelsFactory *localFilteredItemModels; + int modelsUpdateCnt; void setup(); void telBarUpdate(); - void populateVoltsSource(); - void populateCurrentSource(); - void populateVarioSource(); void swapData(int idx1, int idx2); - void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); + inline bool isLocked() { return lock; } }; #endif // _TELEMETRY_H_ diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index fcb1845ac..158352f97 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -37,30 +37,37 @@ class AutoComboBox: public QComboBox void clear() { - lock = true; - QComboBox::clear(); - next = 0; - lock = false; + if (!hasModel) { + lock = true; + QComboBox::clear(); + next = 0; + lock = false; + } } virtual void insertItems(int index, const QStringList & items) { - foreach(QString item, items) { - addItem(item); + if (!hasModel) { + foreach(QString item, items) { + addItem(item); + } } } virtual void addItem(const QString & item) { - addItem(item, next++); + if (!hasModel) + addItem(item, next++); } virtual void addItem(const QString & item, int value) { - lock = true; - QComboBox::addItem(item, value); - lock = false; - updateValue(); + if (!hasModel) { + lock = true; + QComboBox::addItem(item, value); + lock = false; + updateValue(); + } } void setField(unsigned int & field, GenericPanel * panel=nullptr) @@ -77,11 +84,22 @@ class AutoComboBox: public QComboBox updateValue(); } + void setModel(QAbstractItemModel * model) + { + lock = true; + QComboBox::setModel(model); + lock = false; + hasModel = true; + updateValue(); + } + void setAutoIndexes() { - for (int i=0; imodified(); + if (panel && panel->lock) + return; + if (index > -1) { + const int val = itemData(index).toInt(); + if (field && !lock) { + *field = val; + if (panel) + emit panel->modified(); + } + emit currentDataChanged(val); } - emit currentDataChanged(val); } protected: @@ -113,6 +135,7 @@ class AutoComboBox: public QComboBox GenericPanel * panel = nullptr; int next = 0; bool lock = false; + bool hasModel = false; }; #endif // _AUTOCOMBOBOX_H_ From 905886774fd07d78330ec308a68da43460f68b2e Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 8 Jan 2021 19:55:57 +1100 Subject: [PATCH 036/300] Major refactoring continued --- companion/src/datamodels/CMakeLists.txt | 8 +- ...mdatamodels.cpp => compounditemmodels.cpp} | 301 ++++++++++++------ ...witemdatamodels.h => compounditemmodels.h} | 191 ++++++----- ...lteredmodel.cpp => filtereditemmodels.cpp} | 121 +++++-- ...emfilteredmodel.h => filtereditemmodels.h} | 51 +-- companion/src/datamodels/itemmodelfactory.cpp | 8 +- companion/src/datamodels/itemmodelfactory.h | 6 +- companion/src/firmwares/curvereference.cpp | 51 +-- companion/src/firmwares/curvereference.h | 22 +- companion/src/firmwares/sensordata.cpp | 164 +++++++++- companion/src/firmwares/sensordata.h | 19 +- companion/src/generaledit/generaledit.cpp | 12 +- companion/src/generaledit/generaledit.h | 4 +- companion/src/modeledit/channels.cpp | 15 +- companion/src/modeledit/channels.h | 7 +- companion/src/modeledit/curves.cpp | 6 +- companion/src/modeledit/curves.h | 6 +- companion/src/modeledit/customfunctions.cpp | 26 +- companion/src/modeledit/customfunctions.h | 6 +- companion/src/modeledit/expodialog.cpp | 28 +- companion/src/modeledit/expodialog.h | 10 +- companion/src/modeledit/flightmodes.cpp | 26 +- companion/src/modeledit/flightmodes.h | 8 +- companion/src/modeledit/heli.cpp | 13 +- companion/src/modeledit/heli.h | 5 +- companion/src/modeledit/inputs.cpp | 45 +-- companion/src/modeledit/inputs.h | 14 +- companion/src/modeledit/logicalswitches.cpp | 20 +- companion/src/modeledit/logicalswitches.h | 7 +- companion/src/modeledit/mixerdialog.cpp | 23 +- companion/src/modeledit/mixerdialog.h | 9 +- companion/src/modeledit/mixes.cpp | 43 +-- companion/src/modeledit/mixes.h | 14 +- companion/src/modeledit/modeledit.cpp | 24 +- companion/src/modeledit/modeledit.h | 4 +- companion/src/modeledit/setup.cpp | 29 +- companion/src/modeledit/setup.h | 10 +- companion/src/modeledit/telemetry.cpp | 134 +++++--- companion/src/modeledit/telemetry.h | 16 +- 39 files changed, 978 insertions(+), 528 deletions(-) rename companion/src/datamodels/{rawitemdatamodels.cpp => compounditemmodels.cpp} (70%) rename companion/src/datamodels/{rawitemdatamodels.h => compounditemmodels.h} (62%) rename companion/src/datamodels/{rawitemfilteredmodel.cpp => filtereditemmodels.cpp} (51%) rename companion/src/datamodels/{rawitemfilteredmodel.h => filtereditemmodels.h} (61%) diff --git a/companion/src/datamodels/CMakeLists.txt b/companion/src/datamodels/CMakeLists.txt index beb7d4d2d..446f161b3 100644 --- a/companion/src/datamodels/CMakeLists.txt +++ b/companion/src/datamodels/CMakeLists.txt @@ -1,12 +1,12 @@ set(datamodels_SRCS - rawitemdatamodels.cpp - rawitemfilteredmodel.cpp + compounditemmodels.cpp + filtereditemmodels.cpp ) set(datamodels_HDRS - rawitemdatamodels.h - rawitemfilteredmodel.h + compounditemmodels.h + filtereditemmodels.h ) qt5_wrap_cpp(datamodels_SRCS ${datamodels_HDRS}) diff --git a/companion/src/datamodels/rawitemdatamodels.cpp b/companion/src/datamodels/compounditemmodels.cpp similarity index 70% rename from companion/src/datamodels/rawitemdatamodels.cpp rename to companion/src/datamodels/compounditemmodels.cpp index 59b1542d5..039e90244 100644 --- a/companion/src/datamodels/rawitemdatamodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -18,30 +18,81 @@ * GNU General Public License for more details. */ -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" #include "generalsettings.h" #include "eeprominterface.h" #include "modeldata.h" #include "adjustmentreference.h" +// static +QString AbstractItemModel::idToString(const int value) +{ + switch (value) { + case IMID_Unknown: + return "Unknown"; + case IMID_RawSource: + return "RawSource"; + case IMID_RawSwitch: + return "RawSwitch"; + case IMID_Curve: + return "Curve"; + case IMID_GVarRef: + return "GVarRef"; + case IMID_ThrSource: + return "ThrSource"; + case IMID_CustomFuncAction: + return "CustomFuncAction"; + case IMID_CustomFuncResetParam: + return "CustomFuncResetParam"; + case IMID_TeleSource: + return "TeleSource"; + case IMID_CurveRefType: + return "CurveRefType"; + case IMID_CurveRefFunc: + return "CurveRefFunc"; + default: + return "Custom"; + } +} + // static void AbstractItemModel::dumpItemModelContents(AbstractItemModel * itemModel) { if (itemModel) { - qDebug() << "modelid:" << itemModel->getId(); + qDebug() << "id:" << itemModel->getId() << "name:" << itemModel->getName(); + for (int i = 0; i < itemModel->rowCount(); ++i) { qDebug() << "row:" << i << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() - << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() - << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() - << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() - << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Id).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Available).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Flags).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Type).toInt(); } } else - qDebug() << "Error: model did not cast"; + qDebug() << "Error: model not of class AbstractItemModel"; } +// +// AbstractStaticItemModel +// + +void AbstractStaticItemModel::loadItemList() +{ + foreach (const ListItem *item, itemList) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(item->text); + modelItem->setData(item->id, IMDR_Id); + modelItem->setData(item->type, IMDR_Type); + modelItem->setData(item->flags, IMDR_Flags); + modelItem->setData(item->isAvailable, IMDR_Available); + appendRow(modelItem); + } + + itemList.clear(); +}; + // // RawSourceItemModel // @@ -50,8 +101,8 @@ RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSett Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(RawSourceId); - setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); + setId(IMID_RawSource); + setUpdateMask(IMUE_All &~ (IMUE_Curves | IMUE_Scripts)); addItems(SOURCE_TYPE_NONE, RawSource::NoneGroup, 1); for (int i = 0; i < firmware->getCapability(LuaScripts); i++) @@ -74,7 +125,7 @@ RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSett void RawSourceItemModel::setDynamicItemData(QStandardItem * item, const RawSource & src) const { item->setText(src.toString(modelData, generalSettings, boardType)); - item->setData(src.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); + item->setData(src.isAvailable(modelData, generalSettings, boardType), IMDR_Available); } void RawSourceItemModel::addItems(const RawSourceType & type, const int group, const int count, const int start) @@ -82,21 +133,21 @@ void RawSourceItemModel::addItems(const RawSourceType & type, const int group, c for (int i = start; i < start + count; i++) { const RawSource src = RawSource(type, i); QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(src.toValue(), ItemIdRole); - modelItem->setData(type, ItemTypeRole); - modelItem->setData(group, ItemFlagsRole); + modelItem->setData(src.toValue(), IMDR_Id); + modelItem->setData(type, IMDR_Type); + modelItem->setData(group, IMDR_Flags); setDynamicItemData(modelItem, src); appendRow(modelItem); } } -void RawSourceItemModel::update(const UpdateTrigger trigger) +void RawSourceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSource(item(i)->data(ItemIdRole).toInt())); + setDynamicItemData(item(i), RawSource(item(i)->data(IMDR_Id).toInt())); emit updateComplete(); } @@ -110,8 +161,8 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(RawSwitchId); - setUpdateMask(FlightModesUpdated | LogicalSwitchesUpdated | TeleSensorsUpdated); + setId(IMID_RawSwitch); + setUpdateMask(IMUE_FlightModes | IMUE_LogicalSwitches | IMUE_TeleSensors); // Descending switch direction: NOT (!) switches addItems(SWITCH_TYPE_ACT, -1); @@ -143,7 +194,7 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett void RawSwitchItemModel::setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const { item->setText(rsw.toString(boardType, generalSettings, modelData)); - item->setData(rsw.isAvailable(modelData, generalSettings, boardType), IsAvailableRole); + item->setData(rsw.isAvailable(modelData, generalSettings, boardType), IMDR_Available); } void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) @@ -186,21 +237,21 @@ void RawSwitchItemModel::addItems(const RawSwitchType & type, int count) for ( ; i < count; ++i) { RawSwitch rs(type, i + rawIdxAdj); QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(rs.toValue(), ItemIdRole); - modelItem->setData(type, ItemTypeRole); - modelItem->setData(context, ItemFlagsRole); + modelItem->setData(rs.toValue(), IMDR_Id); + modelItem->setData(type, IMDR_Type); + modelItem->setData(context, IMDR_Flags); setDynamicItemData(modelItem, rs); appendRow(modelItem); } } -void RawSwitchItemModel::update(const UpdateTrigger trigger) +void RawSwitchItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), RawSwitch(item(i)->data(ItemIdRole).toInt())); + setDynamicItemData(item(i), RawSwitch(item(i)->data(IMDR_Id).toInt())); emit updateComplete(); } @@ -214,22 +265,19 @@ CurveItemModel::CurveItemModel(const GeneralSettings * const generalSettings, co Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CurveId); + setId(IMID_Curve); if (!modelData) return; - setUpdateMask(CurvesUpdated); + setUpdateMask(IMUE_Curves); const int count = firmware->getCapability(NumCurves); for (int i = -count ; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); - int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; - if (i == 0) - flags |= DataGroups::NoneGroup; - modelItem->setData(flags, ItemFlagsRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -239,16 +287,16 @@ void CurveItemModel::setDynamicItemData(QStandardItem * item, const int value) c { CurveReference cr = CurveReference(CurveReference::CURVE_REF_CUSTOM, value); item->setText(cr.toString(modelData, false)); - item->setData(cr.isAvailable(), IsAvailableRole); + item->setData(cr.isAvailable(), IMDR_Available); } -void CurveItemModel::update(const UpdateTrigger trigger) +void CurveItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -262,12 +310,12 @@ GVarReferenceItemModel::GVarReferenceItemModel(const GeneralSettings * const gen Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(GVarRefId); + setId(IMID_GVarRef); if (!modelData) return; - setUpdateMask(GVarsUpdated | FlightModesUpdated | LogicalSwitchesUpdated); + setUpdateMask(IMUE_GVars | IMUE_FlightModes | IMUE_LogicalSwitches); const int count = firmware->getCapability(Gvars); @@ -284,8 +332,8 @@ void GVarReferenceItemModel::addItems(int count) for ( ; i < count; ++i) { QStandardItem * modelItem = new QStandardItem(); AdjustmentReference ar(AdjustmentReference::ADJUST_REF_GVAR, i); - modelItem->setData(ar.toValue(), ItemIdRole); - modelItem->setData(i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup, ItemFlagsRole); + modelItem->setData(ar.toValue(), IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); setDynamicItemData(modelItem, ar); appendRow(modelItem); } @@ -294,16 +342,16 @@ void GVarReferenceItemModel::addItems(int count) void GVarReferenceItemModel::setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const { item->setText(ar.toString(modelData, false)); - item->setData(ar.isAvailable(), IsAvailableRole); + item->setData(ar.isAvailable(), IMDR_Available); } -void GVarReferenceItemModel::update(const UpdateTrigger trigger) +void GVarReferenceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), AdjustmentReference(item(i)->data(ItemIdRole).toInt())); + setDynamicItemData(item(i), AdjustmentReference(item(i)->data(IMDR_Id).toInt())); emit updateComplete(); } @@ -317,16 +365,16 @@ ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const g Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(ThrSourceId); + setId(IMID_ThrSource); if (!modelData) return; - setUpdateMask(TimersUpdated | InputsUpdated | TeleSensorsUpdated); + setUpdateMask(IMUE_Timers | IMUE_Inputs | IMUE_TeleSensors); for (int i = 0; i < modelData->thrTraceSrcCount(); i++) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); + modelItem->setData(i, IMDR_Id); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -335,16 +383,16 @@ ThrottleSourceItemModel::ThrottleSourceItemModel(const GeneralSettings * const g void ThrottleSourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { item->setText(modelData->thrTraceSrcToString(value)); - item->setData(modelData->isThrTraceSrcAvailable(generalSettings, value), IsAvailableRole); + item->setData(modelData->isThrTraceSrcAvailable(generalSettings, value), IMDR_Available); } -void ThrottleSourceItemModel::update(const UpdateTrigger trigger) +void ThrottleSourceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -358,13 +406,13 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CustomFuncActionId); - setUpdateMask(AllTriggers &~ (CurvesUpdated | ScriptsUpdated)); + setId(IMID_CustomFuncAction); + setUpdateMask(IMUE_All &~ (IMUE_Curves | IMUE_Scripts)); for (int i = 0; i < AssignFunc::FuncCount; i++) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); - modelItem->setData(CustomFunctionData::funcContext(i), ItemFlagsRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(CustomFunctionData::funcContext(i), IMDR_Flags); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -373,16 +421,16 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int value) const { item->setText(CustomFunctionData(AssignFunc(value)).funcToString(modelData)); - item->setData(CustomFunctionData::isFuncAvailable(value), IsAvailableRole); + item->setData(CustomFunctionData::isFuncAvailable(value), IMDR_Available); } -void CustomFuncActionItemModel::update(const UpdateTrigger trigger) +void CustomFuncActionItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -396,12 +444,12 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CustomFuncResetParamId); - setUpdateMask(TeleSensorsUpdated); + setId(IMID_CustomFuncResetParam); + setUpdateMask(IMUE_TeleSensors); for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); + modelItem->setData(i, IMDR_Id); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -412,16 +460,16 @@ void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, con CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); cfd.param = value; item->setText(cfd.paramToString(modelData)); - item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IsAvailableRole); + item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IMDR_Available); } -void CustomFuncResetParamItemModel::update(const UpdateTrigger trigger) +void CustomFuncResetParamItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); emit updateComplete(); } @@ -435,21 +483,18 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(TeleSourceId); + setId(IMID_TeleSource); if (!modelData) return; - setUpdateMask(TeleSensorsUpdated); + setUpdateMask(IMUE_TeleSensors); const int count = firmware->getCapability(Sensors); for (int i = -count; i <= count; ++i) { QStandardItem * modelItem = new QStandardItem(); - modelItem->setData(i, ItemIdRole); - int flags = i < 0 ? DataGroups::NegativeGroup : DataGroups::PositiveGroup; - if (i == 0) - flags |= DataGroups::NoneGroup; - modelItem->setData(flags, ItemFlagsRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); setDynamicItemData(modelItem, i); appendRow(modelItem); } @@ -458,16 +503,16 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const void TelemetrySourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const { item->setText(SensorData::sourceToString(modelData, value)); - item->setData(SensorData::isSourceAvailable(modelData, value), IsAvailableRole); + item->setData(SensorData::isSourceAvailable(modelData, value), IMDR_Available); } -void TelemetrySourceItemModel::update(const UpdateTrigger trigger) +void TelemetrySourceItemModel::update(const int event) { - if (doUpdate(trigger)) { + if (doUpdate(event)) { emit aboutToBeUpdated(); for (int i = 0; i < rowCount(); ++i) { - setDynamicItemData(item(i), item(i)->data(ItemIdRole).toInt()); + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); } emit updateComplete(); @@ -482,13 +527,13 @@ CurveRefTypeItemModel::CurveRefTypeItemModel(const GeneralSettings * const gener Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CurveRefTypeId); + setId(IMID_CurveRefType); for (int i = 0; i <= CurveReference::MAX_CURVE_REF_TYPE; i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::typeToString((CurveReference::CurveRefType)i)); - modelItem->setData(i, ItemIdRole); - modelItem->setData(CurveReference::isTypeAvailable((CurveReference::CurveRefType)i), IsAvailableRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(CurveReference::isTypeAvailable((CurveReference::CurveRefType)i), IMDR_Available); appendRow(modelItem); } } @@ -501,22 +546,43 @@ CurveRefFuncItemModel::CurveRefFuncItemModel(const GeneralSettings * const gener Firmware * firmware, const Boards * const board, const Board::Type boardType) : AbstractStaticItemModel(generalSettings, modelData, firmware, board, boardType) { - setId(CurveRefFuncId); + setId(IMID_CurveRefFunc); for (int i = 1; i <= CurveReference::functionCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setText(CurveReference::functionToString(i)); - modelItem->setData(i, ItemIdRole); - modelItem->setData(CurveReference::isFunctionAvailable(i), IsAvailableRole); + modelItem->setData(i, IMDR_Id); + modelItem->setData(CurveReference::isFunctionAvailable(i), IMDR_Available); appendRow(modelItem); } } // -// ItemModelsFactory +// PrecisionItemModel // -ItemModelsFactory::ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : +PrecisionItemModel::PrecisionItemModel(const int minDecimals, const int maxDecimals, const QString suffix, const bool placeholders) : + AbstractStaticItemModel() +{ + for (int i = minDecimals, j = maxDecimals; j >= 0; i++, j--) { + QString str = QString("0.%1").arg(QString(i, '0')); + if (placeholders) + str.append(QString(j, '_')); + if (!suffix.isEmpty()) + str.append(QString(" %1").arg(suffix)); + QStandardItem * modelItem = new QStandardItem(); + modelItem->setText(str); + modelItem->setData(i, IMDR_Id); + modelItem->setData(true, IMDR_Available); + appendRow(modelItem); + } +} + +// +// CompoundItemModelFactory +// + +CompoundItemModelFactory::CompoundItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : generalSettings(generalSettings), modelData(modelData) { @@ -525,43 +591,43 @@ ItemModelsFactory::ItemModelsFactory(const GeneralSettings * const generalSettin boardType = getCurrentBoard(); } -ItemModelsFactory::~ItemModelsFactory() +CompoundItemModelFactory::~CompoundItemModelFactory() { unregisterItemModels(); delete board; } -void ItemModelsFactory::addItemModel(const AbstractItemModel::ItemModelId id) +void CompoundItemModelFactory::addItemModel(const int id) { switch (id) { - case AbstractItemModel::RawSourceId: + case AbstractItemModel::IMID_RawSource: registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::RawSwitchId: + case AbstractItemModel::IMID_RawSwitch: registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CurveId: + case AbstractItemModel::IMID_Curve: registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::GVarRefId: + case AbstractItemModel::IMID_GVarRef: registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::ThrSourceId: + case AbstractItemModel::IMID_ThrSource: registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CustomFuncActionId: + case AbstractItemModel::IMID_CustomFuncAction: registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CustomFuncResetParamId: + case AbstractItemModel::IMID_CustomFuncResetParam: registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::TeleSourceId: + case AbstractItemModel::IMID_TeleSource: registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CurveRefTypeId: + case AbstractItemModel::IMID_CurveRefType: registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); break; - case AbstractItemModel::CurveRefFuncId: + case AbstractItemModel::IMID_CurveRefFunc: registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); break; default: @@ -570,28 +636,35 @@ void ItemModelsFactory::addItemModel(const AbstractItemModel::ItemModelId id) } } -void ItemModelsFactory::registerItemModel(AbstractItemModel * itemModel) +int CompoundItemModelFactory::registerItemModel(AbstractItemModel * itemModel) { - if (itemModel && !getItemModel(itemModel->getId())) { - registeredItemModels.push_back(itemModel); + if (itemModel) { + if (!isItemModelRegistered(itemModel->getId())) { + setSourceId(itemModel); + registeredItemModels.append(itemModel); + return itemModel->getId(); + } + else + return itemModel->getId(); } + return -1; } -void ItemModelsFactory::unregisterItemModels() +void CompoundItemModelFactory::unregisterItemModels() { foreach (AbstractItemModel *itemModel, registeredItemModels) { delete itemModel; } } -void ItemModelsFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) +void CompoundItemModelFactory::unregisterItemModel(const int id) { AbstractItemModel * itemModel = getItemModel(id); if (itemModel) delete itemModel; } -AbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +AbstractItemModel * CompoundItemModelFactory::getItemModel(const int id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { if (itemModel->getId() == id) @@ -601,16 +674,32 @@ AbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::Ite return nullptr; } -void ItemModelsFactory::update(const AbstractItemModel::UpdateTrigger trigger) +bool CompoundItemModelFactory::isItemModelRegistered(const int id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { - itemModel->update(trigger); + if (itemModel->getId() == id) + return true; + } + + return false; +} + +void CompoundItemModelFactory::update(const int event) +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + itemModel->update(event); } } -void ItemModelsFactory::dumpAllItemModelContents() const +void CompoundItemModelFactory::dumpAllItemModelContents() const { foreach (AbstractItemModel * itemModel, registeredItemModels) { AbstractItemModel::dumpItemModelContents(itemModel); } } + +void CompoundItemModelFactory::setSourceId(AbstractItemModel * itemModel) +{ + if (itemModel && itemModel->getId() == AbstractItemModel::IMID_Unknown) + itemModel->setId(AbstractItemModel::IMID_ReservedCount + registeredItemModels.count()); +} diff --git a/companion/src/datamodels/rawitemdatamodels.h b/companion/src/datamodels/compounditemmodels.h similarity index 62% rename from companion/src/datamodels/rawitemdatamodels.h rename to companion/src/datamodels/compounditemmodels.h index 89f24acdc..de32fc902 100644 --- a/companion/src/datamodels/rawitemdatamodels.h +++ b/companion/src/datamodels/compounditemmodels.h @@ -18,8 +18,8 @@ * GNU General Public License for more details. */ -#ifndef RAWITEMDATAMODELS_H -#define RAWITEMDATAMODELS_H +#ifndef COMPOUNDITEMMODELS_H +#define COMPOUNDITEMMODELS_H #include "rawsource.h" #include "rawswitch.h" @@ -35,51 +35,53 @@ class AbstractItemModel: public QStandardItemModel Q_OBJECT public: enum ItemModelId { - UnknownId, - RawSourceId, - RawSwitchId, - CurveId, - GVarRefId, - ThrSourceId, - CustomFuncActionId, - CustomFuncResetParamId, - TeleSourceId, - CurveRefTypeId, - CurveRefFuncId + IMID_Unknown, + IMID_RawSource, + IMID_RawSwitch, + IMID_Curve, + IMID_GVarRef, + IMID_ThrSource, + IMID_CustomFuncAction, + IMID_CustomFuncResetParam, + IMID_TeleSource, + IMID_CurveRefType, + IMID_CurveRefFunc, + IMID_ReservedCount, + IMID_Custom }; Q_ENUM(ItemModelId) - enum DataRoles { - ItemIdRole = Qt::UserRole, - ItemTypeRole, - ItemFlagsRole, - IsAvailableRole + enum ItemModelDataRoles { + IMDR_Id = Qt::UserRole, + IMDR_Type, + IMDR_Flags, + IMDR_Available }; - Q_ENUM(DataRoles) + Q_ENUM(ItemModelDataRoles) - enum DataGroups { - NoneGroup = 0x01, - NegativeGroup = 0x02, - PositiveGroup = 0x04 + enum ItemModelDataGroups { + IMDG_None = 0x01, + IMDG_Negative = 0x02, + IMDG_Positive = 0x04 }; - Q_ENUM(DataGroups) + Q_ENUM(ItemModelDataGroups) - enum UpdateTrigger { - SystemRefresh = 1 << 0, - ChannelsUpdated = 1 << 1, - CurvesUpdated = 1 << 2, - FlightModesUpdated = 1 << 3, - GVarsUpdated = 1 << 4, - InputsUpdated = 1 << 5, - LogicalSwitchesUpdated = 1 << 6, - ScriptsUpdated = 1 << 7, - TeleSensorsUpdated = 1 << 8, - TimersUpdated = 1 << 9, - AllTriggers = SystemRefresh | ChannelsUpdated | CurvesUpdated | FlightModesUpdated | GVarsUpdated | InputsUpdated | - LogicalSwitchesUpdated | ScriptsUpdated | TeleSensorsUpdated | TimersUpdated, - NoTriggers = 0 + enum ItemModelUpdateEvent { + IMUE_None = 0, + IMUE_SystemRefresh = 1 << 0, + IMUE_Channels = 1 << 1, + IMUE_Curves = 1 << 2, + IMUE_FlightModes = 1 << 3, + IMUE_GVars = 1 << 4, + IMUE_Inputs = 1 << 5, + IMUE_LogicalSwitches = 1 << 6, + IMUE_Scripts = 1 << 7, + IMUE_TeleSensors = 1 << 8, + IMUE_Timers = 1 << 9, + IMUE_All = IMUE_SystemRefresh | IMUE_Channels | IMUE_Curves | IMUE_FlightModes | IMUE_GVars | IMUE_Inputs | + IMUE_LogicalSwitches | IMUE_Scripts | IMUE_TeleSensors | IMUE_Timers }; - Q_ENUM(UpdateTrigger) + Q_ENUM(ItemModelUpdateEvent) explicit AbstractItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, Firmware * firmware, const Boards * const board, const Board::Type boardType) : @@ -88,24 +90,30 @@ class AbstractItemModel: public QStandardItemModel modelData(modelData), firmware(firmware), board(board), - boardType(boardType) - {} + boardType(boardType), + m_id(IMID_Unknown), + m_name(""), + m_updateMask(IMUE_None) + {} - virtual ~AbstractItemModel() {}; - - void setId(ItemModelId id) { m_id = id; } - ItemModelId getId () const { return m_id; } + virtual ~AbstractItemModel() {} + void setId(int id) { m_id = id; } + int getId () const { return m_id; } + bool isReservedModelId() const { return m_id > IMID_Unknown && m_id < IMID_ReservedCount; } + void setName(QString name) { m_name = name; } + QString getName() const { return isReservedModelId() ? idToString(m_id) : m_name; } void setUpdateMask(const int mask) { m_updateMask = mask; } int getUpdateMask() const { return m_updateMask; } - inline bool doUpdate(const UpdateTrigger trigger) const { return m_updateMask & (int)trigger; } - AbstractItemModel * getItemModel(const ItemModelId id) const; + inline bool doUpdate(const int event) const { return m_updateMask & event; } + + AbstractItemModel * getItemModel(const int id) const; static void dumpItemModelContents(AbstractItemModel * itemModel); public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) = 0; + virtual void update(const int event = IMUE_SystemRefresh) = 0; protected: const GeneralSettings * generalSettings; @@ -115,21 +123,46 @@ class AbstractItemModel: public QStandardItemModel const Board::Type boardType; private: - ItemModelId m_id = UnknownId; - int m_updateMask = 0; + int m_id = IMID_Unknown; + QString m_name = ""; + int m_updateMask = IMUE_None; + + static QString idToString(const int value); }; class AbstractStaticItemModel: public AbstractItemModel { Q_OBJECT public: - explicit AbstractStaticItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, - Firmware * firmware, const Boards * const board, const Board::Type boardType) : + explicit AbstractStaticItemModel(const GeneralSettings * const generalSettings = nullptr, const ModelData * const modelData = nullptr, + Firmware * firmware = nullptr, const Boards * const board = nullptr, + const Board::Type boardType = Board::BOARD_UNKNOWN) : AbstractItemModel(generalSettings, modelData, firmware, board, boardType) {} virtual ~AbstractStaticItemModel() {}; + inline void appendToItemList(QString text, int id, bool isAvailable = true, int type = 0, int flags = 0) + { itemList.append(new ListItem(text, id, isAvailable, type, flags)); } + + void loadItemList(); + public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override final {} + virtual void update(const int event) override final {} + + protected: + struct ListItem + { + QString text; + int id; + bool isAvailable; + int type; + int flags; + + ListItem() {} + ListItem(QString p_text, int p_id, bool p_isAvailable, int p_type, int p_flags) : + text(p_text), id(p_id), isAvailable(p_isAvailable), type(p_type), flags(p_flags) {} + }; + + QVector itemList; }; class AbstractDynamicItemModel: public AbstractItemModel @@ -142,7 +175,7 @@ class AbstractDynamicItemModel: public AbstractItemModel virtual ~AbstractDynamicItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) {} + virtual void update(const int event = IMUE_SystemRefresh) {} signals: void aboutToBeUpdated(); @@ -158,7 +191,7 @@ class RawSourceItemModel: public AbstractDynamicItemModel virtual ~RawSourceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const RawSource & src) const; @@ -174,7 +207,7 @@ class RawSwitchItemModel: public AbstractDynamicItemModel virtual ~RawSwitchItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const RawSwitch & rsw) const; @@ -190,7 +223,7 @@ class CurveItemModel: public AbstractDynamicItemModel virtual ~CurveItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -205,7 +238,7 @@ class GVarReferenceItemModel: public AbstractDynamicItemModel virtual ~GVarReferenceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const AdjustmentReference & ar) const; @@ -221,7 +254,7 @@ class ThrottleSourceItemModel: public AbstractDynamicItemModel virtual ~ThrottleSourceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -236,7 +269,7 @@ class CustomFuncActionItemModel: public AbstractDynamicItemModel virtual ~CustomFuncActionItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -251,7 +284,7 @@ class CustomFuncResetParamItemModel: public AbstractDynamicItemModel virtual ~CustomFuncResetParamItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -266,7 +299,7 @@ class TelemetrySourceItemModel: public AbstractDynamicItemModel virtual ~TelemetrySourceItemModel() {}; public slots: - virtual void update(const UpdateTrigger trigger = SystemRefresh) override; + virtual void update(const int event = IMUE_SystemRefresh) override; protected: virtual void setDynamicItemData(QStandardItem * item, const int value) const; @@ -290,18 +323,31 @@ class CurveRefFuncItemModel : public AbstractStaticItemModel virtual ~CurveRefFuncItemModel() {}; }; -class ItemModelsFactory +class PrecisionItemModel : public AbstractStaticItemModel +{ + Q_OBJECT + public: + explicit PrecisionItemModel(const int minDecimals, const int maxDecimals, const QString suffix = "", const bool placeholders = false); + virtual ~PrecisionItemModel() {}; +}; + +// +// CompoundItemModelFactory +// + +class CompoundItemModelFactory { public: - ItemModelsFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); - virtual ~ItemModelsFactory(); + CompoundItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); + virtual ~CompoundItemModelFactory(); - void addItemModel(const AbstractItemModel::ItemModelId id); - void registerItemModel(AbstractItemModel * itemModel); + void addItemModel(const int id); + int registerItemModel(AbstractItemModel * itemModel); void unregisterItemModels(); - void unregisterItemModel(const AbstractItemModel::ItemModelId id); - AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; - void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); + void unregisterItemModel(const int id); + bool isItemModelRegistered(const int id) const; + AbstractItemModel * getItemModel(const int id) const; + void update(const int event = AbstractItemModel::IMUE_SystemRefresh); void dumpAllItemModelContents() const; protected: @@ -311,6 +357,9 @@ class ItemModelsFactory Boards * board; Board::Type boardType; QVector registeredItemModels; + + private: + void setSourceId(AbstractItemModel * itemModel); }; -#endif // RAWITEMDATAMODELS_H +#endif // COMPOUNDITEMMODELS_H diff --git a/companion/src/datamodels/rawitemfilteredmodel.cpp b/companion/src/datamodels/filtereditemmodels.cpp similarity index 51% rename from companion/src/datamodels/rawitemfilteredmodel.cpp rename to companion/src/datamodels/filtereditemmodels.cpp index 1cddff04d..4f4e27262 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.cpp +++ b/companion/src/datamodels/filtereditemmodels.cpp @@ -18,13 +18,15 @@ * GNU General Public License for more details. */ -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" FilteredItemModel::FilteredItemModel(AbstractItemModel * sourceModel, int flags) : QSortFilterProxyModel(nullptr), - filterFlags(0) + filterFlags(0), + m_id(0), + m_name("") { - setFilterRole(AbstractItemModel::IsAvailableRole); + setFilterRole(AbstractItemModel::IMDR_Available); setFilterKeyColumn(0); setFilterFlags(flags); setDynamicSortFilter(true); @@ -55,7 +57,7 @@ bool FilteredItemModel::filterAcceptsRow(int sourceRow, const QModelIndex & sour return true; bool ok; - const int flags = sourceModel()->data(srcIdx, AbstractItemModel::ItemFlagsRole).toInt(&ok); + const int flags = sourceModel()->data(srcIdx, AbstractItemModel::IMDR_Flags).toInt(&ok); return (ok && (!flags || (filterFlags & flags))); } @@ -85,68 +87,85 @@ void FilteredItemModel::dumpItemModelContents(FilteredItemModel * itemModel) for (int i = 0; i < itemModel->rowCount(); ++i) { qDebug() << "row:" << i << "text:" << itemModel->data(itemModel->index(i, 0), Qt::DisplayRole).toString() - << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemIdRole).toInt() - << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IsAvailableRole).toBool() - << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemFlagsRole).toInt() - << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::ItemTypeRole).toInt(); + << "id:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Id).toInt() + << "avail:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Available).toBool() + << "flags:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Flags).toInt() + << "type:" << itemModel->data(itemModel->index(i, 0), AbstractItemModel::IMDR_Type).toInt(); } } else - qDebug() << "Error: model did not cast"; + qDebug() << "Error: model not of class FilteredItemModel"; } // -// FilteredItemModelsFactory +// FilteredItemModelFactory // -FilteredItemModelsFactory::FilteredItemModelsFactory() +FilteredItemModelFactory::FilteredItemModelFactory() { } -FilteredItemModelsFactory::~FilteredItemModelsFactory() +FilteredItemModelFactory::~FilteredItemModelFactory() { unregisterItemModels(); } -int FilteredItemModelsFactory::registerItemModel(FilteredItemModel * itemModel, QString name) +int FilteredItemModelFactory::registerItemModel(FilteredItemModel * itemModel, const QString name, const int id) { if (itemModel) { - if (isItemModelRegistered(name)) - qDebug() << "Warning: item model already registered with name:" << name; - const int id = registeredItemModels.count(); - itemModel->setId(id); - itemModel->setName(name); - registeredItemModels.push_back(itemModel); - return id; + if (isItemModelRegistered(name)) { + qDebug() << "Error: item model already registered with name:" << name; + return 0; + } + else if (id > -1 && isItemModelRegistered(id)) { + qDebug() << "Error: item model already registered with id:" << id; + return 0; + } + else { + int fid = id; + if (fid < 0) { + fid = registeredItemModels.count() + 1; + AbstractItemModel * mdl = qobject_cast(itemModel->sourceModel()); + if (mdl) { + fid += mdl->getId() * 128; + } + } + itemModel->setId(fid); + itemModel->setName(name); + registeredItemModels.append(itemModel); + return fid; + } } + + qDebug() << "Error: invalid filtered item model pointer"; return 0; } -void FilteredItemModelsFactory::unregisterItemModels() +void FilteredItemModelFactory::unregisterItemModels() { foreach (FilteredItemModel * itemModel, registeredItemModels) { delete itemModel; } } -void FilteredItemModelsFactory::unregisterItemModel(const int id) +void FilteredItemModelFactory::unregisterItemModel(const int id) { FilteredItemModel * itemModel = getItemModel(id); if (itemModel) delete itemModel; } -bool FilteredItemModelsFactory::isItemModelRegistered(const int id) const +bool FilteredItemModelFactory::isItemModelRegistered(const int id) const { return getItemModel(id); } -bool FilteredItemModelsFactory::isItemModelRegistered(const QString name) const +bool FilteredItemModelFactory::isItemModelRegistered(const QString name) const { return getItemModel(name); } -FilteredItemModel * FilteredItemModelsFactory::getItemModel(const int id) const +FilteredItemModel * FilteredItemModelFactory::getItemModel(const int id) const { foreach (FilteredItemModel * itemModel, registeredItemModels) { if (itemModel->getId() == id) @@ -156,7 +175,7 @@ FilteredItemModel * FilteredItemModelsFactory::getItemModel(const int id) const return nullptr; } -FilteredItemModel * FilteredItemModelsFactory::getItemModel(const QString name) const +FilteredItemModel * FilteredItemModelFactory::getItemModel(const QString name) const { foreach (FilteredItemModel * itemModel, registeredItemModels) { if (itemModel->getName() == name) @@ -166,26 +185,68 @@ FilteredItemModel * FilteredItemModelsFactory::getItemModel(const QString name) return nullptr; } -void FilteredItemModelsFactory::update() +void FilteredItemModelFactory::update() { foreach (FilteredItemModel * itemModel, registeredItemModels) { itemModel->update(); } } -void FilteredItemModelsFactory::dumpItemModelContents(const int id) const +void FilteredItemModelFactory::dumpItemModelContents(const int id) const { FilteredItemModel::dumpItemModelContents(getItemModel(id)); } -void FilteredItemModelsFactory::dumpItemModelContents(const QString name) const +void FilteredItemModelFactory::dumpItemModelContents(const QString name) const { FilteredItemModel::dumpItemModelContents(getItemModel(name)); } -void FilteredItemModelsFactory::dumpAllItemModelContents() const +void FilteredItemModelFactory::dumpAllItemModelContents() const { foreach (FilteredItemModel * itemModel, registeredItemModels) { FilteredItemModel::dumpItemModelContents(itemModel); } } + +// +// CurveRefFilteredFactory +// + +CurveRefFilteredFactory::CurveRefFilteredFactory(CompoundItemModelFactory * sharedItemModels, const int curveFlags, const int gvarRefFlags) +{ + if (!sharedItemModels) { + qDebug() << "Error: invalid compound item model factory pointer"; + return; + } + + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_Curve), curveFlags), + fidToString(CRFIM_CURVE), CRFIM_CURVE); + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef), gvarRefFlags), + fidToString(CRFIM_GVARREF), CRFIM_GVARREF); + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CurveRefType)), + fidToString(CRFIM_TYPE), CRFIM_TYPE); + registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CurveRefFunc)), + fidToString(CRFIM_FUNC), CRFIM_FUNC); +} + +CurveRefFilteredFactory::~CurveRefFilteredFactory() +{ +} + +// static +QString CurveRefFilteredFactory::fidToString(const int value) +{ + switch(value) { + case CRFIM_CURVE: + return "Curves"; + case CRFIM_GVARREF: + return "Global Variables"; + case CRFIM_TYPE: + return "Types"; + case CRFIM_FUNC: + return "Functions"; + default: + return CPN_STR_UNKNOWN_ITEM; + } +} diff --git a/companion/src/datamodels/rawitemfilteredmodel.h b/companion/src/datamodels/filtereditemmodels.h similarity index 61% rename from companion/src/datamodels/rawitemfilteredmodel.h rename to companion/src/datamodels/filtereditemmodels.h index 08bc65993..24668704d 100644 --- a/companion/src/datamodels/rawitemfilteredmodel.h +++ b/companion/src/datamodels/filtereditemmodels.h @@ -18,10 +18,10 @@ * GNU General Public License for more details. */ -#ifndef RAWITEMFILTEREDMODEL_H -#define RAWITEMFILTEREDMODEL_H +#ifndef FILTEREDITEMMODELS_H +#define FILTEREDITEMMODELS_H -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" #include #include @@ -32,12 +32,12 @@ class FilteredItemModel: public QSortFilterProxyModel Q_OBJECT public: enum DataFilters { - AllFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup | AbstractItemModel::PositiveGroup, - AllExcludeNoneFilter = AllFilter &~ AbstractItemModel::NoneGroup, - NegativeFilter = AbstractItemModel::NegativeGroup | AbstractItemModel::NoneGroup, - NegativeExcludeNoneFilter = AbstractItemModel::NegativeGroup, - PositiveFilter = AbstractItemModel::PositiveGroup | AbstractItemModel::NoneGroup, - PositiveExcludeNoneFilter = AbstractItemModel::PositiveGroup + AllFilter = AbstractItemModel::IMDG_Negative | AbstractItemModel::IMDG_None | AbstractItemModel::IMDG_Positive, + AllExcludeNoneFilter = AllFilter &~ AbstractItemModel::IMDG_None, + NegativeFilter = AbstractItemModel::IMDG_Negative | AbstractItemModel::IMDG_None, + NegativeExcludeNoneFilter = AbstractItemModel::IMDG_Negative, + PositiveFilter = AbstractItemModel::IMDG_Positive | AbstractItemModel::IMDG_None, + PositiveExcludeNoneFilter = AbstractItemModel::IMDG_Positive }; Q_ENUM(DataFilters) @@ -73,13 +73,13 @@ class FilteredItemModel: public QSortFilterProxyModel QString m_name = ""; }; -class FilteredItemModelsFactory +class FilteredItemModelFactory { public: - FilteredItemModelsFactory(); - virtual ~FilteredItemModelsFactory(); + explicit FilteredItemModelFactory(); + virtual ~FilteredItemModelFactory(); - int registerItemModel(FilteredItemModel * itemModel, QString name); + int registerItemModel(FilteredItemModel * itemModel, const QString name, const int id = -1); void unregisterItemModels(); void unregisterItemModel(const int id); bool isItemModelRegistered(const int id) const; @@ -96,17 +96,20 @@ class FilteredItemModelsFactory QVector registeredItemModels; }; +class CurveRefFilteredFactory : public FilteredItemModelFactory +{ + public: + enum FilteredItemModelId { + CRFIM_CURVE, + CRFIM_GVARREF, + CRFIM_TYPE, + CRFIM_FUNC + }; -#define FILTEREDITEMMODELCONNECT(fltrmodel) \ - connect(fltrmodel, SIGNAL(aboutToBeUpdated()), this, SLOT(onItemModelAboutToBeUpdated())); \ - connect(fltrmodel, SIGNAL(updateComplete()), this, SLOT(onItemModelUpdateComplete())); + explicit CurveRefFilteredFactory(CompoundItemModelFactory * sharedItemModels, const int curveFlags = 0, const int gvarRefFlags = 0); + virtual ~CurveRefFilteredFactory(); -#define FILTEREDITEMMODEL(fltrmodel, class, modelid, flags) \ - fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid), flags); \ - FILTEREDITEMMODELCONNECT(fltrmodel) + static QString fidToString(const int value); +}; -#define FILTEREDITEMMODELNOFLAGS(fltrmodel, class, modelid) \ - fltrmodel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::modelid)); \ - FILTEREDITEMMODELCONNECT(fltrmodel) - -#endif // RAWITEMFILTEREDMODEL_H +#endif // FILTEREDITEMMODELS_H diff --git a/companion/src/datamodels/itemmodelfactory.cpp b/companion/src/datamodels/itemmodelfactory.cpp index 892345568..b5b341afd 100644 --- a/companion/src/datamodels/itemmodelfactory.cpp +++ b/companion/src/datamodels/itemmodelfactory.cpp @@ -20,8 +20,8 @@ #include "itemmodelfactory.h" -#include "rawitemdatamodels.h" -#include "rawitemfilteredmodel.h" +#include "compounditemmodels.h" +#include "filtereditemmodels.h" #include "eeprominterface.h" #include "generalsettings.h" @@ -102,7 +102,7 @@ void ItemModelFactory::unregisterItemModel(const AbstractItemModel::ItemModelId delete itemModel; } -QAbstractItemModel * ItemModelsFactory::getItemModel(const AbstractItemModel::ItemModelId id) const +QAbstractItemModel * CompoundItemModelFactory::getItemModel(const AbstractItemModel::ItemModelId id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { if (itemModel->getId() == id) @@ -127,7 +127,7 @@ void ItemModelFactory::dumpAllItemModelContents() const } // -// FilteredItemModelsFactory +// FilteredItemModelFactory // FilteredItemModelFactory::FilteredItemModelFactory() diff --git a/companion/src/datamodels/itemmodelfactory.h b/companion/src/datamodels/itemmodelfactory.h index 01b7626b4..9f03b897b 100644 --- a/companion/src/datamodels/itemmodelfactory.h +++ b/companion/src/datamodels/itemmodelfactory.h @@ -49,11 +49,11 @@ class ItemModelFactory QVector registeredItemModels; }; -class FilteredItemModelsFactory +class FilteredItemModelFactory { public: - FilteredItemModelsFactory(); - virtual ~FilteredItemModelsFactory(); + FilteredItemModelFactory(); + virtual ~FilteredItemModelFactory(); int registerItemModel(FilteredItemModel * itemModel, QString name); void unregisterItemModels(); diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 4d961630a..6ade9264e 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -22,7 +22,7 @@ #include "adjustmentreference.h" #include "helpers.h" #include "modeldata.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" const QString CurveReference::toString(const ModelData * model, bool verbose) const { @@ -144,29 +144,9 @@ int CurveReference::functionCount() * CurveReferenceUIManager */ -CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, - FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, - FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : - QObject(parent), - curveTypeCB(nullptr), - curveGVarCB(nullptr), - curveValueSB(nullptr), - curveValueCB(curveValueCB), - curveRef(curveRef), - model(model), - lock(false), - curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel), - typeItemModel(typeItemModel), - funcItemModel(funcItemModel) -{ - init(parent); -} - CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, - FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, - FilteredItemModel * typeItemModel, FilteredItemModel * funcItemModel, QObject * parent) : + CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), curveGVarCB(curveGVarCB), @@ -175,24 +155,12 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck curveRef(curveRef), model(model), lock(false), - curveItemModel(curveItemModel), - gvarItemModel(gvarItemModel), - typeItemModel(typeItemModel), - funcItemModel(funcItemModel) -{ - init(parent); -} - -CurveReferenceUIManager::~CurveReferenceUIManager() -{ -} - -void CurveReferenceUIManager::init(QObject * parent) + filteredModelFactory(curveRefFilteredFactory) { hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { - curveTypeCB->setModel(typeItemModel); + curveTypeCB->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_TYPE)); curveTypeCB->setCurrentIndex(curveTypeCB->findData((int)curveRef.type)); connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); } @@ -215,6 +183,11 @@ void CurveReferenceUIManager::init(QObject * parent) update(); } +CurveReferenceUIManager::~CurveReferenceUIManager() +{ + delete filteredModelFactory; +} + #define CURVE_REF_UI_GVAR_SHOW (1<<0) #define CURVE_REF_UI_VALUE_SHOW (1<<1) #define CURVE_REF_UI_REF_SHOW (1<<2) @@ -297,13 +270,13 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb) switch (curveRef.type) { case CurveReference::CURVE_REF_DIFF: case CurveReference::CURVE_REF_EXPO: - cb->setModel(gvarItemModel); + cb->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_GVARREF)); break; case CurveReference::CURVE_REF_FUNC: - cb->setModel(funcItemModel); + cb->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_FUNC)); break; case CurveReference::CURVE_REF_CUSTOM: - cb->setModel(curveItemModel); + cb->setModel(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_CURVE)); break; default: break; diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index 3d61e3cb5..c4482c742 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -29,7 +29,7 @@ class ModelData; -class FilteredItemModel; +class CurveRefFilteredFactory; class CurveReference { @@ -89,14 +89,13 @@ class CurveReferenceUIManager : public QObject { Q_OBJECT public: - CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, - FilteredItemModel * curveItemModel, FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, - FilteredItemModel * funcItemModel, QObject * parent = nullptr); + explicit CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, + CurveReference & curve, const ModelData & model, CurveRefFilteredFactory * curveRefFilteredFactory, + QObject * parent = nullptr); - CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, FilteredItemModel * curveItemModel, - FilteredItemModel * gvarItemModel, FilteredItemModel * typeItemModel, - FilteredItemModel * funcItemModel, QObject * parent = nullptr); + explicit CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, + CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent = nullptr) : + CurveReferenceUIManager(nullptr, nullptr, nullptr, curveValueCB, curve, model, curveRefFilteredFactory, parent) {} virtual ~CurveReferenceUIManager(); @@ -116,12 +115,7 @@ class CurveReferenceUIManager : public QObject { const ModelData & model; bool lock; bool hasCapabilityGvars; - FilteredItemModel * curveItemModel; - FilteredItemModel * gvarItemModel; - FilteredItemModel * typeItemModel; - FilteredItemModel * funcItemModel; - - void init(QObject * parent); + CurveRefFilteredFactory *filteredModelFactory; void populateValueCB(QComboBox * cb); }; diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 2b01f2600..753d2eed0 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -23,6 +23,7 @@ #include "radiodata.h" #include "modeldata.h" #include "eeprominterface.h" +#include "compounditemmodels.h" void SensorData::updateUnit() { @@ -32,9 +33,18 @@ void SensorData::updateUnit() } } +// TODO depreciated QString SensorData::unitString() const { - switch (unit) { + return unitToString(unit); +} + +// static +QString SensorData::unitToString(const int value) +{ + switch (value) { + case UNIT_RAW: + return tr("Raw (-)"); case UNIT_VOLTS: return tr("V"); case UNIT_AMPS: @@ -92,7 +102,7 @@ QString SensorData::unitString() const case UNIT_US: return tr("uS"); default: - return ""; + return CPN_STR_UNKNOWN_ITEM; } } @@ -120,11 +130,71 @@ bool SensorData::isEmpty() const return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); } +// static +QString SensorData::typeToString(const int value) +{ + switch(value) { + case TELEM_TYPE_CUSTOM: + return tr("Custom"); + case TELEM_TYPE_CALCULATED: + return tr("Calculated"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString SensorData::formulaToString(const int value) +{ + switch(value) { + case TELEM_FORMULA_ADD: + return tr("Add"); + case TELEM_FORMULA_AVERAGE: + return tr("Average"); + case TELEM_FORMULA_MIN: + return tr("Minimum"); + case TELEM_FORMULA_MAX: + return tr("Maximum"); + case TELEM_FORMULA_MULTIPLY: + return tr("Multiply"); + case TELEM_FORMULA_TOTALIZE: + return tr("Totalize"); + case TELEM_FORMULA_CELL: + return tr("Cell"); + case TELEM_FORMULA_CONSUMPTION: + return tr("Consumption"); + case TELEM_FORMULA_DIST: + return tr("Distance"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString SensorData::cellIndexToString(const int value) +{ + if (value == TELEM_CELL_INDEX_LOWEST) + return tr("Lowest"); + if (value > TELEM_CELL_INDEX_LOWEST && value < TELEM_CELL_INDEX_HIGHEST) + return tr("Cell %1").arg(value - TELEM_CELL_INDEX_LOWEST); + if (value== TELEM_CELL_INDEX_HIGHEST) + return tr("Highest"); + if (value == TELEM_CELL_INDEX_DELTA) + return tr("Delta"); + return CPN_STR_UNKNOWN_ITEM; +} + +// static +QString SensorData::precisionToString(const int value) +{ + return QString("0.%1").arg(QString(value, '0')); +} + // static QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign) { if (model) { - const QString prfx = sign ? index < 0 ? "-" : "" : ""; + const QString prfx = sign ? index < 0 ? "-" : "+" : ""; if (abs(index) > 0) { const SensorData &sd = model->sensorData[abs(index) - 1]; @@ -136,23 +206,89 @@ QString SensorData::sourceToString(const ModelData * model, const int index, con else return CPN_STR_NONE_ITEM; } - else - return ""; + + return ""; } // static bool SensorData::isSourceAvailable(const ModelData * model, const int index) { - if (model) { - Firmware * firmware = getCurrentFirmware(); - const int i = abs(index); - if (i <= firmware->getCapability(Sensors)) { - if (i > 0) - return model->sensorData[i - 1].isAvailable(); - else - return true; - } + Firmware * firmware = getCurrentFirmware(); + const int sensorcnt = firmware->getCapability(Sensors); + const int i = abs(index); + + if (model && sensorcnt > 0 and i <= sensorcnt) { + if (i > 0) + return model->sensorData[i - 1].isAvailable(); + else + return true; } return false; } + +// static +AbstractStaticItemModel * SensorData::typeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.type"); + + for (int i = 0; i <= TELEM_TYPE_LAST; i++) { + mdl->appendToItemList(typeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * SensorData::formulaItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.formula"); + + for (int i = 0; i <= TELEM_FORMULA_LAST; i++) { + mdl->appendToItemList(formulaToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * SensorData::cellIndexItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.cellindex"); + + for (int i = 0; i <= TELEM_CELL_INDEX_LAST; i++) { + mdl->appendToItemList(cellIndexToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * SensorData::unitItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("sensordata.unit"); + + for (int i = 0; i <= UNIT_MAX; i++) { + QString str = unitToString(i); + if (str != CPN_STR_UNKNOWN_ITEM) + mdl->appendToItemList(str, i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +PrecisionItemModel * SensorData::precisionItemModel() +{ + PrecisionItemModel * mdl = new PrecisionItemModel(0, 2); + mdl->setName("sensordata.precision"); + return mdl; +} diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 200f6e889..afd9d7891 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -27,6 +27,8 @@ constexpr int CPN_MAX_SENSORS = 60; constexpr int SENSOR_LABEL_LEN = 4; class ModelData; +class AbstractStaticItemModel; +class PrecisionItemModel; class SensorData { Q_DECLARE_TR_FUNCTIONS(SensorData) @@ -36,7 +38,8 @@ class SensorData { enum { TELEM_TYPE_CUSTOM, - TELEM_TYPE_CALCULATED + TELEM_TYPE_CALCULATED, + TELEM_TYPE_LAST = TELEM_TYPE_CALCULATED }; enum @@ -63,6 +66,7 @@ class SensorData { TELEM_CELL_INDEX_6, TELEM_CELL_INDEX_HIGHEST, TELEM_CELL_INDEX_DELTA, + TELEM_CELL_INDEX_LAST = TELEM_CELL_INDEX_DELTA }; enum @@ -169,8 +173,19 @@ class SensorData { void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; - static QString sourceToString(const ModelData * model, const int index, const bool sign = true); + static QString sourceToString(const ModelData * model, const int index, const bool sign = false); static bool isSourceAvailable(const ModelData * model, const int index); + static QString typeToString(const int value); + static QString formulaToString(const int value); + static QString cellIndexToString(const int value); + static QString unitToString(const int value); + static QString precisionToString(const int value); + + static AbstractStaticItemModel * typeItemModel(); + static AbstractStaticItemModel * formulaItemModel(); + static AbstractStaticItemModel * cellIndexItemModel(); + static AbstractStaticItemModel * unitItemModel(); + static PrecisionItemModel * precisionItemModel(); }; #endif // SENSORDATA_H diff --git a/companion/src/generaledit/generaledit.cpp b/companion/src/generaledit/generaledit.cpp index c2a38b61d..e80038f9f 100644 --- a/companion/src/generaledit/generaledit.cpp +++ b/companion/src/generaledit/generaledit.cpp @@ -28,7 +28,7 @@ #include "hardware.h" #include "../modeledit/customfunctions.h" #include "verticalscrollarea.h" -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * firmware) : QDialog(parent), @@ -56,11 +56,11 @@ GeneralEdit::GeneralEdit(QWidget * parent, RadioData & radioData, Firmware * fir } } - sharedItemModels = new ItemModelsFactory(&generalSettings, nullptr); - sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); - sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); + sharedItemModels = new CompoundItemModelFactory(&generalSettings, nullptr); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); addTab(new GeneralSetupPanel(this, generalSettings, firmware), tr("Setup")); addTab(new CustomFunctionsPanel(this, nullptr, generalSettings, firmware, sharedItemModels), tr("Global Functions")); diff --git a/companion/src/generaledit/generaledit.h b/companion/src/generaledit/generaledit.h index b4a2ea3cc..b2e6caa82 100644 --- a/companion/src/generaledit/generaledit.h +++ b/companion/src/generaledit/generaledit.h @@ -25,7 +25,7 @@ #include "eeprominterface.h" #include "genericpanel.h" -class ItemModelsFactory; +class CompoundItemModelFactory; namespace Ui { class GeneralEdit; @@ -73,7 +73,7 @@ class GeneralEdit : public QDialog QVector panels; void addTab(GenericPanel *panel, QString text); void closeEvent(QCloseEvent *event); - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; }; #endif // _GENERALEDIT_H_ diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index cd83675dc..27374a8c4 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -20,7 +20,7 @@ #include "channels.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, int min, int max, int deflt, ModelPanel * panel): firmware(firmware), @@ -96,14 +96,15 @@ void LimitsGroup::updateMinMax(int max) } } } -ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels) { chnCapability = firmware->getCapability(Outputs); int channelNameMaxLen = firmware->getCapability(ChannelsName); - FILTEREDITEMMODELNOFLAGS(curveFilteredModel, ChannelsPanel, CurveId) + curveFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_Curve)); + connectItemModelEvents(curveFilteredModel); QStringList headerLabels; headerLabels << "#"; @@ -473,6 +474,12 @@ void ChannelsPanel::swapData(int idx1, int idx2) } } +void ChannelsPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &ChannelsPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &ChannelsPanel::onItemModelUpdateComplete); +} + void ChannelsPanel::onItemModelAboutToBeUpdated() { lock = true; @@ -486,5 +493,5 @@ void ChannelsPanel::onItemModelUpdateComplete() void ChannelsPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::ChannelsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Channels); } diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index 7e2d3f3fa..2a65f497a 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -26,7 +26,7 @@ #include -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel"; @@ -57,7 +57,7 @@ class ChannelsPanel : public ModelPanel Q_OBJECT public: - ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~ChannelsPanel(); public slots: @@ -100,9 +100,10 @@ class ChannelsPanel : public ModelPanel QCheckBox *symlimitsChk[CPN_MAX_CHNOUT]; int selectedIndex; int chnCapability; - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; FilteredItemModel *curveFilteredModel; void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _CHANNELS_H_ diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 34cda04f9..7f8b80219 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -23,7 +23,7 @@ #include "node.h" #include "edge.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #define GFX_MARGIN 16 @@ -110,7 +110,7 @@ float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax } CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Curves), currentCurve(0), @@ -813,7 +813,7 @@ void CurvesPanel::swapData(int idx1, int idx2) void CurvesPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::CurvesUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Curves); } CustomScene::CustomScene(QGraphicsView * view) : diff --git a/companion/src/modeledit/curves.h b/companion/src/modeledit/curves.h index 46ac63a01..c66f79cb7 100644 --- a/companion/src/modeledit/curves.h +++ b/companion/src/modeledit/curves.h @@ -26,7 +26,7 @@ #include #include -class ItemModelsFactory; +class CompoundItemModelFactory; constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve"; @@ -61,7 +61,7 @@ class CurvesPanel : public ModelPanel Q_OBJECT public: - CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~CurvesPanel(); virtual void update(); @@ -121,7 +121,7 @@ class CurvesPanel : public ModelPanel bool moveDownAllowed() const; bool moveUpAllowed() const; void swapData(int idx1, int idx2); - ItemModelsFactory * sharedItemModels; + CompoundItemModelFactory * sharedItemModels; void updateItemModels(); }; diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 94949064b..5c15bd538 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -19,7 +19,7 @@ */ #include "customfunctions.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" #include "appdata.h" @@ -67,7 +67,7 @@ void RepeatComboBox::update() } CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): GenericPanel(parent, model, generalSettings, firmware), functions(model ? model->customFn : generalSettings.customFn), mediaPlayerCurrent(-1), @@ -77,10 +77,18 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, lock = true; fswCapability = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions); - FILTEREDITEMMODEL(rawSwitchFilteredModel, CustomFunctionsPanel, RawSwitchId, model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext) - FILTEREDITEMMODELNOFLAGS(rawSourceAllModel, CustomFunctionsPanel, RawSourceId) - FILTEREDITEMMODEL(rawSourceInputsModel, CustomFunctionsPanel, RawSourceId, RawSource::InputSourceGroups) - FILTEREDITEMMODEL(rawSourceGVarsModel, CustomFunctionsPanel, RawSourceId, RawSource::GVarsGroup) + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext); + connectItemModelEvents(rawSwitchFilteredModel); + + rawSourceAllModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)); + connectItemModelEvents(rawSourceAllModel); + + rawSourceInputsModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::InputSourceGroups); + connectItemModelEvents(rawSourceInputsModel); + + rawSourceGVarsModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::GVarsGroup); + connectItemModelEvents(rawSourceGVarsModel); if (!firmware->getCapability(VoicesAsNumbers)) { tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength)); @@ -820,6 +828,12 @@ bool CustomFunctionsPanel::moveUpAllowed() const return selectedIndex > 0; } +void CustomFunctionsPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &CustomFunctionsPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &CustomFunctionsPanel::onItemModelUpdateComplete); +} + void CustomFunctionsPanel::onItemModelAboutToBeUpdated() { lock = true; diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index ef8e68b5f..e41baf672 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -26,7 +26,7 @@ #include -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; class TimerEdit; @@ -55,7 +55,7 @@ class CustomFunctionsPanel : public GenericPanel Q_OBJECT public: - CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + CustomFunctionsPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~CustomFunctionsPanel(); virtual void update(); @@ -96,6 +96,8 @@ class CustomFunctionsPanel : public GenericPanel bool moveUpAllowed() const; void swapData(int idx1, int idx2); void resetCBsAndRefresh(int idx); + void connectItemModelEvents(const FilteredItemModel * itemModel); + FilteredItemModel * rawSwitchFilteredModel; FilteredItemModel * rawSourceAllModel; FilteredItemModel * rawSourceInputsModel; diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 1bf22c68e..808cff7ba 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -20,13 +20,11 @@ #include "expodialog.h" #include "ui_expodialog.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, - FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, - FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : + Firmware * firmware, QString & inputName, CompoundItemModelFactory * sharedItemModels) : QDialog(parent), ui(new Ui::ExpoDialog), model(model), @@ -39,6 +37,9 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G { ui->setupUi(this); + dialogFilteredItemModels = new FilteredItemModelFactory(); + int id; + QLabel * lb_fp[CPN_MAX_FLIGHT_MODES] = {ui->lb_FP0, ui->lb_FP1, ui->lb_FP2, ui->lb_FP3, ui->lb_FP4, ui->lb_FP5, ui->lb_FP6, ui->lb_FP7, ui->lb_FP8 }; QCheckBox * tmp[CPN_MAX_FLIGHT_MODES] = {ui->cb_FP0, ui->cb_FP1, ui->cb_FP2, ui->cb_FP3, ui->cb_FP4, ui->cb_FP5, ui->cb_FP6, ui->cb_FP7, ui->cb_FP8 }; for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) { @@ -51,10 +52,15 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); - ui->switchesCB->setModel(rawSwitchFilteredModel); + curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, + firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter); + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, + curveRefFilteredItemModels, this); + + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::MixesContext), "RawSwitch"); + ui->switchesCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(ed->swtch.toValue())); ui->sideCB->setCurrentIndex(ed->mode - 1); @@ -85,7 +91,10 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G if (firmware->getCapability(VirtualInputs)) { ui->inputName->setMaxLength(firmware->getCapability(InputsLength)); - ui->sourceCB->setModel(rawSourceFilteredModel); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + (RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)), + "RawSource"); + ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(ed->srcRaw.toValue())); ui->inputName->setValidator(new QRegExpValidator(rx, this)); ui->inputName->setText(inputName); @@ -137,9 +146,10 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G ExpoDialog::~ExpoDialog() { + delete ui; delete gvWeightGroup; delete gvOffsetGroup; - delete ui; + delete dialogFilteredItemModels; } void ExpoDialog::updateScale() diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index 427fe9572..f4980d344 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -26,7 +26,9 @@ #include "modelprinter.h" class GVarGroup; -class FilteredItemModel; +class CompoundItemModelFactory; +class FilteredItemModelFactory; +class CurveRefFilteredFactory; class CurveReferenceUIManager; namespace Ui { @@ -37,9 +39,7 @@ class ExpoDialog : public QDialog { Q_OBJECT public: ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expodata, GeneralSettings & generalSettings, - Firmware * firmware, QString & inputName, FilteredItemModel * rawSourceFilteredModel, - FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, - FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); + Firmware * firmware, QString & inputName, CompoundItemModelFactory * sharedItemModels); virtual ~ExpoDialog(); protected: @@ -66,6 +66,8 @@ class ExpoDialog : public QDialog { ModelPrinter modelPrinter; bool lock; QCheckBox * cb_fp[CPN_MAX_FLIGHT_MODES]; + FilteredItemModelFactory *dialogFilteredItemModels; + CurveRefFilteredFactory *curveRefFilteredItemModels; }; #endif // _EXPODIALOG_H_ diff --git a/companion/src/modeledit/flightmodes.cpp b/companion/src/modeledit/flightmodes.cpp index a60c5f09b..224d69965 100644 --- a/companion/src/modeledit/flightmodes.cpp +++ b/companion/src/modeledit/flightmodes.cpp @@ -20,7 +20,7 @@ #include "flightmodes.h" #include "ui_flightmode.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" #include "customdebug.h" @@ -32,8 +32,7 @@ FlightModePanel::FlightModePanel(QWidget * parent, ModelData & model, int phaseI phase(model.flightModeData[phaseIdx]) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModePanel::onItemModelAboutToBeUpdated); - connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &FlightModePanel::onItemModelUpdateComplete); + connectItemModelEvents(rawSwitchFilteredModel); ui->labelName->setContextMenuPolicy(Qt::CustomContextMenu); ui->labelName->setToolTip(tr("Popup menu available")); @@ -1362,6 +1361,12 @@ void FlightModePanel::gvSwapData(int idx1, int idx2) } } +void FlightModePanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModePanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &FlightModePanel::onItemModelUpdateComplete); +} + void FlightModePanel::onItemModelAboutToBeUpdated() { lock = true; @@ -1376,11 +1381,12 @@ void FlightModePanel::onItemModelUpdateComplete() /**********************************************************/ FlightModesPanel::FlightModesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels) { - FILTEREDITEMMODEL(rawSwitchFilteredModel, FlightModesPanel, RawSwitchId, RawSwitch::MixesContext) + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), RawSwitch::MixesContext); + connectItemModelEvents(rawSwitchFilteredModel); modesCount = firmware->getCapability(FlightModes); @@ -1435,6 +1441,12 @@ void FlightModesPanel::update() emit updated(); } +void FlightModesPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &FlightModesPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &FlightModesPanel::onItemModelUpdateComplete); +} + void FlightModesPanel::onItemModelAboutToBeUpdated() { } @@ -1445,8 +1457,8 @@ void FlightModesPanel::onItemModelUpdateComplete() void FlightModesPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::FlightModesUpdated); - sharedItemModels->update(AbstractItemModel::GVarsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_FlightModes); + sharedItemModels->update(AbstractItemModel::IMUE_GVars); } void FlightModesPanel::onTabIndexChanged(int index) diff --git a/companion/src/modeledit/flightmodes.h b/companion/src/modeledit/flightmodes.h index e74dec8bb..33fd87e8a 100644 --- a/companion/src/modeledit/flightmodes.h +++ b/companion/src/modeledit/flightmodes.h @@ -24,7 +24,7 @@ #include "modeledit.h" #include "eeprominterface.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; constexpr char MIMETYPE_FLIGHTMODE[] = "application/x-companion-flightmode"; @@ -139,6 +139,7 @@ class FlightModePanel : public ModelPanel bool gvMoveDownAllowed() const; bool gvMoveUpAllowed() const; void gvSwapData(int idx1, int idx2); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class FlightModesPanel : public ModelPanel @@ -146,7 +147,7 @@ class FlightModesPanel : public ModelPanel Q_OBJECT public: - FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + FlightModesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~FlightModesPanel(); public slots: @@ -166,10 +167,11 @@ class FlightModesPanel : public ModelPanel int modesCount; QTabWidget *tabWidget; - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; FilteredItemModel *rawSwitchFilteredModel; QVector panels; void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _FLIGHTMODES_H_ diff --git a/companion/src/modeledit/heli.cpp b/companion/src/modeledit/heli.cpp index a1142468e..ef86c5793 100644 --- a/companion/src/modeledit/heli.cpp +++ b/companion/src/modeledit/heli.cpp @@ -21,15 +21,16 @@ #include "heli.h" #include "ui_heli.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" -HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +HeliPanel::HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Heli) { ui->setupUi(this); - FILTEREDITEMMODEL(rawSourceFilteredModel, HeliPanel, RawSourceId, RawSource::InputSourceGroups) + rawSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::InputSourceGroups); + connectItemModelEvents(rawSourceFilteredModel); connect(ui->swashType, SIGNAL(currentIndexChanged(int)), this, SLOT(edited())); connect(ui->swashRingVal, SIGNAL(editingFinished()), this, SLOT(edited())); @@ -116,6 +117,12 @@ void HeliPanel::edited() } } +void HeliPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &HeliPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &HeliPanel::onItemModelUpdateComplete); +} + void HeliPanel::onItemModelAboutToBeUpdated() { lock = true; diff --git a/companion/src/modeledit/heli.h b/companion/src/modeledit/heli.h index 2238a67f1..eb3c7d54e 100644 --- a/companion/src/modeledit/heli.h +++ b/companion/src/modeledit/heli.h @@ -23,7 +23,7 @@ #include "modeledit.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; namespace Ui { @@ -35,7 +35,7 @@ class HeliPanel : public ModelPanel Q_OBJECT public: - HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + HeliPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~HeliPanel(); void update(); @@ -47,6 +47,7 @@ class HeliPanel : public ModelPanel private: Ui::Heli *ui; FilteredItemModel * rawSourceFilteredModel; + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _HELI_H_ diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index ae25ae451..f48d7d916 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -21,20 +21,19 @@ #include "inputs.h" #include "expodialog.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" -InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), expoInserted(false), modelPrinter(firmware, generalSettings, model), - sharedItemModels(sharedItemModels) + sharedItemModels(sharedItemModels), + modelsUpdateCnt(0) { - FILTEREDITEMMODEL(rawSourceFilteredModel, InputsPanel, RawSourceId,(RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)) - FILTEREDITEMMODEL(rawSwitchFilteredModel, InputsPanel, RawSwitchId, RawSwitch::MixesContext) - FILTEREDITEMMODEL(curveFilteredModel, InputsPanel, CurveId, firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter) - FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, InputsPanel, GVarRefId) - FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, InputsPanel, CurveRefTypeId) - FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, InputsPanel, CurveRefFuncId) + connectItemModelEvents(AbstractItemModel::IMID_RawSource); + connectItemModelEvents(AbstractItemModel::IMID_RawSwitch); + connectItemModelEvents(AbstractItemModel::IMID_Curve); + connectItemModelEvents(AbstractItemModel::IMID_GVarRef); inputsCount = firmware->getCapability(VirtualInputs); if (inputsCount == 0) @@ -74,12 +73,6 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g InputsPanel::~InputsPanel() { - delete rawSourceFilteredModel; - delete rawSwitchFilteredModel; - delete curveFilteredModel; - delete gvarFilteredModel; - delete crTypeFilteredModel; - delete crFuncFilteredModel; } void InputsPanel::update() @@ -195,8 +188,7 @@ void InputsPanel::gm_openExpo(int index) if (firmware->getCapability(VirtualInputs)) inputName = model->inputNames[ed.chn]; - ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); + ExpoDialog *dlg = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName, sharedItemModels); if (dlg->exec()) { model->expoData[index] = ed; if (firmware->getCapability(VirtualInputs)) @@ -732,18 +724,31 @@ int InputsPanel::getInputIndexFromSelected() return idx; } +void InputsPanel::connectItemModelEvents(const int id) +{ + AbstractDynamicItemModel * itemModel = qobject_cast(sharedItemModels->getItemModel(id)); + if (itemModel) { + connect(itemModel, &AbstractDynamicItemModel::aboutToBeUpdated, this, &InputsPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &AbstractDynamicItemModel::updateComplete, this, &InputsPanel::onItemModelUpdateComplete); + } +} + void InputsPanel::onItemModelAboutToBeUpdated() { lock = true; + modelsUpdateCnt++; } void InputsPanel::onItemModelUpdateComplete() { - update(); - lock = false; + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } } void InputsPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::InputsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Inputs); } diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index 8b2c6bcef..9b360ec19 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -25,7 +25,7 @@ #include "mixerslistwidget.h" #include "modelprinter.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; constexpr char MIMETYPE_EXPO[] = "application/x-companion-expo"; @@ -35,7 +35,7 @@ class InputsPanel : public ModelPanel Q_OBJECT public: - InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~InputsPanel(); virtual void update(); @@ -72,13 +72,8 @@ class InputsPanel : public ModelPanel ModelPrinter modelPrinter; int selectedIdx; int inputIdx; - ItemModelsFactory *sharedItemModels; - FilteredItemModel *rawSourceFilteredModel; - FilteredItemModel *rawSwitchFilteredModel; - FilteredItemModel *curveFilteredModel; - FilteredItemModel *gvarFilteredModel; - FilteredItemModel *crTypeFilteredModel; - FilteredItemModel *crFuncFilteredModel; + CompoundItemModelFactory *sharedItemModels; + int modelsUpdateCnt; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); @@ -100,6 +95,7 @@ class InputsPanel : public ModelPanel int getIndexFromSelected(); int getInputIndexFromSelected(); void updateItemModels(); + void connectItemModelEvents(const int id); }; #endif // _INPUTS_H_ diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index dc4755f53..499df9e49 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -19,21 +19,25 @@ */ #include "logicalswitches.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" #include LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels), selectedIndex(0), modelsUpdateCnt(0) { - FILTEREDITEMMODEL(rawSwitchFilteredModel, LogicalSwitchesPanel, RawSwitchId, RawSwitch::LogicalSwitchesContext) + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::LogicalSwitchesContext); + connectItemModelEvents(rawSwitchFilteredModel); + const int srcGroups = firmware->getCapability(GvarsInCS) ? 0 : (RawSource::AllSourceGroups & ~RawSource::GVarsGroup); - FILTEREDITEMMODEL(rawSourceFilteredModel, LogicalSwitchesPanel, RawSourceId, srcGroups) + rawSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), srcGroups); + connectItemModelEvents(rawSourceFilteredModel); lsCapability = firmware->getCapability(LogicalSwitches); lsCapabilityExt = firmware->getCapability(LogicalSwitchesExt); @@ -642,7 +646,13 @@ void LogicalSwitchesPanel::swapData(int idx1, int idx2) void LogicalSwitchesPanel::updateItemModels() { lock = true; - sharedItemModels->update(AbstractItemModel::LogicalSwitchesUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_LogicalSwitches); +} + +void LogicalSwitchesPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &LogicalSwitchesPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &LogicalSwitchesPanel::onItemModelUpdateComplete); } void LogicalSwitchesPanel::onItemModelAboutToBeUpdated() diff --git a/companion/src/modeledit/logicalswitches.h b/companion/src/modeledit/logicalswitches.h index 05b683b49..f1c2f8397 100644 --- a/companion/src/modeledit/logicalswitches.h +++ b/companion/src/modeledit/logicalswitches.h @@ -24,7 +24,7 @@ #include "modeledit.h" #include "radiodata.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; class TimerEdit; @@ -35,7 +35,7 @@ class LogicalSwitchesPanel : public ModelPanel Q_OBJECT public: - LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + LogicalSwitchesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~LogicalSwitchesPanel(); virtual void update(); @@ -74,7 +74,7 @@ class LogicalSwitchesPanel : public ModelPanel QDoubleSpinBox * dsbDelay[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource1[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cbSource2[CPN_MAX_LOGICAL_SWITCHES]; - ItemModelsFactory * sharedItemModels; + CompoundItemModelFactory * sharedItemModels; FilteredItemModel * rawSwitchFilteredModel; FilteredItemModel * rawSourceFilteredModel; int selectedIndex; @@ -89,6 +89,7 @@ class LogicalSwitchesPanel : public ModelPanel bool moveUpAllowed() const; int modelsUpdateCnt; void updateItemModels(); + void connectItemModelEvents(const FilteredItemModel * itemModel); }; #endif // _LOGICALSWITCHES_H_ diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 819649698..67b01f8f6 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -21,13 +21,11 @@ #include "mixerdialog.h" #include "ui_mixerdialog.h" #include "radiodata.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "helpers.h" MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, GeneralSettings & generalSettings, Firmware * firmware, - FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, - FilteredItemModel * curveFilteredModel, FilteredItemModel * gvarFilteredModel, - FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel) : + CompoundItemModelFactory * sharedItemModels) : QDialog(parent), ui(new Ui::MixerDialog), model(model), @@ -38,6 +36,9 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, { ui->setupUi(this); + dialogFilteredItemModels = new FilteredItemModelFactory(); + int id; + QRegExp rx(CHAR_FOR_NAMES_REGEX); QLabel * lb_fp[CPN_MAX_FLIGHT_MODES] = {ui->lb_FP0, ui->lb_FP1, ui->lb_FP2, ui->lb_FP3, ui->lb_FP4, ui->lb_FP5, ui->lb_FP6, ui->lb_FP7, ui->lb_FP8 }; QCheckBox * tmp[CPN_MAX_FLIGHT_MODES] = {ui->cb_FP0, ui->cb_FP1, ui->cb_FP2, ui->cb_FP3, ui->cb_FP4, ui->cb_FP5, ui->cb_FP6, ui->cb_FP7, ui->cb_FP8 }; @@ -47,15 +48,20 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, this->setWindowTitle(tr("DEST -> %1").arg(RawSource(SOURCE_TYPE_CH, md->destCh - 1).toString(&model, &generalSettings))); - ui->sourceCB->setModel(rawSourceFilteredModel); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + ((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)), + "RawSource"); + ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); int limit = firmware->getCapability(OffsetWeight); gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); + curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, + firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter); curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel, this); + curveRefFilteredItemModels, this); ui->MixDR_CB->setChecked(md->noExpo == 0); @@ -107,7 +113,9 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, } } - ui->switchesCB->setModel(rawSwitchFilteredModel); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::MixesContext), "RawSwitch"); + ui->switchesCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->switchesCB->setCurrentIndex(ui->switchesCB->findData(md->swtch.toValue())); ui->warningCB->setCurrentIndex(md->mixWarn); ui->mltpxCB->setCurrentIndex(md->mltpx); @@ -153,6 +161,7 @@ MixerDialog::~MixerDialog() delete ui; delete gvWeightGroup; delete gvOffsetGroup; + delete dialogFilteredItemModels; } void MixerDialog::changeEvent(QEvent *e) diff --git a/companion/src/modeledit/mixerdialog.h b/companion/src/modeledit/mixerdialog.h index 1beb3b220..04ff1aeaf 100644 --- a/companion/src/modeledit/mixerdialog.h +++ b/companion/src/modeledit/mixerdialog.h @@ -25,7 +25,9 @@ #include "eeprominterface.h" class GVarGroup; -class FilteredItemModel; +class CompoundItemModelFactory; +class FilteredItemModelFactory; +class CurveRefFilteredFactory; class CurveReferenceUIManager; namespace Ui { @@ -36,8 +38,7 @@ class MixerDialog : public QDialog { Q_OBJECT public: MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, GeneralSettings & generalSettings, Firmware * firmware, - FilteredItemModel * rawSourceFilteredModel, FilteredItemModel * rawSwitchFilteredModel, FilteredItemModel * curveFilteredModel, - FilteredItemModel * gvarFilteredModel, FilteredItemModel * crTypeFilteredModel, FilteredItemModel * crFuncFilteredModel); + CompoundItemModelFactory * sharedItemModels); ~MixerDialog(); protected: @@ -62,6 +63,8 @@ class MixerDialog : public QDialog { GVarGroup * gvOffsetGroup; CurveReferenceUIManager * curveGroup; QCheckBox * cb_fp[CPN_MAX_FLIGHT_MODES]; + FilteredItemModelFactory *dialogFilteredItemModels; + CurveRefFilteredFactory *curveRefFilteredItemModels; }; #endif // _MIXERDIALOG_H_ diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index 5bcc69d30..3d95bd04d 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -20,21 +20,20 @@ #include "mixes.h" #include "helpers.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" -MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels): +MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), mixInserted(false), highlightedSource(0), modelPrinter(firmware, generalSettings, model), - sharedItemModels(sharedItemModels) + sharedItemModels(sharedItemModels), + modelsUpdateCnt(0) { - FILTEREDITEMMODEL(rawSourceFilteredModel, MixesPanel, RawSourceId,((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)) - FILTEREDITEMMODEL(rawSwitchFilteredModel, MixesPanel, RawSwitchId, RawSwitch::MixesContext) - FILTEREDITEMMODEL(curveFilteredModel, MixesPanel, CurveId, firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter) - FILTEREDITEMMODELNOFLAGS(gvarFilteredModel, MixesPanel, GVarRefId) - FILTEREDITEMMODELNOFLAGS(crTypeFilteredModel, MixesPanel, CurveRefTypeId) - FILTEREDITEMMODELNOFLAGS(crFuncFilteredModel, MixesPanel, CurveRefFuncId) + connectItemModelEvents(AbstractItemModel::IMID_RawSource); + connectItemModelEvents(AbstractItemModel::IMID_RawSwitch); + connectItemModelEvents(AbstractItemModel::IMID_Curve); + connectItemModelEvents(AbstractItemModel::IMID_GVarRef); QGridLayout * mixesLayout = new QGridLayout(this); @@ -69,12 +68,6 @@ MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & gen MixesPanel::~MixesPanel() { - delete rawSourceFilteredModel; - delete rawSwitchFilteredModel; - delete curveFilteredModel; - delete gvarFilteredModel; - delete crTypeFilteredModel; - delete crFuncFilteredModel; } void MixesPanel::update() @@ -190,8 +183,7 @@ void MixesPanel::gm_openMix(int index) MixData mixd(model->mixData[index]); - MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, rawSourceFilteredModel, rawSwitchFilteredModel, - curveFilteredModel, gvarFilteredModel, crTypeFilteredModel, crFuncFilteredModel); + MixerDialog *dlg = new MixerDialog(this, *model, &mixd, generalSettings, firmware, sharedItemModels); if(dlg->exec()) { model->mixData[index] = mixd; emit modified(); @@ -547,13 +539,26 @@ void MixesPanel::clearMixes() } } +void MixesPanel::connectItemModelEvents(const int id) +{ + AbstractDynamicItemModel * itemModel = qobject_cast(sharedItemModels->getItemModel(id)); + if (itemModel) { + connect(itemModel, &AbstractDynamicItemModel::aboutToBeUpdated, this, &MixesPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &AbstractDynamicItemModel::updateComplete, this, &MixesPanel::onItemModelUpdateComplete); + } +} + void MixesPanel::onItemModelAboutToBeUpdated() { lock = true; + modelsUpdateCnt++; } void MixesPanel::onItemModelUpdateComplete() { - update(); - lock = false; + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } } diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index 211632a2e..c298df11a 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -26,7 +26,7 @@ #include "mixerdialog.h" #include "modelprinter.h" -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; class MixesPanel : public ModelPanel @@ -34,7 +34,7 @@ class MixesPanel : public ModelPanel Q_OBJECT public: - MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~MixesPanel(); virtual void update(); @@ -68,13 +68,8 @@ class MixesPanel : public ModelPanel bool mixInserted; unsigned int highlightedSource; ModelPrinter modelPrinter; - ItemModelsFactory *sharedItemModels; - FilteredItemModel *rawSourceFilteredModel; - FilteredItemModel *rawSwitchFilteredModel; - FilteredItemModel *curveFilteredModel; - FilteredItemModel *gvarFilteredModel; - FilteredItemModel *crTypeFilteredModel; - FilteredItemModel *crFuncFilteredModel; + CompoundItemModelFactory *sharedItemModels; + int modelsUpdateCnt; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); @@ -86,6 +81,7 @@ class MixesPanel : public ModelPanel void setSelectedByMixList(QList list); void AddMixerLine(int dest); QString getMixerText(int dest, bool newChannel); + void connectItemModelEvents(const int id); }; #endif // _MIXES_H_ diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 569b64e33..3577be7ae 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -33,7 +33,7 @@ #include "customfunctions.h" #include "telemetry.h" #include "appdata.h" -#include "rawitemdatamodels.h" +#include "compounditemmodels.h" ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmware * firmware) : QDialog(parent), @@ -53,17 +53,17 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw GeneralSettings &generalSettings = radioData.generalSettings; ModelData &model = radioData.models[modelId]; - sharedItemModels = new ItemModelsFactory(&generalSettings, &model); - sharedItemModels->addItemModel(AbstractItemModel::RawSourceId); - sharedItemModels->addItemModel(AbstractItemModel::RawSwitchId); - sharedItemModels->addItemModel(AbstractItemModel::CurveId); - sharedItemModels->addItemModel(AbstractItemModel::GVarRefId); - sharedItemModels->addItemModel(AbstractItemModel::ThrSourceId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncActionId); - sharedItemModels->addItemModel(AbstractItemModel::CustomFuncResetParamId); - sharedItemModels->addItemModel(AbstractItemModel::TeleSourceId); - sharedItemModels->addItemModel(AbstractItemModel::CurveRefTypeId); - sharedItemModels->addItemModel(AbstractItemModel::CurveRefFuncId); + sharedItemModels = new CompoundItemModelFactory(&generalSettings, &model); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RawSwitch); + sharedItemModels->addItemModel(AbstractItemModel::IMID_Curve); + sharedItemModels->addItemModel(AbstractItemModel::IMID_GVarRef); + sharedItemModels->addItemModel(AbstractItemModel::IMID_ThrSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); + sharedItemModels->addItemModel(AbstractItemModel::IMID_TeleSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CurveRefType); + sharedItemModels->addItemModel(AbstractItemModel::IMID_CurveRefFunc); s1.report("Init"); diff --git a/companion/src/modeledit/modeledit.h b/companion/src/modeledit/modeledit.h index 99337f862..0bd4af9d8 100644 --- a/companion/src/modeledit/modeledit.h +++ b/companion/src/modeledit/modeledit.h @@ -25,7 +25,7 @@ #include "genericpanel.h" class RadioData; -class ItemModelsFactory; +class CompoundItemModelFactory; namespace Ui { class ModelEdit; @@ -63,7 +63,7 @@ class ModelEdit : public QDialog RadioData &radioData; Firmware *firmware; QVector panels; - ItemModelsFactory *sharedItemModels; + CompoundItemModelFactory *sharedItemModels; void addTab(GenericPanel * panel, QString text); void launchSimulation(); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index d3f58322e..25cd576ec 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -22,7 +22,7 @@ #include "ui_setup.h" #include "ui_setup_timer.h" #include "ui_setup_module.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" #include "appdata.h" #include "modelprinter.h" #include "multiprotocols.h" @@ -38,8 +38,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui(new Ui::Timer) { ui->setupUi(this); - connect(rawSwitchFilteredModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); - connect(rawSwitchFilteredModel, &FilteredItemModel::updateComplete, this, &TimerPanel::onItemModelUpdateComplete); + connectItemModelEvents(rawSwitchFilteredModel); lock = true; @@ -173,6 +172,12 @@ void TimerPanel::on_name_editingFinished() } } +void TimerPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &TimerPanel::onItemModelUpdateComplete); +} + void TimerPanel::onItemModelAboutToBeUpdated() { lock = true; @@ -993,14 +998,18 @@ void ModulePanel::onClearAccessRxClicked() /******************************************************************************/ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels) : + CompoundItemModelFactory * sharedItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Setup), sharedItemModels(sharedItemModels) { ui->setupUi(this); - FILTEREDITEMMODEL(rawSwitchFilteredModel, SetupPanel, RawSwitchId, RawSwitch::TimersContext) - FILTEREDITEMMODELNOFLAGS(thrSourceFilteredModel, SetupPanel, ThrSourceId) + + rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), RawSwitch::TimersContext); + connectItemModelEvents(rawSwitchFilteredModel); + + thrSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_ThrSource)); + connectItemModelEvents(thrSourceFilteredModel); Board::Type board = firmware->getBoard(); @@ -1755,6 +1764,12 @@ void SetupPanel::onTimerNameChanged() updateItemModels(); } +void SetupPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &SetupPanel::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &SetupPanel::onItemModelUpdateComplete); +} + void SetupPanel::onItemModelAboutToBeUpdated() { } @@ -1765,5 +1780,5 @@ void SetupPanel::onItemModelUpdateComplete() void SetupPanel::updateItemModels() { - sharedItemModels->update(AbstractItemModel::TimersUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_Timers); } diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index a4b7e7f81..c931f9dd7 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -26,7 +26,7 @@ constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; -class ItemModelsFactory; +class CompoundItemModelFactory; class FilteredItemModel; namespace Ui { @@ -61,6 +61,7 @@ class TimerPanel : public ModelPanel private: TimerData & timer; Ui::Timer * ui; + void connectItemModelEvents(const FilteredItemModel * itemModel); }; class ModulePanel : public ModelPanel @@ -132,7 +133,7 @@ class SetupPanel : public ModelPanel Q_OBJECT public: - SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, ItemModelsFactory * sharedItemModels); + SetupPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); virtual ~SetupPanel(); virtual void update(); @@ -194,10 +195,11 @@ class SetupPanel : public ModelPanel bool moveTimerDownAllowed() const; bool moveTimerUpAllowed() const; void swapTimerData(int idx1, int idx2); - ItemModelsFactory * sharedItemModels; + CompoundItemModelFactory * sharedItemModels; FilteredItemModel * rawSwitchFilteredModel; FilteredItemModel * thrSourceFilteredModel; void updateItemModels(); - }; + void connectItemModelEvents(const FilteredItemModel * itemModel); +}; #endif // _SETUP_H_ diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 3e1a5c49b..423e10e5d 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -27,12 +27,17 @@ #include -constexpr char FIM_NAME_RAWSOURCE[] {"Raw Source"}; -constexpr char FIM_NAME_TELEALLSRC[] {"Tele All Source"}; -constexpr char FIM_NAME_TELEPOSSRC[] {"Tele Pos Source"}; +constexpr char FIM_RAWSOURCE[] {"Raw Source"}; +constexpr char FIM_TELEALLSRC[] {"Tele All Source"}; +constexpr char FIM_TELEPOSSRC[] {"Tele Pos Source"}; +constexpr char FIM_SENSORTYPE[] {"Sensor.Type"}; +constexpr char FIM_SENSORFORMULA[] {"Sensor.Formula"}; +constexpr char FIM_SENSORCELLINDEX[] {"Sensor.CellIndex"}; +constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"}; +constexpr char FIM_SENSORPRECISION[] {"Sensor.Precision"}; TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, - Firmware * firmware, const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels): + Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetryCustomScreen), screen(screen), @@ -41,7 +46,7 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, { ui->setupUi(this); - FilteredItemModel * rawSourceFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_RAWSOURCE); + FilteredItemModel * rawSourceFilteredModel = panelFilteredItemModels->getItemModel(FIM_RAWSOURCE); connectItemModelEvents(rawSourceFilteredModel); for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) { @@ -321,24 +326,26 @@ void TelemetryCustomScreen::onItemModelUpdateComplete() TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, - FilteredItemModelsFactory * localFilteredItemModels) : + FilteredItemModelFactory * panelFilteredItemModels) : ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), sensor(sensor), - //lock(false), sensorIndex(sensorIndex), selectedIndex(0), sensorCapability(sensorCapability), modelsUpdateCnt(0), parentLock(parentLock) { + // TODO ui definition housekeeping + // - remove all predefined combobox lists leaving to code + // - use Auto types for consistency ui->setupUi(this); + FilteredItemModel * fltmdl; - FilteredItemModel * teleAllSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEALLSRC); - connectItemModelEvents(teleAllSrcFilteredModel); - FilteredItemModel * telePosSrcFilteredModel = localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC); - connectItemModelEvents(telePosSrcFilteredModel); + connectItemModelEvents(panelFilteredItemModels->getItemModel(FIM_TELEALLSRC)); + connectItemModelEvents(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); + lock = true; ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1)); ui->numLabel->setProperty("index", sensorIndex); ui->numLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -353,6 +360,9 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, connect(ui->numLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint))); ui->id->setField(sensor.id, this); ui->instance->setField(sensor.instance, this); + ui->type->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORTYPE)); + ui->formula->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORFORMULA)); + ui->unit->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORUNIT)); ui->ratio->setField(sensor.ratio, this); ui->offset->setField(sensor.offset, this); ui->autoOffset->setField(sensor.autoOffset, this); @@ -360,29 +370,29 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->logs->setField(sensor.logs, this); ui->persistent->setField(sensor.persistent, this); ui->onlyPositive->setField(sensor.onlyPositive, this); - ui->gpsSensor->setModel(telePosSrcFilteredModel); + fltmdl = panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC); + ui->gpsSensor->setModel(fltmdl); ui->gpsSensor->setField(sensor.gps, this); - ui->altSensor->setModel(telePosSrcFilteredModel); + ui->altSensor->setModel(fltmdl); ui->altSensor->setField(sensor.alt, this); - ui->ampsSensor->setModel(telePosSrcFilteredModel); + ui->ampsSensor->setModel(fltmdl); ui->ampsSensor->setField(sensor.amps, this); - ui->cellsSensor->setModel(telePosSrcFilteredModel); + ui->cellsSensor->setModel(fltmdl); ui->cellsSensor->setField(sensor.source, this); - ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); - for (int i = SensorData::TELEM_CELL_INDEX_LOWEST + 1; i < SensorData::TELEM_CELL_INDEX_HIGHEST; i++) - ui->cellsIndex->addItem(tr("Cell %1").arg(i), i); - ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); - ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); + ui->cellsIndex->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORCELLINDEX)); ui->cellsIndex->setField(sensor.index); - ui->source1->setModel(teleAllSrcFilteredModel); + fltmdl = panelFilteredItemModels->getItemModel(FIM_TELEALLSRC); + ui->source1->setModel(fltmdl); ui->source1->setField(sensor.sources[0], this); - ui->source2->setModel(teleAllSrcFilteredModel); + ui->source2->setModel(fltmdl); ui->source2->setField(sensor.sources[1], this); - ui->source3->setModel(teleAllSrcFilteredModel); + ui->source3->setModel(fltmdl); ui->source3->setField(sensor.sources[2], this); - ui->source4->setModel(teleAllSrcFilteredModel); + ui->source4->setModel(fltmdl); ui->source4->setField(sensor.sources[3], this); - ui->prec->setField(sensor.prec, 0, 2, false, "", this); + ui->prec->setField(sensor.prec, 0, 2, false, "", this); // TODO replace autoprecisioncombobox with autocombobox and filtereditemmodel + lock = false; + update(); } @@ -393,6 +403,7 @@ TelemetrySensorPanel::~TelemetrySensorPanel() void TelemetrySensorPanel::update() { + // TODO use bit mask bool isConfigurable = false; bool gpsFieldsDisplayed = false; bool cellsFieldsDisplayed = false; @@ -404,8 +415,8 @@ void TelemetrySensorPanel::update() lock = true; ui->name->setText(sensor.label); - ui->type->setCurrentIndex(sensor.type); - ui->unit->setCurrentIndex(sensor.unit); + ui->type->setCurrentIndex(ui->type->findData(sensor.type)); + ui->unit->setCurrentIndex(ui->unit->findData(sensor.unit)); ui->id->updateValue(); ui->instance->updateValue(); ui->ratio->updateValue(); @@ -431,7 +442,7 @@ void TelemetrySensorPanel::update() ui->originLabel->hide(); ui->origin->hide(); ui->formula->show(); - ui->formula->setCurrentIndex(sensor.formula); + ui->formula->setCurrentIndex(ui->formula->findData(sensor.formula)); isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL); gpsFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_DIST); cellsFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_CELL); @@ -525,7 +536,7 @@ void TelemetrySensorPanel::on_name_editingFinished() void TelemetrySensorPanel::on_type_currentIndexChanged(int index) { if (!isLocked()) { - sensor.type = index; + sensor.type = ui->type->itemData(index).toInt(); update(); emit modified(); } @@ -534,7 +545,7 @@ void TelemetrySensorPanel::on_type_currentIndexChanged(int index) void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) { if (!isLocked()) { - sensor.formula = index; + sensor.formula = ui->formula->itemData(index).toInt(); if (sensor.formula == SensorData::TELEM_FORMULA_CELL) { sensor.prec = 2; sensor.unit = SensorData::UNIT_VOLTS; @@ -554,7 +565,7 @@ void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) { if (!isLocked()) { - sensor.unit = index; + sensor.unit = ui->unit->itemData(index).toInt(); if (sensor.unit == SensorData::UNIT_FAHRENHEIT) { sensor.prec = 0; } @@ -710,7 +721,7 @@ void TelemetrySensorPanel::onItemModelUpdateComplete() /******************************************************/ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels): + CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::Telemetry), sharedItemModels(sharedItemModels), @@ -718,20 +729,37 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin { ui->setupUi(this); - localFilteredItemModels = new FilteredItemModelsFactory(); - int newid; + panelItemModels = new CompoundItemModelFactory(&generalSettings, &model); + panelFilteredItemModels = new FilteredItemModelFactory(); + int id; - newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::RawSourceId)), - FIM_NAME_RAWSOURCE); - connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)), + FIM_RAWSOURCE); + connectItemModelEvents(id); - newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId)), - FIM_NAME_TELEALLSRC); - connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_TeleSource)), + FIM_TELEALLSRC); + connectItemModelEvents(id); - newid = localFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::TeleSourceId), - FilteredItemModel::PositiveFilter), FIM_NAME_TELEPOSSRC); - connectItemModelEvents(localFilteredItemModels->getItemModel(newid)); + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_TeleSource), + FilteredItemModel::PositiveFilter), + FIM_TELEPOSSRC); + connectItemModelEvents(id); + + id = panelItemModels->registerItemModel(SensorData::typeItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORTYPE); + + id = panelItemModels->registerItemModel(SensorData::formulaItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORFORMULA); + + id = panelItemModels->registerItemModel(SensorData::cellIndexItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORCELLINDEX); + + id = panelItemModels->registerItemModel(SensorData::unitItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORUNIT); + + id = panelItemModels->registerItemModel(SensorData::precisionItemModel()); + panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORPRECISION); sensorCapability = firmware->getCapability(Sensors); if (sensorCapability > CPN_MAX_SENSORS) // TODO should be role of getCapability @@ -741,7 +769,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin model.frsky.usrProto = 1; } - ui->varioSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->varioSource->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->varioSource->setField(model.frsky.varioSource, this); ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->A1GB->hide(); @@ -749,7 +777,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin for (int i = 0; i < sensorCapability; ++i) { TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, sensorCapability, model, generalSettings, - firmware, lock, localFilteredItemModels); + firmware, lock, panelFilteredItemModels); ui->sensorsLayout->addWidget(panel); sensorPanels[i] = panel; connect(panel, SIGNAL(dataModified()), this, SLOT(on_dataModifiedSensor())); @@ -762,9 +790,9 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin } if (IS_TARANIS_X9(firmware->getBoard())) { - ui->voltsSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->voltsSource->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->voltsSource->setField(model.frsky.voltsSource, this); - ui->altitudeSource->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->altitudeSource->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->altitudeSource->setField(model.frsky.altitudeSource, this); } else { @@ -773,7 +801,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock, - localFilteredItemModels); + panelFilteredItemModels); ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1)); telemetryCustomScreens[i] = tab; connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified); @@ -787,7 +815,8 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin TelemetryPanel::~TelemetryPanel() { delete ui; - delete localFilteredItemModels; + delete panelFilteredItemModels; + delete panelItemModels; } void TelemetryPanel::update() @@ -839,7 +868,7 @@ void TelemetryPanel::setup() ui->rssiSourceLabel->show(); ui->rssiSourceLabel->setText(tr("Source")); - ui->rssiSourceCB->setModel(localFilteredItemModels->getItemModel(FIM_NAME_TELEPOSSRC)); + ui->rssiSourceCB->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); ui->rssiSourceCB->setField(model->rssiSource, this); ui->rssiSourceCB->show(); @@ -1053,12 +1082,13 @@ void TelemetryPanel::swapData(int idx1, int idx2) void TelemetryPanel::on_dataModifiedSensor() { - sharedItemModels->update(AbstractItemModel::TeleSensorsUpdated); + sharedItemModels->update(AbstractItemModel::IMUE_TeleSensors); emit modified(); } -void TelemetryPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +void TelemetryPanel::connectItemModelEvents(const int id) { + FilteredItemModel * itemModel = panelFilteredItemModels->getItemModel(id); connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryPanel::onItemModelAboutToBeUpdated); connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryPanel::onItemModelUpdateComplete); } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 51273d867..224f88020 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -23,7 +23,7 @@ #include "modeledit.h" #include "eeprominterface.h" -#include "rawitemfilteredmodel.h" +#include "filtereditemmodels.h" constexpr char MIMETYPE_TELE_SENSOR[] {"application/x-companion-tele-sensor"}; @@ -42,7 +42,7 @@ class TelemetryCustomScreen: public ModelPanel public: TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, - const bool & parentLock, FilteredItemModelsFactory * localFilteredItemModels); + const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels); ~TelemetryCustomScreen(); void update(); @@ -81,7 +81,7 @@ class TelemetrySensorPanel: public ModelPanel public: TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, - FilteredItemModelsFactory * localFilteredItemModels); + FilteredItemModelFactory * panelFilteredItemModels); ~TelemetrySensorPanel(); void update(); @@ -116,7 +116,6 @@ class TelemetrySensorPanel: public ModelPanel private: Ui::TelemetrySensor * ui; SensorData & sensor; - //bool lock = false; int sensorIndex = 0; int selectedIndex = 0; int sensorCapability; @@ -136,7 +135,7 @@ class TelemetryPanel : public ModelPanel public: TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - ItemModelsFactory * sharedItemModels); + CompoundItemModelFactory * sharedItemModels); virtual ~TelemetryPanel(); virtual void update(); @@ -171,14 +170,15 @@ class TelemetryPanel : public ModelPanel TelemetryCustomScreen *telemetryCustomScreens[4]; TelemetrySensorPanel *sensorPanels[CPN_MAX_SENSORS]; int sensorCapability; - ItemModelsFactory *sharedItemModels; - FilteredItemModelsFactory *localFilteredItemModels; + CompoundItemModelFactory *sharedItemModels; + CompoundItemModelFactory *panelItemModels; + FilteredItemModelFactory *panelFilteredItemModels; int modelsUpdateCnt; void setup(); void telBarUpdate(); void swapData(int idx1, int idx2); - void connectItemModelEvents(const FilteredItemModel * itemModel); + void connectItemModelEvents(const int id); inline bool isLocked() { return lock; } }; From 5457097b5d4d94581af9697e7b3070e1bc654b80 Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 8 Jan 2021 21:10:09 +1100 Subject: [PATCH 037/300] remove experimental files --- companion/src/datamodels/itemmodelfactory.cpp | 223 ------------------ companion/src/datamodels/itemmodelfactory.h | 75 ------ 2 files changed, 298 deletions(-) delete mode 100644 companion/src/datamodels/itemmodelfactory.cpp delete mode 100644 companion/src/datamodels/itemmodelfactory.h diff --git a/companion/src/datamodels/itemmodelfactory.cpp b/companion/src/datamodels/itemmodelfactory.cpp deleted file mode 100644 index b5b341afd..000000000 --- a/companion/src/datamodels/itemmodelfactory.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "itemmodelfactory.h" - -#include "compounditemmodels.h" -#include "filtereditemmodels.h" - -#include "eeprominterface.h" -#include "generalsettings.h" -#include "modeldata.h" - -ItemModelFactory::ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData) : - generalSettings(generalSettings), - modelData(modelData) -{ - firmware = getCurrentFirmware(); - board = new Boards(getCurrentBoard()); - boardType = getCurrentBoard(); -} - -ItemModelFactory::~ItemModelFactory() -{ - unregisterItemModels(); - delete board; -} - -void ItemModelFactory::addItemModel(const AbstractItemModel::ItemModelId id) -{ - switch (id) { - case AbstractItemModel::RawSourceId: - registerItemModel(new RawSourceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::RawSwitchId: - registerItemModel(new RawSwitchItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CurveId: - registerItemModel(new CurveItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::GVarRefId: - registerItemModel(new GVarReferenceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::ThrSourceId: - registerItemModel(new ThrottleSourceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CustomFuncActionId: - registerItemModel(new CustomFuncActionItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CustomFuncResetParamId: - registerItemModel(new CustomFuncResetParamItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::TeleSourceId: - registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CurveRefTypeId: - registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - case AbstractItemModel::CurveRefFuncId: - registerItemModel(new CurveRefFuncItemModel(generalSettings, modelData, firmware, board, boardType)); - break; - default: - qDebug() << "Error: unknown item model: id"; - break; - } -} - -void ItemModelFactory::registerItemModel(QAbstractItemModel * itemModel) -{ - if (itemModel && !getItemModel(itemModel->getId())) { - registeredItemModels.push_back(itemModel); - } -} - -void ItemModelFactory::unregisterItemModels() -{ - foreach (QAbstractItemModel *itemModel, registeredItemModels) { - delete itemModel; - } -} - -void ItemModelFactory::unregisterItemModel(const AbstractItemModel::ItemModelId id) -{ - QAbstractItemModel * itemModel = getItemModel(id); - if (itemModel) - delete itemModel; -} - -QAbstractItemModel * CompoundItemModelFactory::getItemModel(const AbstractItemModel::ItemModelId id) const -{ - foreach (AbstractItemModel * itemModel, registeredItemModels) { - if (itemModel->getId() == id) - return itemModel; - } - - return nullptr; -} - -void ItemModelFactory::update(const AbstractItemModel::UpdateTrigger trigger) -{ - foreach (AbstractItemModel * itemModel, registeredItemModels) { - itemModel->update(trigger); - } -} - -void ItemModelFactory::dumpAllItemModelContents() const -{ - foreach (AbstractItemModel * itemModel, registeredItemModels) { - AbstractItemModel::dumpItemModelContents(itemModel); - } -} - -// -// FilteredItemModelFactory -// - -FilteredItemModelFactory::FilteredItemModelFactory() -{ -} - -FilteredItemModelFactory::~FilteredItemModelFactory() -{ - unregisterItemModels(); -} - -int FilteredItemModelFactory::registerItemModel(FilteredItemModel * itemModel, QString name) -{ - if (itemModel) { - if (isItemModelRegistered(name)) - qDebug() << "Warning: item model already registered with name:" << name; - const int id = registeredItemModels.count(); - itemModel->setId(id); - itemModel->setName(name); - registeredItemModels.push_back(itemModel); - return id; - } - return 0; -} - -void FilteredItemModelFactory::unregisterItemModels() -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - delete itemModel; - } -} - -void FilteredItemModelFactory::unregisterItemModel(const int id) -{ - FilteredItemModel * itemModel = getItemModel(id); - if (itemModel) - delete itemModel; -} - -bool FilteredItemModelFactory::isItemModelRegistered(const int id) const -{ - return getItemModel(id); -} - -bool FilteredItemModelFactory::isItemModelRegistered(const QString name) const -{ - return getItemModel(name); -} - -FilteredItemModel * FilteredItemModelFactory::getItemModel(const int id) const -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - if (itemModel->getId() == id) - return itemModel; - } - - return nullptr; -} - -FilteredItemModel * FilteredItemModelFactory::getItemModel(const QString name) const -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - if (itemModel->getName() == name) - return itemModel; - } - - return nullptr; -} - -void FilteredItemModelFactory::update() -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - itemModel->update(); - } -} - -void FilteredItemModelFactory::dumpItemModelContents(const int id) const -{ - FilteredItemModel::dumpItemModelContents(getItemModel(id)); -} - -void FilteredItemModelFactory::dumpItemModelContents(const QString name) const -{ - FilteredItemModel::dumpItemModelContents(getItemModel(name)); -} - -void FilteredItemModelFactory::dumpAllItemModelContents() const -{ - foreach (FilteredItemModel * itemModel, registeredItemModels) { - FilteredItemModel::dumpItemModelContents(itemModel); - } -} - diff --git a/companion/src/datamodels/itemmodelfactory.h b/companion/src/datamodels/itemmodelfactory.h deleted file mode 100644 index 9f03b897b..000000000 --- a/companion/src/datamodels/itemmodelfactory.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ITEMMODELFACTORY_H -#define ITEMMODELFACTORY_H - -class GeneralSettings; -class ModelData; -class AbstractItemModel; -class FilteredItemModel; - -class ItemModelFactory -{ - public: - ItemModelFactory(const GeneralSettings * const generalSettings, const ModelData * const modelData); - virtual ~ItemModelFactory(); - - void addItemModel(const AbstractItemModel::ItemModelId id); - void registerItemModel(AbstractItemModel * itemModel); - void unregisterItemModels(); - void unregisterItemModel(const AbstractItemModel::ItemModelId id); - AbstractItemModel * getItemModel(const AbstractItemModel::ItemModelId id) const; - void update(const AbstractItemModel::UpdateTrigger trigger = AbstractItemModel::SystemRefresh); - void dumpAllItemModelContents() const; - - protected: - const GeneralSettings * generalSettings; - const ModelData * modelData; - Firmware * firmware; - Boards * board; - Board::Type boardType; - QVector registeredItemModels; -}; - -class FilteredItemModelFactory -{ - public: - FilteredItemModelFactory(); - virtual ~FilteredItemModelFactory(); - - int registerItemModel(FilteredItemModel * itemModel, QString name); - void unregisterItemModels(); - void unregisterItemModel(const int id); - bool isItemModelRegistered(const int id) const; - bool isItemModelRegistered(const QString name) const; - FilteredItemModel * getItemModel(const int id) const; - FilteredItemModel * getItemModel(const QString name) const; - void update(); - - void dumpItemModelContents(const int id) const; - void dumpItemModelContents(const QString name) const; - void dumpAllItemModelContents() const; - - protected: - QVector registeredItemModels; -}; - -#endif // ITEMMODELFACTORY_H From 31c20fc8eba55359e1af92f0650374f67c35c5cd Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 8 Jan 2021 17:53:38 +0100 Subject: [PATCH 038/300] Fix synced --- radio/src/mixer_scheduler.cpp | 4 ++-- radio/src/tasks.cpp | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/radio/src/mixer_scheduler.cpp b/radio/src/mixer_scheduler.cpp index 82dfb7086..8132fe2f0 100644 --- a/radio/src/mixer_scheduler.cpp +++ b/radio/src/mixer_scheduler.cpp @@ -37,12 +37,12 @@ static MixerSchedule mixerSchedules[NUM_MODULES]; uint16_t getMixerSchedulerPeriod() { -#if defined(INTERNAL_MODULE) +#if defined(HARDWARE_INTERNAL_MODULE) if (mixerSchedules[INTERNAL_MODULE].period) { return mixerSchedules[INTERNAL_MODULE].period; } #endif -#if defined(EXTERNAL_MODULE) +#if 1 //defined(HARDWARE_EXTERNAL_MODULE) if (mixerSchedules[EXTERNAL_MODULE].period) { return mixerSchedules[EXTERNAL_MODULE].period; } diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index f85c6a6b2..8346b2ceb 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -174,14 +174,7 @@ TASK_FUNCTION(mixerTask) doMixerCalculations(); // TODO: fix runMask - uint8_t runMask = 0; -#if defined(INTERNAL_MODULE) - runMask != (1 << INTERNAL_MODULE) -#endif -#if defined(EXTERNAL_MODULE) - runMask != (1 << EXTERNAL_MODULE) -#endif - sendSynchronousPulses(runMask); + sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE)); doMixerPeriodicUpdates(); From 87c9cfa610da2db7480bb9ba5a2b6dce51f75080 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 10 Jan 2021 08:23:48 +0100 Subject: [PATCH 039/300] Update --- tools/build-imrc.py | 2 +- tools/build-tbs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/build-imrc.py b/tools/build-imrc.py index b739a3fc6..e0c8cf061 100755 --- a/tools/build-imrc.py +++ b/tools/build-imrc.py @@ -25,7 +25,7 @@ def build(board, translation, srcdir): os.mkdir("output") path = tempfile.mkdtemp() os.chdir(path) - command = "cmake %s -DTRANSLATIONS=%s -DIMRC_RELEASE=YES -DGHOST=YES -DTEST_BUILD_WARNING=YES %s" % (cmake_options, translation, srcdir) + command = "cmake %s -DTRANSLATIONS=%s -DIMRC_RELEASE=YES -DGHOST=YES %s" % (cmake_options, translation, srcdir) print(command) os.system(command) os.system("make firmware -j16") diff --git a/tools/build-tbs.py b/tools/build-tbs.py index 729855df0..4d469c608 100755 --- a/tools/build-tbs.py +++ b/tools/build-tbs.py @@ -25,7 +25,7 @@ def build(board, translation, srcdir): os.mkdir("output") path = tempfile.mkdtemp() os.chdir(path) - command = "cmake %s -DTRANSLATIONS=%s -DTBS_RELEASE=YES -DTEST_BUILD_WARNING=YES %s" % (cmake_options, translation, srcdir) + command = "cmake %s -DTRANSLATIONS=%s -DTBS_RELEASE=YES %s" % (cmake_options, translation, srcdir) print(command) os.system(command) os.system("make firmware -j16") From 99ff3f9f8c37a63b0f787123845ad57eb8c35c49 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 10 Jan 2021 09:35:44 +0100 Subject: [PATCH 040/300] Add 'synced' to firmware options display --- radio/src/options.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/options.h b/radio/src/options.h index acadfd129..bc50f70fd 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -23,6 +23,7 @@ #define OPENTX_OPTIONS_H static const char * const options[] = { + "synced", #if defined(AUTOUPDATE) "autoupdate", #endif From 7699634217e10e0fc988262f95badc8d3187af36 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 10 Jan 2021 18:08:53 +0100 Subject: [PATCH 041/300] Fix #8204 --- radio/src/gui/480x272/model_setup.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index a620b5685..cd9974ac5 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -577,7 +577,7 @@ void onModelAntennaSwitchConfirm(const char * result) MODULE_TYPE_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE*/ \ MULTIMODULE_STATUS_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS */ \ MODULE_CHANNELS_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS*/ \ - IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1))), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\ + IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, MODULE_BIND_ROWS(INTERNAL_MODULE)), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\ IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM*/ \ MODULE_OPTION_ROW(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS */ \ MULTIMODULE_MODULE_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND */ \ @@ -1549,6 +1549,7 @@ bool menuModelSetup(event_t event) lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER); int l_posHorz = menuHorizontalPosition; coord_t bindButtonPos = MODEL_SETUP_2ND_COLUMN; + // RXNUM if (isModuleRxNumAvailable(moduleIdx)) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0 | LEFT, 2); bindButtonPos += 40; @@ -1572,12 +1573,13 @@ bool menuModelSetup(event_t event) else if (attr) { l_posHorz += 1; } + // BIND - RANGE if (isModuleBindRangeAvailable(moduleIdx)) { drawButton(bindButtonPos, y, STR_MODULE_BIND, (moduleState[moduleIdx].mode == MODULE_MODE_BIND ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==1 ? attr : 0)); if (isModuleRangeAvailable(moduleIdx)) { drawButton(bindButtonPos + 80, y, STR_MODULE_RANGE, (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==2 ? attr : 0)); } - uint8_t newFlag = 0; + uint8_t newFlag = MODULE_MODE_NORMAL; #if defined(MULTIMODULE) if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) { setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION); From 54a926322b69e8884ffc7bf2b170148e94f5aba3 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Mon, 11 Jan 2021 11:38:19 +0800 Subject: [PATCH 042/300] add option for s.port/f.port/f.port2 --- .../gui/480x272/model_receiver_options.cpp | 38 +++++++++++-------- .../common/stdlcd/model_receiver_options.cpp | 36 +++++++++++------- radio/src/translations.cpp | 1 + radio/src/translations.h | 1 + radio/src/translations/untranslated.h | 3 ++ 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 38e46728c..6f7a2a94f 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -47,8 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_SPORT_FPORT, - ITEM_RECEIVER_SETTINGS_FPORT2, + ITEM_RECEIVER_SETTINGS_FPORT, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -60,6 +59,16 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 +bool isFPort2Available(int portType) +{ + uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; + if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) + { + return false; + } + return true; +} + bool menuModelReceiverOptions(event_t event) { const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; @@ -82,8 +91,7 @@ bool menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -171,21 +179,19 @@ bool menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_SPORT_FPORT: - lcdDrawText(MENUS_MARGIN_LEFT, y, "F.Port"); - reusableBuffer.hardwareAndSettings.receiverSettings.fport = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_FPORT2: - lcdDrawText(MENUS_MARGIN_LEFT, y, "F.Port2"); - reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport2, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { + case ITEM_RECEIVER_SETTINGS_FPORT: + { + lcdDrawText(0, y, "PORT"); + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; + reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; } break; + } case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index de3547b75..cf47462d7 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -47,8 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_SPORT_FPORT, - ITEM_RECEIVER_SETTINGS_FPORT2, + ITEM_RECEIVER_SETTINGS_FPORT, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -60,6 +59,16 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 +bool isFPort2Available(int portType) +{ + uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; + if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) + { + return false; + } + return true; +} + void menuModelReceiverOptions(event_t event) { const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; @@ -82,8 +91,7 @@ void menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -166,19 +174,19 @@ void menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_SPORT_FPORT: - reusableBuffer.hardwareAndSettings.receiverSettings.fport = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport, RECEIVER_OPTIONS_2ND_COLUMN, y, "F.Port", attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_FPORT2: - reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport2, RECEIVER_OPTIONS_2ND_COLUMN, y, "F.Port2", attr, event); - if (attr && checkIncDec_Ret) { + case ITEM_RECEIVER_SETTINGS_FPORT: + { + lcdDrawText(0, y, "PORT"); + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; + reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; } break; + } case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 48d01b4bc..2b7b86e75 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -81,6 +81,7 @@ ISTR(POTTYPES); ISTR(SLIDERTYPES); ISTR(ANTENNA_MODES); ISTR(SPORT_UPDATE_POWER_MODES); +ISTR(PORT_TYPES); #if defined(PCBSKY9X) && defined(REVX) ISTR(VOUTPUT_TYPE); diff --git a/radio/src/translations.h b/radio/src/translations.h index 73c843389..89923f35f 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -161,6 +161,7 @@ extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; extern const char STR_MULTI_POWER[]; +extern const char STR_PORT_TYPES[]; #if defined(AFHDS3) extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index e0e6f4bef..f9ee2deb3 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -109,6 +109,9 @@ #define LEN_R9M_PXX2_RF_PROTOCOLS "\006" #define TR_R9M_PXX2_RF_PROTOCOLS "ACCESS""FCC\0 ""EU\0 ""Flex" +#define LEN_PORT_TYPES "\007" +#define TR_PORT_TYPES "S.PORT\0""F.PORT\0""F.PORT2" + #define LEN_R9M_REGION "\006" #define TR_R9M_REGION "FCC\0 ""EU\0 ""868MHz""915MHz" From e769d87cb2e5673f049dc6ce4b51dde119f51d69 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 12 Jan 2021 18:26:27 +0100 Subject: [PATCH 043/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 3136f7b6e..da2703ccd 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2093,3 +2093,4 @@ Burt Pickard-Richardson Michal Mesaros Conrad Young Martin Mathes +Jens Rainer Hansen From 9dfe19b0db0f8cd5f7a0260be156897f012e1b06 Mon Sep 17 00:00:00 2001 From: Sjoer van der Ploeg Date: Tue, 12 Jan 2021 22:24:09 +0100 Subject: [PATCH 044/300] Fix deprecated function --- companion/src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 9d490da39..23c5f71b7 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -1585,7 +1585,7 @@ void MainWindow::updateWindowActions() QString scut; if (++count < 10) scut = tr("Alt+%1").arg(count); - QAction * act = addActToGroup(windowsListActions, "", "", "window_ptr", qVariantFromValue(win), QVariant(), scut); + QAction * act = addActToGroup(windowsListActions, "", "", "window_ptr", QVariant::fromValue(win), QVariant(), scut); act->setChecked(win == mdiArea->activeSubWindow()); updateWindowActionTitle(win, act); } From a71f47e5ded55d2d603d713b67a66067a5885831 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Wed, 13 Jan 2021 09:49:06 +0800 Subject: [PATCH 045/300] cosmetic --- radio/src/gui/common/stdlcd/model_receiver_options.cpp | 2 +- radio/src/translations.cpp | 2 ++ radio/src/translations/cz.h.txt | 1 + radio/src/translations/de.h.txt | 1 + radio/src/translations/en.h.txt | 1 + radio/src/translations/es.h.txt | 1 + radio/src/translations/fi.h.txt | 1 + radio/src/translations/fr.h.txt | 1 + radio/src/translations/it.h.txt | 1 + radio/src/translations/nl.h.txt | 1 + radio/src/translations/pl.h.txt | 1 + radio/src/translations/pt.h.txt | 1 + radio/src/translations/se.h.txt | 1 + 13 files changed, 14 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index cf47462d7..345863331 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -176,7 +176,7 @@ void menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_FPORT: { - lcdDrawText(0, y, "PORT"); + lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 2b7b86e75..276332f8a 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -830,3 +830,5 @@ const char STR_ABOUT_PARENTS_3[] = TR_ABOUT_PARENTS_3; const char STR_ABOUT_PARENTS_4[] = TR_ABOUT_PARENTS_4; const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; +const char STR_PROTOCOL[] = TR_PROTOCOL; + diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 2f42f7edd..3ec57a040 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -1221,6 +1221,7 @@ #define TR_JITTER_FILTER "ADC Filtr" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 1e85008b0..93ebebcdd 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -1227,6 +1227,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" // ---------------------------------------------------------------- #define ZSTR_VFR "VFR" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ad49d6997..b61b07c33 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -1225,6 +1225,7 @@ #define TR_JITTER_FILTER "ADC filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index e89998d7f..4dc05fb59 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -1224,6 +1224,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltaje") #define TR_AUTH_FAILURE "Fallo " LCDW_128_480_LINEBREAK "autentificación" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index e18b51102..011fc3b52 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -1233,6 +1233,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index ffaeb38c6..e2e55f51a 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -1249,6 +1249,7 @@ #define TR_JITTER_FILTER "Filtre ADC" #define TR_RTC_CHECK TR("Vérif. RTC", "Vérif. pile RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c1e8c0be0..6c402a265 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -1242,6 +1242,7 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index fe5abbfe3..a0966e1a7 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -1234,6 +1234,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index edfcee741..ea5851e82 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1241,6 +1241,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 412e3698a..db824fc31 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -1241,6 +1241,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 57cf99ba6..44687c1d1 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -1241,6 +1241,7 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" +#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" From 9779359421bd5182e31fe39abfce0f22217e7884 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Wed, 13 Jan 2021 09:54:25 +0800 Subject: [PATCH 046/300] rename --- radio/src/translations/untranslated.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index f9ee2deb3..4039492bc 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -109,8 +109,8 @@ #define LEN_R9M_PXX2_RF_PROTOCOLS "\006" #define TR_R9M_PXX2_RF_PROTOCOLS "ACCESS""FCC\0 ""EU\0 ""Flex" -#define LEN_PORT_TYPES "\007" -#define TR_PORT_TYPES "S.PORT\0""F.PORT\0""F.PORT2" +#define LEN_PORT_TYPES "\014" +#define TR_PORT_TYPES "S.PORT\0 ""F.PORT\0 ""FBUS(FPORT2)" #define LEN_R9M_REGION "\006" #define TR_R9M_REGION "FCC\0 ""EU\0 ""868MHz""915MHz" From 2ac4e7b64e7bee57111c809fa2105c3f48dd9390 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Wed, 13 Jan 2021 18:21:36 +0800 Subject: [PATCH 047/300] fix --- radio/src/gui/480x272/model_receiver_options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 6f7a2a94f..8d69817d1 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -181,7 +181,7 @@ bool menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_FPORT: { - lcdDrawText(0, y, "PORT"); + lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); From 10ea134ae63bf801447960fd28a32516eacc8169 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 13 Jan 2021 17:06:22 +0100 Subject: [PATCH 048/300] Make text translatable --- companion/src/firmwares/curvereference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 449dc0266..64138107c 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -77,7 +77,7 @@ int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) // static QString CurveReference::typeToString(const CurveRefType type) { - const QStringList strl = { "Diff", "Expo" , "Func", "Curve" }; + const QStringList strl = { tr("Diff"), tr("Expo") , tr("Func"), tr("Curve") }; int idx = (int)type; if (idx < 0 || idx >= strl.count()) From ab528f22e0518ea1eb559496a485bd3fcb9b4b78 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 Jan 2021 18:12:32 +0100 Subject: [PATCH 049/300] Fixes --- radio/src/gui/128x64/model_setup.cpp | 4 ++-- radio/src/gui/480x272/model_receiver_options.cpp | 15 +++++++-------- .../gui/common/stdlcd/model_receiver_options.cpp | 15 +++++++-------- radio/src/translations.cpp | 3 +-- radio/src/translations.h | 3 ++- radio/src/translations/cz.h.txt | 3 +-- radio/src/translations/de.h.txt | 5 ++--- radio/src/translations/en.h.txt | 3 +-- radio/src/translations/es.h.txt | 3 +-- radio/src/translations/fi.h.txt | 3 +-- radio/src/translations/fr.h.txt | 5 ++--- radio/src/translations/it.h.txt | 5 ++--- radio/src/translations/nl.h.txt | 5 ++--- radio/src/translations/pl.h.txt | 5 ++--- radio/src/translations/pt.h.txt | 5 ++--- radio/src/translations/se.h.txt | 5 ++--- radio/src/translations/untranslated.h | 4 ++-- 17 files changed, 39 insertions(+), 52 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 41af5720f..004737515 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -857,7 +857,7 @@ void menuModelSetup(event_t event) #if !defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: { - lcdDrawTextAlignedLeft(y, INDENT TR_MODE); + lcdDrawText(INDENT_WIDTH, y, STR_MODE); #if defined(INTERNAL_MODULE_PXX1) lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_INTERNAL_MODULE_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0); if (isModuleXJT(INTERNAL_MODULE)) @@ -1073,7 +1073,7 @@ void menuModelSetup(event_t event) #if defined(AFHDS3) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS_PROTOCOL: - lcdDrawTextAlignedLeft(y, TR_PROTO); + lcdDrawText(INDENT_WIDTH, y, STR_PROTOCOL); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_AFHDS3_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].subType, attr); if (attr && s_editMode > 0 && menuHorizontalPosition == 0) { CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, AFHDS_SUBTYPE_FIRST, AFHDS_SUBTYPE_LAST); diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 8d69817d1..60292b223 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -47,7 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_FPORT, + ITEM_RECEIVER_SETTINGS_SPORT_MODE, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -59,11 +59,10 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 -bool isFPort2Available(int portType) +bool isSPortModeAvailable(int mode) { uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; - if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) - { + if (mode == 2 && !IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) { return false; } return true; @@ -91,7 +90,7 @@ bool menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + uint8_t((IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT) || IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) ? 0 : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -179,12 +178,12 @@ bool menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_FPORT: + case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; - lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); - portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index 345863331..7dc6e3354 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -47,7 +47,7 @@ enum { ITEM_RECEIVER_SETTINGS_PWM_RATE, ITEM_RECEIVER_SETTINGS_TELEMETRY, ITEM_RECEIVER_SETTINGS_TELEMETRY_25MW, - ITEM_RECEIVER_SETTINGS_FPORT, + ITEM_RECEIVER_SETTINGS_SPORT_MODE, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST @@ -59,11 +59,10 @@ enum { #define CH_ENABLE_SPORT 4 #define CH_ENABLE_SBUS 5 -bool isFPort2Available(int portType) +bool isSPortModeAvailable(int mode) { uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; - if (!IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2) && portType == 2) - { + if (mode == 2 && !IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) { return false; } return true; @@ -91,7 +90,7 @@ void menuModelReceiverOptions(event_t event) 0, // PWM rate isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_TELEMETRY_25MW, 0), - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0) || IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT2, 0), + uint8_t((IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT) || IS_RECEIVER_CAPABILITY_ENABLED(RECEIVER_CAPABILITY_FPORT2)) ? 0 : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), 0 // channels ... @@ -174,12 +173,12 @@ void menuModelReceiverOptions(event_t event) } break; - case ITEM_RECEIVER_SETTINGS_FPORT: + case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; - lcdDrawTextAtIndex(LCD_W/2, y, STR_PORT_TYPES, portType, attr); - portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isFPort2Available); + lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); + portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { reusableBuffer.hardwareAndSettings.receiverSettings.fport = portType & 0x01; reusableBuffer.hardwareAndSettings.receiverSettings.fport2 = (portType & 0x02) >> 1; diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 276332f8a..455d6490d 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -81,7 +81,7 @@ ISTR(POTTYPES); ISTR(SLIDERTYPES); ISTR(ANTENNA_MODES); ISTR(SPORT_UPDATE_POWER_MODES); -ISTR(PORT_TYPES); +ISTR(SPORT_MODES); #if defined(PCBSKY9X) && defined(REVX) ISTR(VOUTPUT_TYPE); @@ -831,4 +831,3 @@ const char STR_ABOUT_PARENTS_4[] = TR_ABOUT_PARENTS_4; const char STR_AUTH_FAILURE[] = TR_AUTH_FAILURE; const char STR_PROTOCOL[] = TR_PROTOCOL; - diff --git a/radio/src/translations.h b/radio/src/translations.h index 89923f35f..a9131d8a5 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -161,7 +161,7 @@ extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; extern const char STR_MULTI_POWER[]; -extern const char STR_PORT_TYPES[]; +extern const char STR_SPORT_MODES[]; #if defined(AFHDS3) extern const char STR_AFHDS3_ONE_TO_ONE_TELEMETRY[]; @@ -965,6 +965,7 @@ extern const char STR_ABOUT_PARENTS_3[]; extern const char STR_ABOUT_PARENTS_4[]; extern const char STR_AUTH_FAILURE[]; +extern const char STR_PROTOCOL[]; #define CHR_HOUR TR_CHR_HOUR #define CHR_INPUT TR_CHR_INPUT diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 3ec57a040..ac4392881 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -441,7 +441,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Výstup" #endif -#define TR_PROTO INDENT "Protokol" +#define TR_PROTOCOL "Protokol" #define TR_PPMFRAME INDENT "PPM modulace" #define TR_REFRESHRATE TR(INDENT "Obnovit", INDENT "Obn. frekv.") #define STR_WARN_BATTVOLTAGE TR(INDENT "Výstup VBAT: ", INDENT "Varování: výstupní hodnota VBAT: ") @@ -1221,7 +1221,6 @@ #define TR_JITTER_FILTER "ADC Filtr" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 93ebebcdd..87e879fb5 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -443,8 +443,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Protok.", INDENT "Protokoll") - #define TR_PPMFRAME INDENT "PPM-Frame" +#define TR_PROTOCOL TR("Protok.", "Protokoll") +#define TR_PPMFRAME INDENT "PPM-Frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1227,7 +1227,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" // ---------------------------------------------------------------- #define ZSTR_VFR "VFR" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index b61b07c33..bfe26a48a 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -442,7 +442,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") +#define TR_PROTOCOL TR("Proto", "Protocol") #define TR_PPMFRAME INDENT "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") @@ -1225,7 +1225,6 @@ #define TR_JITTER_FILTER "ADC filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 4dc05fb59..d97f8a516 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -441,7 +441,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT"Proto",INDENT"Protocolo") +#define TR_PROTOCOL TR("Proto", "Protocolo") #define TR_PPMFRAME INDENT "Trama PPM" #define TR_REFRESHRATE TR(INDENT "Refresco", INDENT "Velocidad refresco") #define STR_WARN_BATTVOLTAGE TR(INDENT "Salida es VBAT: ", INDENT "Aviso: señal salida es VBAT: ") @@ -1224,7 +1224,6 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltaje") #define TR_AUTH_FAILURE "Fallo " LCDW_128_480_LINEBREAK "autentificación" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 011fc3b52..013d97c5f 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -455,7 +455,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") +#define TR_PROTOCOL TR("Proto", "Protocol") #define TR_PPMFRAME TR("PPM frame", INDENT "PPM frame") #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") @@ -1233,7 +1233,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index e2e55f51a..e7045e019 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -461,8 +461,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Sortie" #endif -#define TR_PROTO TR(INDENT "Proto.", INDENT "Protocole") - #define TR_PPMFRAME INDENT "Trame PPM" +#define TR_PROTOCOL TR("Proto.", "Protocole") +#define TR_PPMFRAME INDENT "Trame PPM" #define TR_REFRESHRATE INDENT "Période" #define STR_WARN_BATTVOLTAGE TR(INDENT "Signal est VBAT: ", INDENT "ATTENTION: signal est à VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1249,7 +1249,6 @@ #define TR_JITTER_FILTER "Filtre ADC" #define TR_RTC_CHECK TR("Vérif. RTC", "Vérif. pile RTC") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 6c402a265..99b51bf74 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -463,8 +463,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Uscita" #endif -#define TR_PROTO TR(INDENT "Protoc.", INDENT "Protocollo") - #define TR_PPMFRAME INDENT "Frame PPM" +#define TR_PROTOCOL TR("Protoc.", "Protocollo") +#define TR_PPMFRAME INDENT "Frame PPM" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Uscita VBAT: ", INDENT "Att.: livel. uscita VBAT: ") #define TR_WARN_5VOLTS "Attenzione: il livello d'uscita è 5 Volts" @@ -1242,7 +1242,6 @@ #define TR_JITTER_FILTER "Filtro ADC" #define TR_RTC_CHECK TR("Controllo RTC", "Controllo volt. RTC") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index a0966e1a7..31f730302 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -442,8 +442,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protocol") - #define TR_PPMFRAME INDENT "PPM frame" +#define TR_PROTOCOL TR("Proto", "Protocol") +#define TR_PPMFRAME INDENT "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1234,7 +1234,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index ea5851e82..4991d22bb 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -462,8 +462,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Wyjście" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protokół") - #define TR_PPMFRAME INDENT "Ramka PPM" +#define TR_PROTOCOL TR("Proto", "Protokół") +#define TR_PPMFRAME INDENT "Ramka PPM" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1241,7 +1241,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index db824fc31..5543649e4 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -459,7 +459,7 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO INDENT"Proto" +#define TR_PROTOCOL "Proto" #define TR_PPMFRAME "PPM frame" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") @@ -1240,8 +1240,7 @@ #define TR_MENU_INVERT "Invert" #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") -#define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" +#define TR_AUTH_FAILURE "Auth-failure" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 44687c1d1..7f8000575 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -461,8 +461,8 @@ #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Output" #endif -#define TR_PROTO TR(INDENT "Proto", INDENT "Protokoll") - #define TR_PPMFRAME INDENT "PPM-paket" +#define TR_PROTOCOL TR("Proto", "Protokoll") +#define TR_PPMFRAME INDENT "PPM-paket" #define TR_REFRESHRATE TR(INDENT "Refresh", INDENT "Refresh rate") #define STR_WARN_BATTVOLTAGE TR(INDENT "Output is VBAT: ", INDENT "Warning: output level is VBAT: ") #define TR_WARN_5VOLTS "Warning: output level is 5 volts" @@ -1241,7 +1241,6 @@ #define TR_JITTER_FILTER "ADC Filter" #define TR_RTC_CHECK TR("Check RTC", "Check RTC voltage") #define TR_AUTH_FAILURE "Auth-failure" -#define TR_PROTOCOL "Protocol" #define ZSTR_VFR "VFR" #define ZSTR_RSSI "RSSI" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 4039492bc..268eb2606 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -109,8 +109,8 @@ #define LEN_R9M_PXX2_RF_PROTOCOLS "\006" #define TR_R9M_PXX2_RF_PROTOCOLS "ACCESS""FCC\0 ""EU\0 ""Flex" -#define LEN_PORT_TYPES "\014" -#define TR_PORT_TYPES "S.PORT\0 ""F.PORT\0 ""FBUS(FPORT2)" +#define LEN_SPORT_MODES "\014" +#define TR_SPORT_MODES "S.PORT\0 ""F.PORT\0 ""FBUS(FPORT2)" #define LEN_R9M_REGION "\006" #define TR_R9M_REGION "FCC\0 ""EU\0 ""868MHz""915MHz" From 889c16b1f6a2f27934d1886789c1bac1a21ca2ef Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 Jan 2021 18:14:32 +0100 Subject: [PATCH 050/300] Fixes --- radio/src/gui/480x272/model_receiver_options.cpp | 2 +- radio/src/gui/common/stdlcd/model_receiver_options.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index 60292b223..0a2e2e4e6 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -181,7 +181,7 @@ bool menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); - uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | (reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1); lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index 7dc6e3354..fe5affb15 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -176,7 +176,7 @@ void menuModelReceiverOptions(event_t event) case ITEM_RECEIVER_SETTINGS_SPORT_MODE: { lcdDrawText(0, y, STR_PROTOCOL); - uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1; + uint8_t portType = reusableBuffer.hardwareAndSettings.receiverSettings.fport | (reusableBuffer.hardwareAndSettings.receiverSettings.fport2 << 1); lcdDrawTextAtIndex(LCD_W/2, y, STR_SPORT_MODES, portType, attr); portType = checkIncDec(event, portType, 0, 2, EE_MODEL, isSPortModeAvailable); if (checkIncDec_Ret) { From 0ee0c83d6f5ce5585be82539c0f11c3fe64def0f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 Jan 2021 19:18:33 +0100 Subject: [PATCH 051/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index da2703ccd..281e77ace 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2094,3 +2094,4 @@ Michal Mesaros Conrad Young Martin Mathes Jens Rainer Hansen +Mark Trible From 6342305e0af231ae0e374855d781d951ff9b8b73 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 14 Jan 2021 07:10:59 +1100 Subject: [PATCH 052/300] Apply PR #8214 --- companion/src/firmwares/curvereference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 6ade9264e..926c2d2ae 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -82,7 +82,7 @@ int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) // static QString CurveReference::typeToString(const CurveRefType type) { - const QStringList strl = { "Diff", "Expo" , "Func", "Curve" }; + const QStringList strl = { tr("Diff"), tr("Expo") , tr("Func"), tr("Curve") }; int idx = (int)type; if (idx < 0 || idx >= strl.count()) From 84c86f718be6f64b64fc0cbf7913974b9831a1f4 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 4 Jan 2021 12:14:40 +0100 Subject: [PATCH 053/300] Jumper TLite support --- companion/src/CMakeLists.txt | 2 + companion/src/companion.qrc | 10 + companion/src/firmwares/boards.cpp | 28 +- companion/src/firmwares/boards.h | 12 +- companion/src/firmwares/generalsettings.cpp | 10 + companion/src/firmwares/opentx/opentxeeprom.h | 1 + .../src/firmwares/opentx/opentxinterface.cpp | 26 +- companion/src/generaledit/hardware.cpp | 4 +- .../images/simulator/JumperTLITE/bottom.png | Bin 0 -> 27316 bytes .../simulator/JumperTLITE/center_LCD.png | Bin 0 -> 472 bytes .../src/images/simulator/JumperTLITE/left.png | Bin 0 -> 31718 bytes .../images/simulator/JumperTLITE/right.png | Bin 0 -> 29322 bytes .../src/images/simulator/JumperTLITE/top.png | Bin 0 -> 5947 bytes companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 13 + .../simulateduiwidgetJumperTLITE.cpp | 60 ++++ .../simulateduiwidgetJumperTLITE.ui | 258 ++++++++++++++++++ companion/src/simulation/simulatorwidget.cpp | 3 + radio/src/gui/128x64/radio_setup.cpp | 4 +- .../src/gui/common/stdlcd/radio_hardware.cpp | 14 +- radio/src/gui/navigation/navigation_9x.cpp | 4 +- radio/src/gui/navigation/navigation_xlite.cpp | 10 - radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_general.cpp | 2 + radio/src/pulses/pulses.cpp | 2 +- radio/src/sdcard.h | 2 + radio/src/simu.cpp | 2 +- .../targets/common/arm/stm32/adc_driver.cpp | 5 +- .../arm/stm32/bootloader/CMakeLists.txt | 2 +- radio/src/targets/horus/CMakeLists.txt | 1 + radio/src/targets/horus/hal.h | 2 + radio/src/targets/taranis/CMakeLists.txt | 13 + radio/src/targets/taranis/board.h | 27 +- radio/src/targets/taranis/hal.h | 67 ++++- radio/src/targets/taranis/keys_driver.cpp | 7 + radio/src/targets/taranis/lcd_driver_spi.cpp | 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/src/translations/untranslated.h | 2 +- tools/build-jumper.py | 7 +- tools/commit-tests.sh | 18 ++ 50 files changed, 591 insertions(+), 54 deletions(-) create mode 100644 companion/src/images/simulator/JumperTLITE/bottom.png create mode 100644 companion/src/images/simulator/JumperTLITE/center_LCD.png create mode 100644 companion/src/images/simulator/JumperTLITE/left.png create mode 100644 companion/src/images/simulator/JumperTLITE/right.png create mode 100644 companion/src/images/simulator/JumperTLITE/top.png create mode 100644 companion/src/simulation/simulateduiwidgetJumperTLITE.cpp create mode 100644 companion/src/simulation/simulateduiwidgetJumperTLITE.ui diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 3d27550e6..2f05ab859 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -343,6 +343,8 @@ if(PCB STREQUAL X7 AND PCBREV STREQUAL ACCESS) set(FLAVOUR x7access) elseif(PCB STREQUAL X7 AND PCBREV STREQUAL T12) set(FLAVOUR t12) +elseif(PCB STREQUAL X7 AND PCBREV STREQUAL TLITE) + set(FLAVOUR tlite) elseif(PCB STREQUAL X7 AND PCBREV STREQUAL TX12) set(FLAVOUR tx12) elseif(PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index fbbacdb48..e4d15c183 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -179,6 +179,16 @@ images/simulator/JumperT12/JumperT12-x.png images/simulator/JumperT12/JumperT12-center.png images/simulator/JumperT12/JumperT12-top.png + images/simulator/JumperTLITE/bottom.png + images/simulator/JumperTLITE/bottom_right.png + images/simulator/JumperTLITE/bottom_left.png + images/simulator/JumperTLITE/left.png + images/simulator/JumperTLITE/left_top.png + images/simulator/JumperTLITE/left_bottom.png + images/simulator/JumperTLITE/right.png + images/simulator/JumperTLITE/right_top.png + images/simulator/JumperTLITE/right_bottom.png + images/simulator/JumperTLITE/top.png images/simulator/TX12/left.png images/simulator/TX12/left-pageup.png images/simulator/TX12/left-pagedn.png diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 35f7a0bab..23179fd60 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -79,6 +79,8 @@ uint32_t Boards::getFourCC(Type board) return 0x3278746F; case BOARD_JUMPER_T12: return 0x3D78746F; + case BOARD_JUMPER_TLITE: + return 0x4278746F; case BOARD_JUMPER_T16: return 0x3F78746F; case BOARD_JUMPER_T18: @@ -113,6 +115,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_TARANIS_X9DP_2019: case BOARD_TARANIS_X9E: case BOARD_JUMPER_T12: + case BOARD_JUMPER_TLITE: case BOARD_RADIOMASTER_TX12: return EESIZE_TARANIS; case BOARD_UNKNOWN: @@ -148,6 +151,7 @@ int Boards::getFlashSize(Type board) case BOARD_TARANIS_X9DP_2019: case BOARD_TARANIS_X9E: case BOARD_JUMPER_T12: + case BOARD_JUMPER_TLITE: case BOARD_RADIOMASTER_TX12: return FSIZE_TARANIS; case BOARD_HORUS_X12S: @@ -181,7 +185,7 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index) if (index < DIM(switches)) return switches[index]; } - else if (IS_TARANIS_XLITE(board)) { + else if (IS_TARANIS_XLITE(board) || IS_JUMPER_TLITE(board)) { const Board::SwitchInfo switches[] = { {SWITCH_3POS, "SA"}, {SWITCH_3POS, "SB"}, @@ -294,7 +298,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case Sticks: return 4; - case Pots: + case Pots: // Storage pots if (IS_TARANIS_X9LITE(board)) return 1; else if (IS_TARANIS_SMALL(board)) @@ -308,6 +312,12 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else return 3; + case AvailablePots: + if (IS_JUMPER_TLITE(board)) + return 0; + else + return getCapability(board, Pots); + case FactoryInstalledPots: if (IS_TARANIS_X9(board)) return 2; @@ -365,9 +375,17 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else return 7; + case AvailableSwitches: + if (IS_JUMPER_TLITE(board)) + return 4; + else + return getCapability(board, Switches); + case FactoryInstalledSwitches: if (IS_TARANIS_X9E(board)) return 8; + else if (IS_JUMPER_TLITE(board)) + return 4; if (IS_FAMILY_T12(board)) return 6; if (IS_HORUS_X12S(board)) @@ -515,8 +533,6 @@ QString Boards::getBoardName(Board::Type board) return "Taranis X7/X7S"; case BOARD_TARANIS_X7_ACCESS: return "Taranis X7/X7S Access"; - case BOARD_JUMPER_T12: - return "Jumper T12"; case BOARD_TARANIS_XLITE: return "Taranis X-Lite"; case BOARD_TARANIS_XLITES: @@ -545,6 +561,10 @@ QString Boards::getBoardName(Board::Type board) return "Horus X10/X10S"; case BOARD_X10_EXPRESS: return "Horus X10/X10S Express"; + case BOARD_JUMPER_T12: + return "Jumper T12"; + case BOARD_JUMPER_TLITE: + return "Jumper T-Lite"; case BOARD_JUMPER_T16: return "Jumper T16"; case BOARD_JUMPER_T18: diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 1e1588485..a1ef1c629 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -53,9 +53,10 @@ namespace Board { BOARD_RADIOMASTER_TX16S, BOARD_JUMPER_T18, BOARD_RADIOMASTER_TX12, + BOARD_JUMPER_TLITE }; - constexpr int BOARD_TYPE_MAX = BOARD_RADIOMASTER_TX12; + constexpr int BOARD_TYPE_MAX = BOARD_JUMPER_TLITE; enum PotType { @@ -117,6 +118,7 @@ namespace Board { enum Capability { Sticks, Pots, + AvailablePots, FactoryInstalledPots, Sliders, MouseAnalogs, @@ -126,6 +128,7 @@ namespace Board { MultiposPotsPositions, Switches, SwitchPositions, + AvailableSwitches, FactoryInstalledSwitches, NumTrims, NumTrimSwitches @@ -204,6 +207,11 @@ inline bool IS_JUMPER_T12(Board::Type board) return board == Board::BOARD_JUMPER_T12; } +inline bool IS_JUMPER_TLITE(Board::Type board) +{ + return board == Board::BOARD_JUMPER_TLITE; +} + inline bool IS_JUMPER_T16(Board::Type board) { return board == Board::BOARD_JUMPER_T16; @@ -231,7 +239,7 @@ inline bool IS_FAMILY_T16(Board::Type board) inline bool IS_FAMILY_T12(Board::Type board) { - return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12; + return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12 || board == Board::BOARD_JUMPER_TLITE;; } inline bool IS_TARANIS_XLITE(Board::Type board) diff --git a/companion/src/firmwares/generalsettings.cpp b/companion/src/firmwares/generalsettings.cpp index 005900c93..f9df851b2 100644 --- a/companion/src/firmwares/generalsettings.cpp +++ b/companion/src/firmwares/generalsettings.cpp @@ -94,6 +94,12 @@ GeneralSettings::GeneralSettings() vBatMin = -23; // 6.7V vBatMax = -37; // 8.3V } + else if (IS_JUMPER_TLITE(board)) { + // 1S Li-Ion + vBatWarn = 32; + vBatMin = -60; //3V + vBatMax = -78; //4.2V + } else if (IS_TARANIS(board)) { // NI-MH 7.2V, X9D, X9D+ and X7 vBatWarn = 65; @@ -239,6 +245,10 @@ void GeneralSettings::setDefaultControlTypes(Board::Type board) switchConfig[i] = Boards::getSwitchInfo(board, i).config; } + // TLite does not have pots or sliders + if (IS_JUMPER_TLITE(board)) + return; + // TODO: move to Boards, like with switches if (IS_FAMILY_HORUS_OR_T16(board)) { potConfig[0] = Board::POT_WITH_DETENT; diff --git a/companion/src/firmwares/opentx/opentxeeprom.h b/companion/src/firmwares/opentx/opentxeeprom.h index aabbe2284..115885474 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.h +++ b/companion/src/firmwares/opentx/opentxeeprom.h @@ -37,6 +37,7 @@ #define TARANIS_X9LITES_VARIANT 0x0801 #define JUMPER_T12_VARIANT 0x4001 #define RADIOMASTER_TX12_VARIANT 0x4002 +#define JUMPER_TLITE_VARIANT 0x4003 class OpenTxGeneralData: public TransformedField { public: diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 04f02eb29..0b22e4f37 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -60,6 +60,8 @@ const char * OpenTxEepromInterface::getName() switch (board) { case BOARD_JUMPER_T12: return "OpenTX for Jumper T12"; + case BOARD_JUMPER_TLITE: + return "OpenTX for Jumper T-Lite"; case BOARD_JUMPER_T16: return "OpenTX for Jumper T16"; case BOARD_JUMPER_T18: @@ -336,6 +338,9 @@ int OpenTxEepromInterface::save(uint8_t * eeprom, const RadioData & radioData, u else if (IS_JUMPER_T12(board)) { variant |= JUMPER_T12_VARIANT; } + else if (IS_JUMPER_TLITE(board)) { + variant |= JUMPER_TLITE_VARIANT; + } else if (IS_RADIOMASTER_TX12(board)) { variant |= RADIOMASTER_TX12_VARIANT; } @@ -683,6 +688,8 @@ int OpenTxFirmware::getCapability(::Capability capability) return TARANIS_XLITE_VARIANT; else if (IS_JUMPER_T12(board)) return JUMPER_T12_VARIANT; + else if (IS_JUMPER_TLITE(board)) + return JUMPER_TLITE_VARIANT; else if (IS_RADIOMASTER_TX12(board)) return RADIOMASTER_TX12_VARIANT; else @@ -746,7 +753,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_ACCST_ISRM_D16: return IS_ACCESS_RADIO(board, id); case PULSES_MULTIMODULE: - return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board) || IS_JUMPER_T18(board); + return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board) || IS_JUMPER_T18(board) || IS_RADIOMASTER_TX12(board) || IS_JUMPER_TLITE(board); default: return false; } @@ -778,7 +785,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port) case PULSES_XJT_LITE_X16: case PULSES_XJT_LITE_D8: case PULSES_XJT_LITE_LR12: - return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board)); + return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || IS_JUMPER_TLITE(board)); default: return false; } @@ -949,6 +956,11 @@ bool OpenTxEepromInterface::checkVariant(unsigned int version, unsigned int vari variantError = true; } } + else if (IS_JUMPER_TLITE(board)) { + if (variant != JUMPER_TLITE_VARIANT) { + variantError = true; + } + } else if (IS_RADIOMASTER_TX12(board)) { if (variant != RADIOMASTER_TX12_VARIANT) { variantError = true; @@ -1275,6 +1287,16 @@ void registerOpenTxFirmwares() registerOpenTxFirmware(firmware); addOpenTxRfOptions(firmware, FLEX); + /* Jumper T-Lite board */ + firmware = new OpenTxFirmware("opentx-tlite", QCoreApplication::translate("Firmware", "Jumper T-Lite"), BOARD_JUMPER_TLITE); + addOpenTxCommonOptions(firmware); + firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support")); + firmware->addOption("nogvars", Firmware::tr("Disable Global variables")); + firmware->addOption("lua", Firmware::tr("Enable Lua custom scripts screen")); + addOpenTxFontOptions(firmware); + registerOpenTxFirmware(firmware); + addOpenTxRfOptions(firmware, FLEX + AFHDS3); + /* Jumper T16 board */ firmware = new OpenTxFirmware("opentx-t16", Firmware::tr("Jumper T16 / T16+ / T16 Pro"), BOARD_JUMPER_T16); addOpenTxFrskyOptions(firmware); diff --git a/companion/src/generaledit/hardware.cpp b/companion/src/generaledit/hardware.cpp index 9d2e8e452..1d211d280 100644 --- a/companion/src/generaledit/hardware.cpp +++ b/companion/src/generaledit/hardware.cpp @@ -24,7 +24,7 @@ void HardwarePanel::setupSwitchType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type, bool threePos) { Board::Type board = getCurrentBoard(); - if (IS_STM32(board) && index < getBoardCapability(board, Board::Switches)) { + if (IS_STM32(board) && index < getBoardCapability(board, Board::AvailableSwitches)) { type->addItem(tr("None"), Board::SWITCH_NOT_AVAILABLE); type->addItem(tr("2 Positions Toggle"), Board::SWITCH_TOGGLE); type->addItem(tr("2 Positions"), Board::SWITCH_2POS); @@ -75,7 +75,7 @@ void HardwarePanel::setupPotType(int index, QLabel * label, AutoLineEdit * name, { Board::Type board = firmware->getBoard(); - if (IS_STM32(board) && index < getBoardCapability(board, Board::Pots)) { + if (IS_STM32(board) && index < getBoardCapability(board, Board::AvailablePots)) { label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+index).toString()); type->addItem(tr("None"), Board::POT_NONE); type->addItem(tr("Pot with detent"), Board::POT_WITH_DETENT); diff --git a/companion/src/images/simulator/JumperTLITE/bottom.png b/companion/src/images/simulator/JumperTLITE/bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..d7e3f42fc8a247ef4f04200d0ddb2e81cff0eba9 GIT binary patch literal 27316 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperTLITE/center_LCD.png b/companion/src/images/simulator/JumperTLITE/center_LCD.png new file mode 100644 index 0000000000000000000000000000000000000000..f3249de1682f2581d5aa8f11fe6c948b8de2ba4c GIT binary patch literal 472 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58aUX1EZzkxv|GpM$51V@SoVw^s}W84NfWHU|CQbxYJeq1jr9)4pcUpVEe@ v>sS&P8WgP)$&)M; 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 N002ovPDHLkV1id*?8N{8 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperTLITE/right.png b/companion/src/images/simulator/JumperTLITE/right.png new file mode 100644 index 0000000000000000000000000000000000000000..6c455081f38e16955c7388b115e3fe12effd22c5 GIT binary patch literal 29322 zcmV)XK&`)tP)2Zx^HI?a6k|N6=iasjWN;Zg9hU_{hWaK8=hCOl1`szcXidObxiv)tYa-d`lCO(@B2P-{KtR%$2gr%qLUawRSrb54Gfd-FW`O}=CNdG~YX;GF!1Yh;YR zm+#NL`HufN_>B4Dx7%6U;p)A_iJ)q{?3g}xO|@9@SrAEPqOdBSos}@cK1 z&lof5FiHM~fG}SE&);SUp1BFg9GO`;-^AzKT$kry9Gu6s@_)Y1Gx^^9CZ9oYt`UKQ zanBwEla-6*|8TG;-VzgHaxu8y(#8O}1{^z+QU+$JfXG}2DYf))JtWPHz;(IykW}#< z#K`YChih5m@C?=z)(mDmt_^cD_FONoT#NfYR!9L~~y5pK<$s zXbA3O3FmLCTT30|#Ee)~j#7<0gH0Us##qgKeCPSfeLR*iF#*iI%?p|eCPpw3I>zAR zjHH>1&DBh}f1KZhXqH4vD-Uk_0ps-GbFL*S|1)r3+Yjc+@&xc%+eZ_X`|>*oZ5!Fr z&3GV`&zsqIkGln9X7|k)mQFLcF^ZWR8Zp2A4c9rCH(cPs8AMrPOe8Ms2hHb^h`ciR zFam?fJ!~`iJNO{i$7J|H&HX)b*h;H!t7N{jN;i|5sAddn9GfYmF2Bq3WxV+ehBgM^ z-6n%~kK6NS8{Z7mX4OoERzOhvJ0Hhfm?J@^*bRdXOSuPZK)`_cEmASF#d&37bFLYH z&po+J+<1V#O&F_V10a|SLYcXS%!sd#8OV5AQ()Fiv|JPB=74}63!X@RXJ)jvvBcX1 zc^pQV|J&gXp2T~a-PCQJs`l4bQ|)d~E!#Mt#{KiCt>Bs6d`*t1m4CTC2{5FiVc+e~5= zYT`BlN7?#a?xWA$%gyue8LK6+g=_}ucs65*e$HKjl*%?BnMo>x<{Crx)8Nafn&666oDntmHX>f+w(-C1n^iRD0Yp8y z21g!WtL$bD6UQKx@j?flv6;?xd`x_E=A_R(g!N~B=4TE6qmhuh znT<7p{7l9xqkcZ+`Rt7lQu%-~avb`qMxC)A29= z@-O2xuX)XQg~5Ux&tkiwpED$t8b4RkHLhsrt-Z3 zbxZlgEoEk8GrzR~u?nz`3FT+*SU7@-*V5o`NE;o`(#Saz4E+D|KmYUbE5Gt9M_>a^ zw*(s#OY5~4tGt1toqGe9yb8ilkPL30???G^v!<(PB_4}5Q2Aike53kPGs-);%932AV( zRp!ffe^bWE=UAiMfzwUOeIEDYJW`M?0L&Anhi5)pojJpllY+zp#sbjZ^rko6q5n`Q z(dcw$BYg82ceW};n>3U7cXqM-kPV#LceQ#W0Ajo`19z5?rE=M$RIKmm-wxrCj{Q3T zWiy4h^UweM&*Pu|>7NcdQB3B){{$H)XF5hHBz5nIZ0HfA(iT6>NYIEfJ$^U30sM&`dE4jI7GP+IBS( zwX|D{cydt79f=w;8}>_A#q}X{2Vp$+y#h048h}{{YiSLk@;9EstSDpL^5gk!h#02G zwc-KgmEZoxZ~Vp~@$Y)qyACr8-rAJeV&2c@P`->LhYLe!qh%swwz-?KCYI7xlU8eR zH{W?+ZEyQ}L!fWJWxD!U%2#zNZ;^lL`ZNGrn*q#}560=j0Zc&VDAE~1<~^@}{nvjz zULg9`ICwiDUI?3WLqN_)wc~EaMh7#6P9ONd2gVnC!517ckI>96rfKw4_A2j@Fm*>$$%`1N+wWNt%lgC_zkoo*C{^Bo=Ykc{afBEtG```cm@ikxb zHRBC$c*7ChT$ORVn?;35)E+;~22mkq-t#}dv3=+F2o^E)hcyV3EywreAGtI zOI2N1!cLVJHo7#|w?^sYOHZiNqmFKc03l`wX@X`#L!iu!)jrIE>V~MeKY1~RO=-~< z1Zuu07R>*=GH1EY-~7$r+{c`;#Vg8h|LU*)>X=_LfURlIxjK5kcnip z>Dp(1ZixZ$`7l>?{1^f_blED$_Z*dN$^&+wX0Jd6k`l9&BgAXum0aJx7 zMPfPM1l=o&$Nec^LZEm-)Xu8;Icz?iDmD=9dN+7Cd65)Ol1BKA^-Mo|Mqyrw&j0=B@RajL&Thu_egt) z3$Y!ewnu=bfCwh4CAW(iny9who{P*=#&nlbMbi>E$^T)%p`Z_fbVgk>p&kIlvjgwS z3odB;=3`fI{*g3~iYvE7o2ceeGmY=Tcp||e&L98rA0IAAhz!GVeSSyeFOx(h3Ln?! zjSBua9&LhJT4&*5GiJ}(qhq;6>R3gyDy5qH$K@iTxhrjn3AwYsL5v-EI0whxleJYr zd~>;78Z(BqgQe2a;iWK664-U(e4|WM;a~stUyon^^fl1qaC+W9_`wezM0V!wsW|XW643@TyUMeAOk3rVA00EZ_HvHpmUaXu zfXI2~sBP>s~soLgF2TNM=I_GMF8uB+Ic_RDXONCSckwfLP3H1N; zPyh5m+}P+ybOH&)fO%GYR0xY(!)KNj4#wVI#ec_PLUVxB^Gs3OXxjBb-TT@9tW}I= zTMCC5)y`CNujI2WopE&&-3+xW%C=W+wV5OYifaC)U;3rPWl9<^{$@gj8B^i}_|Z{u zWaOHt)<(}GssCLEYCA9c)H&iBf8OVP-hDm@bUD^=Ji8^O%YH0jxLIwJHf5Zg2HdJh zuGQt31YwMbeg9d!c*v&4FaEV(`?aIajm!=xI}?%j5IDjAeBYDsauM)<=V2h`>K&dT zOr7W$0<~3r0k3nbr*=J@N8PJ*#xd``qG7!pcTxPdHHD>mNKFs9Z%tqjcHV=mQ}V?B zvB$|8F~;O;6YwXX(;)0q{m3(r@Gwx!^X1uKW(Qr2w+>s*q&t>8L%fUbTVve~C<17WwU7rvWN4(dBgW!tz^NU$FwpM|KAXvP{K7Bxo@ zBa-YDoBx{*o}VZk&knNA^RcSNX0ox)p_y%O*DB20RYKG1E;qI$BgL$R?t|Yt%CxFH zXRi#F0i4s1e%M?m3k?FiqP|EnK=imBaj`57|C7hdb+b6%K#W94j%cd@F=xR{U`zFC z&~#Pf7fbT0nl1+ge9@&UAZOuA$CjO5q~_Vux~lkZNZl_A&xU{>{_uzI59*ripz45> z|0z}d|LVxt$%rv8)Vhbr0hyF(J5y#sIp)WX^{zUnRi&>c$gYe`=W&~`qx|1Rs~mbw z)u@6)YZUI~>tsAZ+W}p>a>0y0lAPbQ$@$-OAZ+oUF^2|Y(O6;VF4NwXT)O8bkm5~! zFt%EoY}<`Qa)S=}JqolKB@^KxJPC((bj%Vwvpd)>)Vo(}M3oQu`kyMt^Qv!%MqJ*N zF9=!!C8K)A^xWU0f0jtpuQOGA=fp(Yf>{uM6uKVbP~3!E5C72ZFgnWS#E2P+Xwgnuo- z16sn;yw}iCxgiFhIZ1t3d}!3VwLojbjg=a1PU94$ zi9fW4$tc-3v9y`Uwig}k0;P8%WvK*;Yo)1v>4gIAN1Dn0w;V`d@JH&vphKQJQN27* z%+gnWA#~nn!6pZ@&J>eTOt4i!yHkrjv7W?Dr7j&BZ7DY)Pb&6bYXRO+(P5hj zk71u=+FHx>Bz&qJ%tu~{1!N^_f0ESZ?WI;6 zbX0|jY9f2$xk0Z`K(cL@n;;zk>Az=zl2r@Sb`>$s{KzKwrvkD2Sr>&ZqplMhh(wck zNhS^=M_qsOH-Gbym`SMDc*t{xJx4Ik*GY$3PcFd++rLrv*F^2!TSkAj)3a+`t83BF zg0V%FBrdvtxrF7p`qS`=)RDrbC)@u4~C%8({j~32ocR zVK>Wzni1QQ<5cHf(KefawN@ZJ3phb|CsjrTWo9nevyT66Vs`)o4H-fuHlIpVKvkGJ zF@O*_HaVy|M9t@4`?X(tEUE*<5*=(jWbcFf-?LYQIpKmHR9>+eT{YqW2JROt`&+7Q zvpo>*+f|_B0W)or-r=8WN1H(V`d(j^|MNUi&xz!bE6+T!%qR2u^<9omNdk>o% zB+fa@gIxbfOLhA{P&skVM8%2NbGxJ@=~O@wVyq&9Sd;+`cQm?%&#uwY%2m zJ&(lx{~^^g;u5i=o;)uqMKW)x_e#t<$QkvU??cQmLO|HJe(Sf6@A;nZIsQ%(hk3iG z;j0TM7iA-vn|r@*dStGuH$ozT_g<5=s%?T{{3u)0#PMWoT8YuFT%&4!(^Zx|(J#k$ zZU*^8j~Q?!bjfnr5Skc509Dj-q}VZi76(w|_a$HQCF484^E(ehXO0r?B?Q_;oq0fZ zH_s4_G#3b+mBp&u9JlaCh`yf8|7x6s>{Mbt3`b|tg<&1 z{b3iqcr) z%ONf?3eYtbBg}soA-VG`g$=6y;xGQg? zjg%j`SVQW#Cu3LL_)>LEo=Qlau$qcXdSgRgys|6E_Ds?##C*32+MiZYRES1Gbg;!a94M3 zRlDtq6-RNbdRoHm3HYS+<_L*Z9vbj0ZjulQUFH?ZWo)^Au5HQe=e}0^kzM%%j;*#-I#tWeyN#Y@J-Vz1TYIj)+Kyh? zAMSx%w0%Ppo=QeX>Kt=lCOy0|$(gW}`SQC+RHP^g0TT20_0Lkts~WJn6%?%}#;vtP zit&g$kK3BPW~2UNvR2Kbt3Y!+wO#dv40(qwDS24?>L++1X|)`^L| zYRDEXv>R2>&o!_~tJVu`TFt@H(XzElR?XpXc?ccV_uIey+xK%rj1VHB#1su?lFw>{ zHkKIp?Mg_LqL?m)>8;=3=)hP28uUYMKsC^;{Yp zj|wH5A=eo?^qi{ut&drIs5TRPMvONTo5_sbOCW*! zM@?o@Bk7ss5UPt7ac@E_)N=k_Z_9mXV~zRBJ>A-Z=ZGeH{p(+Uj3cSTH@@+W2UECF z9DUD6X~~Wibhwwf_I4DGUb=1oY_i;{Q2Mx|Zbz9q+G6*H!CAdp`k8DO6&jq`|Mk1$ z;C(MkqBlSx0sqxs{nf{sD{NkDVpL`bodtFwVkV!p;6}{^Cqi7*GWlKXr#l-!+?*SA zj9N~i9-a$bhqfw&Z02rBHt}6uYxfa>X#8Zecd6dwhbEofz+kBTG^ZnnY}umMn^9$|#wfJCbDp5LJw@pnhrMQg3bIUF{1CUDzV$0JM^=9~HKD%l3I_l9y- zpN(=Y^)r>~AVxdz*Xl`jt)5}64H|T8S}WCKwHuTss6Y6FKRABmM}Fi;+hf;fW=At<6rqv1j?w#*^=V6O~{KtR%K;lR-Q7Z#S zY$?8OY<-9t0*7g0gMF_6u|>T}ZErmnqk=vib4FX7)dRI{bc5NMiR&@6=6Ff0+eFTp-R3F=BH=;DD*9=18vh)F9(z?bAz#+ePx+@xS}0;N1WWA7%DT;Om37*=CwH z1*0(2c{aF3fsf`am+`{NT4$KI1NN^U`y(C(kb{_JH7k299-A#%8;sbtEOEY z9U4D4y$3Ok12$#&taU6DS9MQ>NdRvlY|3}jg(2p^QAPaU^xh7!zvWxL<)f+LkYTi|&)+n_7Z)<_AwoI@Dqj4_U ztIJF^q#CWR*J^*+<7Dm_6Cu+0;c@d4tbw>(1IC4rl=FruLhN`9DH{qPbd8?xy;X|0 zzjGfGj@Sie4PZ?EtQM5zBicL)!S@Q(xiR$W|9VxHWR1nJZy&A zly)=XSJ5R4xn@;wCMb200;C{@;~{&)+m`%{w~;=DS=cVbsLMvkds%?|%3A z{_p?(BQRskz|k79P2jn9!5dVV*r;z>RoO`h|ClJ!P&m%^cbPc;jgrR$o7~biCM!1j z9{!D`eyMC4=g$_`TRMmxZ`wI`y(Kw#&PZhl6XNDQ#A_|+PW$KR!&1D0`0x4MkVdqbVqPzk-TO_~a|bbo><>#i?0-6L-D-}TS~@LV%PoF7(WrsVc)@;qUW zAb-1LCfKgR_Hm0ZuMg6dMwiwOqY&q{8s{)!tZ8bO&KiV{xsfPTa$b?n21@k!&Udkq zCM@^C6G$9BuiTURv8e6*HcXUwdbCBF@We$7lhMP(D0_^T`rOdT=4xT zX2_bOBQy=TPPG+-Z@$$^PB+PEiEYMXOxIeb^GLM~1#mo3Fzc#(`MCANW)UfWTCpM{U*<2jnN(y$Jy%kR7HFy zy47=YRjXgC5c}t&Tr#Vk23M{#x9fIMm*FATuGP00Vo06Cl-S%zO^|S;H)@j(14HOF zQWoNp?j!+1th|ZqDo{^+15u|Ja~_gBgh}=a!mLVdUTZ^FFMn>Y=e`hr7Vb3R=zNnG zSs@1y#zbv5pt^_SZV!(}IBPReGnM^7#)LU>Mr_pr0Rze*pcs`&s1utTK$REuTOJEd z+UeGhYDUVCz7R8nMRHl{>L_8mKW0Lray+l=w~2{&OoFkt%Xq3??0uiJ2V_^PU8PfP zrLa|LQG=Sc>AtQRq8W?xya9ISwjuVeobM>Jfi{Y2B&--)8W4FO8=Q{%2sifdC7LQ)Wjjiu8)sCm03 zLIS8FxnUlN>v2C1aed@Ds9S@fxh7qQ@(x29b;F#F&N1(^DCT*oK*pl9mnU#nXc8ND zRk8o_ATVKK+D=mjGy^F1$(u126*E}1+t&<1@^?~|$xFw+njjSHv4jSR?Oks`%K)Yj z*S_hlka1&!=RI6sX0V!j#on*lw#ngE=Bu0IFDec+HaL5ri;DVYe(t8!StDx)RE&B% z9?tc!m;HHzr&aN0qCTFOcCkX7gd(%mLK?NhxFi_z-g{|y&&XT@d)sPx6iD*%2n;?t ztWnT-c0Z|E-)d328O#Jf)mn5j-$rBhveOK@001BWNklZ`6qtjCl11fV3`CqO+kY39(pOO_vADK+N*e8Y5jP_-j&CP z(5PJS2Muezd+a_50ej9(Sl@pkAMJ3SA8t|X+RU&{5*kLuGZk*2*2{CvB(CwYtFToC z2lqpggQ!#69e+3k%mf8p$1eu(qS*X;6>#<4NPo0baB7+diX}P{FLLX6#E<{CEybi;h`X$evQ& z?bptT$BkxnV!NU>M(+L423V+enyI@&!_m36m$*E6|E=Hptz)YN`eY`;MHl|O;*X|; zmj*9e<(^;e@p6Gz6PsuAl3JuQ?;$cBiS{Z3-X_JBri)elO9fL^7s6gG4ZxgYoLedPh zR@sophaaC*UH}(C9MXh2CzF-R4H5*_Fwc_YmxHih&SA3$`e&Y5hD#5V;0KSjX@P+D zSnW+1cbq+;RwLV_2+qL*zq?z}UM;3Z#m$l0t6EMjwW(;U#+hl!`9ar7&ckRdO9fR& zU1tIv#cM)+viA7ps&NhiC14?3LDpcq#?383Wn1WxsBA82+T+Oue@tIYDiRdz)PG6dcNGH*y24W2iyu5Rr)NJ*&eQYWc%>9y} zV8ObxN)+7f&l!Jyma5*LGg$Jx=V0DwXVLbVL@lOSR*&u;y4Fxh^x6 zv{gR3rE+$(nqfiPC=~87I74PaId_}u#5TjZuI`|U!)oq%ZSKdj-OqE4XU#>PjodcO zPRUfBjeV@J-w?TL&3azjd$Rj@0NvSQuc4}ir9zQIK@z(^6cNs1+j~X4tbY1%AO)~t z^~G4}RKQpX{O3EnQ7@NJ|1XZ&efHZjq7X3tJ#)L>a^;z~`1n~_LO{yrbl}SL>OzB# z93J#oo)HmRCgnL+2PUF9XjQt?#f14j4@Iuo#0C}5u5G4bysU<_YP)K6$D`wuCHC|F zzNHK=ArZXTXM`d+g7IdVZdPevyI0k*S$mq;#xx$AZS+A86T(vbDDiOB2HOsqI$*L- z;@alGWfC>>ppjmfhfPv;Ri|9F=LYw*NqLqz`DE?w^SWO{Tl8m)0gmzUxkfZkeC9k~ z-m~mCM31&|NVLsQ1G7;k&-;JfF&6`BhkQ}J^QJv3uhkHC)HeKUE#>j`QO91rFa%PX zL(LRx^#R%C%QtrTR5+N6(`_TR1a{&M;`-&qcE@>r&DVU*v3wV4o|#N*U*^FA$-FpE z&F{`lU=E1l5?GhJn8T=VGP2%P9on4Ov2|+^uQ;&TAfP~ue3Yj7R3Wm1JfQ2?1j>UDf}Q}-ZAqk`e%4y24*M92)WD9K?BvQLuh zevn6Y>*XGg(Ep66v-3O_Iu3@oRF)Fbq_!I-$oE;1@qOR-eaA8!>ebd-U1Imre)mk8 zhfHoWsbs=XRE3LfRd#B>tyTYQGTOekWVfB)6&_@4?LR(?2+w;wk97BaJYTe| zVwGf`lbkXg2e;c^nrwQKUMcT-*Sn5=GQ6AYD4gWEbGC1-T0Tb~`&kyseVqnFHCk1) zq$8ng6&3oNrAmjnKK7&0_@d3uM)}$<4sfWn(R=^>yab#P^3h>{h%e7d$z8Qxbo!;^ zVAOOZJU064zV7SBcYW7)9mLKxSXb39*;>t836*YDOYeCf6TCIB(N9sn5*OUK>y{dH z%qD%NUK=E|q*}sv6(n%4R{L#IKH1~rsb~ls>15xF93d>*{2FOP;C!F;&XM5kTNP$N zy&G(eYU+2bW;S~zEs$=5R0|2OyL-hKy%CDlt0OfA434H*Esu$MeWY6KTA8^VV>g$t zS_|gu%#Ke)wKwn?x85kMm<)lJTWK)_UT+(pAi=C#^D`TF0g4NlNM2FA)R-&cz{7{ zek?pQGh`xK5{DKhdi<>!tciy-#3I-9?B16E98dmP`)63PJkT+*6Xvj`+$t0G5m;*` zvc`d75?P8mO=}2Z?NRAbyU7%VAgLQ`-;)CZv&(BxbqL1hCb?z^oB{`0PbQ5R^c*)S z?o#gIe&EiWb?Vls)FHKdsvhY*Zw_j;Iowo8X=%1LIOu5m;c%#l!bKoB#P7m@lRD@v za>Hh-rd@#L8EdC!z%2KOR0Z^;a6bfN^>IInQ=84j0JRIgncN8cs(xsT0#QRDW!HMQ5FTMu2E!t+ zi#E12H!kI{8(3Ydw@DL{hXXhnIGDpps;rYWTBFcBB}3VTHFo;H|L<1PSwGWN!!cOAGPz1?TuBG zwF3n9VA+udO|VrtQI6Qm?5+G`KlWpXq~%<;%cq@_e|Neq%nN~YUerF#-!d1mz2Ek2 z-*#L#+97j<3b%=JkYcM=TJ|D3`qie)^9aKem+n-K)%?R%Td)w+oJ-}McJ7BU;{BNUgzfM*8*eY>U<}6a2a@imFfgd;k+WX%3zI!uP z@6jB!4ML9{jtx$*W4&#+Z(Fxj)yrO?TtAB!FpP>O&KB~w`fh^H2-aSV&8{Pgqw?D4 zRzEFWiy~W8iNu!Co4TtVhJy36G9-688glZ@T&_m7`dZ||nAxnxgP23esNfJVAdS`* zOhCSm?TxP-cWS-GtUHQ!Rb!rJG)s@Q6bHogW-F})Enr;R3lz;vn-WG=(YrNJ@1lvaDEw#vu{0WY*cLncXY=QEYqL%s;QXEk8Oc5k&bXT9~RUgRpwTr?A_BV<;M=jsaFOti?Cx7zKf z)hJ-q0FY=~GxVaEd&i&0P0QKFcciafw0`c!JeJ-tiTB|4`2B28srI%3{OB~PDl|bH z1>zvEk}#oDjkeQXZ12!%ue9JHEPZ}hhy6MY_(-a`?^X-=daahtjWyRyJ&)1?j^1rb zEYOL7=U^fHMcXmLlvd^E>QvaDHC|{yeV6o^pSo58!HeaLc#SfxA~%Ki04w>8ds^-a zVC>bgmUG-_6*Orgj!F#7q}7w+V^{Enc(oaRt&*r2IPGMD0pC9HX-`#O*KzAkr|!z! z`8#WaQDAgT{O+BaF}Uoi1=mds&O<6$H?&py4+3>R5_9N`Q4EIK#~c+K?)otkc&kbc zYlKBexTUf0*I>y4;Ads*uI98VhY{VZZrofv6V4uumnAJ~ckKw+vl!k?$Ny&o0CSsT z%jTA=`k5HSa2>1E26m(hxu&npLs)|RU_3Mp=U5Qm#4-WLM8%G>U5_Siw$7~2nPH1& z+>RbLNZM2&VBzx77{KG7oj+sRyN&Lxe_Um`C_am-+ixi}+$nQAQhTXAI+_5p=ub!B zHf9c_51rdV?-|5*!qHw19d3+ANOha@>q& zjW@H==OOosA44dsLDpB<&Rnbi71E0&c*2ZaJ>lGMtC)DJNVJRnR!gKOq#x2_q!SS7 z!hPcFj@shbl4DcS0MX3s*o3b$r1ku{bIYxTZ2B5cEz0Qm{{H)Jqj?DX$bFNxUD%M$ zOmUo4tT!ex<1ta84O~pfj!>Pnn_k=UM&gzfgC`a>Zj@~IW0cKo^*AaeXhu8L(i|h@nfmJA(SNj`Yrl0* zY6)zW>2YpVn{8q^0_L%rQGNZk_e+IN-S%QUFkLO(YfrR)uq&QxfYu!PtorS710UmN zoAS4JXgA@kC49XZ$T24)0c!>F4OE&btWsxE#xtF@`J2b$XSG?Hfz1X=_QTi8)(x$i zH25OTY9?->9HsWnkoUbQ8~Jg91NN;pxZzeCtyqOvB8=Xnmb&Pnx!asP$~0I*bl`$X z>*^U3aF<`&sy1UtRm#lPwdBFD-c{aMq;W}S!rJ^aqmI&Senxvc%}@>8&NE^el=(-t zS*|qc3tLT@>#&0f7C4(kt<|bNWn(rwR0z?2@C-RS zh-6~+eH!=J<+P?-D)G{c=ej%#rM1#lrP`cx%-Kb|lamn|3enlG<;PcT!En*``Q8N0 z((QpYp{;_JiMuch6JE6u+9(w@Q;j{I#v^7pOWju4s8JwtRgZ1vXHy(~nYc;La_w^2 z+JyMIu9L3H{DxN0Ta{(^eb+lGQ-*{GIH}BN<_b9rH3Vjc)J;o37a6QdDr{9|v#Ix) z2{Lm7N2$pMOrH1#DvtIIX@(iV^YwksET-Plj8J?Ep!d|QQkUi=KsVK>&{`-2&0 z5@0}Nq-&xtTCsstcX58Ju9+J$%Jw%#=-Mz+eog#QU}vAWHP>35Nup#^7_z~enQp5D zM+2i>-iTGd51SIduG)UMbI_|ovE-wfwCQnRj0Z+E2*%H_UHU3y@`=<`5EJE5ttuh^u z@whyet-`~7TyLvGQ#W@u0mlu-cjbuDogdyDZq>L?)<$#qe%N))#I-suG`Kr!1JYA1 zrKKQlnfR((71k{k8((XSW_C+7zdcPB6Sk1F*5N)#w)eV(Sr1&WR#a@} z$2O<-KxPQL!=jtQg}E`AFKJp1`hR3Hhcp+AdQl5&LbpgVwPnXrqqZk)4?USP6=vIEAJJ0gcsLSg z=`hFkeV2-E&9sGWA)oi8Z&jqXSCgDRW`uOXgiCULALhpW+J+dcam}^XaGBt}I>h%j zaYIOUrw3r>|4sRcbEh8nihb|5xNjHg1E4GkND%9ah#a}H{g{q zt=&1@mYCXhUDQv+HgQ#jC1tWmc;|TA);Y8}D%jA(;JiF&kZOnhOyD8a%gV!L68vaD zxK%jv5hyeh^O-}xOZon0G?yz`bz>GT+9w3t8+VD-CG0|g$+R2m`yWXXZsc(1T7hyV z1|QFX2Tw5SJPC`7Hu82fj7SZ;+A#5L_s#jck{B0t6t-Qw)^2!h`_05g&@EZZ#3l0m zhvEY!uCTO=oz*)gK(kWrY(=W1KnAx{5c%S!q(FPz$%vC7$+Wr&0IjpApLFatnC0USTDC!@iTKi{;hwGlt>)&zScBkZ}Hsw0HYwMHOV zG;W%Zxs1)t>Q4&(t;T1n*{09hcCS$LeOqBqkU88JvXLKZax>I-#0YZJ;`t7`uamV~ zw)IOTP59n~A_7FAU!zsKUo%xbQ_GDXY#z{3ef}b!*96`xK{2GUaEow_n1l3TVNWdV z@?J3kyKJ*CY@5_ULpg^QTRl3!&{Ao;V5YcM@tjv!Sk-jk!Gl{S$%9#hsXuSqx>w|E zNQK)*KJvd8WvLo8wbWY^bg8q?V}tMz8PtqjreE21eQ~^_G}%_&Cu~9+YuqK8V)#W( zX->7$U=#qcdNxOU68XKWM4O=dBs|&b(?`1^RGHfJIJ#sFVn!YEZNVEJkNbvMVjP~e z*5Q9|Y;x9i5q8@`*t{St9s4sL{yy%z-R5wA(M9ztN;4!h>IxJy=Tm*g&b6!EC^gXP z3aJ1z*?+a9_TuhQZkg|~DDG^vZ@+!15~HokW1A^?=nl0m3T#;0w07Ws@9i<$>nbdOTSPr!aErAbgNanUhdD*nL&rAThsCR^~G|km-dKmLhKbRnVsWF#hw`e ztx8!s3S^0~q;P%KG~tIsELuew6nV03I?lB)d{a_lRFgK3AH3n~a}S4I`5fSC#={2F z4jRxzG^+znLlL!OPRD)_dl^rIxswVgEqK@JlmK1s*(-~@rD{>-?Aun zL5#Amx6TI~jvlo=?uKcLf*-~X#tU1usA{`qW-<}+{};KJKG#8?p0~EudHzT&_BUOQ zXbHVnMeQ>2rIGFsdfAt;?c4FPsWS?9F2NY#uCZJ7a9A~tLU2}Vr>e$evMp_%yiR2f zsoMrW5Yj5UZR@p`lN#}xV6Ao5ipUv9_*r(jO_sL)-x?^yrEfUsn$AeA8O(^AYRPs* zB8`*Q<>@LNj~(WAeIIjrUM4{hh<(&O0_ zdTgy=m3P&O4gA)On*xKS{9QDYePyfJA(zQSjs%lvAe!ZQZ7*fkUZZpKjhBpZn1f5T zf976i;?THQ9G&_b<{B+VL*EidpdgF_XAh&gZuMc_bn2 ze-5b=OYEuEMUM)yZ0f%6a-pSsP*2ugeze`8#tjX^>=ASp&SuMNw1;Z2i9M4h#9c~T zXScL!=42eRIyY-GG|!lnE!*4hwYS?E^r0rRJj;Q)aozzwJvpP$uy3+YqDW1YQ8s=S zC~=~GtrB2s1iL!Bf*~eU`@t9W%jB8tdk?8~tLY{H2aJ))48UT`V|K||C!x}zNR|sP zmkOwCT918JBOIQ$1@BbFj#pKaqNNWH1-GEx2}^UUt+rzqHC(Y>HK%z&8!FjN#oZuv zkz01DmXu5)YA@Y+JsvZqL5){#*ahm?K{Fu?bk+JZk};89XO?1{9|fI|d=tN87LCF! z{jD8G#SeJDJZMIDZlt=)7qtu31awehmU;4EW+10E!MWO0*!Es)2i#RlRKOV4FsEA6 z<#l0vM#ZK5=FV!hJ%ykUIkuSZF4ZJ`QNRKVxgW=D?YuO`$`s1)}zD zvh)F;+4qDkFV&jE*x-1+JmT_n3bQo}^JYfaCkqi~2;2#%g8?nA4LFvGk8Mu%|NPbq z^{nRZ?{L5?ITj0qCfw6HuP)KegAJRMNlh3eZQIjI59L1>@2Du4TwA=h$|{TANr zHiaD5+DCG$ep#N>&NnwB@t~(Ftm9a#BE(E#5W{m^s|0G4+k@0%hpDI}o!BcHji~uu zOZS0e_A$1x9n!s2ho=r5I~umg)v`*4InBsR)dI9;e2UO}Eh^tD{I^%_mboT319(sf zfJ))uHs;=v#ULNfh;Tyx*HVKTyOf`fbOkA&JcKR@Yi<&L6Ocnxuhg-TYh4ye!lf4@njCgzB#%n z%&4S9m*_NFWenHJ;&|fd%D*1hvE=)I7=h>(6BMFm=Q@|tF17xd?;J^V6t7XVHRe+8 z6^A92XSk?uN}oUUn^9}9%yiK5Z0CKg`l?akH`^UJ&p>1dSU7Uk+}nt`rFxOO)CjpJ zvCE1_+4-(4nIZaIpAF06AEyG7-eL#SoX!VIzFw~5$>quM-ecb7irKZ}c^zM9RJ}c; zeTL7O5t(Z_E$i#tiJe`9001BWNkl412~nW8zz{mC)gSp7V}7-1E`q!j`%x+L&}yQMx(CU@U61H~MPMH{n;MEw5?; zX{PAO;QWqoS$%F=KxtjwfLNTF~N|4FOQE&2eYKI)J30l|zF^xfIL`~&ImZvh^{w`Y zT#-iqw(|r+BEf8a7Ad~emc20Tz2NbM^F8RXC_KDWkb|S+@|!Y%W`?b*VEVadHg^bD zG-I}#X{I{6V#1QsYL!X${*gwwn*#EvkzV9Jb2;oUW8Y0!+ew4Ke)!fJo@i`WBsT<( zJ*Ixzi3|rJa2*fEzr5J}zllgfqys2>rBGY)eXT{_j0tT9*(SN?mh|hSR{ai2S7o72 zRYL0@64>9ACF<9u7MG!xmRbnc>!@shEw#2)mNFy#+4^Qn4`sH5Gm&8X&k*dQRg0@O zHfvzEmka3hu2p;IovrRY*9KmraEKYYuQ4j?W&3TQe8U}2H!`;^^XEPJc3)O4<^mNP zc{)%r6d^mQGIWu@jAk)zK&`uee8?}JO$hgoJ&q(tit(HKxyB8o*$=g?vm{^CgfG&b z(5A=Wv2?)5+G|tp-3$cbjGoQxqty89zFod_254~0ybZMEXUZ4+A}h*yWW@r$Fj`TU}_V9U+tr^?11lCITatA*g0uk(5&8yw!9 zxptmQ7Yd$giR+@UCR%35B(_&H6c-=Til*Z+u1jr8Ydg7BqI|6``{(MMZ|Szwc~D!e zy|OCnfiJ3^;(HhH!+7}nOg^z{{9`w6*(=J2S|0^CXIgEBBSK^ofYKeu16CD1dO7i` zq6Q;mtL#l_%u8+FK1%I(Hf-E5(BVbFz4nh=T`Y3GPgSVaZdR{@vc#XXj=BfGR>AzD zoU%2I@4G4fyvUb~9gYNt*tXG}?@}hr##&jmtEym!P_nfO(z`2;-*NAgiX9(mN{sq-{HFf(@Z%`g`u~q5&_&g#pzCozFyDa zp@oQod5`2#Kx?1mL6=%jwcWyz*_ZiJJ-Gb)Mzv;~@r+>oC=_eIm)tR{B$8spL{K1M zDZW-K%C0=w6$@Xh#g-BD%mZnLMNn|v%%l!EyY-{Z(pF*BwX%whB}Sq1yIJC)lYH=& z1w&gP@xaaw`2lSs36j{(bp~ma&*y7;piDl};v&BWEUa~k8)cVhm;dduoKY>J+Ld8W zF6x%l4Bt_xU4oPs4X~p{n|6a=E5|zI z%bDvtj@)3)gL7bISKmw~iM%lr6++`18#DprXbNK+=GcKdJ~W|r1s~8(?)uaI?I5a#}D%o<@ZaEjN&2DDqSSspf33L~twX#rq zMbpd#%{-ptRWa^SVd!{5XG`Z3ZRXK7a#6gR2RE^{+x`yOh4x&{R2!9^REoQ+?8gOt zwlmneoEO4Hn&}0ShgfeZzmsaZ9O+tXjdZKjO^ego#ewi1zi|iJ!bGsiyQq-AlRqu3 zGncV((WrpZUMp?Pfbl&3;1B*_yy6wF_!yzBnz$H*Z$ERX%!m;y57x^^PLmFLrz(>% z=Ua{Wp+h{fPZSRH;=KLVq}CBE=Z87V}T@%bxX`O1&ESNSIhlSwhR+VyIK->NCq zp$;xES$SY~#<^5yBdba7!!;fL%s6tbNHBHQ4)Q=`1Dr)$4p|C}5w2Pp>2qCV;RL~$ zw4%0vT&pvoWBZQcovWRf&BmB`BkP024Re$CF!&$+(I1UJ{KG#ynD$k#de!mz!Y};7 zk5!mB?1o?6!2qQE1?@U}s;ZOrOYQt$C!2fk70O&HLT3Lpu2Vo4oqWPPOT~w6`$hq~ zU4aWrBS%Nhj4zCAPg;t*X>r0u?Wh^^S!=0B`PREatz(iPYQgfDtesfj{TF}n7vuf! zfB%t+jrn}T8{TjTSN=XE8VR-(&=LtWiqz*v-uGdzC{=SMb~C%eUDf7rsSZ5rBz9|% z6X^|+^AMC zb5o>n(?Y|Tu@8LU1LM#B?9YzxU-!D#9fVC}GV`9lKkxHCZ`_S3YR?rU-bf{r{hHUj z<{l7fO<<&M6h2g?XjSZRV>|rZpQ@199Kx0?I(PA*Yp-@ysag&03Y-iDA3)LGkLsk2 z+lDegZRZ_RFnDXT)5~^w&e?*7cAqRnd=^Q~cz*x)e}DYm@BQ8}Hz8=$biRuT4FKa6 zx}EJGcP0tqGk{!dsIi*WRIB+_ttXA(?WPcW9=Cp;r>bxX*C(7w(-(S#xDgI+))4T+ zg|k|})_O8WrmR^qK3AFSl{@d4!AzT(_|_&)hc>hOZ_e>t@qg%){QB?y?(ZJzI)sfB z=RFdPst$qkKZHM{9*rtdgE;|v7=xY{HoARgOV_DZHF`o_EMPm>=rz}gs*Z~QSn8Tfol-tiVmb$PAbM7yJ#n(tV(pKCYSHhYb22*)DD z8Rr*#!517f%6B1b2n?YCzdZN7+}9D6MGLUu?l-;ZO~=J{)pYF@_A`K*g+gaGebJ7# zCU&~qhI4Tj*w@1@4CzY<;4EA(es6!B$+rFTc?Mm_GoD}CJ#edv96SSZ@_W_MS(_LT zw!QcKfFm*ozx~_4JwEiI4;{pe(1k`JbO_5U|9eM4u6^DrsZYg827_(<>aYIlgV19V z-aFU&Djp)gV@LC?5&>JK;H+ZFY5(s#MC-tmriJQXA{Qp5(cQsaqs#j(xVE1b7f=y21r)!dsj zAsWR=h)XRvx2hY8WQ__iG{U`96BtWa11OuJi67!^LPX~cHSSuc4UzEMS7$lzS)ekyrkX6i1pP z#c`jassUXHS2O$&sJlyaoM{Fg6!#@x@+HRwaiwU?LS~Bz1MIXvy4(UT-lnKiZ&taR zmda2(5L<*~@jWqO2!V>cDxcg81tT#w9~~gFufNn1qNT#L^f%e6>Sn0s+xWvV1il+e z!)C4`ozY~!^;^F+{`}AX{O$|qJA7eOb-sgUzPEv5ALns|MU0mQJkQrCGI-4am2 zr}bp4+E}GCS+*hAbV8RD^0||}HHrfiFda;lSAM^%P`;&qOe!}p8yXvhCekX%2K9Pk-c9ehIIBi}_$L*z&?@rIclo)P#i(X3zIl$%8zIuyE9sVfKS z==${XkNfrfP(2VNl{j|XsiE2{1F!RK^#|&E$UWOD1nLaclj?@pg^sI=DR#w~opHN( zWrhMw{%li6<0q5eEwgxjq&P=Hm_gOK&gXyr=O5PraE0hdG2N9MF?-(Q3_c z+|tvyz;2dJ1PnCfni1v7v%l|s?;9Wd;0F(hrC=jqD?~8f8CslEDaKeb&V|Ct}XqvUbt`gmu z=2|Q9(evQvHAo|qm#E)w{^oBUnk$vnPZH zj9Sf2?SinxX2{N3{WZF76brGC<`CPZKt``-EXTK7>kNfHT^#sivPmb;Nq}ak&g@CG z${M927v;yjJFe$!D8y%f9Wn-|VF8dkkW@N+~366b=%BI3%sTjM#&szCl*LU0gZ>lcc6>1&w+1nR3Lyp2A z27lOU7|CPa)eT_==8in@`Fx}m<38nDA!x1>H63*w$&RWHk&Qc^_jndF_`CZ~c1Cr% zu|W>RcJ{@9t0#X*1tKw#;;8FLGLyK~0<{h-SlSn@mG%Q%t5@k(rMvx|?YtS}iMsT6 zEd5DsK6+704Qmo>gAg?DiF8GpGl$sT5IO3a%o9Nn*Rc~3jYo*5Ly!m0jndhd2_SWN zs#P6UdrnqZx}IoHsD6MUl?{{ey;Z0qSWB&-G(qg@xV7_1CCh8IXFjQ%b{0Dz?8$sU zn3>5*`wdATW$Vvs(#(h(D-}jQ61d(9uSw?Vz|Vy zy_=gS9)ETStq!FIXYPyv*Q$yQYXgtZzcZ2ej^8YmwSvHAMu$E*@_C4tXMxD15kuq< zIE2n;Ey*wUFj4mh*{Yd(Kf_ht9>FJiwZ?X<%=92pk@@_r)(v5v5IF#hSi0SaU5ne2tsoZ zK|5E+UlR}pplEMQ(v>`0yOTq>!6pldr|KppKPkTrLBTHpUDSBxg&Kbz1Z`qAC^elt z|BY?@{P%KSXIVS}j)JW!e`$ZuOSJ*e;889NIw@26e5{ola)71S0ukqVDZJ zxo}~r1^asyI@(-0VPUn8pPVD;It6?oGNvt(96vb(?iAzm5O*kqyQ+$YIywpwuT`#m zsYY@+LXWct-)>FkQBfPD4n1fGCR{B-F@Pr+)NQprDwch%g1S)%6NYet;*oaojeG2$Ok}1J`B+1rhFbZR%>mh-rkJg52 zkB%Ar&Zk=UWGU{f6v=?7s2(T)D+F?zWNv!V!uMH9(G2Fv_4~D2Rt=T?I5E;W@KLCJ z6ex2llPDR?((wgKf1;`hhLB1Pfgy6vaaeOk;2~B0g0^;Mg$kRg8DO?h&V;l7KFm^x z@o0DTp6p&uvum;2<+dP>!J%4kF`Q^{@PhlC)%wvvh^tDQY`-CBN2f+57wm&?s=em7 z20V+xtWJbnv_v*i8e|>tmG45#oF8>=%uk4$H0EFao5-Q30hgd`V5 z#?~tK?`#ngb!dUp4HsNr+8K9M`w3T`wxlo0WPya8E@i#6K^an)xIl=U^AoGixKb|t z`q#hyDd5$TY^}(PMQ~_`NOB00Xj<$%3=kVlTGb3>m(bV0)(Vxp5^_CTbu@bQkIBQb zqnlM7%UfCEYI{h-(lEm8+8leW@Hb$x`p%w?dzJK_YO%mY?G{gJr{UD*uIiUd^=7$N zO?QZw@dsh&9U@1&g~%*!gUBsa28s|l?*Vg(iD%w2ztIXwTV}rElSh)7Lnl6L-`A?( zv1>NuqMertHaK8mHIC|{v8o9jwUPr_u*UaGR zQk#u)7?arj8;Xvh@gjARuA~^z24DQeUwmJpinPtLSw2{ek?SRrmgmbg2>^w_S&oy> zxu!jz2Ce&~GS>i@dAm21v%6ug#DCmW>bfZO_~f>Y5WZ^roUz-R7U9|cu3A;GDAKlQ z&2+B>q0)twl4y)kAk5Nz*9Nx=5o?Gp^EJ`=JayMmu^|XdkRBnC?2Ly>%~{mT(#ED~ zA@Xnh#%~xa@+u= zNG{h8ablb4GC(G3CczvqC`8V4MeXOapzH))0>pBEszC8Ryy_CdS!+3H;!rimAutXh zq9!QLW0k#Cu_cZ!_n>VDL7u47d?yFYTpfmN)o0gcSNim%#Q?1)S5>|Y#qC`Ydaae7 zjaoAok+S&8_|9SGsOz{nt}qc+wYJI2q^X=$RJ%=%h-LD5 z4p(;aEZtJsE@37*&y+eZ5Ox?jQXB%2q#)`wb7^=%v;eP2az2B|k>a3jGGIu(lQ?_j z)Xk7HBA=?3{!ZJT>``uuRRR}fDGlBG99A5+jFP#lZSX9z)m+;4)<=J~RDz@dmeKn~ zjb!aUF=>FJ5GL+Th@9t%`;?WYpldQwkL)`{&MW@$um0+<-hJ+jBayWbnXoTqzV43H zH1K?MaLa#hs>$14%+!=;D?5gbzG%_C zC7k0?ri%#MP$|?<0LLl~CNt=Xq(!C1E^oKg+e^EKIagl}OS{?j7cSGQ_nSZds86RA#)x%J-!E32>`lVkw7>7P!LDwO4o{9C&9uF@+>!!j-->1!;f#u9)T|MF0h$i|; z{Yv5j1Zm?=*~ab)b~s4U-GR4B>xg`Y?c0=*GAG+KSUoi-Th$r(Ijn2dM)#y3>;TO8 z%Y-B20_A&NsM*<(Yvi2FQ*3f1H%yZ2gy1m=89P7@E$B*BOZFpIX@;G4#pXRHcL8gC z566(}HR1(fo4}4>E!8vZqNTVqwA}8N8HM<>vtx^PyfKkaT3KO^@1Vg+>$1+;X&}rM zX^ZU)k-1L57q!>3@TP4tlAL?Rg^KUZ>c|8_LgZ*XiUV2u?PrHEH}#5X$!v!3>r$PQ zt@77R^GPiCqqHaic=`6k_C>$x2;sogN^_Qxd0s;Wk0DzZ-2^i*=-sb62==SLoBK z4=JZG&4qnTCQ&BGWG}VBi2dJ4{0rx&*AZI-T&uLW0oJt^*72Yn1HY=o*nyb5hd7Du zC1HUpm1m1xjzklVoW-!03U7vZzw#@;a=24b*ZFOLSpLs-$ygC?_AdKFnLTe2u`A-Y zRGzt$LoNLsQ#fd|Rk|=YqX3=Bqwm+Q?mw1BfEXo70H^C@5!x}B@#26+MdFrP2i9ob zn}%Ag&0H;hPdM~cZI#m`CeN1GH6hOYO&0>fmp&{}V1slph>E7eUl4ZOsOzoM0dA+w z6&?f)1q-kWSbJ|xxy?d zNC;!ZE=NlQy`vo|xah+hvzTE4x-aMGYxJ0pQX)!skUq9WfJ0czKX`5|{H1U=SQMNnh6&lUumT)`?)MdU$WxsISQ8vW*(l7negXmV- zW;UA$qG++pzxHdtcHBGQE%%CDrZQv*96dTZ684E4&vT7+RXGgpAEThODDE^WL5& z?xyM#XOfJJ_lojd)gz_di6-bH6JQWWK?L?Q_7o-#05i&N0Om2^x5raQG9M3Rt5y92wTT)PVO8mKjpGILmMT4Fs}{%%)n-fO<|JPD4_h$qiQwFuJu zC@aN8<~kvA02djpFi+Gr_YMPelX||h|GZS_a>!HY_=4xdTFNHMza0gD2P-v}3V>&V zf*j#Cq-0ZXPXY|5T5f9qWNEc@Fc2?x-&9OEDi7O8-OOU*7#DOBAE`^!D(|ta?D~Vs zHuEi2(nFdtJ~Nw#1-pdMUaIPL747oOLY|(ZUhCF}pSgeUc*9m{+XGxKRc{2ba4)*; z#gGl$E9|*fc4bqsoj-TsfC)LX)0Qj)I}_zPX-fsOE~+U$g!!9r%g&ebCfFMr;!`m3C(iM zivlCLW)dH%^NJ?OZ=$aA;(G&7+)`^XYcmtaM0*6ze4fAWYxd_)2@3wGk!_L}k$|W; zbIr9j%^U@wdRJPP*IM;L>;{6oZ&k;2trd& zl z?25&ux(l`R8ZEZ!UzJ6hJycHw=frD>dJSRmadD?yC_j@fm?H=~B}S~wATR=o_Bc6M zKJj* ztukqbHUV3TdmK$%e?L1~>hJ%X1L#!1TIW%%9frF7?uv=;s&hJPUq}19L*UA3w{}I0 z%niIc_+l3dLOE8>e$(^-qxm_}IhM)HR3jxuY4I2}&6ZIia_qJ5YbHS$Oxz~6@7MO) z+Rp^*&u2t#$!?W?hNDAR)=^QiJIZ}IX?p=$F0h*MqHe#VDmJn2SFLq6$6cy@b5A;l z?Q#2#=5(Z)-{*N!fKMEoShOeDpefIm`N*R8PydWhzx&Jqb(t27{H_{v-2oV>RCmF9e z&TX^OVaip@V24ak127YGlxNaDvw!PYge3b#o77EQ7$iT~R9A+L?uFxft<#zj-N6_@ zjUTW9+mLGCRK(!>TH_$ioW_%E_j1t5R}g73D|%?IYt=uYT2WUTVKU z?Uv>SP=HyF+xWyqlwQ8HDsuLSsLvw)N%iXeoBK1Ow%JjRL3$t#U~JV%cUR)Vf%Qc$ z8LyNXr5fd;JsI~ZO57FbcfI$ecDWr@t^`&Hcv{u;CcZB!wU^WE71Pf52^1yQ{EAn; zVto3ieXNQk%tL&;yB?a5%Pqq-TS~jDzK$_JmE(U9G?^i2!?M0VY-Xa6YZMQl^)mHopq}s{%E8Sh6VijeQYx- zwUYsU!5CS473obREn^7)%kwAV#cuEX-eJyFAE@n9+6E5Tv)icQ|M)PxCZ39|`sb?Gd);`(z`EDdo&6skXPmyC9M z0$G+5mx_k!kz~~WM{f;mn@}bQ0`bVl9c8ci9|rREI?`xF?!SFlvW=PYI_D-x3M!QU zyUe#a49PxC%5!J#l5O!-P1(=2!w%=Mln3?W>>AfcQrUfnh~D|ul#K#FCoVjB9)AE> zo->)P+?&QR-ap&V!(6#hO?wryW3L?is>;^}uFaTD=tXgb#vM=n4^h3`b*N>qIq~iu zoL=k(bggc=4CYDgY2|@1Sf>|l*N1k6rFN}BhdDY#2vQDXlA($;NBvrwIqZt@eNc6> zQB3qVZ4Z9NR^*K+`a+C>f-UO zsx7hGtM;NetJSel@~SlDR;vv9unIp-dWxZ%83+4mxq>6MnN<-K12hJ)jSkXgaUE)U zzwJzGqP4W<|M=HASb5pz%>-@&+KX_EpzZXS1XLbrj~Qq?J8bDi=LRVJ0O?DKZz?@I z1iCBt?19>6+*MlCT<6Fcrh?Ut7j-=1S`aaTjWBC$YtD`T8)?S&J_2Fm=KF?8Qsu8K0ZXn*e0F*id!GyyHQD$1iIw2cFslnpxJWDDkz;aA*8JFH+|O z5cf$%XeTZIIB9RJ7qq=Elk51jZ4hNh?ghG3aD`*5!a--Skmi6UgFx!Jkm!!r!_26; z5IN6~q(`oI($WnAxm_q&fwSF7zAp}6;2)h9;NL)d0f zI@EUALS;s32|p9t?$jUzWqpy|3H#HX&s}Ok*jA-QL&=R7jDb(T-s;H6YljK?UjBRL0=2tjhVMX$ zamDOeY*u~mJ5%Sw#DyW6Cw;ahz7e70-cjX4O#ySA8RSPP+LY$v# zhcMS$3#Z@>0wgXhu0W=yHa4zb_#rZ?UDrRDcjiRSpJpZcl0mk=gM!9NS=X`D{C z{8j79TxR6R*GYBTFtw%FL^pG{+CHf?y8&u5#{tHk)aGE9>iBLova3FM$j1hi1GXH$ z_WN0#1kLd$t!=*hemC_G$lEIl$By!7oqR^}VzzmhpCjW7VI$2*W{~%O>uExeB=`9F zpa1#$??Pmn&j4f*miyr8XtTSCzecF9hvKer@%+%p?&y37t3ZIEM730IDzO11U2A^`qwAJrKM>=g8iu%b2t%_}*H*Wx4#g;~rM3-r zR1EV*Q=NPs2@lh`0S~RTT6)KLI{)3$IwQ3IUsW#E=D;aXU+18w>%v+RJF|jR$DJUZ z2@~*pqhw3yu0q33LpG9jQQ=G@j9aB@#?HPILB`aB%>pELYjT7*`8{R8Y^jvz%Kt0| zw8X%0n2Vef)Stx1MZ3kXx6N8HeKVEZOgKv2Ho+|bNN?^L#&j1)3!w$`UP`p6h15yQ zY%In~@MG6KuZ|_sI6yleH+S7SI$zvr;3r8A)b0 zI)hW@q!-nPA)>}VdfQmW*(5S%?e5G7{5;1Z*DCkRGvq~`A1*R(rD<^A;LMb72)|d@ zkcV9q5wjNP`ma`5sy_je>urlg z789~@3B{3~7p{j^$Zu%H;9K z)dKXb>X!^ea7jGrTpXX9-Q!KbO@#|B?XJC=t?}}K8Xe4A!BFH7l zXP4mG{jopijLoV>u$S5i7Q)z9?k)#@_%;ugpqU%7-ij0~ZTT!}+vv760%OegQPY4h z;@TuIlF$gTBQaO?5#{%JZh|q(Br#&w?^%RyZ8Ot=JPWntdFD$sz_q6BzzG>iaKgG& zEcJy?dvUQfgYo`QLm@n?MHt3As5?};S92QF_Y2x7_N4u*x2l0=GLb-ANEPb#SI)G2d0Rq*GD zfKVV#07~x3Hbt(dZvUBUhsYr=4O#J->Hm4T?ev;^gaH7agfRD8qw?LQ9{4W4V0PrFL{ zEpuZZysJkob@oB8)i%;nK`M}kCYeBN`%%3%rX!LXDTz&AZpHR2^v}AioR@P5d9sF^ zXQNOe|L49)I=wp~EH-ghUEGkXXB%%uM?-{>GB#AUg~rJdDrsj)ptUMh4q~5b{f`f; zP?nK;ZI*4*S}DschV&~XDl)bRQ5G3{rfE+?ozB#38Ya??l#qm8<6>4 z=Zp4k7-gfa#wqJrwL*go)LzSKK-rsCVfw*rW7_UGinc4nN-UcAF>cjK4RczWxlTY_ z)b?k6_Gca6|NBS&{Z5=DLl|$aM}9unN20X1^@s6+9@Es000*PNklXpwL9pPBA4QMC=h4<%KZJn+#xnxEnB_pSM3bUO zc2{N?e?)v)f1Qp}3Z<^fjLV60`OhUHS-$=D+xq(T>(KS!!GreqbUMYGH*ez6qernU zOO#UD@5A8`$Kx?-t?mB0uC47m@4VCc-QM1|&wKB^*T#DB;zetx=i9cmkH*qRm-@eb z`?igHb91wg^V3g1wR`Wr`)(Wm?(VKV*WBvE{2E8U^;h>^zI@r*ee}^sjb8PIX6Dz$ zdZ0~xxpa@V`mX-WuXfDGoH6?7qcL?~d{ZA^uXrpv^i2Ke%b5DB=bBIbcrJeG-ou9v z+jr)tt@x+kT8HLjee`GTYNzk|NG=*rauUsYroQ60#?(FdtvR(eeb*ZJ4o{fR*TTH| zt^WG1wwg!%^&Q?oo4$)C&96R^o%*T0ugPVDKDch7OS-QQ{khZzz0fm_$=J}Z5Bz0a zq7j~mKblXnWNx)ZcIvP1E(i3)<(_%xH0FD*Yt94o%k?79!S}pgUC?{f|9<)9mz`k} z0)xML^{U;IfYjmVpMP!xeEjjpjWJ>K*3*)f#GRBwbOiJsM<(D)kgQk zbSa+35vVY~1Bv>pjTBgI5WMKtGkw=s%nJiWi^kPG&7O*^dXZ;#W{Q*h9j4_}aIkPtO#^nfrpcg!YxBeU&)J}XwCTa(tnFH_#oHF?8xzmYb z0=(cAv?Ei%ka=7mb>Y;IrRyy^=wJiQ09*zN)(_a|K8DR2^~~u3)Bp&8*T>yui6B8b zI%15Dx_A_yToY>y{g#IjXkhdR%@G_BK+(i73?1-z|NZyl@#DvPKNjQ&t#J_?MoJ$5 z45pzpD6H;j4n0@&fv477q8+8i0JJ`}RU1dzyv7-tyHEiF6c{9TV2~oIjYkN~CHmdy z@PN(}{xTm5Bi;b6?nT5mtxI49pgX7_EBMLW06xItI>B0W4`XpYIk-TN^oHjEFvh|C zYtb0A;bZ_3WSI@bjS{-~Ejsk^h*NElmoDtWb(wV#3BWt2)gwXH$i@2LT}(W>M=c&v z0kUD^xLyGQd9E1FOzT-7Sjw)!FeXkFNVDzwW!&0Z`S>1+L#x5_xb0gCe0+ z09FjI;&VF~BMYLj9KmEGK`B|A^ATmI9dn0yVR-MrLwbTN-J@kCC#`cQ04*2~4hKVX zMfb2C4~(Abw}S`ko9Pk;3Xh}}^R))jW7dsK!y=LllFTc50WR^t zLBI`~akHa=KJN#e(8F3BSUfsvG{FQdNQ|XUpuaF31;dyKpES6PL?2QNDSSq52LKcf zgHRL#;=)|O#L?J|1mq+N;2EKzhl-km+|$E0Xwfr5SA8&Kgyx&@=ut=oth|u>;fEhy0P54HPcM`^EZ?UuVCTT8HWUa^B6#JY zDu%*3;4|~+p2p$w;wRCV8zXw9-|l4eH`g27F^I>|iYN4_R5U@C8)2kknHvQ%W=t>s z`F$_+g+))S38#tE@Kh-bK%pa2isArAyZmub^pw!SI;X2d1Ysaptu}(qA0>1Q7ZegR zL3GcxXy=nqDVH)z$;1ftmC*@Y5T+xVgpDFfxURIId&^sf@zIX?Fm|fldWNUN6H^Eb z3y~GMgn3`pGzhVY1Vp>~l3I93r)O@ICpM{zbr?4y z2P&Sq2PNInnpHDV8{HGH;IZy~@WBTc1-e;X0A(TM6%FjV>{aIxg=zgnUjQWIdeNDh zOIB|8&O{CXJ#`F#lideTb!yrLAOgd1eP@P3t=f3445!3gfVKlgSQLa#1C$OJ=%DBS z6mS{SQ!Ml&rG?E^lOm}DOtsF<*g+sY=e|Gz zhQlBXT+hS|UDPBnUdF}%q)>#*>{{RjpvkufB~TOyL!m89qbR8{F+_x}@1znaq1OWN zi2ecm;2B=5rotV<%HG1glndPRT~Hr|$^|g@a-Ij5v^9pp!4tqfrxZCXhZf&P^%MfV zfi|KUK-=poUYQRoXnCp<$(wtjG}D64!9R-HvsA=u5$F=m4xKGl8-s^9sl~SJa00F? zUJG%W0T$@`nvY>pEeEAgayM9% z+>K7#fibqQ*jmwiGH5);4?p~{&+oYrUP%3phhuED8@YRZ4_#rhr!X+e>Gh%s;8bi| zCx>7NpLU{SDVK910M_Ax49op|H>LI8YLum1D0~KkY@jt2iM${?3p?|gJb$dCHjsh9 z=gyvp^AHXsnLaN}x)bn}(7hnM&DHYFb`Y4))6z&qGv{GgNWZ1C2VpxZq`ep?(OI#A z8+wNBIgdSG$CJA=V=U*HA{(}?IU@q^09R;*e*lYblRJ?5u7=xY)=S2Su^r90$LG(V z@3}98VT>qBwJIx%A!MqyT|+wuhHBMM4_~Ss4#B%PlDU9oFzdG76MBh}yOm*7rc9oX zl~hIMzE`{>}i6{#wV0(<@s5VZg5<#=4KfHBGOVp5p$@4lXV zxiKxa-RE;S5V_~{dEM~bI2@GnIBpck!HbC{6$TeO_{GPIo(_bbUer`vtF~)VtYGTV z7hV&P^XMk?0*`tP2qQ%`9!X~&&v@XG66^4^)Qga`Wvze+&)FvQ$ejqL-T&sBZz=@1 zH;r^4p;$qu*#5UGjvJ9HrZdm;t0isu_&$H6YHB3($B^1WmrJjimBAVBhDhkwPeHUGPxfBMLX}eBE;@_T6_$I6=wWq?PE7%M#sj zUc3IgO?U*er7b)zmrtGK|4wg7xxLFQ*Dl;9mUND%rj+XV{3F*&?)_8nGOf_7{U>J~t*WfYHtw4;={K_w!df zvWgOiZ3E@}vC-B3ocZbLjPsG-C?Dgyr|^1#M>Jlyh~#~_W&rKQLHH3?n_z9eyW%v# zzk?m=t#)EG8?_sqhxu+~*9M z*SEgH&LOzh$lk$ulRf3H)fTpN9^I9XkkP%hg8^4AUPa2=BON5l9yxnIm-+0TdrZ}0 zBwr7Wy-((QNi~oR9M^Qsjhts0R6YFwBxX)3U$aOy&%4k&p&^b4J0!y8|e&9*L0%Gktyd%j!fRBSZ?Udn}x;9A&=XPF6xDn0y;jn zD_mx9v6Ee{2pNzf0YRjS^^yu%u6Ws_JHLpyX6ww}b>{6Og{L#+bY;tmpS`dHYDpD( zr_TYkr2O`pXJ4#7bI+K{n7@`->*b-JJdbbH&QgPG{bpd0b=<$ZzPVm!z^RtzGKUxY z>Lf*8?OlMda~ZBj8RY9oT{E4&y+5O${y73{VDZ^!pFJys#f$oWiGZj>Vux;m|g2#8#*0WQMU_5=>w0!7hcCbU@)-pAzVv2q zWx3FOA^tnp3}(WZRfe58p0ivSW2&z>@Kj3!X4k=92hED&C)fNhd2B{zSAuD(GO3CB@wy{?|uwWTA^Tr&sgymr23nfdowpIx0N5sHuN@^Kkg+kxWjOi2Y)=TDY@ zmXXtP`1dpi@jSy09=Q(U{TdfcpW_;NI z{7^_X67nfOw-5;!W(+>0=Ojr-8(N}9b+ z_Pib@dZ-@Bi})LaF8|qf7(Akl7ndEJvJw_6URB#ZX07i1>y@Q^sng*LbbO@H+xwt%RR5PPNHv~9lrYNtBYL&XIl0^ zVFj2xi35lWW=2rHpFPVgtf>|f`_astPGsTE6eZJ_&r4*5@|-xQAWPZ`oUL^Rg0uC_ z_!S8t-D8CrbsleKDB0t=SL51<~7w{**WpH$Mg@C z9KQJCi)X$c>IYgJxvPbve#l~`$gQ7{R;v6v141_9I!PU} z7xsTIU`u<{-rt3hqXX~fyvd4o?RTeJhFkCPBP+wq+g77JD^=!6_A2E{4Z?Zk$98A) zwEp#eDm8N`{Jj=&WQXJY&D(A+=h?io^dj>mr*2s`eQh;PsV3p6bdH3WC)eQe_%pd? zAamRGHFz|XY38hqA8tFkX8_%KrS=?tjMH=ETdk6>J^afMxlKG2eQW#tX94@7wstCT zYsPSM^I#w+uNBug91n4WGPt)=fn@FdcER+2R3WzgfXn$HQS{tre>WkL*WPKA5}5&Q z zUTg5d%&Li^txa-i3Zg!>^NW;-?X1Cz_Iu)ubiWRHG$m{NSsU%UA;87%VV939{^{aC)7luW2t{jXMr68MlM~s$^Lvke22k3G z*fMC4KJTd!xxVprUC*z1yR2$wM3Ye+I*(M5ksJM%4rU$Y$nE51u{G97J!2VfLmtV8 z9YG`nyxkguW6K)a@7OE<(O{Jz`Q6czz?(BP~>X>Hc^)Qn!ocFTC5 zpp0?E0KI6WhUixQ${2= z6n`fm25#--Rv@W^PXmLs^CghwFxtlQ5dqY=2c&H~QDY5dSOAzZ4E=5td6zQQ9u25l z6sZ$}HRA5}ZRZ6>HF(l=VDRWHV}N<^VJZ7sVvn-c#>+BvdKt17k?Uz~Iw4(Ly+&0! zXgBAl0;U{@=#hgPCs7+srH|S5g}=-G&P}rgt&M1N2m4O*ww+Uw)m}PU9zJTdK>l6N z{g}M}ODGL7__qmOzlz(}KgUV`1Xx|WGLJm3g@Daqx7gNk)?W+RIss(>MHwe;b#JTN z*>f&Uda|^*TThL>GQi{UCW?w5ibR&%SW18Eur@?nqO^lU(P>d=r8%_dyP;5G!fA^& zwm2L+66(a^t(0WXW4Ff4;i6*0V|MKbuP0TlQ}1A$*5-}NQ1pc(!|SQ%y-kp(MpKvG zIN2wDsUV6lnj2csG7IxMh;6(xp_&EORJ!+%J)~S3-<#?#c<*foFYySEz71M?RMo*q z>pwK*?d$Deur!&8&$n8~04^C)JGi-x@zzuOy{(=8<{3}BBW;S`z8AK5|O;_l5|yn6W} z9zJ;-%W~ZR6{i+S;Jsqxdbh7$#haHe;>r6T#qa;~5ApGDKZ(ar9_`BC!l^iiL1m!i zHo_U51}ob+#AG3{ZNuR0-%uM?unq5tXK1LrGkj}`wWD6t-R%^Z6}*5{_HYq{835Y^ z)$a|^KoC*KxVW(~cXY(9dl=UHN2S;n=TNY8uqy>m)6typXcBe7f$@kC z06a31!rX&bEh>bwrSnOav<)8bVM^tjrwZkgjyR{BjhJ&j)qXbmy8woIY{R1tqa8Ae z5lw>Xb?~`IAX?9<(K@sI!~Fa!!8~;hj$LuYdj&|M%Vh#6SM`@A1R)|Bt61{$@Xq zaNLV;>)Oyli2B)|{v2QY@yqz+cmENOpS~9l-Z@?f?VQ-&uW+B+lwJ8Oa@>n#=K@9= z^Q<|Sowzp{bC2I(v(t5ASFFF8R7;u*5#dS}>BQ2=+)^tJoOt3Y}HP4ggxK!{f{m!ho z#}~%MsetupUi(this); + + act = new RadioUiAction(3, QList() << Qt::Key_Up << Qt::Key_PageUp, SIMU_STR_HLP_KEYS_GO_UP, SIMU_STR_HLP_ACT_UP); + addRadioWidget(ui->rightbuttons->addArea(QRect(40, 15, 70, 50), "JumperTLITE/right_top.png", act)); + + act = new RadioUiAction(2, QList() << Qt::Key_Down << Qt::Key_PageDown, SIMU_STR_HLP_KEYS_GO_DN, SIMU_STR_HLP_ACT_DN); + addRadioWidget(ui->rightbuttons->addArea(QRect(47, 65, 70, 50), "JumperTLITE/right_bottom.png", act)); + + act = new RadioUiAction(4, QList() << Qt::Key_Right << Qt::Key_Plus, SIMU_STR_HLP_KEY_RGT % "|" % SIMU_STR_HLP_KEY_PLS, SIMU_STR_HLP_ACT_PLS); + addRadioWidget(ui->bottombuttons->addArea(QRect(150, 73, 80, 30), "JumperTLITE/bottom_right.png", act)); + + act = new RadioUiAction(5, QList() << Qt::Key_Left << Qt::Key_Minus, SIMU_STR_HLP_KEY_LFT % "|" % SIMU_STR_HLP_KEY_MIN, SIMU_STR_HLP_ACT_MIN); + addRadioWidget(ui->bottombuttons->addArea(QRect(24, 73, 80, 30), "JumperTLITE/bottom_left.png", act)); + + m_mouseMidClickAction = new RadioUiAction(1, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ENT); + addRadioWidget(ui->leftbuttons->addArea(QRect(5, 15, 70, 50), "JumperTLITE/left_top.png", m_mouseMidClickAction)); + + act = new RadioUiAction(0, QList() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_EXIT); + addRadioWidget(ui->leftbuttons->addArea(QRect(10, 65, 70, 50), "JumperTLITE/left_bottom.png", act)); + + //addRadioWidget(ui->leftbuttons->addArea(QRect(10, 65, 70, 50), "JumperTLITE/left_scrnshot.png", m_screenshotAction)); + + m_backlightColors << QColor(215, 243, 255); // X7 Blue + m_backlightColors << QColor(166,247,159); + m_backlightColors << QColor(247,159,166); + m_backlightColors << QColor(255,195,151); + m_backlightColors << QColor(247,242,159); + + setLcd(ui->lcd); + + QString css = "#radioUiWidget {" + "background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1," + "stop:0 rgba(255, 255, 255, 255)," + "stop:0.757062 rgba(241, 238, 238, 255)," + "stop:1 rgba(247, 245, 245, 255));" + "}"; + + QTimer * tim = new QTimer(this); + tim->setSingleShot(true); + connect(tim, &QTimer::timeout, [this, css]() { + emit customStyleRequest(css); + }); + tim->start(100); +} + +SimulatedUIWidgetJumperTLITE::~SimulatedUIWidgetJumperTLITE() +{ + delete ui; +} diff --git a/companion/src/simulation/simulateduiwidgetJumperTLITE.ui b/companion/src/simulation/simulateduiwidgetJumperTLITE.ui new file mode 100644 index 000000000..407b3954d --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetJumperTLITE.ui @@ -0,0 +1,258 @@ + + + SimulatedUIWidgetJumperTLITE + + + + 0 + 0 + 501 + 310 + + + + + 0 + 0 + + + + + 501 + 310 + + + + + 501 + 310 + + + + Jumper T-Lite Simulator + + + background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.757062 rgba(241, 238, 238, 255), stop:1 rgba(247, 245, 245, 255)); + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 122 + 287 + + + + + 122 + 287 + + + + background:url(:/images/simulator/JumperTLITE/left.png); + + + + + + + + 0 + 0 + + + + + 800 + 155 + + + + + 800 + 155 + + + + background-color: rgb(215, 243, 255); + + + + + + + + 0 + 0 + + + + + 122 + 287 + + + + + 122 + 287 + + + + background:url(:/images/simulator/JumperTLITE/right.png) + + + + + + + + 0 + 0 + + + + + 258 + 26 + + + + + 258 + 26 + + + + background:url(:/images/simulator/JumperTLITE/top.png) + + + + + + + + 0 + 0 + + + + + 258 + 106 + + + + + 258 + 106 + + + + background:url(:/images/simulator/JumperTLITE/bottom.png) + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 12 + + + + + 0 + 12 + + + + background-color: rgb(255, 255, 255); + + + + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 10 + + + + background-color: rgb(247, 245, 245); + + + + + + + + + + + LcdWidget + QWidget +
lcdwidget.h
+ 1 +
+ + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+
+ + +
diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index cab7600d4..6692b0ebb 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -91,6 +91,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato case Board::BOARD_JUMPER_T12: radioUiWidget = new SimulatedUIWidgetJumperT12(simulator, this); break; + case Board::BOARD_JUMPER_TLITE: + radioUiWidget = new SimulatedUIWidgetJumperTLITE(simulator, this); + break; case Board::BOARD_JUMPER_T16: radioUiWidget = new SimulatedUIWidgetJumperT16(simulator, this); break; diff --git a/radio/src/gui/128x64/radio_setup.cpp b/radio/src/gui/128x64/radio_setup.cpp index e3fd06bb3..6579f1523 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -251,7 +251,7 @@ void menuRadioSetup(event_t event) putsVolts(lcdLastRightPos+FW, y, 120+g_eeGeneral.vBatMax, (menuHorizontalPosition>0 ? attr : 0)|LEFT|NO_UNIT); if (attr && s_editMode>0) { if (menuHorizontalPosition==0) - CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -50, g_eeGeneral.vBatMax+29); // min=4.0V + CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -60, g_eeGeneral.vBatMax+29); // min=3.0V else CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMax, g_eeGeneral.vBatMin-29, +40); // max=16.0V } @@ -411,7 +411,7 @@ void menuRadioSetup(event_t event) case ITEM_RADIO_SETUP_BATTERY_WARNING: lcdDrawTextAlignedLeft(y, STR_BATTERYWARNING); putsVolts(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.vBatWarn, attr|LEFT); - if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); //4-12V + if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 30, 120); //3-12V break; case ITEM_RADIO_SETUP_MEMORY_WARNING: diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index fca6e047c..9cd46a377 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -195,19 +195,29 @@ enum { ITEM_RADIO_HARDWARE_MAX }; -#if (NUM_POTS + NUM_SLIDERS) == 1 +#if (NUM_POTS + NUM_SLIDERS) == 0 + #define POT_LABEL_ROW HIDDEN_ROW + #define POTS_ROWS HIDDEN_ROW +#elif (NUM_POTS + NUM_SLIDERS) == 1 + #define POT_LABEL_ROW LABEL(Pots) #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 2 + #define POT_LABEL_ROW LABEL(Pots) #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 3 + #define POT_LABEL_ROW LABEL(Pots) #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif defined(PCBX9D) // TODO defined(STORAGE_POT3) && !defined(STORAGE_POT3) + #define POT_LABEL_ROW LABEL(Pots) #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, HIDDEN_ROW, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 4 + #define POT_LABEL_ROW LABEL(Pots) #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 5 + #define POT_LABEL_ROW LABEL(Pots) #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 #elif (NUM_POTS + NUM_SLIDERS) == 8 + #define POT_LABEL_ROW LABEL(Pots) #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, NAVIGATION_LINE_BY_LINE|1 #endif @@ -326,7 +336,7 @@ void menuRadioHardware(event_t event) 0 /* stick 2 */, 0 /* stick 3 */, 0 /* stick 4 */, - LABEL(Pots), + POT_LABEL_ROW, POTS_ROWS, LABEL(Switches), SWITCHES_ROWS, diff --git a/radio/src/gui/navigation/navigation_9x.cpp b/radio/src/gui/navigation/navigation_9x.cpp index bbc958c63..746dfdde8 100644 --- a/radio/src/gui/navigation/navigation_9x.cpp +++ b/radio/src/gui/navigation/navigation_9x.cpp @@ -82,7 +82,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla } #endif - if (event==EVT_KEY_FIRST(KEY_RIGHT) || event==EVT_KEY_REPT(KEY_RIGHT) || (s_editMode>0 && (IS_ROTARY_RIGHT(event) || event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)))) { + if ((s_editMode>0 && (IS_ROTARY_RIGHT(event) || event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)))) { do { if (IS_KEY_REPT(event) && (i_flags & INCDEC_REP10)) { newval += min(10, i_max-val); @@ -98,7 +98,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla AUDIO_KEY_ERROR(); } } - else if (event==EVT_KEY_FIRST(KEY_LEFT) || event==EVT_KEY_REPT(KEY_LEFT) || (s_editMode>0 && (IS_ROTARY_LEFT(event) || event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)))) { + else if ((s_editMode>0 && (IS_ROTARY_LEFT(event) || event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)))) { do { if (IS_KEY_REPT(event) && (i_flags & INCDEC_REP10)) { newval -= min(10, val-i_min); diff --git a/radio/src/gui/navigation/navigation_xlite.cpp b/radio/src/gui/navigation/navigation_xlite.cpp index 9cbafda8b..5484b9525 100644 --- a/radio/src/gui/navigation/navigation_xlite.cpp +++ b/radio/src/gui/navigation/navigation_xlite.cpp @@ -29,15 +29,6 @@ horzpos_t menuHorizontalPosition; int8_t checkIncDec_Ret; -#if defined(RADIO_T12) -#define DBLKEYS_PRESSED_RGT_LFT(in) ((in & (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_LEFT)) == (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_LEFT)) -// set to 0 -#define DBLKEYS_PRESSED_UP_DWN(in) ((in & (KEYS_GPIO_PIN_UP + KEYS_GPIO_PIN_DOWN)) == (KEYS_GPIO_PIN_UP + KEYS_GPIO_PIN_DOWN)) -// set to max -#define DBLKEYS_PRESSED_RGT_UP(in) ((in & (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_UP)) == (KEYS_GPIO_PIN_RIGHT + KEYS_GPIO_PIN_UP)) -// set to min -#define DBLKEYS_PRESSED_LFT_DWN(in) ((in & (KEYS_GPIO_PIN_LEFT + KEYS_GPIO_PIN_DOWN)) == (KEYS_GPIO_PIN_LEFT + KEYS_GPIO_PIN_DOWN)) -#else // invert the value #define DBLKEYS_PRESSED_RGT_LFT(in) ((in & ((1<SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); } +#elif defined(RADIO_TLITE) + ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15); #elif defined(PCBX7) ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); @@ -136,7 +139,7 @@ void adcInit() ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); #endif - ADC_MAIN->SMPR1 = (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); + ADC_MAIN->SMPR1 = (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (VBAT_ADC_SAMPTIME << 24); ADC_MAIN->SMPR2 = (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); ADC->CCR = ADC_CCR_VBATE; // Enable vbat sensor diff --git a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 55bcdeb66..3f57a743a 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -89,7 +89,7 @@ set(BOOTLOADER_SRC bin_files.cpp ) -if(PCB STREQUAL X12S OR PCB STREQUAL X10 OR PCB STREQUAL X9E OR (PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) OR (PCB STREQUAL X7 AND NOT (PCBREV STREQUAL T12)) OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES) +if(ROTARY_ENCODER) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} ../../../../../targets/common/arm/stm32/rotary_encoder_driver.cpp diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index fbf963b71..2a7f14ec5 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -20,6 +20,7 @@ set(LINKER_SCRIPT targets/horus/stm32f4_flash.ld) set(RTC_BACKUP_RAM YES) set(PPM_LIMITS_SYMETRICAL YES) set(USB_SERIAL ON CACHE BOOL "Enable USB serial (CDC)") +set(ROTARY_ENCODER YES) # for size report script set(CPU_TYPE_FULL STM32F429xI) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 479dd88c5..e63e13e14 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -275,6 +275,7 @@ #define ADC_DMA DMA2 #define ADC_DMA_Stream DMA2_Stream0 #define ADC_SAMPTIME 3 + #define VBAT_ADC_SAMPTIME ADC_SAMPTIME #define ADC_VREF_PREC2 300 #elif defined(PCBX10) #define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_DMA2) @@ -315,6 +316,7 @@ #define ADC_CHANNEL_EXT2 ADC_Channel_7 // ADC3_IN7 #define ADC_MAIN ADC3 #define ADC_SAMPTIME 3 + #define VBAT_ADC_SAMPTIME ADC_SAMPTIME #define ADC_DMA DMA2 #define ADC_DMA_SxCR_CHSEL DMA_SxCR_CHSEL_1 #define ADC_DMA_Stream DMA2_Stream0 diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index a287605df..e32f8c72c 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -128,8 +128,21 @@ elseif(PCB STREQUAL X7) set(LINKER_SCRIPT targets/taranis/stm32f2_flash.ld) set(LUA_EXPORT lua_export_t12) add_definitions(-DRADIO_T12) + add_definitions(-DRADIO_FAMILY_JUMPER_T12) add_definitions(-DEEPROM_VARIANT=0x4001) add_definitions(-DMANUFACTURER_JUMPER) + elseif(PCBREV STREQUAL TLITE) + option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) + set(FLAVOUR tlite) + set(NAVIGATION_TYPE 9x) + set(CPU_TYPE STM32F2) + set(CPU_TYPE_FULL STM32F205xE) # for size report + set(LINKER_SCRIPT targets/taranis/stm32f2_flash.ld) + set(LUA_EXPORT lua_export_tlite) + add_definitions(-DRADIO_TLITE) + add_definitions(-DRADIO_FAMILY_JUMPER_T12) + add_definitions(-DEEPROM_VARIANT=0x4003) + add_definitions(-DMANUFACTURER_JUMPER) elseif(PCBREV STREQUAL TX12) option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) set(FLAVOUR tx12) diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 6ffff9df9..ca2acc903 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -117,7 +117,7 @@ uint32_t isBootloaderStart(const uint8_t * buffer); #define INTERNAL_MODULE_OFF() GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) #endif -#if !defined(PCBX9LITE) || defined(PCBX9LITES) +#if (defined(INTERNAL_MODULE_PXX1) || defined(INTERNAL_MODULE_PXX2)) && (!defined(PCBX9LITE) || defined(PCBX9LITES)) #define HARDWARE_INTERNAL_RAS #endif @@ -432,7 +432,12 @@ enum EnumSwitchesPositions #define STORAGE_NUM_SWITCHES 6 #define DEFAULT_SWITCH_CONFIG (SWITCH_2POS << 6) + (SWITCH_2POS << 4) + (SWITCH_3POS << 2) + (SWITCH_3POS << 0); #define DEFAULT_POTS_CONFIG (POT_WITHOUT_DETENT << 2) + (POT_WITHOUT_DETENT << 0) -#elif defined(RADIO_T12) +#elif defined(RADIO_TLITE) + #define NUM_SWITCHES 4 + #define STORAGE_NUM_SWITCHES 8 + #define DEFAULT_SWITCH_CONFIG (SWITCH_2POS << 6) + (SWITCH_2POS << 4) + (SWITCH_3POS << 2) + (SWITCH_3POS << 0); + #define DEFAULT_POTS_CONFIG (0) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define NUM_SWITCHES 8 #define STORAGE_NUM_SWITCHES NUM_SWITCHES #define DEFAULT_SWITCH_CONFIG (SWITCH_2POS << 10) + (SWITCH_2POS << 8) + (SWITCH_3POS << 6) + (SWITCH_3POS << 4) + (SWITCH_3POS << 2) + (SWITCH_3POS << 0) @@ -544,6 +549,11 @@ enum Analogs { #define NUM_SLIDERS 0 #define STORAGE_NUM_POTS 1 #define STORAGE_NUM_SLIDERS 0 +#elif defined(RADIO_TLITE) + #define NUM_POTS 0 + #define NUM_SLIDERS 0 + #define STORAGE_NUM_POTS 2 + #define STORAGE_NUM_SLIDERS 0 #elif defined(PCBXLITE) || defined(PCBX7) #define NUM_POTS 2 #define NUM_SLIDERS 0 @@ -644,6 +654,11 @@ extern uint16_t adcValues[NUM_ANALOGS]; #define BATTERY_WARN 66 // 6.6V #define BATTERY_MIN 67 // 6.7V #define BATTERY_MAX 83 // 8.3V +#elif defined(RADIO_TLITE) + // 1S Li-ion / Lipo + #define BATTERY_WARN 33 // 3.3V + #define BATTERY_MIN 32 // 3.2V + #define BATTERY_MAX 42 // 4.2V #else // NI-MH 7.2V #define BATTERY_WARN 65 // 6.5V @@ -710,6 +725,10 @@ uint8_t isBacklightEnabled(); #define USB_NAME "Radiomaster TX12" #define USB_MANUFACTURER 'R', 'M', '_', 'T', 'X', ' ', ' ', ' ' /* 8 bytes */ #define USB_PRODUCT 'R', 'M', ' ', 'T', 'X', '1', '2', ' ' /* 8 Bytes */ +#elif defined(RADIO_TLITE) + #define USB_NAME "Jumper TLite" + #define USB_MANUFACTURER 'J', 'U', 'M', 'P', 'E', 'R', ' ', ' ' /* 8 bytes */ + #define USB_PRODUCT 'T', '-', 'L', 'I', 'T', 'E', ' ', ' ' /* 8 Bytes */ #else #define USB_NAME "FrSky Taranis" #define USB_MANUFACTURER 'F', 'r', 'S', 'k', 'y', ' ', ' ', ' ' /* 8 bytes */ @@ -889,8 +908,8 @@ void ledBlue(); #define IS_LCD_RESET_NEEDED() true #define LCD_CONTRAST_MIN 10 #define LCD_CONTRAST_MAX 30 -#if defined(RADIO_TX12) - #define LCD_CONTRAST_DEFAULT 21 +#if defined(RADIO_TX12) || defined(RADIO_FAMILY_JUMPER_T12) + #define LCD_CONTRAST_DEFAULT 25 #else #define LCD_CONTRAST_DEFAULT 15 #endif diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index f70e1e690..5cf8513fe 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -46,7 +46,7 @@ #define KEYS_GPIO_PIN_LEFT GPIO_Pin_12 // PE.12 #define KEYS_GPIO_REG_RIGHT GPIOE->IDR #define KEYS_GPIO_PIN_RIGHT GPIO_Pin_13 // PE.13 -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define KEYS_GPIO_REG_EXIT GPIOD->IDR #define KEYS_GPIO_PIN_EXIT GPIO_Pin_2 // PD.02 #define KEYS_GPIO_REG_ENTER GPIOE->IDR @@ -362,6 +362,13 @@ #define SWITCHES_GPIO_PIN_B_L GPIO_Pin_6 // PA.06 #define SWITCHES_GPIO_REG_B_H GPIOA->IDR #define SWITCHES_GPIO_PIN_B_H GPIO_Pin_5 // PA.05 +#elif defined(RADIO_TLITE) + #define STORAGE_SWITCH_B + #define HARDWARE_SWITCH_B + #define SWITCHES_GPIO_REG_B_L GPIOE->IDR + #define SWITCHES_GPIO_PIN_B_L GPIO_Pin_1 // PE.01 + #define SWITCHES_GPIO_REG_B_H GPIOE->IDR + #define SWITCHES_GPIO_PIN_B_H GPIO_Pin_2 // PE.02 #elif defined(PCBX7) #define STORAGE_SWITCH_B #define HARDWARE_SWITCH_B @@ -406,6 +413,11 @@ #define SWITCHES_GPIO_PIN_C_L GPIO_Pin_11 // PD.11 #define SWITCHES_GPIO_REG_C_H GPIOE->IDR #define SWITCHES_GPIO_PIN_C_H GPIO_Pin_0 // PE.00 +#elif defined(RADIO_TLITE) + #define STORAGE_SWITCH_C + #define HARDWARE_SWITCH_C + #define SWITCHES_GPIO_REG_C GPIOE->IDR + #define SWITCHES_GPIO_PIN_C GPIO_Pin_14 // PE.14 #elif defined(PCBX7) #define STORAGE_SWITCH_C #define HARDWARE_SWITCH_C @@ -453,6 +465,11 @@ #define HARDWARE_SWITCH_D #define SWITCHES_GPIO_REG_D GPIOE->IDR #define SWITCHES_GPIO_PIN_D GPIO_Pin_8 // PE.08 +#elif defined(RADIO_TLITE) + #define STORAGE_SWITCH_D + #define HARDWARE_SWITCH_D + #define SWITCHES_GPIO_REG_D GPIOD->IDR + #define SWITCHES_GPIO_PIN_D GPIO_Pin_14 // PD.14 #elif defined(PCBX7) #define STORAGE_SWITCH_D #define HARDWARE_SWITCH_D @@ -539,6 +556,9 @@ #define SWITCHES_GPIO_PIN_F_L GPIO_Pin_1 // PE.01 #define SWITCHES_GPIO_REG_F_H GPIOE->IDR #define SWITCHES_GPIO_PIN_F_H GPIO_Pin_2 // PE.02 +#elif defined(RADIO_TLITE) + // no SWF + #define STORAGE_SWITCH_F #elif defined(PCBX7) #define STORAGE_SWITCH_F #define HARDWARE_SWITCH_F @@ -587,6 +607,9 @@ #elif defined(PCBXLITE) || defined(PCBX9LITE) // no SWH #elif defined(RADIO_TX12) +#elif defined(RADIO_TLITE) + // no SWH + #define STORAGE_SWITCH_H #elif defined(PCBX7) #define STORAGE_SWITCH_H #define HARDWARE_SWITCH_H @@ -624,6 +647,11 @@ #define HARDWARE_SWITCH_J #define SWITCHES_GPIO_REG_J GPIOD->IDR #define SWITCHES_GPIO_PIN_J GPIO_Pin_14 // PD.14 +#elif defined(RADIO_TLITE) + // no SWI + #define STORAGE_SWITCH_I + // no SWJ + #define STORAGE_SWITCH_J #elif defined(PCBX7) #define STORAGE_SWITCH_I #define HARDWARE_SWITCH_I @@ -733,7 +761,7 @@ #define KEYS_GPIOB_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5) #define KEYS_GPIOC_PINS (GPIO_Pin_4 | GPIO_Pin_5) #define KEYS_GPIOE_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14) -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE) #define KEYS_GPIOA_PINS GPIO_Pin_5 #define KEYS_GPIOC_PINS (GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3) @@ -789,6 +817,11 @@ #define ADC_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) #define ADC_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) #define ADC_SAMPTIME 2 // sample time = 28 cycles +#if defined(RADIO_TLITE) + #define VBAT_ADC_SAMPTIME (ADC_SAMPTIME + 1) +#else + #define VBAT_ADC_SAMPTIME ADC_SAMPTIME +#endif #if defined(PCBX9E) #define HARDWARE_POT1 #define HARDWARE_POT2 @@ -900,6 +933,24 @@ #define ADC_CHANNEL_POT2 ADC_Channel_12 // ADC1_IN12 #define ADC_CHANNEL_BATT ADC_Channel_10 // ADC1_IN10 #define ADC_VREF_PREC2 330 +#elif defined(RADIO_TLITE) + #define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_DMA2) + #define ADC_RCC_APB1Periph 0 + #define ADC_RCC_APB2Periph RCC_APB2Periph_ADC1 + #define ADC_GPIO_PIN_STICK_RV GPIO_Pin_0 // PA.00 + #define ADC_GPIO_PIN_STICK_RH GPIO_Pin_1 // PA.01 + #define ADC_GPIO_PIN_STICK_LV GPIO_Pin_2 // PA.02 + #define ADC_GPIO_PIN_STICK_LH GPIO_Pin_3 // PA.03 + #define ADC_CHANNEL_STICK_RV ADC_Channel_0 // ADC1_IN0 + #define ADC_CHANNEL_STICK_RH ADC_Channel_1 // ADC1_IN1 + #define ADC_CHANNEL_STICK_LV ADC_Channel_2 // ADC1_IN2 + #define ADC_CHANNEL_STICK_LH ADC_Channel_3 // ADC1_IN3 + #define ADC_GPIO_PIN_BATT GPIO_Pin_0 // PC.00 + #define ADC_GPIOA_PINS (ADC_GPIO_PIN_STICK_RV | ADC_GPIO_PIN_STICK_RH | ADC_GPIO_PIN_STICK_LH | ADC_GPIO_PIN_STICK_LV) + #define ADC_GPIOB_PINS 0 + #define ADC_GPIOC_PINS ADC_GPIO_PIN_BATT + #define ADC_CHANNEL_BATT ADC_Channel_10 + #define ADC_VREF_PREC2 330 #elif defined(PCBX7) #define HARDWARE_POT1 #define HARDWARE_POT2 @@ -1152,7 +1203,7 @@ #define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream5_IRQHandler #define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF5 #define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#elif defined(RADIO_T12) || defined(RADIO_TX12) +#elif defined(RADIO_FAMILY_JUMPER_T12) || defined(RADIO_TX12) #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1) #define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_USART3 | RCC_APB1Periph_TIM2) #define INTMODULE_RCC_APB2Periph 0 @@ -1692,7 +1743,7 @@ #define KEYS_BACKLIGHT_RCC_AHB1Periph 0 // LCD driver -#if defined(RADIO_TX12) || defined(RADIO_T12) +#if defined(RADIO_TX12) || defined(RADIO_FAMILY_JUMPER_T12) #define LCD_VERTICAL_INVERT #endif #if defined(PCBX9E) @@ -1887,11 +1938,15 @@ #define AUDIO_SPEAKER_ENABLE_GPIO_PIN GPIO_Pin_14 // PD.14 #define HEADPHONE_TRAINER_SWITCH_GPIO GPIOD #define HEADPHONE_TRAINER_SWITCH_GPIO_PIN GPIO_Pin_9 // PD.09 -#elif defined(RADIO_TX12) +#elif defined(RADIO_TX12) || defined(RADIO_TLITE) #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #define AUDIO_MUTE_GPIO GPIOE #define AUDIO_MUTE_GPIO_PIN GPIO_Pin_12 // PE.12 +#if defined(RADIO_TLITE) + #define AUDIO_UNMUTE_DELAY 250 // ms +#else #define AUDIO_UNMUTE_DELAY 150 // ms +#endif #define AUDIO_MUTE_DELAY 500 // ms #else #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) @@ -2013,7 +2068,7 @@ #define BT_USART_IRQn USART3_IRQn // #define BT_DMA_Stream_RX DMA1_Stream1 // #define BT_DMA_Channel_RX DMA_Channel_4 -#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T12) || defined(RADIO_TX12) +#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_FAMILY_JUMPER_T12) || defined(RADIO_TX12) #define STORAGE_BLUETOOTH #define BT_RCC_AHB1Periph 0 #define BT_RCC_APB1Periph 0 diff --git a/radio/src/targets/taranis/keys_driver.cpp b/radio/src/targets/taranis/keys_driver.cpp index 6883740c4..a7e7d794d 100644 --- a/radio/src/targets/taranis/keys_driver.cpp +++ b/radio/src/targets/taranis/keys_driver.cpp @@ -204,6 +204,11 @@ uint32_t switchState(uint8_t index) ADD_2POS_CASE(A); ADD_3POS_CASE(B, 1); ADD_3POS_CASE(C, 2); +#elif defined(RADIO_TLITE) + ADD_3POS_CASE(A, 0); + ADD_3POS_CASE(B, 1); + ADD_2POS_CASE(C); + ADD_2POS_CASE(D); #else ADD_3POS_CASE(A, 0); ADD_3POS_CASE(B, 1); @@ -235,6 +240,8 @@ uint32_t switchState(uint8_t index) ADD_2POS_CASE(D); ADD_3POS_CASE(E, 4); ADD_3POS_CASE(F, 5); +#elif defined(RADIO_TLITE) + // Only 4 switches #elif defined(PCBX7) ADD_3POS_CASE(D, 3); ADD_2POS_CASE(F); diff --git a/radio/src/targets/taranis/lcd_driver_spi.cpp b/radio/src/targets/taranis/lcd_driver_spi.cpp index e1efa8e15..556c250fb 100644 --- a/radio/src/targets/taranis/lcd_driver_spi.cpp +++ b/radio/src/targets/taranis/lcd_driver_spi.cpp @@ -20,7 +20,7 @@ #include "opentx.h" -#if defined(RADIO_T12) || defined(RADIO_TX12) +#if defined(RADIO_FAMILY_JUMPER_T12) || defined(RADIO_TX12) #define LCD_CONTRAST_OFFSET -10 #else #define LCD_CONTRAST_OFFSET 160 diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 2f42f7edd..967f94c40 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -288,7 +288,7 @@ #if defined(PCBHORUS) #define LEN_VKEYS "\005" #define TR_VKEYS "PGUP\0""PGDN\0""ENTER""MDL\0 ""RTN\0 ""TELE\0""SYS\0 " -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Dolů\0""Nhoru""Vprvo""Vlevo" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 1e85008b0..c8d872ed0 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -294,7 +294,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ad49d6997..dc4ae41ce 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -294,7 +294,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index e89998d7f..24bf15860 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -291,7 +291,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index e18b51102..ae5d03fb8 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -312,7 +312,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index ffaeb38c6..d124583e8 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -314,7 +314,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Bas\0 ""Haut\0""Droit""Gauch" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index c1e8c0be0..4034b6664 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -315,7 +315,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index fe5abbfe3..098a54af7 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -292,7 +292,7 @@ #if defined(PCBHORUS) #define LEN_VKEYS "\005" #define TR_VKEYS "PGUP\0""PGDN\0""ENTER""MDL\0 ""RTN\0 ""TELE\0""SYS\0 " -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8212ce84d..96ae0b342 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -312,7 +312,7 @@ #elif defined(PCBXLITE) #define LEN_VKEYS "\005" #define TR_VKEYS "Shift""Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 412e3698a..8fbe1a59e 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -305,7 +305,7 @@ #if defined(PCBHORUS) #define LEN_VKEYS "\005" #define TR_VKEYS "PGUP\0""PGDN\0""ENTER""MDL\0 ""RTN\0 ""TELE\0""SYS\0 " -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 57cf99ba6..3b45f2655 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -306,7 +306,7 @@ #if defined(PCBHORUS) #define LEN_VKEYS "\005" #define TR_VKEYS "PGUP\0""PGDN\0""ENTER""MDL\0 ""RTN\0 ""TELE\0""SYS\0 " -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Down\0""Up\0 ""Right""Left\0" #elif defined(RADIO_TX12) diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index fedc875c0..2702a0d69 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -41,7 +41,7 @@ #elif defined(PCBXLITE) #define TR_POTS_VSRCRAW "\310S1\0""\310S2\0" #define TR_SW_VSRCRAW "\312SA\0""\312SB\0""\312SC\0""\312SD\0""\312SE\0""\312SF\0" -#elif defined(RADIO_T12) +#elif defined(RADIO_FAMILY_JUMPER_T12) #define TR_POTS_VSRCRAW "\310S1\0""\310S2\0" #define TR_SW_VSRCRAW "\312SA\0""\312SB\0""\312SC\0""\312SD\0""\312SG\0""\312SH\0""\312SI\0""\312SJ\0" #elif defined(RADIO_TX12) diff --git a/tools/build-jumper.py b/tools/build-jumper.py index 6e7f3d733..2df2aa46d 100755 --- a/tools/build-jumper.py +++ b/tools/build-jumper.py @@ -9,6 +9,11 @@ import tempfile boards = { + "TLITE": { + "PCB": "X7", + "PCBREV": "TLITE", + "DEFAULT_MODE": "2", + }, "T12": { "PCB": "X7", "PCBREV": "T12", @@ -53,7 +58,7 @@ def build(board, translation, srcdir): command = "cmake %s -DTRANSLATIONS=%s -DJUMPER_RELEASE=YES %s" % (cmake_options, translation, srcdir) print(command) os.system(command) - os.system("make firmware -j6") + os.system("make firmware -j16") os.chdir(cwd) index = 0 while 1: diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index 6c5642ccd..4e2ca83ea 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -139,6 +139,24 @@ if [[ " TX12 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then make -j"${CORES}" tests-radio fi +if [[ " TLITE X7 ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on TLITE + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=TLITE -DHELI=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi + +if [[ " TX12 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on TX12 + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=TX12 -DHELI=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi + if [[ " XLITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X-Lite rm -rf ./* From 5ed069a35400efe3ed76f5a74b72bd3cc9b0b720 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 14 Jan 2021 13:58:31 +0100 Subject: [PATCH 054/300] Add missing files --- .../simulator/JumperTLITE/bottom_left.png | Bin 0 -> 27753 bytes .../simulator/JumperTLITE/bottom_right.png | Bin 0 -> 27701 bytes .../simulator/JumperTLITE/left_bottom.png | Bin 0 -> 32145 bytes .../images/simulator/JumperTLITE/left_top.png | Bin 0 -> 32097 bytes .../simulator/JumperTLITE/right_bottom.png | Bin 0 -> 29857 bytes .../images/simulator/JumperTLITE/right_top.png | Bin 0 -> 29865 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 companion/src/images/simulator/JumperTLITE/bottom_left.png create mode 100644 companion/src/images/simulator/JumperTLITE/bottom_right.png create mode 100644 companion/src/images/simulator/JumperTLITE/left_bottom.png create mode 100644 companion/src/images/simulator/JumperTLITE/left_top.png create mode 100644 companion/src/images/simulator/JumperTLITE/right_bottom.png create mode 100644 companion/src/images/simulator/JumperTLITE/right_top.png diff --git a/companion/src/images/simulator/JumperTLITE/bottom_left.png b/companion/src/images/simulator/JumperTLITE/bottom_left.png new file mode 100644 index 0000000000000000000000000000000000000000..e6332207c7a1b4694362aac15df9dc3b2eb95761 GIT binary patch literal 27753 zcmV)QK(xP!P)9ZwSS^xX2%-pA{x_WM=Za{Em5Cs(!RP;IkhNysw%2mT5e(!vv5%<34k1%|rM)<;u z5l-QR^N6#kIExAnfC@cN)m3%&&dgtYSFUgF&v`meb)(+vh3q(8=j@%s%C(;1`;4o6 z<2Qfn{J;M1|0_P@r7w!pZWsG8;&6zV=lGaEN_&B)dKG;?mm8l`=HvbTzdp`;4~}Qv z_N?pApkE$IX{7N8k5R{*Nzy?fmR}J|Vv9ZU1My{FR>* zx88P348suJ{}k2#Hy&HWb>1;=ocGaO|3C2_Tg<<8{*BK2ufE$;G4oShvo((Y%DkWV zPB(hSfA)Ddn!o?dJ)h1sAI+NS+-aQRi6^ebKmN_%#yfxVr{Zt_`1j(Oci$ZsFWnUT z{XTZPU0l3)G0N9}!#B)V9(gFHDdPDrdTG4qWv__mzu=y@Fbpy2!#1)KC1%}v=HF*W zw@TPjmfz=jQ+l15{Ia~(+#pD4|Eqt_^YQyQ{_bHM?={!+J3Y6Q^&*!Lu z(yec_UTn8%tz|wlN4S3}-7<;aixma{>i8NJ%#wD1skr+SaKnU8B8xW6xAsmb^TP*v<;+ zcD?9#*19#mTj_lR2^7+)X$!Mbx~^XTz=IFQpTGOh;_v?CkK(b1ABbn&^W3;}(@pEO z7cN{_5W!pD`qo(=UVY+9Jod=Lagbe5`q+Vq6Ghd7c?+s;A7AkhCaTdQD=fMBmjH)N|`Q z>7IQT^EhF$O3YJ4r?K~a?DvP5r@8*Ee+Nxai8zeo8A-Pg{2Y@_hOV@CZ0k{CC0@-_ zujkIX=TtA4+I;jTq?n_Nt}T2MAZ1?vws4!L5rVc~o?E5<7}Hz!-Eo zON`@O$1xtpm|}{)^oz!rr-cC5`JY$j>pBadsPj><9^*JJ^DcsGf<}T#%cM2Zee2vp z21fhdG@x(Pw?%$#h@^F<08GLUw9v3`ShuWx03P56imD;~C|&d@oquFA8CtG=VX$4BuCw zGdBg*ca6TA@SdBnj;4GIctrQreYBqXVCcJL?nU!MU*~14^V-F3(7YaEe|jx0Ub-B& z-g#G)d7%lRe*JsXO$#z8Z+XjGW-*E`)D5p*eIlNC?6G>YzM2T#x(FVeCR$Gv^Juch zMoeZrOPfw_IL~LNa*+b7fL?`*5d?(XqB#QhI4cuZp)+)gAQ`7d*xhl#8j)Lb*7uwi zp=3tWI3++Takp`=z+n|)%ky+zCH~Su+n>=dmC^LC1c}CS7!P%9%XLu&)7*qx*Om1> z7Xq59&?s%*rxne#^Bd}s#?(Dc{1zCCupHapO=y&b3FgKO5*B*bD!`lOs07=XLF4vO z&l{&IL@H{s5N`8e5nj?UbpTgS1ymy`#Echjb^yd%e2e=l7VZVQU(8i{Kz7lpOp%DPUb-)UOs zcbV(CUR&w0bn7*qehdkOEs`2A#gHx z3j~W%iI}%BMI{8?ciJtJ-w1!ed@VdMtpt8NX2?IIEHh|=qm59Ov5#j2oe{*`h}!dH z?e968t7f7=>Z+zY2vx+~gnSc7jd-W&sF{6uzRHGTn5rgQgstass#-dZsHY)|(shQD z$GNS{J08*Ay4DtA&46tR#WcJ!iXFq#GT&V(>)%p(HnFHcBsindjOTB8PFcTOgkNLq zzOVDBwVlRsotLgEg>t0VM*H>o6}iPqZ{`IHX^*iq1X;j~qo2`Wmxa(#X)SA@^RhzI z?@~$?As9BBfmTVM|DNYYxN}>?g`rJ+mdIwx^(;DO9 zcppn_j`{eK^O?`)_Pk1X#hQMKm`e3BpAmX`_clRCbLjZIg~%7Jy?i!CnWf9eRhU^z zR>p7hRUsG{WE#gO%?q{PO1bO$9M|d$aZg9sc^q@Mj@<-PQ698?wGI8=AeMzdm$CKT zQ2>?VH4>u1+>S@|MU=20pZUxjt_TG5L%)vaPG-uX`j<4DJSbr=p_EDgjj=#wVAkQapv<4Na2m>5Xdgw#6fgNLC$kAjGd z%fBN(5sumKLnzPt=XuG86^{5UKZnPPtouF& zGXfWe7m~>`Q{)NBgXDSgPR43;9w&y(=Y{q2EQRwk1CpIYu)^N9m|Rdz&I~9VX(C7j zdC^8_3o|v(T@eh%aRyzCN2EXml^4ci&Jv!BN#RSlmxWp;ILYx^1nnqWTHsgfQ#ZWWQ|FU7fJACgKMOJP6$OYttELSXg@ngN;bd*WAD&0a z$$?V~@;-T>k+cl`6x5bvoV++Bk}M?nK0H55R5Dy90%8kEZM_?{YDRGbb4)OfjGVZRJTjEu5)jjd{Y=8m_nl$>1dyi* zU(9J+pNfHF=Xz!>%y-Rmp9R(y4Q{%e!hv3_v09;y{SzzL*+4TX;r=- zg`b(-{-rJRG9Zu)-KFJyF71Dhk-{>P$(QHJ&*FTT#%*fIyqJ;45=rp!*rMf(q%tDn z@7X+}Al6)FJ|{0%Awx#-Kn-n30C}P5@Z;ipVo5lWwCB9xL)e1lK9|2o5?0wZ<*_Aj zGEwF4&Xekqgk>fuYDD@1vdtGjCwvZtNk$8i1X7-llI*^GK8d9yM;+If!qeyE`QOq) zWy4`-I>_%A3BH#Ey|X6H+LOc|a1`4?#^(}}pJ$p`sB9^N{4CDX*7&C6#LOBvD->Qg zC+NS0j`Q_d;Bo!Umljk+(WX-itg1+CPtYdv&Fl@$(3Un(CO|!yj;pO>OIwjpGgGdy z;uOKi6UGVNV(7e>K4>OvEqoSS6exsWgn0Vi4$>{=*%}AmJ8QXVBLb4wYYW=?Z!-Lr zVBJDNS<}qP)ZlqjachCq+s}WzE022 zXe+Pfd31Z8G6?u=wyw{@Js^03HD6c`(*wd^jilJa|)*?Ol==bL+^`Fuy1v z%@$-jPfBLOjt!~IBl{e(6Yf0c%og+y5R?DreKL_{O`VyH^Kzp#;M`llZ+Wvb8i{j! z8QJ#TG7~xXL`5{`teLXypAkj&mE?Ck&$kdw0p;=XSyWiOjtIgB&DBg7kraWI0q;S+ z$>_6epweuTa6K~*WnMW?Xwn>>lX>N5Irl+M#UV?f86MVbzehs@k+d{JRyj1rf47+4 z60}2zk1yfxGtA;=Wy1&IpDx8-}!oyn3=TL%gNqhZkxJrVefbnx_Pm>w8 zh@S+&-?J9aPZ}veaD0YsoXw-m#QD9m5ZGeWNXRAA-7hRes+YaG%W1_b07yp)cQ)JU6Ho zncAHXpWp$dgwKQG3o|ssiQTd_IT>3M(btN={TU6F34ofDM9BM_NquB($@_QB!<;o1 zg@$1Fw2{!0Cp;7SmLNP&%5yC;ac9CIL1o`SBztk|z4CpsCi3SNcoc$p95ehSPRhxd zb)+@YQ@$S$j-Q%!PR{N~h$ag!e$V%0Sb1g+e-c_ogdQ)makEe)0@@P%nIH0eY~{M} z9KKVY-^_PJYXM))DhOWln%Be~cid55NEt@@LJKGcymsx{N~Bk>UR_^W-)oTij1t#T z10AwE%z%W!jDa$)%lm8zA`2G7ZN|1a5zKEVB;)6vWHYZFr5U=F1fx9;y&IoTxSs_T ziTC>T>rZ0HEf_ebtg(ErOfUiXzQqJQhk`F7w#<-ZYM*DDl4IaId9F0KqMs>w(r?7D z8S-b-lzxY;#4difC8)BN3coM=DA~XcVx19-Z&Z zh0uAIFJE5Q-AwL#1eBKfPbJ4bSbw2&hTCtyy$S-|?6%u(t4wjrEw@xA*YA3m9`MK` zkHn=*msTn0iI7mxXIk_E(?Z1JL3whiA#@MMgL6#YOdp6+_&#P>3nG25dpMl=#1l`{ zaqHa2AAfwEFcW6LM%O)by~d_7>v{`1J# z66Yy1p~r7zL&jvdh}UGFNZCj!C$kURR#; zk(}q;3^O->m)D!Q_dbj|^&FvNuB+$K&y%9mJ#N1F=6a6k#bI;{3Ed+Sil7N;bC<}~ zKIoH01$h$+&4Jur&r=KHQ79W_tbdLDMf@#pr?!EWkDg?|7dSMUzvBw^(H_$@+ zWu3QeCbXkZ=j%KUnv3r9OG>x2fUwlk=VDMVo(5`xqR(~zTW`I!GPZuSFcw7mUCeJG zqp|9~`rI#1?O=hW^L39vU4)*e^L4F0Gb)mx)Yxpc(2wEg>RNp#;pqkSGtHo8A`_31 z29s2LtruSRH;U>}Y3G0S4tl1}vtYAUdFY{s>UWxB6NHJ=Ld1lv@fY|aOlaBu)_FDq z=vOczv_KLK`5+Tnh;vQM2$~bz+q`cO5%Fw|@58K?F!qSE&oqy8rG z`#e2QziaJv=sf+cXX`MNnUSq^YshLCDp7h(vfWD|=xb@_c`TY|-H(h$1x5PvHJn!p zQ6Hu&zpLmiqchVdX;y1ZC%4BcX0}qZX+?CGqen5^+lJdqq&x`(wnwZWW{OV$S`sG|w8#@4@-+O%9jp6VFp$P92t3bb9>Q{cXXIcgOlUnJ=09zOqG^)& zd5l!~nVx68i|I{2em4D1ytl_-gjSNDYeGGZ$7r9D)|(D$AK6iu1w?irYs`KJkJIN5 zUI-H`A*?5k%wae|X7=~C=V%{`0W|>UUHf_s(n!D)Z-U~B(R1qXoC$~%V$Dv(;fb^M zW5G$F9|3=#5RT*bGok}}j)V%GF(P~kr}w?@eU*`(@r-B0XMNUZ#og){+XRFOLFfJJ zzy53e`K!PBt9ah?o)@3~>7QQD(S5Vit3w3Fu+_F*cCcA+SeRQ_nRzu9LuU#@(}7VT znbd;RaGoZP*PkYZnb2eR^C(0_5Bi;&oI)(4Gs9U67sGUYt{61Pw-uT8h#tUBX zg4OR*l6Tt<%?BQMpbCO_zx&~{?TFU+vBTC2TbzhqVR8lk> zn8f5~(F7#op`(>bjJ^;$gafJeo)6mS+4Q9^jM|wo zc^q57|Kho?^ZB~hz3%OHSTW4ULEBMOQvJJaS`l?L+cn@ZWoV{De4dhb%N{mST5}Y5 zrzypJ4Q)IVjbWy*^@91n^ssF<^o zHZ5%V4GN-jgKSiNXxrQ`!iA>>VCNS7Zg0CaI0s~1D)(;py$sW{?LH&?Wx*KDY2z{3d+hQ5{_p>OJ@;F_O`p)Y~0yj!9@pq|;&33HU#|}{|5}R`ry3|rL+RHJNY;)7*hRp*C!zneHA(L2W z)8T98F{7G!Y-$iu%}AsQfiLHH@)2sqzoyT%s@|iAYp{IKADSB*6~{rn?pusE}bKW)xd~Kx>STh ziCmDc;Bivw6?pU=N;EX4@1dYpn`Q;$yws3pa%=SWyyrdjytlpWZSk3(`I%2z94-_1u$|+~B&M8DFdiPpwp*GV5^C{Lu zq&K|b4R0UN6tW=170AUVfmu6JIQSkU_-K;W$UqD*xA2{qb%x6BcYE&aE6DXlH^9V?w*)!a2L_RdXb{5f_Js}+GkYWI( z(OF99Yb{Bq7lpn(>CA+qJxh4bapZTQNl3=e@YwLybVxg%w80R0cTNg?f%A{^=!ll- z*8-M4ib&ycC*_&$C>RPtrF^30c^)7M z(M~AGjGg3jp8b+vjNWkjAPmhcWGqzZ7bpm=1Z>kD)r&N5?EA>l+4o}B8_~s_nsIap zA(}b<;xGOpUi;eD)}JkNAPWJ#=b!!ApT&><=#N%^!Y6*>C&sg$^{grYY}(lFmjhSl znBbhOW7tifMt4pKiR%||pweFNkd0>zX#~WhOLY!*G?kXLm*aQ3qd4_-l>eEH*8OK69?Jh*Uzr(m$H!CnV}xN@g@C;@Gkd zH2~Sex>g=$LrpP*7oRZSTG^1xp(4MW$4D=;?i;N>!l6AvUVmrk%0vqiO$U#?g`>__ z*iR7t3%=kBR)=X8bhMXsj-moT{^LJh@1wAy?N-f^d<68 z$Q(g4LSkEbq63E4Ev&2%nQ>}`NyXh+GSFu91dqsaavnggAqa%3JV~}`?MHCP&Od~q z3@`SnB-G3&lfv486Y2$KmYubfUv@}YYmuCXTRsQhC&N`scBN#$;uWu`&^{j~Ry);1 z2nrGY2_=HcWc?n8Tk`F;S3HJDUJs8R~VSB9B z%+6;w!BF7ScI{Ilj2X__#PHZrBxgA~!_ESO+KT=NLQ)g7#;EU@&K!W|9*-T;6B47m z&e`_XIYt5jhu?sdUtQ~b4M$<3Atr`V&G-}&6LJD9oVdXW8P)4U-wI)QK|YJ=k$)2% z735FEhJ%qN81HA=rs6FrAW(VOFv3MeGdk;tkgbjMnTT;j3M^lIK*pPys80nBk>Ixp zh0dogg=;L7oEb=o>X&47qJQ_q(!(9`luG4&K z-hSq1ex^Q82tv+9MF~VWuYUEbt1x-)bDvuYHyLUUs5d~l%WbFumzsu;% zyh8sA?@3C4cmkz(zcIJ9nF}VoG1jD>eF?aa8kh}R(uYs&&}YQc^?v|qL) zKQ+83#!y(_%UsY47<~lH8QW3O99z~>Mkh>9&BSTsp*eA}jgg4HlZ~wG!Hfn-9G!j?4!})+yS2#1i`S`001BWNkl3D{rL zr`mwbd>tzN=tvHD&*>PpnKvU{n^}Tc%Tt>&^?6FkMs(QCXplnBd?2@<<}L;AmdVBY z(UHix%h3+|qe@C;?hpyF|BOk0noCDE1~W1fMa=w^^7ILqu?!o@c#J9>QcG(>I6Ey2 zvG~FhnlN~QNwm}`))=-`t>Nim_2)j{Nh+Q!mYVW@es2pl2_KmTV&WVrpf=Kd#Q3uP zg&yQw)bZvwzd2s{%2(F@4>vkI7xtwbkfKe(9X9qDaF|PD7o%#SO~@A56v|AY=E3*w zQ2Rh8bGVc6m*zmEe1;hbKxXP5snB%hO$hCF2=&?V%y3}1A!gqI8JCHBL^ed!)bd73TX_mzJ3Ae{9*hol zhIY;l^hF{hYn3Tgn0et$1iqmltQm^-5GDIQ^+@F8k158_h4PQfcIBnErsg6ScxCBG zBVpKi6(D$OTtg;TW}46q%Y_K&U?PEQ&UF3n{oe0Y!67?eu>PCA>6_wpuY27Mg+OLD zA}_xS(~1XFy3sd44L6`T$$1>(IgMisw{uj1+LU%I$3nsyk&bROW2U7bbD6fsXxcC_ zTM?0<=`f^;c|eZ`3nGu5{wSM91^F`B-tJM3KW%JT7D!a;koe|x<9nKcWx`SWQ>)t? z=U5qz51^<(PQ)I~YFNE%C#SDBuevdbOR9UOzgtS|&|D!mLNK#A&YUHEhipSib81R2 zj3-SHTlcjGzeiS^r53&VbdVW-o9O6>v2+YntQqatK}LJEOQ9zdnIjKAhSu2Qo_p@8 z%uFparJ4yLfj|HAKd(haN*Mb3umAdL5^OR4j0!jlszWM+&6=AgTBHaM?MypXE$of{ zJQwz$7bH(*2eGvsiJaP+z6K`j5H?fzXShG6ZbyXtW{^lV7PPrNPgW3S{sTHv(=6hA z`(8OBwK~w+y^IF@EHj>ww8MlHzD9EPzfk~gDJ~-135i}5Vg=zzn{J-JUF))CA>)vL zz-%JVFzjCi11G{7fzG!Xg(EqgcT5QyIj?y_k_RWi%*vjKgkjX$Gm2|W(18lUY^&1` zkxhm^)iju(sTt^1rroUX#MtT~uw@p|U-H|({oC;`|MD+2R45^!82a?$QuDdw+wVeWmva|PI1>t7ye8CEUSkVX*Q3NnO@M`!HL~Z$30)J?cMB4L za#bRoxsJhG<1?A4!1HZqG9A8wzL|YWou&hCLZo6y9gMv0MkXA$b7HcPn@~sL!)7KE zfijQm(6g{ZD)bP9V{fAs!fNX=a7K`@?V{A-;9Mdc`$kjzc3eLMstWU zHGrVKLx-cCPI?jDS8$be4kmKeC5))Y_884L^!82&JqA3lhO~jC8;{ExuR};GP+#w7 z8sf_FN3$T{Y}7rJGw>&W@+a|47zWN6EKPMO^qFiA*s9zS7qmohGGf>>~y4j7guGnrWDja&k1XET^{C+Jw@ zc!)mf+;h>A@71F!G16Wgg!+Lrb%rBp!cZ6% z1#G1g?BvsLbb+B$g-np`%n==Fd=2|DSZld3Na4OdbzGW}8}W<3_>0wPq#6W;|04N9 zY^%%6P==-Mdq4vaYfyc!szhd-F%)Y_PNoY)Y0MnfxRHv1=s@aFRcT~4qf~f8;!xIj z*~!K{exGM1HR7P)%8W%cVL@btU^Hiwu|pK#fDTd?Xm<8WfLO5@*1WWKrSPG!Gt_4!j>?CA7w7o+z74GmYnUZ_`8iNu zbb_Xsc1~GiX(CyWVFI$ocjze-4((q{e0uO{7tM$SP+%~dj0r-tpDAdl$!*`+7CfN) zk(o|RtE3-A0SLLsnDPt!{ont6eBIZ5UA*jNFFQjJ3LPqXpgICUer9D#04P)_!6C$< zcT*zLKmYST*JpBW{Y-W$rgo&mE<;hy1`+=}v|s ze5bi#*hvC5AuuhN+Rx)4tYkQ7L{B~!!I)^cIJAqrSX0Y2w{-P*Jth;Y(SkI%dhRI! zh_Qb!rpKYY0XYX%gWxeJ0ZGM15)jhzUPyYC?Uq)J;r z+KixPIG`-n>*S0;YW_p(dEk7J358h<5i`^_p`Dx2hr?xaN^BwQ_vfc*K0|@z_;!be zDRg+{5FJg~K#19l5++M0ehWFjtX)_Gm<@eJJJV{uQAiRhX>)kb0hey3AqN@uflhtfEv*Q zf?2)=?E%QoJt|NKOD1bXF}lKI8n88oU$!7Lu|o&hNW~M@aMEVJh79DFoRQ9K2bj}y zDG6s{}v4M<&2 z@bf?a^Hm6FoT??zTujI;qtj03$OL%KbDmR+j5Jm~*LyeEIW(jm5vOPe?^IN;m4z~s z!%S2I^6mp+)I7d9btJMf7|^=Q#J)$DYYGmUh&*VXxQyLdqR!&<9GNcknSbxmGCVuX zvN;qff;FeGH%-nd<*#J>pNNxlIkgO3Bg)-5O;%jX!sIvJV@~-S%=e&UGSfjpVpI;1 z&uHs5rZ#Y1_ko^yOASvJA&VuZh;v4Ha7kHP6N)FCsyw3UqzPh3WvKKHGt`kCEp~R8 z;10-*pOB0}xMmHf@5GP}#Sy-dAp+3aLPU(}W}MVMT2%QDR7dbNU-LCpSgF3?H-6(c zsy0>Vu(SD)X}WxWqdodl?1!=sgr-t9 zJ+ql({xJV!I-lTdr>R>u66Z!#8B*!(QbQ98KCYYpcIi3&5;WIo*3C%vtMp*ank&I} zsMV*}o-mH^a7-zTgsCJFq5(o{PcRc!%$z&qtot6;;9lS%)zuUB^r^K~mr`K&C|Ndx znlUsl@;ZOc=X}nYz2mshN+5C~*021^uT+yjC_rt>C-kTHGwCR^lWsx?iYSR5>OLh- z5d!bgTb~l2g4%fi^F{>1jKvgG`vd|GE2A(oZI^mWS~(cfa~}+Lf8Jpu9h)P89finb zfn0(vHDR4nD3YI3un|o~Di9-mGnvaFQJ2-*T=6*>jWCpMMYco#$!HEwnr0pLq%gA6 z^B8{goH3Q#bbCl?=Y0yR&M9P@ot5T?fzFWwI`Ei4{8HAy*RYufwX{9BV`~IrNY^87 z%dnLRnobNtBUj{|Vz(FD`i40<0frLPlVjW0z5^0d9)~^FVt&V3Ww-17rQOL?G}XCgL5Fgzcp*m;N!Lv|jr1{b>0bA=A|TyF_y|B&WPVbMTg(u9=sDTOGh>4w(B z!UbO}aw2jdoiZv#CRe`*SJ2YOVZkiDpfD;a!ZRa8+oioRYtKSUWqMHwhxTbh4Qe!dcnd;- z)^hZU&d6pnQ)h2)q_`@>L0zi;3M6FER;Sdb)HIRoQx7>hoO-l`vV=FYeCd~d>FV7+CiRjz>i0*v?5MprhYe!3KM_xmXlq0b}??B(uK zF^kudNn9$5qMbg&BRwGp#Q$auV zqyv*ieDAHi71505MnY+5TeD@TK5vvw6J3mG5*x@qC*A70sD1XkwRh zY7BUcNd4z}@ZC#FKhHvgh|)(z1lQYUk>1g!M-jt>e*Zlheb~z{=CBjTi3&YZV419R zKxbr0K>e%h?Ev!R&8ZU-VRN7@wTUe|Q79}N_B0H0c*_TES|)J#e)bR8ZjaS^$)>|%o+M!6yAvDdqkctwc-tdMutQy<%MVmh}2>m>c z0x43WNQX9fQB+0mr0}V_Ep6G#oWn@ncQO&vKf-G%SU5HkzTRJH-ZIWq$uS_E|z88HV6p$1WeQIVB zt=8nwHtl5;#ipD??sZLl13O~|Z0g~ckT%oz4DIx7(^LI;pFC-rK-~c)ULeJ|z%!q7 z)5}J!K+YaxA_#5Vj4Vjp{)`)gT0l8b#lkB99rfOF_~n`g-&2I9iB?^K<{=*+`N&7s z`8uG9r+y?r#E4=V)iC74f>d>5NPJZc^eyKK_9opuzOg;A|w(bl?+Q`~C zXChMTx<{j#(%FkSW7gNZH-LqSQCLQN76$cQXa`al z=x=E;A^|$<9RFvy${L9N+@PXFNW?f%`B$bI+TQ@bChj( z-k~}C?9cw}`V5VS?{DqxZb?E9@>i%Q;(vz_@S(8hH-GatS0>Z*#B>@v(Zv7MA>Ynh z-tv~3q-8q}eO^l+@Y?)KIaYnTt(M+V2V)_cm;P!L*{2o0Z`&27Jlo;orp$R=6 z3KFU8j1Ke`34}cdRtvL#pfrY^IwD-Am`-TkIj5$S2juco>M?}lhdWon(lSfI4w+Cr znvYr_Fy!LN^7ovuK*_6Ldf3>3#->9wL9|EPDlwLXo4o%d0w02$5}qP53%LNK6--u# zq7L=(en;oOJ1psAM(%{PyikS&(RqjNDU(D3Md)Kp&0y`r5o!?gf9H38=aYoMoMPo$ zKTmZ?zH9n4$Ip2NdL3|VpdbB}Q~1s%F6=o;jhs3ypJ7ck_( z$tQ%d?P1UEDZ1v)C}MH+!Qm`B$~;lo(Lp}}7abpaE$qaVATWc) zMyYZj$0a)_sg)0C-Iq1;h?;#@aFqEc}#KEPI=6%_%6v$hyDI=~HU7vj_9FK7=I1 z#5Y0%zD8@L%~Z8}k{D1Kj;@iAIiP3A9B^2Z#N4%Z@fvHS#3hc043!hBXc`)U$$9y= zfBU!fnR|3mo>RU&HRKi(XXoM8_)M3YX9>EJ!e*v9DLu`5Nnu1X9M{)#OXn~BQS=!w z0f`~$ek0wPnw_}}G>ZcbfgE~vSb0n}I|WfD-`D)W5FybG^Hb`(JM=dq9kOp#U^8}n zW%5oz`sJ9~Xt|)ndGHxJ(Uzxx;lgIf<$)V<1me?_p3FM)xUG#3sN<$Re@NR#`&kkK z8mropZE1UNO7ewY_=Qg@M6%FQ#i7T=lvDQ-5cA5u&e!UYJ*#oBsmYcQ@OX3|6)gSE z@BB{nS?K-cpO6rcIUr=g>nxNtj-UIvpR1^Wovax>@HL_p)jw$0XWcj3#}?FjmabK_ zB%om@B>&{`2fX$cnj`84GOaL=!1lNtu4)96Q4it1KO=0O&|D=ay%mJTnT;ALIVbzE zc5Uy?xg~EgO5tNGnT*QIiD(>mhkA$XH{iSkY6)mkM~h%sfcX5ulFZ>V6RTm2vz$uX zgwxP_LQ=$#wpeoF$kxEfeCNSB+DqACW1qwU8)|T|qs9N-fyJANjH$qdNj(KIa}%SO zTm+?%*w#3O1~i!x6h=){RQ1tGA44$5@i?SldnJ_ zLS6SA-|-!15XXdUM2$yf)B4dlI^V>nHD!du^$~imQmwhC1Ls%$6O1Con3`|h*Mc`v zS04Q?WTE>x;mSyp-gS*fLh%ANiS={YJxhp4j$OVl=TvJdAvGhUM#jn6c%(l@v?8IJ zQ?!itHLU)dQbJU~q&lF0ioS?!*O%ld=R4BJ!7R*3<=>`xRzR-#J!T9`ZcCvKC6;D* z!}5YmCdVO16uz+0$b?;UC`GC5_h_5NbY^DCWjj6Ra?^3b2**C{{!6KSq3z1#y%<<` zA?=p^0}~pp$fGZ`;C&VYnju=+u?`WQOLNVH(oC|mvmo%;WKw8M?r_9&MwF}O!=XeG zy&y7$0Bup-TideBBoGtoIZkmcDYxAOq~EW z(wpm0kvk4qn(913U|l0YKy6R$BG{{pz-$(Dt!tC25lv`g>P9R^k%xWD$*6y&S@nBP zG9o;T6tngr|A5Su9L}`8t+5FOC>v3ZJY_UWXt2_eW1@+`d(O#utn5NDwVjpf%OF8V zF5 z0wOZ?^|n3#j1W0ZgdsIA8Bs~3 za&#WhF5W2DOvsE@BVGU4gU>MD0X;hC#4PB;VA{6hkIp+fBRgatIun(qbj}0ty$;xP z)=J3;d6yd1_&FIR*M1N^iXS+El!$~W+!D-x6GB>&onWgnYA_27doK6r*G6KxU3zW(*EuW85ow2=+(r}K2a-c|1)q31*M^X_-Qdu@=TG3q?c zwbn)7i=l7y&K{ctx;CKIvtIL>*Q|=7N4hto3HCXder<;Av5`pj%jD-8`LPg+A-#fz zyEHqZIhA|Vy)fbDJS5Pel8`8g4qu|_9%afyt2V9v3{Q+>`+lHf@q|CVfujd*wlSc) z%pUvY4QP&Vf(hq-0aT0*#1qDGas-gF@Lp7rX%!tGe62Xkd9v-V0><|C& z4=WQ2_S25@BnoCqYl{&rh_WDXoLw4H_P3rT!gleCDjCUuo?ngYJ@0u>ok!*m5dMkr zq}9dD(xh{WzD&r7^SkSQ5&-&<4?#>NW|O^~g&EDNErJj1J}pfxKZD*=!c9!8In_Hb z+(tUELsBS0+FDtW76kfUs8Sz3DQW2xeq<5!zuN2tSW`2c{945Y*yUj9i}`TILfq=N-lZ&0YIcQ&2W44XMDk zOcWkok6}tNM-N~^vBP0w^Af*=9X2#4W+bc7#>$yi1)&%_)9j;|ktOKsPSY+p2Q)^Z zXO;`->pJWcXIota>}ZJoRsxJtQDv`kGEEd>DB0GYGiq`k5)$&>tJp=)m0*xI({(EL z5i|Orw^`0M^(&MEkM#v)Nck^xjx?$6LuV1SoYzS2_s(~|Gk)_of3xnpD5ldrbe?Q^ zLVmu}d9@RIh6JIiF2%5#Hx>41{PN1nmN#Oc2Db1p;Y)~$*~QcrsJgGTxENodP(@d4 z#(AAo<*3|^?4)n(~@dtnK2X(w!Ptl9U)*+jYkq7pLJfIsgg@A>F|CZ@z)Id#5 z#YCh!A#dS;-ix%?-EPifV7H-Ykq93|z*A}kNCZz4eL^GEF>Ss&&;dB0fd}EVwp9^F3Zu;Drbh?+$(RE^cJx}v5 zfhokcB`CEnKl`&kTf>M#Oy2F6W{Soux^*sverJ1m)8L4f@br=Sd)w#iby3o$suM!o zblL_|*{ulZe+VEGR^jf^12Q8vMh57l51@q^32$^jt_ekcS$9+=95;iTp&7$A6I0^b z7r^8VP7XtPG#z^ExvCEziI99}vU39w0+(hHs<+rHMt5 zVp0hbIj`K=I74(@qYOOR%yVn0ofpk^pa@e!K=%{@i@CQToESx>fuXdLd%pH-zxIqE z@LcL$bx#QbF@&y>5a?Qxn24XYIkl<;|zVG|K8foxmw$adz4ha;EagY7^^b8>-X?FQ&^iEyMP}JX=KZQ9pSBJLwsFj#$ z;eZ|uGtz?{g&IQ#6{G=lmOv-ixTHrtL9|?FW<1f}^8gO5Y+0X+$a?fw4>*iG!5#?Pn0INr zHNChSXnIE=5h7EC>Aaz*R8BSj%?Z#oZlk5-n1VeY1I{pN0NK1k0}?J`(xEjO6(KwR zPA3`<=eUy@e1FHt1*c_}h%v=j=hYvG0p&>4c`^+Y8+Z326ONIPvIQjs6q?gLZF<>X zp?l=o1Fw;M8Ja7h;H1wQ001BWNklBs#qD8%(F&AX70kVzIedT-5((3faY&taOo&vit= zCW`6vByEsFQ_^(B>{~}cI&`%%12R)`PT|5x2}@(@7r?PniJJC_tf|@kT(0Sq>I--- zd8VTW(YCd;>y(Y4NK<6$j5p-X5En`1NDv0p>l{Re;W2P@Lh zy*++960H^Wo$P06L=n4SvgTSy#S3awCIO&!>aw$SUvF1$!Q)~o3O~=i)?ClAxgw~p zAAK&y|E}-)uG)KmHu}>!W_Y@drZk5(tu!V*N8=Nd%gHD6M{}%qwx-m(>s$#zDj2_m z&QXqrP=&_N-kOBqrtcoL45Vq(X~E{AM4xo}1wwawY`i-mF^Yn=q}-G-b@J|0ny`hJ zoro4xtQPh-jY1r*q;s7JU~1Kmn#;-;4pms<9+10lt>va3PWT`cr0~$9N($>o3zjF& zQg)AR%nY{-`PtMmkh#^j_EXL#^lr#@uuRAia469BE;l#P-_ojbw9$J!A&*<u!*GEP>`RZLy#IvWh!hC9&}v2| ztLMPsd?)&Gt@vC`s<-h5SpMV)=@N1*oGIvBk;?=T&Sxpgns za@ZWu<}mrtc(oqZbUNRg&d6b@YjT7`=SsM$`ci1ZH0wsCLuzeKIY&6a9OUf~r(M&d*3#BC~^=RJXXRzGZ3e}aQ2Xso#5h0x?o^L4t$IU>T z6F@EO$=8iVNP=8UwqWv*w3{=&EKCaaHT1DVH)9{RZQ1O>jt?eGScr&0iDV|EPMT?z z-Nuln?G1yLF9x7-D*vFhUd>gY8NCMeH~eijiOw zD$4s5>X50UYczKnuS1O^TE9o~UC<6CQlRN);uIRtd}!=?C;Lw*(Cv)Vv$aQre%E^% z%IkNnl~R?#db06eoihOC}!-r)xWnbo7YKkn8zD-a#1}IdiK6`t+gZ#z%~(>7+>!> zVE=hK4sBY{S!JQ8zuoQGwxOOW?IR+e;bo^EMb8!yi!hCr7z&b*5P@e1X-%c~u`j`) zJ>5fSLSqvQmIF`>?+iH;ifv`Bp@b5@3uJxj*)j**kC6_o0h^qVc_;|T{LE?Su#u|} zi4=-*D>NSGb#SXNGm8X<(2drEP+My#p~AX)kJC)2`)R(M@8Wr81Ry)qJRf7)Z^3-P z9F`7s0@)PkQ+H-QgTliC!HD|=o61mV1mL{qm7(1Os&+kI(ShjM{KMuyra$}l$~)fi zj#*OE@Ii|&+t45XB)tXxf!cH@dT^f|T-P%?)LAkG@iHsX&de1?dJdP*ZPr|IrXsyH;5~3!dma%5! zJ`P4bZa44uaTv}5O6Tew9XB@`;TCZgmiEdszCIx#OZRaM-$dwZ-Er4@IokB|T<@?u z6Pf9N{txV`Z0MTX-xtKyjS#xlj=sa5mK3~HF!qUb$l;&UUYpF5Fe5n+WkgE(oOZ)v zvK-s`71S7H1VHUPAt9|t3Gx9oGR)YWk&BaQhdzQxwI>H`ILqQd&xaL#6+Uly%Uj+~ zC}fZQ0oyuR!`R013)8_tr(s@%+%a}W2A2D_`6V0`7_lGUgcM&oS*Db%Vc}w8rqqk( znRc>~9gPf)Qqo#Fdlz04soz2O(jpO!n8>2lIM|-21|TG(7M+j{XredMS_sdn@QBwj zH-kAFCRl&ZEK8escECEkX94Wcpx^r>?fC2K%uG%K;^rpqR_*6m<6CgL$jN;NeBOY9 z8~S-hG-q`~9ee3L-~P$AKb!A34`4*wFZu&Km!=I1I6`i}JDJgQ<@L*6enNYu+tZ<3 z&;e{p9lB<;@{4395M}$lGO{dae9Gjn!CKz~Wpcxe$bbkxnh9VV%v!N;HQ%0K3K34` zgrbMOZPjJSM?$BQ(uBeLSx_0C>mD{gBmnl<-PX|yhaRc@$JB^|_S6aXaGYD3YakWd zD8vL~Ct)CzRFG22@E&8$BO1B}A_eSqPm{{)LqS+l4#bc)yrdt<8b<4>d7~z>38iy1 zU((q2(oe`(vya5{>GWUP>tsTH#(Q`^xxRG#noc}suLl!*Ok1v3+45sBh1hE|U<-IZ zbBFE1rj&Z$A-}~rH$(ylG}rNZkg17wyp~3I*8ZlsKnOkHYq5|Hm}PSd|l`4F%g!NPV~FX5bjKzwVGcv$5n^a zs;NUCf1juO=ZO!r)1nRMgObw=;4o!|WpqjgLNsIACM(iru_j~*4hbpEk9-SaI5Fm& zLQ)5Gn(0#K*#I)IiNzFD3q*ErwwYkE+ktZ`CZg?Q z?J}VT+vn44)=l2@G( zfC)V=IJa?5D3qRd&PP@v{5*e_-%oR>l5K^1^}Cs$PRK*5`(ao!uuC+1Vwy%fMBBR5 zEXa8hM)(8c`y@JRC$_NSOLM?p+yfebSdtpnd52+zq?9eT$NmX~u01QF8o|=e?XX#h z1p@^JwbOuIf2h^C&;^;3Ty$#q2^oT8s;sjhVB)qlYVXAJ2)^;?RGEK?budty)twLub zTHIykB6Dxq!hu{7GG0D-4t-3y7|F+YPF3j^tS+eepHfIXUlVZVIYRfYcfG65gSs!- zi94YbnT$+D?0Yz&>NM?0PR-AGq=F$T80^TK5evJ^Hnvb z__g7nA$p`rEZ=KD*C5*O%n-{0B5V9E^@Fnm#r812n^9vP*hM%qcR|OQ8KIaiwW}Ia zUboExGoyDxQrJ+1GRj*ZiYTdVd?fS9_q8_ZTKLEiyN<1NF_vTGXMegs+S7d(WQNb*)4z}p!us=$t#kMuM0<9WiXN2#DY{VQt0O2hy#fmBfHG-ETLIII3?^WDMXi#E)`NmLIpE?2KirVWfJk$&xE}lgank>BP}VRsPS4P%x^@*#|4}JZK(>AuQz4I9L-0HQzxU0fonx`20 zp$dts*RICpn=aS$pSb$O(iEicqBumQd(IK#G}eEEJ@KZa{c7(!TcT@S^^P(FlmMi6 zEm7)qk3ITW&6{v8!3lN(dOF8>ro(w!d5bw`_=UF4AuT>#L6FbO<1vp&5h}E-ch>x< zhlS|ELcK%ileq;Gaurx@NC9FK&PvMj>%*U!t_GJk3Sx*khqhxZdjsgEt^%}&)3!=> zY#G)XTKZ)q1oVP(jCtEBy^x-zBp3>U%(Q0Ij5_k)dD6Rc5}3BM2JOU>9Dua>@*OhN0$G^P*-NnvYL>;U}*1SChSlJ}#bIjQwF>nbv5hYXA8(PWPvk zv9Dci<=4{m(-`wOM}H!({DrE$t?dt;+E))%s2I`d9?~F>KJr*Rapj39Ly0n#D7G_) zqo7^m>H9;JDeAZam2A$VaBYAPk4mPi5NH*r`dvyv_fqMw5a^a5_-K&9mSFh*+cC7F zH>H?M0NSaoIdX}4hdns+`%&xrumUqb+SD^jFpqL`Ae-^d=pCGF;FMU`X)^)D$u}CL zgoPn?v|4Q)LJD>|&M8jbwHkQ6v}?{S;iMH6Rp?U+Wl3>W{!5#m-rubK&-*!#SjfCv zKLMmbbimK}`_{jD?z5lu?0C+zpA-M`um4i><&_b5`<=JP1NT1=fAIT%5Vzfa+Yut3 z>?$<(cqJ6(n5$M_eI+$+-kQ6dUXL!i=u2OPK_#eu7Z)#FtmwhcT$^|rlh8mlW5y|N zzwNfTzP}#RVX8u)!bz7`PZ5t@c|2~pd~;m9d@-h&;)%yjpOiaLwM@)$;o_2%q^;QG z57F3#IPSmi{&@JIhpW(YmSGmKACHHToiapSChcrQ@rHQB>*KIKo%9oWfSC3&Iya5c z$Kc3cq=Y`w^k9f*IJZMfGXLnN-o4j$g?~S3d(7K;M#^T zeCXdk6z~1}_f}7G4HK1I5}nvR*fOz@|JX6Up4d?W1Q?xDx(T%=)MxR zk3aT!MHE5|)r?T8?!yno$%T_DY${Z}>yEhCU##e2vHyp-E+MgB@;fdbPU0{g;`+7g z^*%B$BtWiTU4+)KYf+!Et0>|_|Ma1ycR&{vbw~g_`uL-9+a1d;&oyCdf2i{^$H%`Q zFiQ5m4;}I$=oc^_2GG!emSyA_p5tRmS-nhZn$t!oHCr>_Npz~}h{hatYRHxuQ`I1C zL-#K9L(PQF6L9R>(oE0dHDx2ja-N3B_WOAZ`*eQd2F!on{Xd##oH_h%1j@iAxtRty)`9KBFL+1D)1ONI_>o__DqowwmipTsyrMhv^Wv-F|x&Ok!N|t^EQLFhV2s znzCrE{q=pdeXm`=7G*AR^~#c@Bs$PG?iK>R=HvY+sb}F1ZOkPU+?Cp4#2(eM!^YNu zq(}2xpzC)a-QNy!HcXjXE==WD3jsacS!5=@4v9vVSoRY*gHQ|W)KSJkDS@U|TQ1FH z`_>69&Pd#H=$)szw`_hQiLK9{%1roZ&pmHUTjQ_)c2QBm!mZ2C>Gjjfh|hoC^W(t> z9*jpGePr2S<>80oBOm@q+s0O5_cQ&2l)E#yx z-g4+=M!%6j*b*Q&5*Ft%|9R&=-FwD{!)pE4LZJPw|LR;%bKLX1dn!ZU_mTS+#%}Gn zWz*k!+pQHv$hH=v_q%?bvtF=#S{PA~T+q9&=fw;4S-M7t{;sgQwCIb5QiB{XiNy>ImUdDovOnC29!JacU` z2_nv-0&7Y$hhdt|dF=7Wsx7^kRmXgd3oTnug1PUz3fl#{D=ItLRpe1^%#)=cry>Q# z|IM`RKaHq{c!$1@p^m#>5Y=wj)pM&>Y8qRDruqbAr^;_Qw-A>65?!f2iAu2JpoVja%o70fRKlb#~uNT<( zG*GDJTA)CBu?y+!+p7q}Oe`D40)PbyBYG0L8A+h9#shZFb2QT;(8;Aorxv%7HcXLe zk<&}J7;q~z^mLHIjgJ4$^UjPb${8WR&jW~{qG&mFy1v4~{kX6B0AiA**y=*_%j_$2 zmC{uP6O&iupk$zFUN!j5H{D#}e_hkxDOe$Ck*j;ZF=?mSHG`UR2bT0>NQR(%SkTYL`Uf=bb(LpMyklnR5jFO!qtU;+(X5qp&0DY_a593ONhQ^TO9OeJ?zrNu zzPr?r!lkS3P;3)IQcpij{dM)#obm*oirGGz<3{%TjqZJ;=dFTk#*e+XbbQT6#y2XM z{a?%cvy`rqei|#Yc0*t7?4uJ%=PzcPn6ie4^ml{&*2dTOr>lUeCf=~C_W!i@J6H(h zqLAFUI2jiQZi(xa$cBnER)0&={(6o>m-Y9qWpTE1D-zf(0!mK9)uGkiQ$m47LTBbS znM@_pcOM5bVn*VU(R4J;Eq+lLoxKMF&2(+gTcO&o=6Rf2^(8-xgi^Bu+gkyO_iBeM zGrk)>Fy+>QfH$!B(u?Xy1Di{nV4J2H`TB}HND$;Ek$#ChaXkLBLLfGhd$#Vs6`?pU zYOxg!IM-^VNGq*IT}9+4Y(xPj`TildRkhxF4OB-57MC) zA>de?QB)SqEU|+2xs{i_7`7DxX^b-xgC=Z%ViUss3un}52kNfbw4o9~>tEnj=Y}Ur ztZ?U@8!_P1J+>Hs>o_;emyR4}^h8$$-w0F0?*Gm(y^%mU>o}}T+-z$J;d?)F?-`TA zOcYI^+FM^*1VT9i9!nJzj+V50YF;0G?6J6h{aRe8_IuP!K_w5#MxVw*W$bEZl!f^( zwWOf>p0OYdITS1L^$Sf%xEL)~L~-@lS%_uA5(qb4zGg{NrUUVA4DDYH`REuYE(o7T=q9%50OBX|rW=3E>L4PR%3H(lu|f#XQc`8@m>2 z(|yNz{jUBr*lq%-@}jgbp-WF=Tp|xGBhjJ9)22~sNV3EdhHS*MKaBB2(_j}aU8u~j z=!6`LwL&!(B+(CxU!kJA!&tK!YtP7j(d<>|4}IKw|Gm`_T74cJA}Keh2l;sLAGoKd znB9kq`xG&tm*3_(chxQ_QO$mY9dtkOnT1W=UsQB@3)>=4@Uy#qaA!+ zVqA$MdXOfs3png*UVxZF!c|d&+Awy18kcXm9CttW?wF>w`OL|(*n;J?j$mGrf2x0> zFRS*haBn(PbTSOPxb@as;!^9vBc`j+^w3(&b<1@Y;McTn?Ys*ATQp!H&||^TO_y$3 z39}O6#YNbU2RRhm4%o5uEl{t8JMXx2jVS3IrFfJ*S(}xfT-aptC;1Pyq6;4bvZ0^~ zSO2Qm&QUs>&EDSLRfSfb4#(54vX%^^X{k{-8_Af5*?oILgBS9(qjvpQe8pG1-5Y9* z8~xe~y3v-FZp>|@3i6iOK6^q}7C-oaz5Y~SAr#<9gP1|rXkr|ZxDmuZ?=w~aG6mS< z-KqhYQrWP7AF;(1@8z8R`ujYt`2e=x*ZVf-UQM6XXR$clBm|T-sQLQfzkM*S)%_E0 zU$nrrtMQpH`^;K}CKCC=kxM71-q6C0b$06a zwaj?eN54}@5c}|{YO!mn(Os-2$#fWNI&-&VM=e6CZw+DUyDfNU(d^nFcWFfP{`bAV zp1+h!KM@Z;^iV|y)n7E6tfok2qD|)d7!U?TH)d?nySLgiESG7m?d?^5&Q^e9YB6Vl zV%i$e@|=$Tt!_yEm%JTld!{n@8u1e%SZz(ToQ~#17+RkIaSr2sfv6o`ClNVDCGgug4PDYszxG@^~$-lGeNL-uo)*yW{pdNGEIe@_(Lp@NrVV|O9v-UwB&St+x9DG+R0Sm-!;O}t)v6RatQ zjbWv+#grPD8Qv|zYk^7&IdI!^!&3LUH{`dCL&u;IhwAWO`N~(;0coQPF}!>CNjPb{ zDmVDFy~KNPV*GtUYbKxT&uJIY#){OV&Xdn(BPMifcQ@9k_4u*G(2WFi=<|xI%JH81 zPNB0Js_Q^F^BniycW+JR`GPO_!n$*kG<~(n0WV97>DTCsdIzWp?5?Gs$}bR8lzymY z#?|Xr^J+XOt0-Rf(wD`R#~zQL|An8A`|iItE?-s= z+a2-v<5y}F;fX6(sz9vk66NS`DMzQQvm)pD`0ta;MZ=vNA^Z2$la3`s;mRMFOp`4Rk`QoKuj=R7ZS%jXxPtj#&4{rBUVW4~&QrmgDz z+IQ8NCt(;}>@Muq(3_Z2?CMjsXvY$oJcV5~ywq3gIE zzU<5D-i(1wNX{E4!?I33GU9b%*o3OY>|3FK!3TX|Mi%|6UiGTCuboU=n2=29A9<1u zg#!EQ^{rKVAXRg~)?R_|sr&QQ&OXDj_P)0L()-G$Eyw*BDx@8cc5e;mO=EQeDq5g1 zJ@=mH#_PWPb#)WdsuiZTD^E;S<3?91!_L(n+^z4g8iOcDE~xFq+P^wa0_1c&t>>N8 z<|Z|s7~_0sb)w0*_|}) zcfCGyA;zfwrMvH0q9G4H{NS>ilspTrn-8?6>zC##nzNcG5vZ)|&o;ER--YOp-_tBC zXtU1F*!;dj^BdC_nXJbKBMf)*nE|Ju=u&7`t;oLeE5EV|fzmoP1$=V>-@}L=m8NBG zAmqQfP285>g5?4S>>sMRmrrlq7F~=dJH?DmCxtVYvW>yw2A3OqV%(z20>Hc2%&q)U zp9}K4;ie9GO(hJpnW#G4%6@b~1f^+q_HhDdWL4QQ6M%%di*KZky!0o~Hd~#dT{-sc zn_?*|ZuV+j1Ze--e55OD5{}{Q(hy{*PPZ34{{I-Bly*YrC+UDr6TV zzxkGv>foE^u`+8#0D9g2*kiwz70*jZ(Jjg*)hb*(sWl2U`KZisZNG$xYkT_r^(tJd zcUcKY7fz}l;)&}|#N|tuD+6BaFRagy?=XfghFLbegv9mgw5z=k#zRFK%N%vFmq6)G zmNsd%*J0B}b5sHG!WXhT zd%zY-J`88LeU($0t%>R2klpm^SHHSWn21BBf@;|&)xULCx6N($1|Vttg6&c z_lNZzM94K`@AP`y(%o9^e0BS&-}Mqitsnhv(dhE>3+mSbq_QBAgCYj=0{_KS`}q*3 z)4npNauRf%BOEdXrqb6Qg@WjhUw^y`f+Z3l79W=ghC+?qh02(<^tmg}kc#Vv)3`8P zh?{RdHX4!O)4Yt;aXMAgq?RLJU0UJ2;^nW1Pyf_UkAMGXwf@Lhf$BSdM#oxq0MgcH@*K~bQ^l!2bAg`t zK|(;NSL@!l&4Huk33TW)$kO36@0PY%FKEN=BtYjHu-4#^b~l>PS=#2ld9*6U(sGgo z5xiL^Ap|GOa73O;dqd46gdyJRAjdeJ)kF#ENq6<4CQR8@f>9ciyBj$L+3`;&!sz}> zNp8QkQ7h;KGqo-(ia>P8eDpnNF3?8v;(NSm%_ZCW_B-yVJ^bWV7Xj8e=#Tp_JbqXJ6*bPX-&P=^|G^-zN;R9`ge&RAF8GnobMa6_R+7&OLD|@QEDdOwQ(Oe z?JmV!mN2HYuhV~PPXR?Vx<*iCuAxHR|N3~n3b9ewMcYH6j1@Ty7j|(NPb-3`8H1`- zP#VHcW0)GkiPp$;f#ebblfBUjPo6B@i)30vtEISldNulKDR1@;N7c!7;iPIwt#$2K z^~BYc*lYVTI|d87H+QZ6B^Ir(`IhEaqZ1*BD&SgA$FX((VJrF$^)z1R5gURKVfu5I z%UjD+NMy$#&DXK*KWM+3(}n}ib~YEKP@2#eB>N8BA970D1KJtsko_JTw;J8sBw>Yc zL&w7!)}Uvd9(6XR6zuL>4YiXWvShR~&|7W!f$X(rd&=LrY?8Ur?wqX2(C>yennc$r z9NKo_mVivUu(L+cNk7b5~%V`}m`e*D>9G+wDue|LD2v z;?`SktD3aV&2)CZgi28R(>Mse3*pE{H|prBPreF)n}(Yz>bSPQzGyg?3CChqoLrQn zZ;V5?<2<@Z|~Ff@s8 zqB5#5p}5|{e~*X7xi{XlK>pLmmSzxwcy;km@>>zag{3^0yEsYnRgG}{+B(OT;bO@Q z6tkWlt}l+UvP1_i_FdJa&IJ&|3G&N#Af~%;vRll6+0Wib^m9uf+E+%brhMbc6$E4SWvO=Do%C@!F6K;aWI8We zJN8FprO;q)P;&iH=Rkf4H@dCSp{v)bV6Yi#N406nDcV|7{oH(c$y0HPx_lLC&88d; z#@bEfENHVs+v_fcLMI$I>oHTZ@yCem>*%Dj1Kcgad~Qjb^H{;MX)vR3G!1%mgc`7& zUsiT>1Ul+Ar3R=*9u{)$ZQ!GS$xB}Hb`9Pd*Ni*1ZYcD;mzHA8!w`el**<2OV?ny5 z?@90T4%>(OaO9wD#Bd!uH8D9f$qBO0!V}4U3SHY7m?jWyz=F6e{Us*S+9h~i;IdGH zps2N)7iO;hgWYjcklo3;S%_h7wWH^y@MqZdHD72$7h?>4I ztrd?B**TlA3UfzGTAMZZGc4TbCYFs-Dq)X@m4PL2`hMAap@v7NCEtSdQs)^tB=qSB z#t0!ORk;eU-BAj?sYZYG74a&=xR{YgYRqQW4_2+j~UUH6#g_4uy2Uz z#PkbTDVaYkP>2jXUeo!IHaN8~^Ejl)<(F}Xqe_bs1o~38xvP@sk*CD*;;`0Wk1fa< zsbVL%>hH2Jv4lf~-rW?}57(=SQF{o?ayBmC*?*uc zMcr4=aBuyl-)_4J55AA_v9!* z$F}E!>BID*Yh-#%)j_*3)ef8Anh6Y(NmBtwu#txuE=Qbl;SXtr&So=Zb|ms%L>p9W zJeTKWX7@ET*YH&EG{_N2I7_HSf!I2_`C1Hdrnz3%$K5dZ)H07*qoM6N<$g3L<0 AEC2ui literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperTLITE/bottom_right.png b/companion/src/images/simulator/JumperTLITE/bottom_right.png new file mode 100644 index 0000000000000000000000000000000000000000..ee59125478ae72b3b874fe20f86c870cc5d60509 GIT binary patch literal 27701 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

5Vof*zrxEQAEbNv`4+FxPBlub87a5{#Gz%s38 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 AdjJ3c literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperTLITE/left_bottom.png b/companion/src/images/simulator/JumperTLITE/left_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..60e4d30a71d55050b798c2e68955d744d89fdeb1 GIT binary patch literal 32145 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 Y2P!qJKvCt;761SM07*qoM6N<$f?0yWzW@LL literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperTLITE/left_top.png b/companion/src/images/simulator/JumperTLITE/left_top.png new file mode 100644 index 0000000000000000000000000000000000000000..5ded766e054554aec26fabc8b9d2645b8e0bc7f4 GIT binary patch literal 32097 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/JumperTLITE/right_bottom.png b/companion/src/images/simulator/JumperTLITE/right_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..55c1e5f5f7d3c57cf8b2a3bb8e17cc696ef185f5 GIT binary patch literal 29857 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 YAMioMX+sfVi2wiq07*qoM6N<$g0Y@ zX|Hr$S*|_ita`)_q!ATt06ReJuy11A#@@sa#!vde-?D#`F~%5UG>Q$PpkN0o2r7tu z1EP1;oIB^Fj(U#!WYqmUxXruYnXIg}s%A6hxVrOd#x<6o{n?-0_kABZ{^LLXW1LQ> z@sgLkWaP8!^*WYiIlkMr?fA||K3muI_$0so^FROdxGvY{|NMQqT*i|pPsaIt9?w1Z z-2Li&&XN1%`&^UHb6x)C**u@;A0sEO5+?B_66u7i*!Ue3#8FaeAm zhV@Zt+YG@wHvyR=Gb`tt_?(;j@*a$X^SD?3&-ZyJKbvdv83gAZ z5jYt4>_ISDxmo@X2YcczF(D>5gZnLQ43K-ku`?-UV3rDq%zcnjOAq%$(##0lmwOLM z72iROT+cb&%NmDwu%@tPFynD=n47WZe)-A0cwXi?pJm=Z^D{s5DbdPzJcoJZ`JQLy z3t=$ha$fF#&JFXW)hZK|8>1rNSgRnvkU6g<6Q=l#&-je{fPHQgFXyAqnRrh$=jHhs zx1WcG;5n9X{|%2ERRS&!264GfbORGZk6^LGg7yj=3;Lf=sa+1{;=g57>Z!0dp-uZFFc*X}a}Ajh-ybuO@wBGEteI%Mi|-2oJr+EXTxVvqwz0(9 z1my^r%=H-;*ZX@5AOD|w5*vIO9h;DO0O%U^lh5;TGl$iyZE4N}MKT7!3GSIC(b8^U z=5a$9#%F0};&`s#4<>|xdA8jjG+eG9ID8{+U2lQSk}2#|%!Z6>h_ zHF13}j|1Dc-#nD|<$a#7=ddL69208>>UcL}hpG;zyC2HlfKI^ldQrSI0EvXEedkoo6gD<0Mf-6>WM$|mph<;qj17QYZHa}MH+>hW;`|f^kGmJqKOwrh)fm+X<%}skDX3Ep+U;q059fJPDKm5b- z^MMb1VEpU9{_A+aePxbqF8e z2f&e&-}SC{jlcPuzZu{6ecyKw+|q3Kt6_Zmf|dlUZ~NZ%x*krpLC+6*FNixw9BSn+(j2g~8H%p9@g3U78#p`p}2Q zFa6Rljeq*5e;O}+=}SL_U{-05oc*PIVyh=h+AbfL=R~@qN%}h5NuqlDEspL$i>1iG z$?Dq^nfJ$!9u>xHjbyvMOnhQvnjVj}AlKrS!8j)5BH(I`iD2TjSaUQ8KHjB~b0!%3 z^jClNSL5w(fBO;GfYU8OND(TkrMvw=tCyu5d;>2C9!=w9McXT@tPmPaVcW#mcy33c zEJ3Jm?qhqtDPC>u)AI-8&U|0cKU&StfnhD}W=h-iz2ff1O_qK$`ROaZ;w%1lPRbxc zP$Rtj{r7+W_v6*Ce)aJ)vz6wip*UdYCYoDd`) zFcyIJy4St#4*iD$0t{*fINF}fxU*F$0~-^&#&veFOgJ;bB4o}v&L5LW+Lf)FfjcwS zQn~CVem~Q{QR4=Mj0;c2W(sd7!1!POOVade!*6&-=XN`vB<% zvTG%74vf;AxhhGY-vMKpi1^mIISkQZHXdexwLFOB+7%+;V2LzP>g%lycE#jJ`Q~Pf zPKJyMO?G01YcXSY^(VkbteejrowJlW*?@f~@@LZq(f;k<{_Xf3!baVr@-a;aVBFw0 zzVVF*;ll{+A+(7)5BZ+s=5s=z3@DMas0;i{zi6vHOI%B2OPnPi4g~e{xq~yK;@k#k zKK4meiu=vX3eUD{<6w~`53S&5t`(JP(3<)DoX`23rxFh_k+`i9p!{I7Ge&@=)qcK% z;Q1Y}{MUc|*W+8hY#0wb&P zuX(;*mX>&rWlXRDYT3Ro$32)(;Z2X-S*pE)H!U^TQm_SfGK38wkW$7?Y#$;T$YC18 z1W0(kk2L2Q`TP(6@DC4Z{-GcGq2u$M6RqeV5c7UEhw^14`LjR!vkyWWEfXPYKr#bf zz9)oqV1JRX>w&es?fY?0+zNXGK7X&E2@Jc)zcgTRRBIHFYlO}?T^Ipza-T@6iRyYd z)HJG?dvkpVn84NF{oUUkYCKGm^I??S=QXc+&4HQn?(cv9`^Rtn)^Cj;{J|eQXl57F zG=_J6k)C4)Fj~M?em?rqj~*Tl>YQ3WYopL4fB*TP|M|g8 zvC($`t>a6-^h*yWS(S0Sn)h+P5|m7ANk1m z@P|Kq5Q}T~KqdDD_26TXoMI zc)mYXtx`voavs%}&Jvk9I*>9ZWw8)!$ZuVhuy?YflP?BLX0pyuWx`RvRK#Y?nb<@L zVfLIy;>XMf;0j{|F#Y9U{^fBW)cLMRo;4=K1exPQ^M8&In9rgC;%epEfV13_3}9>8 zb6y~-nw!~G@WeHN%`vTd_eyYha&6xlNZDUC!Lh^GH6vsb)=^cEsdCF%I z#L$X+l2N>>iq~OV&&iodus`TP1MuIHJ9Dc}RoiXJ;)Ofn#r-KLwCgj3yisvR>!Z@2 z)B=BUd z>iNyPEJ}u;nG)E_|3uJsg-8!Ah`MG%t!}wFb=JE|RqdnMzWLY{oPQ+EsAhAqiMLlI zYbe4r>KWWWV~afvAriiYh#=kkZ)PSs6+#C6Mq)$IsNkHR``G`T#r$KtO+-u~geE)a z7+j1Ag=r}C3X84*p75nOXm(%LO@bc%_dH%DPO1oHoLz}JWT*2JKQxlabFq_1ahNw!o{7tE>?)Bb`~61ilw3hXk`FTgh1B{T z4Q|cMW@a`fMrxr zWK#;x_ktM+EHJ@5lWb3IO zu~8x5QPI4f3+69laH^AFP41m|ck&RJ66e7Yvhz%m?egpuwltBj5pDeaazi%V|F>;z zb$+gui?c~ulucqR2_}RIC<}_s`r|+Tv>Q zZAmo&XGCqyX16orOJD^638C|zNg-pOnFlAHj`=idz1A8c51zv|^Hj@hOyEZoV94K$ zWTxw9FBMY7Q-CS*lR}A124E?F|Hg0p#`wp7{KtbZsANl|3Cm(PKJz(tcSdEuS_?JP z52^o0gq{)jPNbVV856@ZsO!!>zvN54XT&9)Q{aqZPiHTOzB+tOB9 zmdgI#w%qR7C`7s4vTOpnracDI&!oSX5kq8iYhTt%@DEu#L2Yb_A;l zJ3D$__lisO+}-KW|=5^Wt<%L+p0+Js&&pY zySpga&`8^oFr-yby9s#2{RvX$x$$$!=0vhd*oWwGdjM2u161BRSv25xc`*dFfXtYo zRrM$)@H2412)>{xiMNMoQ>KkTZB<{u&)Ea=L|cNFstz$I@{x}Hh%jNr^E zadC)zx|M$=7oGR95U!PUI+%G^kmFhl>c)deiwDwCBUg+#N@lR-IP9W|&}OcFt|xb_m?dClcj#aM0HiE=zx6un78L(eM}w~a zNhTVSW=e-B5AwX{Na@g06(v3QcbC>#rBN#e9C%-=p;`~h1a?fjr5i~mC_#;sD_E>{ zh*S5XKD0t?GIsx}6_K4t>4}{E-@j)Bcy!I|zK_nGp=%CI3_%@Aj=(OIUPrPc(V07H z(%Od}a<5hZkVAW&E@OgQRnMekuh{sI1i&wK&SPF>ZgtPge25I8^A$>M?+ zL*&_c{qros`uo{+kDf7`I|?*oni9L`z48A;+z>edAT(HeDVFe!l^SkN;}m4abpd8q z71qzV)WMIo7ai@wukA!i6N-=?fdMnZrt;lJx=n!oZpMs{)cm7!UPu$n#^KL*6+1iU5g{LGgZ|s&cP;(9-~H};Az(CB z!kl?-)OeB@U;DLRd#|6f7Bwf(heoweYLe`Rs69yd}_DXeLQb+((SozhA5r zKO(fp#^-n$O$AA2|C!L_Ig{@)j zQr=bfQ0t;a86Df{{NT!Pu_xcTYP&eC4p*(i;@Ld6t3V$y(^ji29;waQcTMb>M)-7z zJcLoqe7JbzzA5Gl(UM)u4^^d+-VioGEQHRvk?g4OsOZB}>c zAt$Y(Ia{@o<8<+W3F+2M5R_<5XR124%p|xbd{^MZ9S+v@=}rS?l1BUnGox&kiS|fM z&!o8By%~~k&Ej!Wz~GwsBwLbIi@TK78%Sg%_lv*yi|^&Zq#U#IA}c(i(%2h{+|+Bb96&LQ9|iH7I94Z{R${a(HHi)VSEUjqKQat?weJT zKk_3#a+h#yvB%TqcT;)O9cwXQisapyhnKQ#e#*cXT5nZ2WH+3gxN6bd3_$pvOI4m& z-M6G#+qATqz^zf17m;ad?Q&9|kw_uIj0j8!^(TMwCx>c|Brsi4(g`?baoErDD|WX!5-M&y}>@cm|z+GAK%*Bz-PObIFuaieZ2 z0!U`*E5Gt9?+KRt?6=hxNmN8?!w6K5M9RPZ>%V?{`?r7l_=>OiisSrz@7B@Vq<*fo znYAwTJZ~yYCldsxpstOSTa}KobM4b2ygB{CQ~*^v4Mf%Eh~x$pqn1(4@lrzEsOFp-NzYI2 z8`b>X-~HX=&2N75aeba0!UmE1*tDR+n$yD7KUXa^W)9A)RztR7cY%HzevYp0G`F2@ z3&AW&LzNT$+|p=Iz@OP?9P0LWRXgY$ELiVMP7~P7xN2#tC3lxQ6n{5==h_f8?Ioga z0ag?y#zxb@fK`^ccZi#SM^JRCUjSf{aF3_o`&#WscI6K^w%StZR4p^_HhPxzIH_L= zoPsOlK!&4N_J?~Q7j56rgpbq^Dm`hl>eqE5ZBLY&l5_yFH%d6 zJgPWSoBPC;=KfK~sAX(4>!(B1Ecc;?ja83{ODD+j4d3t$$9Tw_Q|H!ZafV%7U%po$ zrkQn=$-1NN<|3oDPE6QWbRBVDYo>9twv$#anxLafi-yqoOtRr}yC^Id4Y5*U6LlF^ zCFkUsu6cvm+?g-Wh_t@mT@m9~j^K z&EI?wm&6f^2k zHH;8R%x{wVk-iX@CVEV4&L@b$g8Ez&f~M8=H-6(c9#a3Dy7bNpA)C2dl1+SjDA!5f z@y1U!dzaeL?$D(DfYYSmjQupHBZtDBqd-qfvL$k>GNbkj+eL68!&Uu9>68$kGw3!s z+^BLI%ZJ#BOhsZtR5LGjIm9BqO#C>{4g-X!w1-0L<=pT2p6@vl|3TU;-lOEHJrUy- zzr$Xk-kE*of7iY_BDty5n&{lA7EjzURHI}Q*l2dNU}*1S|kYXcJ0-kzBHCA*fX{?q$yX-tYb1BiWyO z<{Df=+(N5kYpupP95!_wn6Wx{Ji>HgzM0Ril5HS+ZzyN=*(ldiKU1j=VzdK)t)67p z>KWGB!0OJD&7ix2g``LbnrnXX7k}}P;Hc;j4s;xK&h=)NsNqO`B$z_GOj0@C(0i_`J9rW)5wdiR_(?6Wdg*YxNS-?bE5?U+gTMYLvu6U&DqDBO4&0KY?L5bM zHn>HBkLE0w@xVD!VJG@GZ!E2=eln0s$KLJl|pT5UWW;?32PO+Rzhe~R*~N<41SS+#qtlwQQt5 z+(hBr-qpU%R0B-M7*Nwu|0(dJQipSM&lCX0T%2w#nytP~MAO7Lz#;50!8%WMJXjPC z?TMiF`&wP|`vHDw2-gU(CG|S#6nw3n-hC|x_qDq+q$(jAj>O5?Q+LU&+tLMnnxpr>n-a+Y( zp3R8MeXqBbo>AQi-UK`3n#^(H00DyW>*MOh8_2V%F1bF~-eD9#y%V)|!5dVV*r;1w zbrQlCueTK2P&m%^*PP!fd#_+g+hj`{$7#~e%FpmMminc#X`D~RO9M_{(>v$pGm8DT zTdQVTNA^U@Wxhiqd)fcT1+NM~#uv^#KMycHk+gz*o20gY@*J_4msqctfdj)1~s(CqMK0Eu%m4}Kle^BCg&!En*dEdiwaM(^>6#OZ+i+0nHl|# zZF;V?^Ut|atk++SLYJG8+q22@ggt`%?UI>b43I)amXyrWl}4A=4xusEo}V{aWB<3n`qb!S#xxRrUBQfwqo$Zw_3^RCOIv!&3KIITFZ1EskWg2jwcFc zU6n5%xAbKuK7=EZ=4X;c%Xwh{LZj4rA-&737$QtV!24!#oM< z4Effkk&l@jzYCUM)ZDPuMk5`?S(O)d*=dLJMu8>=8SE)&$e?T%1^#aTs;9DfGKUKxvz!l zs`i__kDLF@T6YbK1EKz@iug=)tLNsbR=-vu_RmMTWL7;5u3Tqs7wDoc!$YoJt8X*J zkUEDcvAN`!Bb96o2ngdHGVx@~UL4nzkC-JPN|> zN>5rlVw>CRxi5sDg*#0+FUV#<4j_z)iztY}001BWNkly>B2RGJbl=wu(Tv4;-T=FE z+pYmytIljGH<9Yh5G`&?R3x?4E)*a$hYAKI^V_@HWNcB%sO2W^D7UeNf!~ZhOk{Tt znnb00@9s@~zagqqoIIEKgd4-3oAd3Hp+<(njq7dG4%F2s*Jo8N69e|17^5~3v&4IH zjF>qbay)AfE`u-5VfA9val(s{V3xdivH?^Om#h_t8Fk2-sV45KnEp~xz)`@piOpO` z!*qpgekT-~=YB$)of&(ejl1i?EQ)zvDv+@#?d1vF6`I7xT~+MAJP1sfn6}fD0a?Yh zWSV$ZQ3NBb+U;x71LP0>;17J*PdLI+`{n zzA!2rRO)fq#mWRlHUHl4{odiWEQ;cVsQ9~fgOFS#2|qUkCRoxr_XbKc$$cS$j~n(u z%Q(6iOc6qFwWxiRM%X6-p;2z{Mq~DMQm$*nZU52#J3Z(ZpCRyFwN4$|_JlQ9x(>Z@F!V>o(7)7g(bE=$N@TO>o|YST;+5_#A*YBckm) zIR@VwS~`mP)V9JcQTEw3CEu;8e9a6DooYLVS%|V-EWgmTw(U2TFp7^PhseM3E5C9O zm7P~3-66Uq7|D$~&QBzl{UwbVMgggI-JV6rnFKbWZJ$xO5H)q;8IyO9_52tUS&(c} zJnpmA^*X6HYT_&p5*kLuGZk*2*2{CvB%t4R6}GD2*p)?!BnM6Z+OPfEfviJ3dVdg; zW>-3-7GKw~{26ga`T8@WKMQa*b6D+WTu8J-mU$mGL;*LP9`s3dzJ8Rj`kD8&SIsHA z%3xXr?^U5_^}AQ#!-pBDt5TXV#w6I$pMkCeW`6s(fBTT=5HJ(Nwns1yTM|S?+wEvI zw^xjQM#vXZ&Hc_j@%&F5e7o%ng4UwHIK9G_s`yzdF0ocZW)$xF6xH2+?TmOlXjUh- zD_UdZ?g?8|vc$PWpHAAhaZxVXD0f#ll3>Dn-t(R#0YDTj(w$Hym?EGiDL+Tl2GxN0FdW++xu4f{s#S4*dus0YPTA(~FlNk)&ovJ6^IKK#3QqZ0R~4^atF@GkJiiIQ?G^i8RiN+oQ$q#n zqxOd~_p!Rs4Ai#M1G>3_MXgSvFC(y#%7j2c&}Nkd3NrYn65|H+3beDbyhov$6F*6UqtgZrPn``^ltRt=is=ikl;~SM?fMYE#h$ zGBeYX^CP)gw#xH}Xo0GuuGy)5Mx-`LvvXcZT|WX{&pLP92!d+HWR2VQ{sCgHaq9(l zEx~QpI!bsDxdFn&DXN$dtsuq?CMGgf?tX`1hS!{c+h*wLx4+m0E!}hNkD=Qlr zE6IqxHt3uYYBo9llgA?vMmNDQsHK#)ka4N9c8q|OC9?xprdJmlbmZ`$ht0&}&id?ga?W&Hxx0#CZQnR&93gW#!tdALCUyQWRnB|4e+f?J_-ej@Q z2n9V>a%AaHQj^~L$f|vmw%sch;IY|5Ihetz#$DAP%@sz}23qa~wpV_5#(^&Kn@x;^ zBm9m1^~T^=ZMDD{Y*L-3|Fp37*!zvY@c|Y1pdaUh@ zSRfjMP?TsDe@}a~L%yiq*#PWX4Pi%Z!*4Lq4b)M`gdoiXC#|FMBpGGSw8^Gq`?HAq z|MkIKoNhbO(RXaSHR#J6M4Az#to%&hGIvmD4P)(V0Nok3O?hgrJ2!zjAc{+1U8gm~ z+1K0JB%|WDd%WHo*t~2*d}eM7!z9HbXz9V z$0OJ2St|O)sNb1u_2_$>99m9=$oZj*678GwUipk}N#Fnd-+u&2SbAfSyDNWS%+l)G z|F6nI^84Pb=3JY9BK}(~!!g>UyoC@W1F*{GdOm|AJ-}5v<(XU8O0N4wo@KW_U1Gf$ zTl?rdOB^-dAuh#-ETRj6vDNvG-EMyD$A0YCLV+6cwN{tdy|mvwljb3l+wXrb_gyNP zFyx@`%DBwHLC2&YO-9@Imh86kyTXHvt^LPOmsozc{``gRF&pQ8TSOlK74@9o*V`tj ziL)ngO=C{^-$wj&hhgluVl7Sa28kC>$K1-?8IoX;Y;EWcrW7YpGd z@d?YO0p0)lU;pbMHdQYc9t%)MD`&Ozp7$}qTLT;Y6y+zi$-7oV&ZPL!q}!fzzXk~{ zsh03v1qnQ>)qb0l|L*ZD?Jy%St2WrzIfTs`sRTWYv>|Z5Pm8Qba2C3U8FH<+Q@&PT zw^8jp?12n5v02q#q?wkVyL-hKy%CDlt0OfA434H*Esx0rK2j}qt;}3Tu;%hrYr%Y< z+3{jkyN?DwoUfI?oX;@TjV$=Z~@A zk=T@Z-cOU(TwH%13NRRSdGb1wHOhB3C|FgpI#Vf@B?w>O_)pfam zaZi#hcnK+RvSpafQe+O_P`pY``$d8Z4E+Hc81Hrsz*B9KMJJG)H%-$L2@6~J?9=# z(YYtvBG7Fpzw>_x9Kt5T72h~MGf3MT$8gvV&vMW00qqaE1~!4sutN!-vmv`h-Yyk( zVON8fQEI&r-&T7{nwY)Pa0rCKwLI52z(mW$XL8f08yiL9ASyP#YZ$?6#2yjb49X5R zA)-+<-48g8YQ7OQ!2}2r-!w&kEHTY}=r7Xks_J72MyfA2zhquy3#c$?VR&F&jyG!n6&WB%#ev_NgS#e@%URaSQ8Iv zh()gHnOY?`6ZhnwwSR^s%L5$~J7Er6%B?a{AAz-IqOGbba{ok@qE55MidcJ8IvbdT zAgLRBRLuf`LmUeIX&)I0W(x;eMddz8BL+RkO^Un39v-xea^|d4w+M-aYp-?AE$t_* znr!>-d(09WW~j66hr^*J3O9k^5WfopPU@hy$PJsRnsxz}XRMu`0kb?KQWemX!u=2| zlTN$k0H}PP35rxY(7)W`(IE~y+2sCVnn*D!Kg>e>IVw6fI6iPR1-sVeIWDqq#NOTi zHWvfbHcV!6Bk-&Gp)FO8O-R}0UM+-2*p$Jr$m^nwEzONfIqU{j*XnK3gyiJ_P6iI< za2F3W7#W4;GtLm?t#5tnVW(5T&t4e;OcV=-Sdmy3&RI3$>qZNNkYSSCJJOx+GL|q& zV$o62vBk0305KMuGgsDjqhbi%@1<3Edly_6JHOs?+M^cE^&)uB*QuK6o#6ya?XIHy zQGxre=p^Z2&|q$~YU#e7))?EZ#lyIjDp{ouVF)u$2pIxphjh|`^bU!SOJac^PcXeh zqKZSz(``psT3CeWkz5E&oppd-6N%)HyE^e5^Xd*oL%?iT9T=ZoYjHn`46l>s8PuD# z3b)xiOx;zD#~QYqb5KjC>fpzu?d9l?+WE%z#wyC%0RnrlUYq1G?uyIDX8!OG|M2*^ zpZmE((%4=wziCb3>rS_Yc_A=c1kw#5a}nE{Ed=T0$Eu1UyLqjKZCka{vKP_OuQp|# zM;Lz9hHKWOjeXk1ZQ9+{zN1r(b}cpv}E!blHL)YU1dY&>T`9^ZI!lHxe?C` zAtQMqEL$zmP={r)mK4t~~Lo9_sDsxSwHv|si&OyiiQ4MWv0}U)aPj*w? zPjs#HYhxYEq;u~k_)Hsm9Q%XhQ;xHJy`}uU8R6YY(6)L}53pAA4w&o{|J&%>Kc}jE zY?U}Fa~3JiMkhb{lRtR?v@E{M8fmgbs4?okvBR;!33jZv=IOR|TUEX670UIyT(w|s zR5Wq6kiXS;6MROn_F`;y9Z?*W*FLxUY3W)N*`i7$wu~)-y4qnVI6o^xa;KvqC*REF znsTeJMJ|k)&1yV|IfRS~4gmww0-*9a6Oiv?d*ds|omy`(>yDyb)tILl&C+8n#Q`y~ zyDdl^oZ{YIplD{=lrXZ2K369~Ds_6E&$FA2a@_ry5h6{`mU6rNV%+w3m5&Vqo3NG=GXbBo zTCp{O$KTN@Yc2p=yN_mQ7^ev_gf~3aS>=uk4jPn>3XPf(O&^X^b>6=-(x^PBALPln zYK`<(X-R_w+9;Y}1`WG>(7ReuIkBH<(_odi-8v)~JD9*NC5=6L;eI#AGf~Wl4aSTi zoY^yDZffvmqQdZvSB3!DHtD^3xowT;dz#RVclc}tfi`+;F5yKxN@llWy>%6|YC#3A zw>_d4xe7BE&BW>mnN{Ptx&k*7E%N29b~|b{3RpD&^v<-6davqzwv>;2t>$|j>1!9Q z-@7r7rI#kL768+%0o=j#Z3DRIG^r{yK^z6*Ah1Hy@lBlr78M0B2!~F4r3Eix>GR7v z?AK|)M^epmw_3p0Yqe}{thr|Dd6X7#^lnpPfldUx2i*!UY7-4pT9rE}v#STF&6M#% z1M0h^&-~Q25(r)_XT)oiX%)FCya!mh-Zl^M;EW>Ov6gcP`7J_|CgP~Xz)V^_DL!@u zUx-(m;nymOnt{_!CK&MT6QA}}^>rP$?sV#|%$>ipHW&p)$Hed6sTqUI2#B;qI3A8v zMrv(W{7|EDt2?-H#wZ3u?PHFL4R`&R3A|OMhBd+>B;3;2&ug$`0r0yrc2{#+mBWbM zFm2piJQL0yjh7`YYIp4j*s~bkOvnFc0|0ZIW6S22tNNK3#Bd+0)CP8>3c086%}ZE< z{9?Q`eZyD~-^4Nj$3(@BvR#iRZnn;>&zWJ1X55Y*Hb~l3AYkG0(HOwvpPfHr+PjVJ zt$$o)xhOt+QmbJaqOHnjLu|A|Kh%>ni~e*3Zqu^ZwK^!-+}L*e1!1ga%Dn;~#t1ee zT-!w3<$)|h=ncB;NpMi)(E{fAYqL12%W*TBHQvldpNHHlE{0H6gRHNzow-*3E2I}m z@Prw;dcqg=<*>WdDiZBtzts}y3F(*g80n--x^SQPx}&x@w&d89G(a>nJ2v6_3~4=o z?%Z-~A)CI&Q;RZse!l;{+h|_GK5{>#Z5KAAGgBNV73+;j%y>*xXag4$vLjR{?WWhZ zypgyi&C+OWV)fS>jfN?V=$#g939~s`CuP))P4EClnXe8W87Ew;-?@?g4A(4PAH7u_ zm^o*y#qhQ*p72xEh1vEvW!X&Blhxeas=LS5ld&jTX5u*fIs3kGK=1yuYVVG<)-dl{ zX=0Vy!ZLerr(O4(2eREdB6O_5j5I^q{@fb*Y%aHyc>?&`oW52P{G#m{Jh7l3qyK2X*M94s)DqY#)8pK#HrvE-1k7VKqx$}B@0SXl zp0zFEcwo9(y4Rj)U$85lYk<}q`mFlxaRVRYW}EW2_lGj!tR;NE8OSjwBLQm#@(om) zDXdawQpP)-wfURJ;&-)Knt{y*O7_Fo%GM37nl$(#&1xoYp&X_5&5-xKDI585f&=!g zHhADx8?9J{SR#zx>9o65+Rofvd!vlM4hxSXyL#PvSaJ6E=Sz^s@3{- zrQl$9R^Y{~dbcWBUWB-fuA6vEg%Irr&ycf&NG4`Kr*V&6PHVcQ5--houFJDfS}Sc; zs?9maoB^;lWrT)8boOhx_^OWaOgz^&02#9T48(-C3R))a!YoX9)kbKeRMbp0_IMhP znBgpSTV}v%+$#Jza|1`I$p%cG_y#JD_6=!<8Nl=XeaJ6GFVMmXS!CcH!>Kq#IP-qZNF94C=j&NZZfM9Eu%n;x!<<8 z-^mlxTELTSX|@D5L5)Aw+Wx5nDvOZzP^(M_WIQg9WvlRTAJ^OJ(A3SHO~7%3@m)D$ zbmx~hhg&u7leN(tejaumGjXks3k~kh+JN*_OKB;HTPD8hR)uv-#m3j#B6+zj9LWFl z1|Nm`XU9_h`wWb=w%Jug$sQq|+fi78LdE`^`(L#K0yS|vA<*WOMacj^a7Kh7n?}W> z)v#6muAl{n1NcjY2Q6h*ja5ftdnksqk671diFE7s&n5eM&I_|OtmS7 zZ)UedbM0xen6QPUwGPigvMJ>yJjxz8@m!^M))zo8XoU5i6iW4(7se%oaeqx=8)!sQ88Qm zO<={nQjVoYZBN=BdNOA!%(lZmqNU97a3s#sVUF$RE*0IHX$#vzKJQ82sz`6ICOLh~ z2xzgRxw7P{ononB-jzKYw=9kWB_4|_Fbqn}OxId)uvZ-2@7F6RhE!^gtIf|0 z0JY{D0+_9u*J_RVEI-&1?0PeH+o6^ng4@;~^W8RZ$S#gjn?480w0d>H0i0|bIF$SV zSTvI?6}9qbCYB}C0i3JWRJE%01dmGKcv7uWmq{%)_w#!4&XK!Zx3%DyfYKo?Vpoc+ z9|ZHx11ti}XS@pm6pp)}-Kun{wL7QV5>wl*i~5P!Ca$Wmq)ZkG?;LO2I)^q#1sj?e zoR=46tG`ho$10F}vhr}51V0)OZWWGv1PaZ>eCE*aQog?#&E-l~-IxU=*tVAq&0&Ik zj>)te>*pUy5+3Am=URbsCI%nRfd@}8>O2XHi#GChG>k}%yXr5M3T$<~D~WMYM`7E= zYwd>Dw%<%_1l^LgOk5&Ae<(g+;#%7{lsfZXIM#|fGOq?x*xL}4O~oBI*eCJt4%S5C z&$b^Ojk#)D1y&ZEZry)|qD4$}9v&j+2egX%qqdCb*-3jXk`!j(HRgn3W36OrdQy@44OftjwZ7)G$I}l+Cdz>2lJV zI2fAi?|W0uy5)}>FxnM0J82(9h-b;*cNf@OLa$mG;$VHF_?aZk0MJkX$1m*3XfWi@ zH!W*z#LsVQg1wRv_FPWY5yIzMBM>Y?6Ec^vxmo>5!N1k`Of}o|S=;UvYQAqP>YuZdyFwLHBjCcFVSYsiXj=~i6H*v;^E*=AwbHmQS#atxIMa=oiWo1pt7JlX2gN4p|a zncDO?x?~MvMji5P!5bcr`-WL!9G5y49*(FZXxp%%H>5 zt?Bsu`g}Rnr}l_$LhKbRnVsWF#hw`etx8!s3S^0~q;P-MG~tIsELuew6nV03I?lB) zd{a_lRFgK3AH3oF^9+Yw`5fSC#={1)B?F*|XjTWDh9YXmoR0k<_A;IZb0-y0TJWyb zDFM3NvsV^*OVy&z?>_80ZMCp%zhzPEf*56AZ=DY~96f4#+zrzf1wV`(j2E_QQPpGh5MO_w8DLhn^kyG(p(q&tLO_GN7QcKq1X8HGESV2p6r z*sXdvtQtolIIFc&Rbw*QmNrjbr!t4sZG#^OX_ei!^;*kGjrdKl);eoN*R3M$Rak?xly6n;%eB@LgekGR><{g9qOV;Q4ZGH=(-{-&6`ckRzs&&z$f-IZ5@4H-RDIe66wU-}lcc^hg zgD`sporSa6@*3@-8f;?EqzQ4C($?86t(rL*$E?oH+6>JzCS}X^Hhk~xwg!FFjB$*|hW(Iz5=CmFjI!~wK#3FeYn1?7BiPm16$~+<+7G^{UncKl-+M@{TTM3s zIADxSW&l>CIp?rT&N>N|4n?wDc)3(SWz%}>vl`*>zAbpCDt5f8niMU4cqq69?M_&l zTWz%+yQtxc?W#G=3))c0ZYu5usf*mQOSPnA5>b2U&g=1*DGh48a>FiA#}1kaVW6wl zpOK7-^g6Q?+x#f#gyfs}9kXZ@Zs~9BI4XX?@5_T`bmvB@yL?f*P)$GwC1#l?4`v2( zS`(bBO@(dmwRXT=wL}GsVGVPtHCEEW!kgI&R(N+PYfQ;3JBSsB!`rQ8C!2}bx0mdj`x71<+5PcUi zn7E64`$c`Qeb^j0Gqfpm$h|<+-c6Q1;4}N4u;ry%a~K;O@0V9xo=#!5W?|mU2>WCq z!VG~s0d+8-rL_UaGV!s^ss5j9y-?3;zfty>G&b#LaoC2LqkD3Eek1)ZN-%M*lYU~G z3IJk<_uB&Xq!8)hebM?fV;bV`33i;;)29SljxjI$xIB5j*XUXa9M8i+*z0yMhCKlO z?zUW8g_jRPuUenmf7?#(b^i+j)9BWmCm?;cmc#mt9K#g*HkXr086_unDdqtxWHP^LtA2?d>*HVT)WXt7MqdjJ#AWKx@XQ2));$^1Z@;d)024Yw|FF2ZaEr6b`Pj^f);0`*%Ve z?Er!{3T+_iXPX%f0y`&-XAr3cLFex$w{85{xeg+@EQ!dL3F83IvM%E^o_|l*o0&OX zQ95e*uE%Z-fDm4^?GpI}Gs_t%xvDtb6St}21bf1cF`!A?bVqgCg*aEEnO4iA>bg$q z<C_lfYQaR73c|#PwQcrw;?TsM#XRWFg=>+|sq;^^ZVohiMHSx7xDtqLvpO zckp{$wB7KgRwSFEi|0i?8!pDFmW=LI_te4j^Q||DCvzzF&CyL^MkOV>M5oazW4KNh z#}h|a{`I(zCEx$U2t>D-pb#}X*SVB-srAo%=SZTXc#Wd1F_&_$I4r5W!$o~l`uw45 zMyP7BSBjldME-M~o=ex3GhUjyD zHY|&OoC-{OiycgJIv*(cdby4#mnX;X9`i0&%&r~p>-a*W>g^frGkngB$lS|mS>NaW z4!($xpmn`f=c+(EzNu02rVJ8*gC5<@(Ji6dB#-PM%C3-NFG}-dy48dyqoZK1lTP4+ zC>Q%ccNuh0^P%y!>w%(s^6f={Cv5U;XJ;|&8RLwJZ@pGRhv#|DJMM7LN1F><>Yiw0 z(osd}<`{#qsL|f&t2y6MKlYO|{PeQn&P{Jh`MOQ!UacTI$kTvplxg#vc>Wfxi}w5boO|a$w;TjunH%v# zJ&6Vb21=tA%8iN2bYA$RMzZ$f7x~ts^4$$Ub}g!Nbj$bRbMnOmLjt}$J|-Q^lFE*w zBisITKx40HUIW{fyj^}b_Xo)J*gFtp72e-Bc-<9`xJMy3ZN9ZA0wp>g9ZZ`O+Dn zwNx?8T?iU1v}&@uWx%%8iVL?*Y>6OV9pc6>j@st)i`IfI zH=my>8+S;$R*S6`f@8kU>yd15cz5R7c`jWjc&a6?i^7^{nIV(dUeQood`K&rj>otz zwJojf1k+fwI2ZMF8ws;mdTsCJ6)UBD0H;qNo~#H#U+-MD41C?9Hl z6yTg`wHb~Gkxc+fcN`B`RrKiP#H)%LjF7FeH>EKzwR!s}wcpvWal=4|7X|m)KW=rg z$oW21p<26Hy$;F}f6_YY9{gGb^NVuI)--bN-b zhp)`%XLTC#d~QmxI9s?Va%drQP{v4pt+eai9AFEuUG^oro1Tenf;)IYYLBf8nG7Rs z+hG%I2rb*k?0ES;Ns188q@F8@30|JbB##1G`y>y#)OxDz7LLrm%$MrHO znsLT6g7u?Nto>ec$E=b_iV+h*frO>_TCFI%@?=*me61E+M$j`4q!|`L!F4l}I^^uu zk2XtNg;m$eDmIoFh0gD0iI-0D!CMv#ZGprKJ3HhTw2dT4VmsFvq)|Sf@9BXu`ACb4 z{2H*Z)+uh3U7}t7x65)ywTNn0hB>*YTT(N8N1=8VYWQ!LQgfWD;9``k=yB}j%5K_O z$3FR1X-f<28DF*0-;j+y)k3 zY{MKoaL0!x)UMzI+R0si+P@tIW^HDSNeIrkI;VlxjB!l7Rk8eL43BqZksUX6n+Ll$ zb}c#_Xe8(&00RlGCw;lq>VrqfzO!dTh?$m5kz}OUk}%X+j~M+$jqohJSC!s)?5mP3 zXYH1A(c0{0W{#zzZk9lIAzCX7wO2IFOwi2ZIbIdx9u)SKG9|#Z6g=Ot9fw~ zYrF05kX>lc)l9Wf=}D!yyUKoC&}Tb?t;=~KT%?&^AbE-PmhwBPrpuA8wbn?tO5LyJ>O4MeV2=^I2=DNBP#f zLak$xAZo$#n5>;x-~HEr{nz7<|M-uORBX)Wmwd^W9Kw~q4~a&CEd{hh0*xZ|xybuI z>=mVIuEcI;SGcR%94^&?XPv}u4RRvAA#z><)oc^7D~dOBN$j>X;^lohS8iK+(rU}0 zQX%H+S}Q>8Q*X)(GrnFz>v#6NT61oS6mD8*7&G?X_r7=h^{R0tvka#1NO!g~Z`O14hq&0z&x>5L0m7-O#!;S6m^L(m8Vsi*vvgq8! zhpxTaRi$b*xGQin6np?hdq1j^Hf|fr0JWWWOu^u-%}y`dsc z^E_3BOSnGaOq#yXBgBnxaI=PhA1<8L`nA@RIWlFP&*vF* z9q)Ml)b4>>Rpj6qkdxo5j?UV|fUxbo=LZ~-Ie5=|-ZMV*p${FzjL?NfA#@1KPyY9g zg53MORZ^ddlMDvi_;p|RbqArxB)oU7^;Nt?u46~@tr7uSrQob$$!Y)X-~R1kpRq?y z3P4P&*P#d(()fwnUK5PTfO9q;+t=A=5a2E)wQ!zAde&mA!k-Qf8iH?;W5woE&$86n7qTNz!%5aYV)E7X!-oD zZ++|WL*vpss?hhZb`wNdve*RB7dof#f2Tc$!6>jF=Dj(n#s%^epDa-xy?9B5=KJtt8?K@|L&U)itV;mjJ9_KZq(3P`Or{3EN)V zb^;h=n4+?&y&gisDW|>O>cK2^eldeGu5JGrE#P9vZZW+m_+pbVREc>~wWk9{Yjt37 zo!3yzeiksnZOXF~_{dNGjuc0lBgJu_qN)L12v;-w5U9ILbew4h9u)VbU;3rT4RNJt z%tB_12?Ol3Kf2rkF5af7Q*Tzeo0iH@JP=!iWbr*QVhDkXyegmE3?|%2Y$6x)`U)_D-e1|WLs?K-N%+EGZ z?Bo2SKl-EhmjT4Ew^G-68Ql_4!Kd|PtlC(mGg-DF*mOdd6!N)~y)}vh6fhl3m7iR{ zt5CkBe@rSjFdLRehh<6b*cS(f%mN{^5NOB$O_1C_k{s|ERULdn;3MBfO+(~JG4Y0( z9i9>RF43%C-jtg~9Xb@cRjDfn>gf9P@{jxd{8Bv-B$YUJ+^M13D+90dZS@E0d&oW8 zD+KBc)syOm*oBU(iYa!*nVoUFcx8qHO#WCs;1B*_eDH%GJS3Ka zjexBX8TF0utyTT`=@R=M>n0!VfV+W{CC93`ZSP+FI<~55+SH{!Qk);VFnCNo831!9@z9{{7$o{lk2a4b129 zc=A615D4s;!*~I})rST0ysA!k#+?o}jGJvdtFwWT@S@Jv3(A2)Z- z88b_$C5B^Fm8t_1i;DBybmuhL0p~nv0&r26C8PgUsmn!ie1N0ZM!_s$!Vo$+5!R`XE1AS|&Nva?oyjjkKTLM)^?#C9o=(W@ED z@$J?+L!nO>2R@l>(#d-gpc$$&ds3~kMybd}`El=#>p2?=v04kDf>WfI)MBm;QFDFf zH~|reoH_nkF& zDatwTP0`@&|(Q5eMF4_gf*dCa@IAN=9lByP1ptpf{| z_C;%@{Q}qORk~H_ZhvPxZw7gyF8v)#e^Q%|Uer>y-a-`^GW5jv)BP)Pv!$6CuW@-aw1zuY*clC7s#6Byq_+zZi4rmJ-TMy zYV(g%_3L(q#@{=S>fpZjfVkWi!zGUG-P}C!__IT3btpABb7u^=R#j|R8+d%a&P3un zu30K;1%b_s4t;Xu^AIoZ0+C50hR7js2%XPbl0V(UMBN``t7hu`3|D!31fS^D8r!Wh z(}P4s=JT^!H-vdYY>2|lCXjhscgl({g4L_?X&i)`;$9r6=#bh6=N|W2R8g(x! zZCa{obghMfvB`NKhMSEkCuH@a?ogBgqHd#nKRW~X5 zN%?IE3VsRbqQ)yfsPPwrpiRsMrKXeTzp;(q|6cCvEQ=?=QLt6zFYWKS$tz7_K5^R^ z+hwtiLp!L#psrQA)5(mMKqP-m)V^=a*Q%1`ULjs0URj=sT3&8z zp8~#+WJ+)!NwT#WjKbLHdPtz@6Sbk*qhm(D^QqQ7S&BO=MKT~Nss{?d3W3}vnTKAq z@O_q2G=q6^{rg%itA@&coEYgG_$bsq3Y0mONt6s`>G%SrKT*{LLrA5Dzz{j-IIKA% z@Q|v0PFp*(LWRxL3@}?LXTsTkA7&}Uc(l8EPj)Y-*|pg1a$69`;7~2N7)~@ec)@ed zYW?US#8stDw%-u6qf?`j3--Y`)n0S00nef^s}ms?Es>3s23ZGu<+~6w=SN*zl1WfJ zBC@3z^AqAGjrli!^EdaJJ>nX8F4*W9o$S>#(a)FKNYp;`Ri&-0M<%>?QC~`YafwA8x z(ODPfS(==09ktw*6&5U1H+S(rA<0FNv9*f*J6nWA9a`XY!v)t*?Tovs{e&w|ThbS0 zvOvO4m$F{kpbV)?Tp&cw`H59$Tq&1+?Q38A6!2m(cgW)(VyUB;S*-pi^;>XqnlM7%UfCEYI{h-(lEm8-W+?a@Hb$x`p%w? zdzJK_YO%mY?G{gJr{UD*uIiUd^=7$NO?QZw@dsh&H$;wh3z1pe29aB;3=|=9eh17Y zCZ2iA{6;GzZJGIsPaa8T4xRY0eP64B$FA9si*{Zr*x-PL)i|n)#;PWC)Ji7cTDzV5 zew%h-UsM&^w%Y1+uVuKMfN?mrT{DBLOKmpJVN7E8Zzwv3#*5TNx{_i<8+`E>fAM{Z zD$+K~X8B+>M(&qLTHY`BBmfiwXE{zj=brX_8no_{%3K3r=I!24&hCb}68~{isq3Q9 zB)0M$0{4}cU4c3t@?F# zj+mD`P3qU$b=s8>@wpA!)?1N&%5ejfBDvf@#EEUD%K(|EnFMpdpb$Cl6}6wwg0d5I z2@uQksRG6O@TyA)XRYO+i9^*Khrl?5h?<}{k5%?o#g;g_+=I3q1bL!P^PLOKIrd=dj|q zWt7ZaZG&f#t>)6Uw?6u_r4l3!u#Da>Y9wp-iAe(#g)nh%Lgc(p+^4KG1znSgdSu@r za(?0;|IY9H&fVwEI1*V4kqP@!=IicAO#{zI2e^6*T1M7*|^9;a;*;jp1UFN*~~xK z;EWG0)#_m=+2FNQInVW9i(=hRv@ey_izV&&y*!;S-m5!sy9$>6u2u7SZO?|hiVmf6 zfXpLVsfLL(Q?VI_Nn?OWcE0yosAUoeS+u??ZqnP{_O?Sa1i)nugWf~r{nm5_y1>4=t&FDa9?moAP6Z)R$4d|N}kkQXd>;ZATd|2TJz=5=w3}_{h}tW z1O7G-CbAjBzz^nOfqMYc^;Z9t_ax=WlA2lZz*0esoEsqbo4@&+2jkEOEa*Cf&O5Q* z+2i5I@4Bh*(a&jfXJ9#VSyxXuHlm4sQooY8072TgQ?{|Yf*lT0ba&uw(mEoaVf!{^ zq|C`S4OUOh$yRj+eh%wewb4B(2s;2X{xadnxIp=yAJpvZ$USmS<|#Hgk{c$;eM0b< zgp3^^hZc0DswMl8t2Dz-yJGX6le>VmzK3JT^&0VluuWh`u$Jl>cF|JY8Cq_4%Zx(& z+1as0JKmVcC#|e7$9K@+q;*+m?KBYPinPUchREC};EUSpS$NYn8A;Bw;zGsuW_4r& zAt7=!9>syI{r0=Vn45aVv}86z_~%lcldbaCP4!~l1Wah{_>L6Wa*KD*>6dWRofKow*&{1gk7ePf7Mn$Nc>feUEJG*BD`IJ4y!XyVB532XNVXzn+53* zGw&Hm#x4^SnWY>d0+F%U_XI_v=HrgVK8MH=5~@IlOl2oOOb?qM15Fcosh|=6GhcT? z@00rIQK)UZReySixR#bXn7eMrHh!xL3m&1wO-AiH{q6wn{yA_H>A}m z%n&(*h%cLW3xQ*saeJuT?B~H$VUC;=g2q?Qxml5sIg4b6*triaH@zgdkG=fn8L;Yc zY?I)tk zAWuo0`i0vB&Hiq*Z7tT3Le5>`#$BOLt3ITh!Za85F_}b}9Fx7&1|#-=Bk|9jpI%37 z4REc};s#jPT3E-6b`1Qg5@QEq@;k&ybT0`DT&cWU>~bWUaO5n8y;OKJ#C!YO-+s7L zQP;UPKrH{~zGSQjH+z@;q0FAQh}aeJTPn}o$)T41jwu|p*(zO_hf#peH@nY_=t}3TtUnc0JH>}V*dcZ#Ipd0! zh^zJ}pz{CnSTD6rJ<%h2eC0P4h<8A+-t6Z1WJA``n|W_f6L(W}iZe+@#(PD1uIiD} z?nD#xkqIz}qaXtN8G8y72Y?x6Hvsb(@Y~}rM{UQh$3IR`ByAo~w-VrInV$a_$5CP8 z4n6i?^Fv!@!*&8B|Mld*#_74|S5=<0Mr*BQH9UiUW-c>+L@*Q7?uga(No%7zD%Pk! z>OLvMajW=<%BEQi*(O4qCbU;EqZIC&NmWXx$!CNDB0%WZoWE~qgX)i0^ zXBJ@T?`PWNxSwyfS99>FB?W9P&!=C?Wx;2) zxNqH(YpD_m)8KWu=ZQgJcM)t)!?|*s zG?>;cM7Y00!$Yf7tl1X&jE(VhZWu`p=dx}7uKFv76p_0CR zx*YNpI=>_shLmjT?MZ;)RLgA*fGn++4hG`I?wg7U zN9AD~she3$9OHsc;v;p5TIF|aE4%)nvdw%;mGqEijL*#GVZkmTw3n*7T}8V*vyi9f zsMosn;dk!q9dFnwZF_*rrRt3!7Vbs2y%@5Adxbss%C2lGw)5vM955kgcG{9Y zv7ZIO=k#}v&g=25RS9IG-3{4{c7JE2*Qc~M{__e|m=bzac~xhCp5KYVWhid$+eW^HET zm}rl{na}h0eb4^HL^|eA`%c4XRf){rkSGvRPRdb@>;82h}}St-&@sjU2BD7 zN6u{TjOa6-xKl}55CIFZlY)fVX0DW_gPr5DbcTH`V(+8IZKg<>(qns9?X|VVS}iJ? z&`TlyN6auL!I+sAxDy|VwJl}8D|Z;$CZ)Tpk`N~~ui>|g223c_+ez!bETIN_ZKsV9 zhLC)#dJt)(85N#q(l{n#>)qa3-2tjnNDvnjl^r5S+Zl&=tedJYFV(xuL}|wA=UXaU zVjrpbM;c@83QIZ_(Bkl_mH!@aXGiu5U3SIdQr(4GdW{xa^{>jJ&0eY}f^*_EM7@Tv z__(-JE|i~17t9faof0EfW)K(wMSGkaEFXGAY(K+V%{F#shdoc0;9273QzYG)BGyoy zZ(Ho+D`WrMyM!%}_Q@hIxD&LFJ-(>7HW9M+kw>9(13$Z0_7WPI6S9pogCjCtR#;GC z3y~cR;@K4ON1F40RCfLkk=Zn6MieSV7p*dBhBg6PihCSQTz@}1TI%orn*-=nz*^@~ ztsREC{qBm1@2Yb;YhOqEyF=j0X}5MojLZ$ZI{0E23PL$n&VJMM0HgUi(K(jM%v2*K zMrrXFHO-b$A#&`spKB&T7);zIwx8Gb+S<(6IIZpm(yeukq%Sk_TdvOCIsIca+V zS}w4f@uF_OqbfGB?^msLHpgA6eREGbhwX9uj^=cvnd|dDDZnR=O)T0IY|xZ<%Y0>Hbi_YkWB1l-%1wjoo&xMD{Go`N$dueCIH3euAk zm7bqej6`~0`O?741=w3L!*E+2k(H)Ed)c6G(unnp9O+^fTt~CzQOs+klPSkY+FcTY1 z6{5y(K6Y^Z?9VzhT};7A&1pQzb}wf=KDR25B2_yBXlvK- zsT}`tmZyshte)4~&tuSCQUC(lV9+u)KdFUhMYH^$v5c z3W1J^UG2GO$-Y#J>oM`GE%_#}rFSL_3n9>`?MQR4rE0?UiU&*iUI`AXS8Vd8r6x>R zOPEc7t4n9W71y7mVrhsIqEP*TyJWQ66UefhxKuP$k0hi1KXGed+k`Sf5Qs-U?kIcB z|1gm6*O5jea{ukal5NbCpK~69q@Y6izsr1^!;tLLq&#=#F4-1e)s+2QJM3^COLm?RgLQh*c7149SZdcA zbeN+F+gJw+vp%|7T2Ms_uJ02CR$5t{*Qm1gO!(U-b~;opuGsk2-;4M zNkHY1_LzaTv%{8NbZ&sc1xQ~?d{gP!A<$j9XAjgqLT7iRKGNG360 zE#vz6pa1zI)75HwMkt=WR`rR|^bodLln%9BwosXoTEfr7wmUTlL0Mm9cf$U3=W~}@ z5Vlom(NJ<@7AJO_D>7+*$$qH-rr1*!vawx0w)eT(^xQu=H&PfvCp(39pzXxzwtr+8 z2gbmsUvG6}D{Ve}IbAj4jGQ)SE#JFPiE;g%v_MNHoVdBCN&67Uc65oi>aqp<| zp(d|qRn?@UGGiTp*s2AUT_MiTy+fGmt%XzY1_2Tm7Fcz9Vl$@HV~5zUd)@2q z{nBzhRiZh5`ImqB?j?i?Qt;0LdK#zGEq~RzGM5=S@^w<(HcV|PHqp)8t+r1p&2E6& z%yEFRC$%})r8>S_jqIvV9`dn4<$x{6ul@V1PJ-t6lh!uheZQOf2juM)g=0r~v`#)F zc`@5O%d!679{0=!n=Z2hMmbX)Kx7O;U9=B{b^lu30*0bg22m}Qn@Vf|N!QvR!sxms*)PO+ zsD>e~9m3Eo)wR{F>Ue(q<`6fMoAY7<*qE%kmSk6J`|=zKPN65VxSBJ(!M%H_&qgX{$!)M)`g*zhEYo!g;`Ad}kWc z^|Dt>&2zU6*+S@X>v>8y2lflGW;2+~b*x~y+`0{iSpvdXt--rlJtN@_JR3N*_G#j^ zEw*}YkHXsP>@5aPY8&MW&ZUY6I}l>R_-Jrvn{7nDP3pB*E$DVt3(^vm9ow4!FSmxN zfG{_FYl-Ij6yCFcWIm5}xZYY%Tx#23N5wF2G}X!Hk?=5`8}QIdtEG30r}N(}tusRV z|5fEuZ4R6Q^?eR{x-P6Gu`?@3b=(QknJ@v@8zoypcNH3T8nThJiwb8NVcaTJGj{f! z2r{M~Y!)D~TazQi$@P>0v!znrEB~_?&=Lc~VJ>n`P=6917ws0m-ZpE+^vzUqGvO$8 z+XS}&Aia5J7}H%GErb@#dnwVP7E&iIv*ASvd^ic=mv=;`6Z2+<(}SdchY_2>Y(_$$ zOtAe<+#}m(Pu8NST0m0>%}R~fW+a*2=nPJolU`IGhKL&f#BF04XOqa7wYxJT@QXPX zxmI~z-XTBK`QaktR+7qeYn0@3PL)-lPAL+~B zsffvoMQxM7Fr!-K@yukYqWTjcx!$%|WHBKdmrxw(dEtI&gkD3OA5!WVxk%UHw9f`TBk0{sYy$QxFlf;N!zh@D; zwarWe@-Ebp=bbOr0N0wf11Dr8!3pb9vDD{2rwJ*jZ|9BMOvPSW}?aKy)kwnS=N2TViyI+H_QR%*nJB#62l7lp}B~eTOE>_3? zheDGa249TVZdgmaZFduXQGT43(v%pVwSmek0=8C&mH)voN6uhZgPP;-?Ru9CUh?>E zJYOteVxW$RuFWbX;I8KUO=Z1n<-5(XT$2Gu8W;KXQQHJ5y2@jb?~K}}x7dEmBh%B1 zwve=`rZ&#+db*YSo>WlZsZ-{1$TgfDkAN7t0wftWl+a6n{Io&s^HHP0ii&g0F*qFZHin^-TpK84v|A#8nWUu)Bp2w z+vzpW2m=5-31RNH%GHy0;_cjGV<$S$TA5SbZFB-i6M3Cf##g3+DI8GNjl~(3aNVXT5Ts9Mu&q; zCJk(3o1+(AQi|O(^D9cE_Hp{l@tY$V13arx2?PeAS7W`r`9>EDz zC}~7|=Pu>tIwmGIFN6)~3IPFOk<9qcc{hR|MyqxKiOA8}2s=+z4L+=65s$#38Q#o` zDLJXC$3VE>*UxJj+M_z}T2IX1?ISy(&}*?))obIkIk{n?o7giqVXvjQ))?4R#>XaO zOYNoU=dLP{_|OMGbgU8yVgKo$|LGu5?w^cTq#PG!ne0;Qi3^b98ul3RPP#6?WGTY~T?~rG&buy9jJ1NAEef(nwQE<@! zb*V_ Date: Thu, 14 Jan 2021 16:58:10 +0100 Subject: [PATCH 055/300] Properly space swich/pots check text --- radio/src/switches.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index bf230850d..727b05096 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -663,6 +663,9 @@ void checkSwitches() } int x = SWITCH_WARNING_LIST_X; int y = SWITCH_WARNING_LIST_Y; +#if defined(COLORLCD) + lcdNextPos = SWITCH_WARNING_LIST_X; +#endif int numWarnings = 0; for (int i=0; i Date: Fri, 15 Jan 2021 10:25:57 +0100 Subject: [PATCH 056/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 281e77ace..d068a43f0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2095,3 +2095,4 @@ Conrad Young Martin Mathes Jens Rainer Hansen Mark Trible +Marc Dubois From c139c6eefdf64292fb6d55800c15be09d5e41b5d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 15 Jan 2021 15:22:39 +0100 Subject: [PATCH 057/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d068a43f0..41ce9cbb0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2096,3 +2096,4 @@ Martin Mathes Jens Rainer Hansen Mark Trible Marc Dubois +Alan Wilkins From 1f01fa6d47b6c0674581d3580e9dc589cc2e6a85 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 17:19:25 +0100 Subject: [PATCH 058/300] Radiomaster T8 (#8219) --- companion/src/CMakeLists.txt | 2 + companion/src/companion.qrc | 12 + companion/src/firmwares/boards.cpp | 16 ++ companion/src/firmwares/boards.h | 10 +- companion/src/firmwares/opentx/opentxeeprom.h | 1 + .../src/firmwares/opentx/opentxinterface.cpp | 23 ++ companion/src/images/simulator/T8/bottom.png | Bin 0 -> 16616 bytes .../src/images/simulator/T8/left-pagedn.png | Bin 0 -> 14872 bytes .../src/images/simulator/T8/left-pageup.png | Bin 0 -> 14853 bytes .../src/images/simulator/T8/left-rtn.png | Bin 0 -> 14883 bytes .../src/images/simulator/T8/left-sys.png | Bin 0 -> 14832 bytes companion/src/images/simulator/T8/left.png | Bin 0 -> 14620 bytes .../src/images/simulator/T8/right-dn.png | Bin 0 -> 15515 bytes .../src/images/simulator/T8/right-ent.png | Bin 0 -> 15472 bytes .../src/images/simulator/T8/right-mdl.png | Bin 0 -> 15552 bytes .../src/images/simulator/T8/right-up.png | Bin 0 -> 15620 bytes companion/src/images/simulator/T8/right.png | Bin 0 -> 15355 bytes companion/src/images/simulator/T8/top.png | Bin 0 -> 3878 bytes companion/src/simulation/CMakeLists.txt | 2 + companion/src/simulation/simulateduiwidget.h | 12 + .../src/simulation/simulateduiwidgetT8.cpp | 67 +++++ .../src/simulation/simulateduiwidgetT8.ui | 258 ++++++++++++++++++ companion/src/simulation/simulatorwidget.cpp | 3 + radio/src/CMakeLists.txt | 4 + radio/src/dataconstants.h | 6 +- radio/src/gui/128x64/lcd.h | 3 +- radio/src/gui/128x64/model_curve_edit.cpp | 2 +- radio/src/gui/128x64/model_input_edit.cpp | 2 +- radio/src/gui/128x64/model_mix_edit.cpp | 2 +- radio/src/gui/128x64/model_select.cpp | 6 +- radio/src/gui/128x64/model_setup.cpp | 97 +++++-- radio/src/gui/128x64/radio_diagkeys.cpp | 11 +- radio/src/gui/128x64/radio_setup.cpp | 26 +- radio/src/gui/128x64/view_about.cpp | 2 +- radio/src/gui/128x64/view_channels.cpp | 7 +- radio/src/gui/128x64/view_main.cpp | 15 +- radio/src/gui/128x64/view_statistics.cpp | 12 +- .../src/gui/common/stdlcd/radio_hardware.cpp | 54 ++-- .../src/gui/common/stdlcd/view_telemetry.cpp | 2 +- radio/src/gui/navigation/navigation_x7.cpp | 29 +- radio/src/keys.h | 5 + radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_general.cpp | 12 +- radio/src/main.cpp | 4 + radio/src/opentx.cpp | 19 +- radio/src/opentx.h | 18 ++ radio/src/options.h | 3 + radio/src/pulses/modules_constants.h | 8 + radio/src/sdcard.h | 2 + radio/src/simu.cpp | 9 + .../targets/common/arm/stm32/adc_driver.cpp | 3 + .../arm/stm32/bootloader/CMakeLists.txt | 2 +- .../common/arm/stm32/bootloader/boot.cpp | 5 + .../targets/common/arm/stm32/pwr_driver.cpp | 2 +- radio/src/targets/sky9x/CMakeLists.txt | 1 + radio/src/targets/taranis/CMakeLists.txt | 33 +++ .../src/targets/taranis/backlight_driver.cpp | 8 +- .../targets/taranis/bind_button_driver.cpp | 100 +++++++ radio/src/targets/taranis/board.cpp | 2 + radio/src/targets/taranis/board.h | 37 ++- radio/src/targets/taranis/hal.h | 127 ++++++++- radio/src/targets/taranis/keys_driver.cpp | 9 +- radio/src/targets/taranis/lcd_driver_spi.cpp | 2 +- .../targets/taranis/usb_charger_driver.cpp | 4 + radio/src/translations/cz.h.txt | 3 + radio/src/translations/de.h.txt | 3 + radio/src/translations/en.h.txt | 3 + radio/src/translations/es.h.txt | 3 + radio/src/translations/fi.h.txt | 3 + radio/src/translations/fr.h.txt | 3 + radio/src/translations/it.h.txt | 3 + radio/src/translations/nl.h.txt | 3 + radio/src/translations/pl.h.txt | 25 +- radio/src/translations/pt.h.txt | 3 + radio/src/translations/se.h.txt | 3 + radio/util/build-firmware.py | 5 + radio/util/fwoptions.py | 11 + tools/build-radiomaster.py | 12 + tools/commit-tests.sh | 9 + 79 files changed, 1051 insertions(+), 143 deletions(-) create mode 100644 companion/src/images/simulator/T8/bottom.png create mode 100644 companion/src/images/simulator/T8/left-pagedn.png create mode 100644 companion/src/images/simulator/T8/left-pageup.png create mode 100644 companion/src/images/simulator/T8/left-rtn.png create mode 100644 companion/src/images/simulator/T8/left-sys.png create mode 100644 companion/src/images/simulator/T8/left.png create mode 100644 companion/src/images/simulator/T8/right-dn.png create mode 100644 companion/src/images/simulator/T8/right-ent.png create mode 100644 companion/src/images/simulator/T8/right-mdl.png create mode 100644 companion/src/images/simulator/T8/right-up.png create mode 100644 companion/src/images/simulator/T8/right.png create mode 100644 companion/src/images/simulator/T8/top.png create mode 100644 companion/src/simulation/simulateduiwidgetT8.cpp create mode 100644 companion/src/simulation/simulateduiwidgetT8.ui create mode 100644 radio/src/targets/taranis/bind_button_driver.cpp diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 3d27550e6..3150a86ca 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -345,6 +345,8 @@ elseif(PCB STREQUAL X7 AND PCBREV STREQUAL T12) set(FLAVOUR t12) elseif(PCB STREQUAL X7 AND PCBREV STREQUAL TX12) set(FLAVOUR tx12) +elseif(PCB STREQUAL X7 AND PCBREV STREQUAL T8) + set(FLAVOUR t8) elseif(PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) set(FLAVOUR x9d+2019) elseif(PCB STREQUAL X10 AND PCBREV STREQUAL EXPRESS) diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index fbbacdb48..ca17c812c 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -190,6 +190,18 @@ images/simulator/TX12/right-mdl.png images/simulator/TX12/bottom.png images/simulator/TX12/top.png + images/simulator/T8/left.png + images/simulator/T8/left-pageup.png + images/simulator/T8/left-pagedn.png + images/simulator/T8/left-rtn.png + images/simulator/T8/left-sys.png + images/simulator/T8/right.png + images/simulator/T8/right-ent.png + images/simulator/T8/right-mdl.png + images/simulator/T8/right-up.png + images/simulator/T8/right-dn.png + images/simulator/T8/bottom.png + images/simulator/T8/top.png images/simulator/JumperT16/left.png images/simulator/JumperT16/right.png images/simulator/JumperT16/top.png diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 82e645d05..d6c010814 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -87,6 +87,8 @@ uint32_t Boards::getFourCC(Type board) return 0x3878746F; case BOARD_RADIOMASTER_TX12: return 0x4178746F; + case BOARD_RADIOMASTER_T8: + return 0x4378746F; case BOARD_UNKNOWN: break; } @@ -114,6 +116,7 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_TARANIS_X9E: case BOARD_JUMPER_T12: case BOARD_RADIOMASTER_TX12: + case BOARD_RADIOMASTER_T8: return EESIZE_TARANIS; case BOARD_UNKNOWN: return EESIZE_MAX; @@ -149,6 +152,7 @@ int Boards::getFlashSize(Type board) case BOARD_TARANIS_X9E: case BOARD_JUMPER_T12: case BOARD_RADIOMASTER_TX12: + case BOARD_RADIOMASTER_T8: return FSIZE_TARANIS; case BOARD_HORUS_X12S: case BOARD_X10: @@ -232,6 +236,16 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index) if (index < DIM(switches)) return switches[index]; } + else if (IS_RADIOMASTER_T8(board)) { + const Board::SwitchInfo switches[] = { + {SWITCH_TOGGLE, "SA"}, + {SWITCH_3POS, "SB"}, + {SWITCH_3POS, "SC"}, + {SWITCH_TOGGLE, "SD"} + }; + if (index < DIM(switches)) + return switches[index]; + } else if (IS_JUMPER_T12(board)) { const Board::SwitchInfo switches[] = { {SWITCH_3POS, "SA"}, @@ -568,6 +582,8 @@ QString Boards::getBoardName(Board::Type board) return "Radiomaster TX16S"; case BOARD_RADIOMASTER_TX12: return "Radiomaster TX12"; + case BOARD_RADIOMASTER_T8: + return "Radiomaster T8"; default: return tr("Unknown"); } diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 1e1588485..fe00e77dd 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -53,9 +53,10 @@ namespace Board { BOARD_RADIOMASTER_TX16S, BOARD_JUMPER_T18, BOARD_RADIOMASTER_TX12, + BOARD_RADIOMASTER_T8, }; - constexpr int BOARD_TYPE_MAX = BOARD_RADIOMASTER_TX12; + constexpr int BOARD_TYPE_MAX = BOARD_RADIOMASTER_T8; enum PotType { @@ -224,6 +225,11 @@ inline bool IS_RADIOMASTER_TX12(Board::Type board) return board == Board::BOARD_RADIOMASTER_TX12; } +inline bool IS_RADIOMASTER_T8(Board::Type board) +{ + return board == Board::BOARD_RADIOMASTER_T8; +} + inline bool IS_FAMILY_T16(Board::Type board) { return board == Board::BOARD_JUMPER_T16 || board == Board::BOARD_RADIOMASTER_TX16S || board == Board::BOARD_JUMPER_T18; @@ -231,7 +237,7 @@ inline bool IS_FAMILY_T16(Board::Type board) inline bool IS_FAMILY_T12(Board::Type board) { - return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12; + return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12 || board == Board::BOARD_RADIOMASTER_T8; } inline bool IS_TARANIS_XLITE(Board::Type board) diff --git a/companion/src/firmwares/opentx/opentxeeprom.h b/companion/src/firmwares/opentx/opentxeeprom.h index aabbe2284..fdb5247a5 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.h +++ b/companion/src/firmwares/opentx/opentxeeprom.h @@ -37,6 +37,7 @@ #define TARANIS_X9LITES_VARIANT 0x0801 #define JUMPER_T12_VARIANT 0x4001 #define RADIOMASTER_TX12_VARIANT 0x4002 +#define RADIOMASTER_T8_VARIANT 0x4004 class OpenTxGeneralData: public TransformedField { public: diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 04f02eb29..f0d50338e 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 Radiomaster TX16S"; case BOARD_RADIOMASTER_TX12: return "OpenTX for Radiomaster TX12"; + case BOARD_RADIOMASTER_T8: + return "OpenTX for Radiomaster T8"; case BOARD_TARANIS_X9D: return "OpenTX for FrSky Taranis X9D"; case BOARD_TARANIS_X9DP: @@ -339,6 +341,9 @@ int OpenTxEepromInterface::save(uint8_t * eeprom, const RadioData & radioData, u else if (IS_RADIOMASTER_TX12(board)) { variant |= RADIOMASTER_TX12_VARIANT; } + else if (IS_RADIOMASTER_T8(board)) { + variant |= RADIOMASTER_T8_VARIANT; + } OpenTxGeneralData generator((GeneralSettings &)radioData.generalSettings, board, version, variant); // generator.dump(); QByteArray data; @@ -685,6 +690,8 @@ int OpenTxFirmware::getCapability(::Capability capability) return JUMPER_T12_VARIANT; else if (IS_RADIOMASTER_TX12(board)) return RADIOMASTER_TX12_VARIANT; + else if (IS_RADIOMASTER_T8(board)) + return RADIOMASTER_T8_VARIANT; else return 0; case MavlinkTelemetry: @@ -954,6 +961,11 @@ bool OpenTxEepromInterface::checkVariant(unsigned int version, unsigned int vari variantError = true; } } + else if (IS_RADIOMASTER_T8(board)) { + if (variant != RADIOMASTER_T8_VARIANT) { + variantError = true; + } + } else if (IS_TARANIS(board)) { if (variant != 0) { variantError = true; @@ -1294,6 +1306,17 @@ void registerOpenTxFirmwares() registerOpenTxFirmware(firmware); addOpenTxRfOptions(firmware, FLEX); + /* Radiomaster T8 board */ + firmware = new OpenTxFirmware("opentx-t8", QCoreApplication::translate("Firmware", "Radiomaster T8"), BOARD_RADIOMASTER_T8); + addOpenTxCommonOptions(firmware); + firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support")); + firmware->addOption("nogvars", Firmware::tr("Disable Global variables")); + firmware->addOption("lua", Firmware::tr("Enable Lua custom scripts screen")); + addOpenTxFontOptions(firmware); + registerOpenTxFirmware(firmware); + addOpenTxRfOptions(firmware, NONE); + firmware->addOption("bindkey", Firmware::tr("Allow bind using bind key")); + /* Radiomaster TX16S board */ firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16S / SE / Hall / Masterfire"), BOARD_RADIOMASTER_TX16S); addOpenTxFrskyOptions(firmware); diff --git a/companion/src/images/simulator/T8/bottom.png b/companion/src/images/simulator/T8/bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..055c7db62cb0a0eb7c7e030bd50d7b2006ceb716 GIT binary patch literal 16616 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-pagedn.png b/companion/src/images/simulator/T8/left-pagedn.png new file mode 100644 index 0000000000000000000000000000000000000000..6b290e3688dba87820b9ab96da45889e961595ed GIT binary patch literal 14872 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 KMNUMnLSTZhF8A~R literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-pageup.png b/companion/src/images/simulator/T8/left-pageup.png new file mode 100644 index 0000000000000000000000000000000000000000..c155649a1a55a508822ffc212c29d86f0b550258 GIT binary patch literal 14853 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-rtn.png b/companion/src/images/simulator/T8/left-rtn.png new file mode 100644 index 0000000000000000000000000000000000000000..95cc700f64757fd7fa98a195b23efbd7c9ab38b0 GIT binary patch literal 14883 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 Vy*jtNAOZjY002ovPDHLkV1jd<3vd7c literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/left-sys.png b/companion/src/images/simulator/T8/left-sys.png new file mode 100644 index 0000000000000000000000000000000000000000..7943ed93d40cc6a55e6321a21861e1049c43eae3 GIT binary patch literal 14832 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 W!^Y(+vx)Qo0000i2M&yIs1b&t5fX;@ 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

Hj9b2td-REB&)<6F7kGp}v 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 O0000Z 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 e$!3ud5&s|S0@M!;dp`C60000~ literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/right.png b/companion/src/images/simulator/T8/right.png new file mode 100644 index 0000000000000000000000000000000000000000..1016d8de50338748ea2e24bd02d738dc037e3d74 GIT binary patch literal 15355 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 literal 0 HcmV?d00001 diff --git a/companion/src/images/simulator/T8/top.png b/companion/src/images/simulator/T8/top.png new file mode 100644 index 0000000000000000000000000000000000000000..962119223e77db691f915ee748df48155614091d GIT binary patch literal 3878 zcmV+>583dEP)Ww(1;p2>bZg?D9V&5SKeZK)-D zO-Mg}{Ky6^=WHn@i&}YJ+OjOI-}QQJ_pJ6|IezWqc`VQA{c+5!skyL~dHkOL#(r!A z%8KN=erU4=7~yknN_V$A(i?yCeU``fRDZQDPnFBE{Mu#tGsID?mAa?=0Cgx>BVNiT zmXFWT583X>UiTFr%le|duAhnxw9osRh$H{3=2dvk;->VueERgse*gY0AF@zZ%mHA~ z(GYGP5C}vR=xhWU?Ybz3^1j}`*oXQV1(^{EVILodcB%#D1>`=AZ4_KM59&C>mDh;p z3CWX~mj;{p{JZ_4l7Yf20!ZSj@5xKpBs+(RkFM z^ee3>TjutAKHl%)!ZN5vj2JOB5?qm6`uRK@{G3TSm=JtkU=x{@^>e2zdSDe`6o4ww z%1;^RV^N-eF3+NDq{xg3N@X8h6Q3&ze%9ITVx85xAUU$0KHh*JFc{p2-GAK{*VhCQ zD&lj$WU}+mF5eqshHH#?;yN=a7ytM5dVG9rbEBPZI}^WAOuM|x%a<=(@T>A2Sw%6n zi44dNje+Ba;&|nFEE|m~z_P)d^*AHpq9NOJrbIcM$o`uSKK$AqRb8On7hN4aWE(cwKvRRAK!YO94=*w=TabB2;S@~;S6$Jg&+ z5Tc%p0VXT%&$@AcB%&yhMA*A5$Cio437_MLW1{F%eEah1)vJ|B*pU^ha@KYhRso`d zp8;k7m=FxIfe|AsN5gx_^mvVojq(so9EVQK@;GSx2XR8Xagot+xvD^taD89Ac(Kbc zoGnxZ!uBvxxD2f1RB6wC!8Sx#a3DNIXLo4MwAf#rC*XQ=V(>9o`S`XMWk&?S1i@8g zqH>$tdY0#<#KB-J*QJkFJU(x9kivj^<3&>aIm0vCqa&c)@>BHl5)WA& z1B0_1&uv7c$~ld;afliBQ?j`dh!TRYRircGgC#mA3a&H~ZT~pa&Ju!eAq^@et_*xG zUyBkrI3N2zV&LUES1&3Xo2ZohIr=llC=)7TSDtaz=;U|#{Q0xp-`@}W9n419a36z4 zsLZN@z!fP!Dx>3^m^~FCdE@yVINsBtW1vHEz@ZJQ0*Dp}ulb&ds)92klA+3EDnqy( zn7NEfK6DOV4-RP7pSMUH{5~IVL5^e>nXWTdkBr4kXY>WG1lVp4F0Mqm3SnZx3IWNE zpQ~*ilPzyc5|r{NN7=*UjfhkE9omVytd-=)j~{b6^y5-bWh9kd)U|3HynfADoE2A2 z#Ek<>v8YiAWKM0FM`md#j4)r5)aiO2CAj5S_I6_AMv<|(q-q0a`n z>dQLty(l=&V8uFeQ*u=K&Gsu|XFW~$RCF$5g82C}Z#~|;d1LAJ_BMb0`gLd6-}blt zZU4XR_V#w$^Lv@0n3h|VWz{&fcQMNP+@IM`$yC>~{y4wND6^7$*5^!9S!#yWQ9F~3 zo{I08Oc&vJMz%T>Q*Ea68Gks!@2HYgRmQ*7V^-lEVHveZ=1dOH<^4+BUp{>JupUbA z0;x6&oLO+s%4JsmEXcD(Q+lyJYTV3`qEa)OpRqWS^As6M$0O3_KG8QicEWjieziS7 zS!WMWmCdZvQ`St$o3$hIo6QjgzZx6)q>3T7LCHItkZ|0)ckk@7*8buoy%}Nkna!Q= z%%sZT$k3S#Z?o^HXJh%4J|Zg4V9~y7MsATqWjct#dl6w^<9pFL#@Q-^_ohlG*5|D3 z=qtcXXFWFYg_&GCAeEW^vr0hO&wFe&AKIV@mB$=ahfzj<>R8nM?c29@DYbt&`P(VA z27W#G$+g6{Wk&+DvL9te%x%PAYLP5(LUrJ!05T#Pey$u+Il_?xoXJAGNEi^AzB*Dk z;oPVXf0rNGkm<#usM`>FYu;ErDIiDdKoux$1I zgwmODj)LD?6d0U5*jE(BQ6&)teDs`U9#sU&1;8sgK%&_GCTN*vJ?ZTWJRQ+oKArh5eqK_ULREz+=mg*{WJhvw4I&Jf{FPtfoGh2`Mb%X#Lj^xqQATGjCMq_Ck%}c% z#j(v?61xJ4N>2d}Ft@Kos$T`LA=qu4OMp0RKg{3<}AOs}%i4E#uU zb)OxA-yuIk&??ij+$`Q~J8DOAjU1ITP8tP1 z&&AlBNeD`|vX$qFNR{WFVJCfM1-)I^;vtTDPPT$Mh#&l0fXMzM-;GEx!5=oUY&F(@ zF6%#%u{%88OnTfP~9MWKRczyg4r2yLIUF=RmdX%%5?z7F4IHb4n&z} zY3G*h-Wpte&LyRR(Q~xjN!5NM=g)pOCXMDC8NMBY-$Q7ve>x~d8!gJKx4 zK5prlMAen51h?dnDy|!b%?Zno{}B<&2749Jk8(R6ZGO_({M)FjTez=I~9?5ht|8&2aR{?qh5+1So?ABW_pXf%Q>#KcW3RzvNHjfY zJK12C7}|GUMfPul+ac-+&6_UEn?DNZ}v+;hn{5a`C ziJQEi+S2uKx0IKPy8{R2z@9&upHp0GFfYv?EIavhAacbj`MSFP0Rd$gTjVuDba<;R zEe3O6lkVrK@jxqSsxQ2I`_3-^{r=s)f4du2ETw+f;m(|_m*^gV%tixi5@4IYxeaq> z-;@Wogpy@hMndAY5kP1Dl)BR-SUnFm@3ahys17nSqA}R*uF@0!wSGvgGHTBtb6^JR zENnxD8VE1h5HK10Bq~dNMREOXR08q9ay%s)^nry$AzJR*ub1MEbr0r1Hf~8Ms5o0U zooiLL(Ou1y!e%-KjYYEvK<72m_!DNnGV9=o@z~q&M zdS3;r-VUA<+CsW#(D!=%W0!~f@{-T}Sfa!C_bLO7O1J{P=K=po2?8=W9fzzr^h3OY zLo8nWw!mUv$?{ekYU$YmVd1lf!BfXFE%hkRgVp=b$$C~Ds;KJ~D!NWG)`)j>v|i?~ zVb+3W@qG;7@6IzE9D)P7-l?()5~o?WAC|Uf`s!E%2@z@ab=%7>WSvr%-HER6Tbm6c zP;K%u)^J3W`!BxNaH#r4@2ue3jTJA^2)HOFAWtrhteQ^X4~D)3{%?aBm zK6_Acdw>6G`@^0;*WX_BwEx^OhG0nb{eBGoNnAEXd@M0D2EH4R$?**X&EoThY`$-V zci!D)uU%L5H4ZMcir+yW9p7?aY3N9RL(|!vJB|I&3EQ52C_^)4M#3G&AhrqvEX~cB z*d@@Ln1$pb2G+@Ysq4W3wA0r_B4BK>N?OZiIJBdTk5!Y)=oM<;UT|XZfN8SH*V}qk zxz|?tbz2k;4R6PV%k{SXsHCw#+~qg{4!%zK4apU@vZbvr`TX;rf9%JPJJ+t3acOY2 zX1KGIpLq61t_h&%zo-O5rdD4E0SI0nc?Pk%$KvW@d*E3#_5SryKaC#C@}Y#nIcU+k zjeRe8wYu-xMq4|A$RnjR_BZhy_NDz+miKX7bRL3jw-w{PNE$!3Y34#T_C~S+G??ZI zvf2M|uQ=+AGy3OUnO+aUG6cdd^ENfaTb(q12NV(WK+2dDAhF?L@#fQA$Sz^HO-xur oQQC>EU2_c9vxf5D&CQMd4}-c{8UZKiR{#J207*qoM6N<$f?jN)(f|Me literal 0 HcmV?d00001 diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 9b9cbc130..c2707a5ca 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -16,6 +16,7 @@ set(simulation_SRCS simulateduiwidgetJumperT16.cpp simulateduiwidgetJumperT18.cpp simulateduiwidgetTX12.cpp + simulateduiwidgetT8.cpp simulateduiwidgetTX16S.cpp simulatorinterface.cpp simulatormainwindow.cpp @@ -43,6 +44,7 @@ set(simulation_UIS simulateduiwidgetJumperT16.ui simulateduiwidgetJumperT18.ui simulateduiwidgetTX12.ui + simulateduiwidgetT8.ui simulateduiwidgetTX16S.ui simulatormainwindow.ui simulatorstartupdialog.ui diff --git a/companion/src/simulation/simulateduiwidget.h b/companion/src/simulation/simulateduiwidget.h index 66939f429..af965684f 100644 --- a/companion/src/simulation/simulateduiwidget.h +++ b/companion/src/simulation/simulateduiwidget.h @@ -116,6 +116,7 @@ namespace Ui { class SimulatedUIWidgetJumperT18; class SimulatedUIWidgetTX16S; class SimulatedUIWidgetTX12; + class SimulatedUIWidgetT8; } class SimulatedUIWidget9X: public SimulatedUIWidget @@ -278,6 +279,17 @@ class SimulatedUIWidgetTX12: public SimulatedUIWidget Ui::SimulatedUIWidgetTX12 * ui; }; +class SimulatedUIWidgetT8: public SimulatedUIWidget +{ +Q_OBJECT + +public: + explicit SimulatedUIWidgetT8(SimulatorInterface * simulator, QWidget * parent = nullptr); + virtual ~SimulatedUIWidgetT8(); + +private: + Ui::SimulatedUIWidgetT8 * ui; +}; class SimulatedUIWidgetTX16S: public SimulatedUIWidget { diff --git a/companion/src/simulation/simulateduiwidgetT8.cpp b/companion/src/simulation/simulateduiwidgetT8.cpp new file mode 100644 index 000000000..ed86889d7 --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetT8.cpp @@ -0,0 +1,67 @@ +#include "simulateduiwidget.h" +#include "ui_simulateduiwidgetT8.h" + +// NOTE: RadioUiAction(NUMBER,...): NUMBER relates to enum EnumKeys in the specific board.h + +SimulatedUIWidgetT8::SimulatedUIWidgetT8(SimulatorInterface *simulator, QWidget * parent): + SimulatedUIWidget(simulator, parent), + ui(new Ui::SimulatedUIWidgetT8) +{ + RadioUiAction * act; + + ui->setupUi(this); + + // add actions in order of appearance on the help menu + + act = new RadioUiAction(5, QList() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 40, 35, 20), "T8/right-mdl.png", act)); + + act = new RadioUiAction(6, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_GO_UP, SIMU_STR_HLP_ACT_UP); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 90, 35, 20), "T8/right-up.png", act)); + + act = new RadioUiAction(7, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_GO_DN, SIMU_STR_HLP_ACT_DN); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 135, 35, 20), "T8/right-dn.png", act)); + + act = new RadioUiAction(1, QList() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ENT); + addRadioWidget(ui->rightbuttons->addArea(QRect(25, 180, 35, 20), "T8/right-ent.png", act)); + + act = new RadioUiAction(4, QList() << Qt::Key_Left, SIMU_STR_HLP_KEY_LFT, SIMU_STR_HLP_ACT_SYS); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 45, 35, 20), "T8/left-sys.png", act)); + + act = new RadioUiAction(0, QList() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_EXIT); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 85, 35, 20), "T8/left-rtn.png", act)); + + act = new RadioUiAction(3, QList() << Qt::Key_PageDown, SIMU_STR_HLP_KEY_PGDN, SIMU_STR_HLP_ACT_PGDN); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 135, 35, 20), "T8/left-pagedn.png", act)); + + act = new RadioUiAction(2, QList() << Qt::Key_PageUp, SIMU_STR_HLP_KEY_PGUP, SIMU_STR_HLP_ACT_PGUP); + addRadioWidget(ui->leftbuttons->addArea(QRect(60, 180, 35, 20), "T8/left-pageup.png", act)); + + + m_backlightColors << QColor(215, 243, 255); // X7 Blue + m_backlightColors << QColor(166,247,159); + m_backlightColors << QColor(247,159,166); + m_backlightColors << QColor(255,195,151); + m_backlightColors << QColor(247,242,159); + + setLcd(ui->lcd); + + QString css = "#radioUiWidget {" + "background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1," + "stop:0 rgba(255, 255, 255, 255)," + "stop:0.757062 rgba(241, 238, 238, 255)," + "stop:1 rgba(247, 245, 245, 255));" + "}"; + + QTimer * tim = new QTimer(this); + tim->setSingleShot(true); + connect(tim, &QTimer::timeout, [this, css]() { + emit customStyleRequest(css); + }); + tim->start(100); +} + +SimulatedUIWidgetT8::~SimulatedUIWidgetT8() +{ + delete ui; +} diff --git a/companion/src/simulation/simulateduiwidgetT8.ui b/companion/src/simulation/simulateduiwidgetT8.ui new file mode 100644 index 000000000..6370888ea --- /dev/null +++ b/companion/src/simulation/simulateduiwidgetT8.ui @@ -0,0 +1,258 @@ + + + SimulatedUIWidgetT8 + + + + 0 + 0 + 480 + 300 + + + + + 0 + 0 + + + + + 480 + 300 + + + + + 480 + 300 + + + + Radiomaster T8 Simulator + + + background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.757062 rgba(241, 238, 238, 255), stop:1 rgba(247, 245, 245, 255)); + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 113 + 280 + + + + + 113 + 280 + + + + background:url(:/images/simulator/T8/left.png); + + + + + + + + 0 + 0 + + + + + 254 + 158 + + + + + 254 + 158 + + + + background-color: rgb(215, 243, 255); + + + + + + + + 0 + 0 + + + + + 113 + 280 + + + + + 113 + 280 + + + + background:url(:/images/simulator/T8/right.png) + + + + + + + + 0 + 0 + + + + + 254 + 35 + + + + + 254 + 35 + + + + background:url(:/images/simulator/T8/top.png) + + + + + + + + 0 + 0 + + + + + 254 + 113 + + + + + 254 + 113 + + + + background:url(:/images/simulator/T8/bottom.png) + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 12 + + + + + 0 + 12 + + + + background-color: rgb(255, 255, 255); + + + + + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + 16777215 + 10 + + + + background-color: rgb(247, 245, 245); + + + + + + + + + + + LcdWidget + QWidget +

lcdwidget.h
+ 1 + + + ButtonsWidget + QWidget +
buttonswidget.h
+ 1 +
+ + + + diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index cab7600d4..0b2b6b853 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -100,6 +100,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato case Board::BOARD_RADIOMASTER_TX12: radioUiWidget = new SimulatedUIWidgetTX12(simulator, this); break; + case Board::BOARD_RADIOMASTER_T8: + radioUiWidget = new SimulatedUIWidgetT8(simulator, this); + break; case Board::BOARD_RADIOMASTER_TX16S: radioUiWidget = new SimulatedUIWidgetTX16S(simulator, this); break; diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 47a1fed2a..b3a8c1fdb 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -345,6 +345,10 @@ if(RADIOMASTER_RELEASE) add_definitions(-DRADIOMASTER_RELEASE) endif() +if(RADIOMASTER_RTF_RELEASE) + add_definitions(-DRADIOMASTER_RTF_RELEASE) +endif() + if(TBS_RELEASE) add_definitions(-DTBS_RELEASE) endif() diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index dcdd6e5c8..005fbec56 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -208,7 +208,11 @@ enum TrainerMode { }; #endif -#if defined(RADIO_T16) && !defined(INTERNAL_MODULE_MULTI) +#define TRAINER_MODE_MIN() TRAINER_MODE_MASTER_TRAINER_JACK + +#if !defined(HARDWARE_EXTERNAL_MODULE) + #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE +#elif defined(RADIO_T16) && !defined(INTERNAL_MODULE_MULTI) #if defined(BLUETOOTH) #define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE_BLUETOOTH #else diff --git a/radio/src/gui/128x64/lcd.h b/radio/src/gui/128x64/lcd.h index c6b32dd3d..3a94f2365 100644 --- a/radio/src/gui/128x64/lcd.h +++ b/radio/src/gui/128x64/lcd.h @@ -181,8 +181,9 @@ uint8_t * lcdLoadBitmap(uint8_t * dest, const char * filename, uint8_t width, ui #if defined(BOOT) #define BLINK_ON_PHASE (0) #else - #define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6)) #define SLOW_BLINK_ON_PHASE (g_blinkTmr10ms & (1<<7)) + #define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6)) + #define FAST_BLINK_ON_PHASE (g_blinkTmr10ms & (1<<4)) #endif inline display_t getPixel(uint8_t x, uint8_t y) diff --git a/radio/src/gui/128x64/model_curve_edit.cpp b/radio/src/gui/128x64/model_curve_edit.cpp index 66f153b4d..c2168ed02 100644 --- a/radio/src/gui/128x64/model_curve_edit.cpp +++ b/radio/src/gui/128x64/model_curve_edit.cpp @@ -165,7 +165,7 @@ void menuModelCurveOne(event_t event) } break; -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_MDL) case EVT_KEY_FIRST(KEY_MODEL): pushMenu(menuChannelsView); killEvents(event); diff --git a/radio/src/gui/128x64/model_input_edit.cpp b/radio/src/gui/128x64/model_input_edit.cpp index 840e41a82..ce76e0a89 100644 --- a/radio/src/gui/128x64/model_input_edit.cpp +++ b/radio/src/gui/128x64/model_input_edit.cpp @@ -49,7 +49,7 @@ enum ExposFields { void menuModelExpoOne(event_t event) { -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_MDL) if (event == EVT_KEY_FIRST(KEY_MODEL)) { pushMenu(menuChannelsView); killEvents(event); diff --git a/radio/src/gui/128x64/model_mix_edit.cpp b/radio/src/gui/128x64/model_mix_edit.cpp index 92e10ee69..281256f46 100644 --- a/radio/src/gui/128x64/model_mix_edit.cpp +++ b/radio/src/gui/128x64/model_mix_edit.cpp @@ -85,7 +85,7 @@ void drawOffsetBar(uint8_t x, uint8_t y, MixData * md) void menuModelMixOne(event_t event) { -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_MDL) if (event == EVT_KEY_FIRST(KEY_MODEL)) { pushMenu(menuChannelsView); killEvents(event); diff --git a/radio/src/gui/128x64/model_select.cpp b/radio/src/gui/128x64/model_select.cpp index cf6410d63..bcf04117d 100644 --- a/radio/src/gui/128x64/model_select.cpp +++ b/radio/src/gui/128x64/model_select.cpp @@ -203,7 +203,7 @@ void menuModelSelect(event_t event) } break; -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_FIRST(KEY_PAGEUP): chainMenu(menuTabModel[DIM(menuTabModel)-1]); killEvents(event); @@ -212,7 +212,7 @@ void menuModelSelect(event_t event) case EVT_KEY_FIRST(KEY_PAGEDN): chainMenu(menuModelSetup); break; -#elif defined(NAVIGATION_X7) +#elif defined(KEYS_GPIO_REG_PAGE) case EVT_KEY_LONG(KEY_PAGE): chainMenu(menuTabModel[DIM(menuTabModel)-1]); killEvents(event); @@ -244,7 +244,7 @@ void menuModelSelect(event_t event) #endif #endif -#if defined(NAVIGATION_X7) +#if defined(ROTARY_ENCODER) && defined(NAVIGATION_X7) case EVT_ROTARY_LEFT: case EVT_ROTARY_RIGHT: #endif diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 004737515..468597b29 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -111,7 +111,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2, ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3, #endif - +#if defined(HARDWARE_EXTERNAL_MODULE) ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL, ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE, #if defined(MULTIMODULE) @@ -156,6 +156,7 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTRA_MODULE_BIND, #endif +#endif #if defined(PCBTARANIS) ITEM_MODEL_SETUP_TRAINER_LABEL, @@ -183,13 +184,16 @@ enum MenuModelSetupItems { #define REGISTRATION_ID_ROWS #endif -#if defined(HARDWARE_INTERNAL_MODULE) +#if defined(HARDWARE_INTERNAL_MODULE) && defined(HARDWARE_EXTERNAL_MODULE) #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : 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)) #elif defined(PCBSKY9X) - #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE) + #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE) +#elif defined(HARDWARE_INTERNAL_MODULE) + #define CURRENT_MODULE_EDITED(k) (INTERNAL_MODULE) + #define CURRENT_RECEIVER_EDITED(k) (k - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1) #else - #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE) + #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE) #define CURRENT_RECEIVER_EDITED(k) (k - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1) #endif @@ -335,6 +339,32 @@ void onBluetoothConnectMenu(const char * result) #define INTERNAL_MODULE_ROWS #endif +#if defined(HARDWARE_EXTERNAL_MODULE) + #define EXTERNAL_MODULE_ROWS \ + LABEL(ExternalModule), \ + MODULE_TYPE_ROWS(EXTERNAL_MODULE), \ + MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) /* PROTOCOL */ \ + MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) /* SUBTYPE */ \ + MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) \ + AFHDS3_PROTOCOL_ROW(EXTERNAL_MODULE) \ + AFHDS3_MODE_ROWS(EXTERNAL_MODULE) \ + MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), \ + IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), /* line reused for PPM: PPM settings */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* RxNum */ \ + AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) \ + MODULE_POWER_ROW(EXTERNAL_MODULE), \ + IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), \ + MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) \ + FAILSAFE_ROWS(EXTERNAL_MODULE), /* ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), /* Range check and Register buttons */ \ + IF_PXX2_MODULE(EXTERNAL_MODULE, 0), /* Module options */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* Receiver 1 */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* Receiver 2 */ \ + IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), /* Receiver 3 */ +#else + #define EXTERNAL_MODULE_ROWS +#endif + void menuModelSetup(event_t event) { int8_t old_editMode = s_editMode; @@ -370,26 +400,7 @@ void menuModelSetup(event_t event) INTERNAL_MODULE_ROWS - LABEL(ExternalModule), - MODULE_TYPE_ROWS(EXTERNAL_MODULE), - MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) // PROTOCOL - MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) // SUBTYPE - MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) - AFHDS3_PROTOCOL_ROW(EXTERNAL_MODULE) - AFHDS3_MODE_ROWS(EXTERNAL_MODULE) - MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum - AFHDS3_MODULE_ROWS(EXTERNAL_MODULE) - MODULE_POWER_ROW(EXTERNAL_MODULE), - IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)), - MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE) - FAILSAFE_ROWS(EXTERNAL_MODULE), //ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons - IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3 + EXTERNAL_MODULE_ROWS TRAINER_ROWS }); @@ -912,14 +923,18 @@ void menuModelSetup(event_t event) break; #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL: lcdDrawTextAlignedLeft(y, STR_EXTERNALRF); break; +#endif #if defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE: +#endif lcdDrawTextAlignedLeft(y, INDENT TR_MODE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, moduleIdx == EXTERNAL_MODULE ? reusableBuffer.moduleSetup.newType : g_model.moduleData[moduleIdx].type, menuHorizontalPosition==0 ? attr : 0); if (isModuleXJT(moduleIdx)) @@ -1013,7 +1028,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_PROTOCOL: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PROTOCOL: +#endif { lcdDrawTextAlignedLeft(y, TR_TYPE); int multi_rfProto = g_model.moduleData[moduleIdx].getMultiProtocol(); @@ -1052,7 +1069,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SUBTYPE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SUBTYPE: +#endif { lcdDrawTextAlignedLeft(y, STR_SUBTYPE); lcdDrawMultiSubProtocolString(MODEL_SETUP_2ND_COLUMN, y, moduleIdx, g_model.moduleData[moduleIdx].subType, attr); @@ -1095,7 +1114,7 @@ void menuModelSetup(event_t event) lcdDrawTextAlignedLeft(y, INDENT TR_MODE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerData.mode, attr); if (attr) { - g_model.trainerData.mode = checkIncDec(event, g_model.trainerData.mode, 0, TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable); + g_model.trainerData.mode = checkIncDec(event, g_model.trainerData.mode, TRAINER_MODE_MIN(), TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable); #if defined(BLUETOOTH) if (checkIncDec_Ret) { bluetooth.state = BLUETOOTH_STATE_OFF; @@ -1182,7 +1201,9 @@ void menuModelSetup(event_t event) #if defined(PCBSKY9X) case ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS: +#endif { ModuleData & moduleData = g_model.moduleData[moduleIdx]; lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE); @@ -1308,7 +1329,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE: +#endif { ModuleData & moduleData = g_model.moduleData[moduleIdx]; if (isModulePPM(moduleIdx)) { @@ -1462,7 +1485,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE: +#endif { ModuleData &moduleData = g_model.moduleData[moduleIdx]; lcdDrawTextAlignedLeft(y, STR_FAILSAFE); @@ -1523,7 +1548,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS: +#endif { #if defined(MULTIMODULE) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { @@ -1602,7 +1629,9 @@ void menuModelSetup(event_t event) #if defined(INTERNAL_MODULE_MULTI) case ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER: +#endif { auto & module = g_model.moduleData[moduleIdx]; // Lite FCC / Lite FLEX / Lite Pro Flex @@ -1687,7 +1716,9 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: +#endif if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) { int8_t value = (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; lcdDrawText(INDENT_WIDTH, y, STR_MULTI_SERVOFREQ); @@ -1708,13 +1739,17 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM: +#endif g_model.moduleData[moduleIdx].multi.disableTelemetry = editCheckBox(g_model.moduleData[moduleIdx].multi.disableTelemetry, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_TELEM, attr, event); break; #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING: #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING: +#endif g_model.moduleData[moduleIdx].multi.disableMapping = editCheckBox(g_model.moduleData[moduleIdx].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_CH_MAP, attr, event); break; #endif @@ -1739,7 +1774,10 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS: #endif - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: { +#if defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: +#endif + { lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS); char statusText[64]; @@ -1750,7 +1788,10 @@ void menuModelSetup(event_t event) #if defined(HARDWARE_INTERNAL_MODULE) case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS: #endif - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { +#if defined(HARDWARE_EXTERNAL_MODULE) + case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: +#endif + { lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC); char statusText[64]; getModuleSyncStatusString(moduleIdx, statusText); @@ -1848,11 +1889,13 @@ void menuModelSetup(event_t event) checkModelIdUnique(g_eeGeneral.currModel, EXTRA_MODULE); break; #endif +#if defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE: case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM: if (menuHorizontalPosition == 0) checkModelIdUnique(g_eeGeneral.currModel, EXTERNAL_MODULE); break; +#endif } } } diff --git a/radio/src/gui/128x64/radio_diagkeys.cpp b/radio/src/gui/128x64/radio_diagkeys.cpp index a3d058c3e..803ff7eb7 100644 --- a/radio/src/gui/128x64/radio_diagkeys.cpp +++ b/radio/src/gui/128x64/radio_diagkeys.cpp @@ -49,10 +49,15 @@ void menuRadioDiagKeys(event_t event) displayKeyState(i&1? 20*FW : 18*FW, y, TRM_BASE+i); } - if (i <= KEY_MAX) { - y = MENU_HEADER_HEIGHT + 1 + FH*i; + if (i == 7) { + y = MENU_HEADER_HEIGHT + 1 + FH * 6; + lcdDrawTextAtIndex(8, y, STR_VKEYS, i, 0); + displayKeyState(lcdNextPos + 10, y, i); + } + else if (i <= KEY_MAX) { + y = MENU_HEADER_HEIGHT + 1 + FH * i; lcdDrawTextAtIndex(0, y, STR_VKEYS, i, 0); - displayKeyState(5*FW+2, y, i); + displayKeyState(5 * FW + 2, y, i); } #if defined(PCBSKY9X) diff --git a/radio/src/gui/128x64/radio_setup.cpp b/radio/src/gui/128x64/radio_setup.cpp index e3fd06bb3..26b80cb0f 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -79,13 +79,13 @@ enum { ITEM_RADIO_SETUP_MEMORY_WARNING, ITEM_RADIO_SETUP_ALARM_WARNING, ITEM_RADIO_SETUP_RSSI_POWEROFF_ALARM, - ITEM_RADIO_SETUP_BACKLIGHT_LABEL, - ITEM_RADIO_SETUP_BACKLIGHT_MODE, - ITEM_RADIO_SETUP_BACKLIGHT_DELAY, - ITEM_RADIO_SETUP_BRIGHTNESS, + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_LABEL) + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_MODE) + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_DELAY) + CASE_BACKLIGHT(ITEM_RADIO_SETUP_BRIGHTNESS) CASE_PWM_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_BRIGHTNESS_OFF) CASE_PWM_BACKLIGHT(ITEM_RADIO_SETUP_BACKLIGHT_BRIGHTNESS_ON) - ITEM_RADIO_SETUP_FLASH_BEEP, + CASE_BACKLIGHT(ITEM_RADIO_SETUP_FLASH_BEEP) CASE_SPLASH_PARAM(ITEM_RADIO_SETUP_DISABLE_SPLASH) CASE_PWR_BUTTON_PRESS(ITEM_RADIO_SETUP_PWR_ON_SPEED) CASE_PWR_BUTTON_PRESS(ITEM_RADIO_SETUP_PWR_OFF_SPEED) @@ -152,10 +152,14 @@ void menuRadioSetup(event_t event) CASE_GYRO(0) CASE_GYRO(0) 0, LABEL(ALARMS), 0, CASE_CAPACITY(0) - 0, 0, 0, 0, - LABEL(BACKLIGHT), 0, 0, 0, CASE_PWM_BACKLIGHT(0) + 0, 0, 0, 0, /* ITEM_RADIO_SETUP_INACTIVITY_ALARM ITEM_RADIO_SETUP_MEMORY_WARNING ITEM_RADIO_SETUP_ALARM_WARNING ITEM_RADIO_SETUP_RSSI_POWEROFF_ALARM */ + CASE_BACKLIGHT(LABEL(BACKLIGHT)) + CASE_BACKLIGHT(0) + CASE_BACKLIGHT(0) + CASE_BACKLIGHT(0) CASE_PWM_BACKLIGHT(0) - 0, + CASE_PWM_BACKLIGHT(0) + CASE_BACKLIGHT(0) CASE_SPLASH_PARAM(0) CASE_PWR_BUTTON_PRESS(0) CASE_PWR_BUTTON_PRESS(0) @@ -251,7 +255,7 @@ void menuRadioSetup(event_t event) putsVolts(lcdLastRightPos+FW, y, 120+g_eeGeneral.vBatMax, (menuHorizontalPosition>0 ? attr : 0)|LEFT|NO_UNIT); if (attr && s_editMode>0) { if (menuHorizontalPosition==0) - CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -50, g_eeGeneral.vBatMax+29); // min=4.0V + CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -60, g_eeGeneral.vBatMax+29); // min=3.0V else CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMax, g_eeGeneral.vBatMin-29, +40); // max=16.0V } @@ -411,7 +415,7 @@ void menuRadioSetup(event_t event) case ITEM_RADIO_SETUP_BATTERY_WARNING: lcdDrawTextAlignedLeft(y, STR_BATTERYWARNING); putsVolts(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.vBatWarn, attr|LEFT); - if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); //4-12V + if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 30, 120); //3-12V break; case ITEM_RADIO_SETUP_MEMORY_WARNING: @@ -450,6 +454,7 @@ void menuRadioSetup(event_t event) if(attr) g_eeGeneral.inactivityTimer = checkIncDec(event, g_eeGeneral.inactivityTimer, 0, 250, EE_GENERAL); //0..250minutes break; +#if defined(BACKLIGHT_GPIO) case ITEM_RADIO_SETUP_BACKLIGHT_LABEL: lcdDrawTextAlignedLeft(y, STR_BACKLIGHT_LABEL); break; @@ -478,6 +483,7 @@ void menuRadioSetup(event_t event) g_eeGeneral.backlightBright = 100 - b; } break; +#endif #if defined(PWM_BACKLIGHT) case ITEM_RADIO_SETUP_BACKLIGHT_BRIGHTNESS_OFF: diff --git a/radio/src/gui/128x64/view_about.cpp b/radio/src/gui/128x64/view_about.cpp index 02e8e4508..b168f91fb 100644 --- a/radio/src/gui/128x64/view_about.cpp +++ b/radio/src/gui/128x64/view_about.cpp @@ -42,7 +42,7 @@ enum AboutScreens { #define ABOUT_X 2 #define ABOUT_INDENT 4 -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) #define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGEDN) #elif defined(NAVIGATION_X7) diff --git a/radio/src/gui/128x64/view_channels.cpp b/radio/src/gui/128x64/view_channels.cpp index f71d833c1..ffb4d9b33 100644 --- a/radio/src/gui/128x64/view_channels.cpp +++ b/radio/src/gui/128x64/view_channels.cpp @@ -26,7 +26,12 @@ constexpr coord_t CHANNEL_GAUGE_OFFSET = CHANNEL_VALUE_OFFSET; constexpr coord_t CHANNEL_BAR_WIDTH = 70; constexpr coord_t CHANNEL_PROPERTIES_OFFSET = CHANNEL_GAUGE_OFFSET + CHANNEL_BAR_WIDTH + 2; -#if defined(NAVIGATION_X7_TX12) +#if defined(RADIO_T8) +#define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) +#define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGEDN) +#define EVT_KEY_NEXT_PAGE EVT_KEY_BREAK(KEY_PLUS) +#define EVT_KEY_PREVIOUS_PAGE EVT_KEY_BREAK(KEY_MINUS) +#elif defined(NAVIGATION_X7_TX12) #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) #define EVT_KEY_NEXT_VIEW EVT_KEY_BREAK(KEY_PAGEDN) #define EVT_KEY_NEXT_PAGE EVT_ROTARY_RIGHT diff --git a/radio/src/gui/128x64/view_main.cpp b/radio/src/gui/128x64/view_main.cpp index ee486269b..b3cea8de7 100644 --- a/radio/src/gui/128x64/view_main.cpp +++ b/radio/src/gui/128x64/view_main.cpp @@ -93,8 +93,9 @@ void doMainScreenGraphics() if (g_model.throttleReversed && CONVERT_MODE(2) == THR_STICK) calibStickVert = -calibStickVert; drawStick(RBOX_CENTERX, calibratedAnalogs[CONVERT_MODE(3)], calibStickVert); - +#if defined(HARDWARE_POT1) drawPotsBars(); +#endif } void displayTrims(uint8_t phase) @@ -213,7 +214,16 @@ void displayVoltageOrAlarm() #define displayVoltageOrAlarm() displayBattVoltage() #endif -#if defined(NAVIGATION_X7_TX12) +#if defined(RADIO_T8) +#define EVT_KEY_CONTEXT_MENU EVT_KEY_LONG(KEY_ENTER) +#define EVT_KEY_PREVIOUS_VIEW EVT_KEY_BREAK(KEY_PAGEUP) +#define EVT_KEY_NEXT_VIEW EVT_KEY_FIRST(KEY_PAGEDN) +#define EVT_KEY_NEXT_PAGE EVT_KEY_FIRST(KEY_PLUS) +#define EVT_KEY_PREVIOUS_PAGE EVT_KEY_FIRST(KEY_MINUS) +#define EVT_KEY_MODEL_MENU EVT_KEY_LONG(KEY_MODEL) +#define EVT_KEY_GENERAL_MENU EVT_KEY_LONG(KEY_SYS) +#define EVT_KEY_TELEMETRY EVT_KEY_LONG(KEY_PAGEUP) +#elif defined(NAVIGATION_X7_TX12) #define EVT_KEY_CONTEXT_MENU EVT_KEY_LONG(KEY_ENTER) #define EVT_KEY_PREVIOUS_VIEW EVT_KEY_FIRST(KEY_PAGEUP) #define EVT_KEY_NEXT_VIEW EVT_KEY_FIRST(KEY_PAGEDN) @@ -410,6 +420,7 @@ void menuMainView(event_t event) killEvents(event); break; + case EVT_KEY_FIRST(KEY_EXIT): #if defined(GVARS) if (gvarDisplayTimer > 0) { diff --git a/radio/src/gui/128x64/view_statistics.cpp b/radio/src/gui/128x64/view_statistics.cpp index 2b46be36e..0765cf9b6 100644 --- a/radio/src/gui/128x64/view_statistics.cpp +++ b/radio/src/gui/128x64/view_statistics.cpp @@ -31,7 +31,7 @@ void menuStatisticsView(event_t event) switch (event) { case EVT_KEY_FIRST(KEY_UP): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_BREAK(KEY_PAGEDN): #elif defined(NAVIGATION_X7) case EVT_KEY_BREAK(KEY_PAGE): @@ -41,7 +41,7 @@ void menuStatisticsView(event_t event) break; case EVT_KEY_FIRST(KEY_DOWN): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_BREAK(KEY_PAGEUP): killEvents(event); chainMenu(menuStatisticsDebug2); @@ -140,7 +140,7 @@ void menuStatisticsDebug(event_t event) break; case EVT_KEY_FIRST(KEY_UP): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_BREAK(KEY_PAGEDN): disableVBatBridge(); chainMenu(menuStatisticsDebug2); @@ -153,7 +153,7 @@ void menuStatisticsDebug(event_t event) #endif case EVT_KEY_FIRST(KEY_DOWN): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_BREAK(KEY_PAGEUP): #elif defined(NAVIGATION_X7) case EVT_KEY_LONG(KEY_PAGE): @@ -278,7 +278,7 @@ void menuStatisticsDebug2(event_t event) break; case EVT_KEY_FIRST(KEY_UP): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_BREAK(KEY_PAGEDN): #elif defined(NAVIGATION_X7) case EVT_KEY_BREAK(KEY_PAGE): @@ -287,7 +287,7 @@ void menuStatisticsDebug2(event_t event) return; case EVT_KEY_FIRST(KEY_DOWN): -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_BREAK(KEY_PAGEUP): #elif defined(NAVIGATION_X7) case EVT_KEY_LONG(KEY_PAGE): diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index fca6e047c..1bc66768d 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -195,7 +195,9 @@ enum { ITEM_RADIO_HARDWARE_MAX }; -#if (NUM_POTS + NUM_SLIDERS) == 1 +#if (NUM_POTS + NUM_SLIDERS) == 0 + #define POTS_ROWS HIDDEN_ROW +#elif (NUM_POTS + NUM_SLIDERS) == 1 #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 2 #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 @@ -238,20 +240,20 @@ enum { #endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) -#define EXTERNAL_ANTENNA_ROW 0, -void onHardwareAntennaSwitchConfirm(const char * result) -{ - if (result == STR_OK) { - // Switch to external antenna confirmation - g_eeGeneral.antennaMode = reusableBuffer.radioHardware.antennaMode; - storageDirty(EE_GENERAL); + #define EXTERNAL_ANTENNA_ROW 0, + void onHardwareAntennaSwitchConfirm(const char * result) + { + if (result == STR_OK) { + // Switch to external antenna confirmation + g_eeGeneral.antennaMode = reusableBuffer.radioHardware.antennaMode; + storageDirty(EE_GENERAL); + } + else { + reusableBuffer.radioHardware.antennaMode = g_eeGeneral.antennaMode; + } } - else { - reusableBuffer.radioHardware.antennaMode = g_eeGeneral.antennaMode; - } -} #else -#define EXTERNAL_ANTENNA_ROW + #define EXTERNAL_ANTENNA_ROW #endif #if defined(PCBX9LITE) @@ -262,7 +264,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #define SWITCH_TYPE_MAX(sw) (SWITCH_3POS) #elif defined(PCBX9E) #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF - MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH - MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) -#elif defined(RADIO_TX12) +#elif defined(RADIO_TX12) || defined(RADIO_T8) #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SA - MIXSRC_FIRST_SWITCH == sw || MIXSRC_SD - MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) #else #define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF - MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH - MIXSRC_FIRST_SWITCH <= sw) ? SWITCH_2POS : SWITCH_3POS) @@ -322,35 +324,27 @@ void menuRadioHardware(event_t event) MENU(STR_HARDWARE, menuTabGeneral, MENU_RADIO_HARDWARE, HEADER_LINE + ITEM_RADIO_HARDWARE_MAX, { HEADER_LINE_COLUMNS 0 /* calibration button */, - 0 /* stick 1 */, - 0 /* stick 2 */, - 0 /* stick 3 */, - 0 /* stick 4 */, + 0 /* stick 1 */, + 0 /* stick 2 */, + 0 /* stick 3 */, + 0 /* stick 4 */, LABEL(Pots), - POTS_ROWS, + POTS_ROWS, LABEL(Switches), - SWITCHES_ROWS, - + SWITCHES_ROWS, 0 /* battery calib */, RTC_ROW TX_CAPACITY_MEASUREMENT_ROWS - MAX_BAUD_ROWS - BLUETOOTH_ROWS - EXTERNAL_ANTENNA_ROW - AUX_SERIAL_ROWS - 0 /* ADC filter */, READONLY_ROW /* RAS */, SPORT_POWER_ROWS 1 /* debugs */, - - 0, - - 0 + 0 /* EEPROM backup */, + 0 /* Factory reset */ }); uint8_t sub = menuVerticalPosition - HEADER_LINE; diff --git a/radio/src/gui/common/stdlcd/view_telemetry.cpp b/radio/src/gui/common/stdlcd/view_telemetry.cpp index ecdb96aa9..07a31442c 100644 --- a/radio/src/gui/common/stdlcd/view_telemetry.cpp +++ b/radio/src/gui/common/stdlcd/view_telemetry.cpp @@ -34,7 +34,7 @@ enum NavigationDirection { #if defined(NAVIGATION_XLITE) #define EVT_KEY_PREVIOUS_VIEW(evt) (evt == EVT_KEY_LONG(KEY_LEFT) && IS_SHIFT_PRESSED()) #define EVT_KEY_NEXT_VIEW(evt) (evt == EVT_KEY_LONG(KEY_RIGHT) && IS_SHIFT_PRESSED()) -#elif defined(NAVIGATION_X7_TX12) +#elif defined(KEYS_GPIO_REG_PAGEDN) #define EVT_KEY_PREVIOUS_VIEW(evt) (evt == EVT_KEY_FIRST(KEY_PAGEUP)) #define EVT_KEY_NEXT_VIEW(evt) (evt == EVT_KEY_FIRST(KEY_PAGEDN)) #elif defined(NAVIGATION_X7) || defined(NAVIGATION_X9D) diff --git a/radio/src/gui/navigation/navigation_x7.cpp b/radio/src/gui/navigation/navigation_x7.cpp index ac4a4bbdd..311a53d9f 100644 --- a/radio/src/gui/navigation/navigation_x7.cpp +++ b/radio/src/gui/navigation/navigation_x7.cpp @@ -56,8 +56,11 @@ void onSwitchLongEnterPress(const char * result) int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_flags, IsValueAvailable isValueAvailable, const CheckIncDecStops &stops) { int newval = val; - +#if defined(ROTARY_ENCODER_NAVIGATION) if (s_editMode>0 && event==EVT_ROTARY_RIGHT) { +#else + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) { +#endif newval += min(rotencSpeed, i_max-val); while (isValueAvailable && !isValueAvailable(newval) && newval<=i_max) { newval++; @@ -67,7 +70,11 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla AUDIO_KEY_ERROR(); } } +#if defined(ROTARY_ENCODER_NAVIGATION) else if (s_editMode>0 && event==EVT_ROTARY_LEFT) { +#else + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) { +#endif newval -= min(rotencSpeed, val-i_min); while (isValueAvailable && !isValueAvailable(newval) && newval>=i_min) { newval--; @@ -217,7 +224,7 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t if (menuTab) { int cc = curr; switch (event) { -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEUP) case EVT_KEY_FIRST(KEY_PAGEUP): #else case EVT_KEY_LONG(KEY_PAGE): @@ -232,7 +239,7 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t killEvents(event); break; -#if defined(NAVIGATION_X7_TX12) +#if defined(KEYS_GPIO_REG_PAGEDN) case EVT_KEY_FIRST(KEY_PAGEDN): #else case EVT_KEY_BREAK(KEY_PAGE): @@ -310,12 +317,14 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t } } break; - +#if defined(ROTARY_ENCODER_NAVIGATION) case EVT_ROTARY_RIGHT: - case EVT_KEY_FIRST(KEY_RIGHT): AUDIO_KEY_PRESS(); // no break - case EVT_KEY_REPT(KEY_RIGHT): +#else + case EVT_KEY_FIRST(KEY_DOWN): + case EVT_KEY_REPT(KEY_DOWN): +#endif if (s_editMode > 0) break; // TODO it was != if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) { if (l_posHorz >= 0) { @@ -341,12 +350,14 @@ void check(event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t l_posHorz = POS_HORZ_INIT(l_posVert); break; - +#if defined(ROTARY_ENCODER_NAVIGATION) case EVT_ROTARY_LEFT: - case EVT_KEY_FIRST(KEY_LEFT): AUDIO_KEY_PRESS(); // no break - case EVT_KEY_REPT(KEY_LEFT): +#else + case EVT_KEY_FIRST(KEY_UP): + case EVT_KEY_REPT(KEY_UP): +#endif if (s_editMode > 0) break; // TODO it was != if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) { if (l_posHorz >= 0) { diff --git a/radio/src/keys.h b/radio/src/keys.h index 44c1e53fe..0d4bc37dc 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -82,6 +82,11 @@ inline bool IS_KEY_EVT(event_t evt, uint8_t key) #define EVT_ROTARY_RIGHT 0xDE00 #define IS_NEXT_EVENT(event) (event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)) #define IS_PREVIOUS_EVENT(event) (event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)) +#elif defined(RADIO_T8) + #define EVT_ROTARY_BREAK EVT_KEY_BREAK(KEY_ENTER) + #define EVT_ROTARY_LONG EVT_KEY_LONG(KEY_ENTER) + #define IS_NEXT_EVENT(event) (event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)) + #define IS_PREVIOUS_EVENT(event) (event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)) #elif (defined(PCBHORUS) || defined(PCBTARANIS)) && defined(ROTARY_ENCODER_NAVIGATION) #define EVT_ROTARY_BREAK EVT_KEY_BREAK(KEY_ENTER) #define EVT_ROTARY_LONG EVT_KEY_LONG(KEY_ENTER) diff --git a/radio/src/lua/CMakeLists.txt b/radio/src/lua/CMakeLists.txt index 5b89c1f65..fee1d9f15 100644 --- a/radio/src/lua/CMakeLists.txt +++ b/radio/src/lua/CMakeLists.txt @@ -29,5 +29,6 @@ if(PYTHONINTERP_FOUND) add_lua_export_target(x12s ${LUA_INCLUDES} -DPCBHORUS -DPCBX12S) add_lua_export_target(t12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T12) add_lua_export_target(tx12 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_TX12) + add_lua_export_target(t8 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX7 -DRADIO_T8) 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 37634ae1c..b3c3f5ee8 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -42,6 +42,8 @@ #include "lua/lua_exports_t12.inc" #elif defined(RADIO_TX12) #include "lua/lua_exports_tx12.inc" +#elif defined(RADIO_T8) + #include "lua/lua_exports_t8.inc" #elif defined(PCBX9LITES) #include "lua/lua_exports_x9lites.inc" #elif defined(PCBX9LITE) @@ -1779,11 +1781,11 @@ const luaR_value_entry opentxConstants[] = { { "MIXSRC_SE", MIXSRC_SE }, { "MIXSRC_SG", MIXSRC_SG }, #endif -#if !defined(PCBXLITE) && !defined(PCBX9LITE) +#if defined(HARDWARE_SWITCH_F) { "MIXSRC_SF", MIXSRC_SF }, -#if !defined(RADIO_TX12) - { "MIXSRC_SH", MIXSRC_SH }, #endif +#if defined(HARDWARE_SWITCH_H) + { "MIXSRC_SH", MIXSRC_SH }, #endif { "MIXSRC_CH1", MIXSRC_CH1 }, { "SWSRC_LAST", SWSRC_LAST_LOGICAL_SWITCH }, @@ -1845,7 +1847,7 @@ const luaR_value_entry opentxConstants[] = { { "ROTENC_LOWSPEED", ROTENC_LOWSPEED }, { "ROTENC_MIDSPEED", ROTENC_MIDSPEED }, { "ROTENC_HIGHSPEED", ROTENC_HIGHSPEED }, -#elif defined(PCBX9D) || defined(PCBX9DP) // key reverted between field nav and value change +#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T8) // key reverted between field nav and value change { "EVT_VIRTUAL_PREV", EVT_KEY_FIRST(KEY_PLUS) }, { "EVT_VIRTUAL_PREV_REPT", EVT_KEY_REPT(KEY_PLUS) }, { "EVT_VIRTUAL_NEXT", EVT_KEY_FIRST(KEY_MINUS) }, @@ -1882,7 +1884,7 @@ const luaR_value_entry opentxConstants[] = { { "EVT_VIRTUAL_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) }, { "EVT_VIRTUAL_EXIT", EVT_KEY_BREAK(KEY_EXIT) }, #elif defined(NAVIGATION_X7) || defined(NAVIGATION_X9D) -#if defined(RADIO_TX12) +#if defined(RADIO_TX12) || defined(RADIO_T8) { "EVT_VIRTUAL_PREV_PAGE", EVT_KEY_BREAK(KEY_PAGEUP) }, { "EVT_VIRTUAL_NEXT_PAGE", EVT_KEY_BREAK(KEY_PAGEDN) }, { "EVT_VIRTUAL_MENU", EVT_KEY_BREAK(KEY_MODEL) }, diff --git a/radio/src/main.cpp b/radio/src/main.cpp index db12ee20f..8ff9cd736 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -531,6 +531,10 @@ void perMain() } #endif +#if defined(KEYS_GPIO_REG_BIND) && defined(BIND_KEY) + bindButtonHandler(evt); +#endif + #if defined(GUI) DEBUG_TIMER_START(debugTimerGuiMain); guiMain(evt); diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 73b39c407..7a222635a 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -334,6 +334,12 @@ void generalDefault() setDefaultOwnerId(); #endif +#if defined(RADIOMASTER_RTF_RELEASE) + // Those settings are for headless radio + g_eeGeneral.USBMode = USB_JOYSTICK_MODE; + g_eeGeneral.disableRtcWarning = 1; +#endif + g_eeGeneral.chkSum = 0xFFFF; } @@ -523,6 +529,15 @@ void modelDefault(uint8_t id) g_model.switchWarningState |= (1 << (3*i)); } #endif + +#if defined(RADIOMASTER_RTF_RELEASE) + // Those settings are for headless radio + g_model.trainerData.mode = TRAINER_MODE_SLAVE; + g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_MULTIMODULE; + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D8; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; +#endif } bool isInputRecursive(int index) @@ -917,7 +932,7 @@ void checkAll() checkFailsafe(); checkRSSIAlarmsDisabled(); -#if defined(SDCARD) +#if defined(SDCARD) && !defined(RADIOMASTER_RTF_RELEASE) checkSDVersion(); #endif @@ -2183,6 +2198,7 @@ uint32_t pwrCheck() #endif event_t evt = getEvent(false); DISPLAY_WARNING(evt); + LED_ERROR_BEGIN(); lcdRefresh(); if (warningResult) { @@ -2192,6 +2208,7 @@ uint32_t pwrCheck() else if (!warningText) { // shutdown has been cancelled pwr_check_state = PWR_CHECK_PAUSED; + LED_ERROR_END(); return e_power_on; } } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 52a51719b..bb756305b 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -69,6 +69,12 @@ #define CASE_GYRO(x) #endif +#if defined(BACKLIGHT_GPIO) +#define CASE_BACKLIGHT(x) x, +#else +#define CASE_BACKLIGHT(x) +#endif + #if defined(LUA) #define CASE_LUA(x) x, #else @@ -382,6 +388,8 @@ inline bool SPLASH_NEEDED() #define ROTENC_HIGHSPEED 50 #define ROTENC_DELAY_MIDSPEED 32 #define ROTENC_DELAY_HIGHSPEED 16 +#elif defined(RADIO_T8) + constexpr uint8_t rotencSpeed = 1; #endif constexpr uint8_t HEART_TIMER_10MS = 0x01; @@ -437,6 +445,10 @@ extern uint8_t potsPos[NUM_XPOTS]; bool trimDown(uint8_t idx); void readKeysAndTrims(); +#if defined(KEYS_GPIO_REG_BIND) +void bindButtonHandler(event_t event); +#endif + uint16_t evalChkSum(); void alert(const char * title, const char * msg, uint8_t sound); @@ -998,7 +1010,13 @@ constexpr uint8_t OPENTX_START_NO_CHECKS = 0x04; #if defined(STATUS_LEDS) #define LED_ERROR_BEGIN() ledRed() +#if defined(RADIO_T8) + // Because of green backlit logo, green is preferred on this radio + #define LED_ERROR_END() ledGreen() + #define LED_BIND() ledBlue() +#else #define LED_ERROR_END() ledBlue() +#endif #else #define LED_ERROR_BEGIN() #define LED_ERROR_END() diff --git a/radio/src/options.h b/radio/src/options.h index acadfd129..4f1a1a077 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -97,6 +97,9 @@ static const char * const options[] = { #endif #if defined(HARDWARE_EXTERNAL_ACCESS_MOD) "externalaccessmod", +#endif +#if defined(BIND_KEY) + "bindkey", #endif nullptr //sentinel }; diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index 15a2c2283..6fbec157e 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -203,6 +203,14 @@ enum MMRFrskySubtypes { MM_RF_FRSKY_SUBTYPE_D16_CLONED }; +enum MMRFrskyX2Subtypes { + MM_RF_FRSKYX2_SUBTYPE_D16, + MM_RF_FRSKYX2_SUBTYPE_D8, + MM_RF_FRSKYX2_SUBTYPE_D16_LBT, + MM_RF_FRSKYX2_SUBTYPE_D16_LBT_8CH, + MM_RF_FRSKYX2_SUBTYPE_D16_CLONED +}; + enum ModuleSubtypeDSM2 { DSM2_PROTO_LP45, DSM2_PROTO_DSM2, diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index fc9a06b92..b6888f1f8 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -145,6 +145,8 @@ const char * getBasename(const char * path); #define OTX_FOURCC 0x3D78746F // otx for Jumper T12 #elif defined(RADIO_TX12) #define OTX_FOURCC 0x4178746F // otx for Radiomaster TX12 +#elif defined(RADIO_T8) + #define OTX_FOURCC 0x4378746F // otx for Radiomaster T8 #elif defined(PCBX7) #define OTX_FOURCC 0x3678746F // otx for Taranis X7 / X7S / X7 Express / X7S Express #elif defined(PCBX9LITES) diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 6d086e100..2e9c6a89e 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -273,6 +273,15 @@ void OpenTxSim::updateKeysAndSwitches(bool start) KEY_Down, KEY_EXIT, KEY_Right, KEY_TELE, KEY_Left, KEY_SYS, +#elif defined(RADIO_T8) + KEY_Page_Up, KEY_PAGEUP, + KEY_Page_Down, KEY_PAGEDN, + KEY_Return, KEY_ENTER, + KEY_Right, KEY_MODEL, + KEY_BackSpace, KEY_EXIT, + KEY_Left, KEY_SYS, + KEY_Up, KEY_PLUS, + KEY_Down, KEY_MINUS, #elif defined(PCBTARANIS) KEY_Page_Up, KEY_MENU, #if defined(KEYS_GPIO_REG_PAGE) diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index c6f5ee182..c6dddda75 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -125,6 +125,9 @@ void adcInit() ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); } +#elif defined(RADIO_T8) + ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15); #elif defined(PCBX7) ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT << 0) + (ADC_Channel_Vbat << 5); ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); diff --git a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 55bcdeb66..1a69afdff 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -89,7 +89,7 @@ set(BOOTLOADER_SRC bin_files.cpp ) -if(PCB STREQUAL X12S OR PCB STREQUAL X10 OR PCB STREQUAL X9E OR (PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) OR (PCB STREQUAL X7 AND NOT (PCBREV STREQUAL T12)) OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES) +if(PCB STREQUAL X12S OR PCB STREQUAL X10 OR PCB STREQUAL X9E OR (PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) OR (PCB STREQUAL X7 AND NOT (PCBREV STREQUAL T12 OR PCBREV STREQUAL T8)) OR PCB STREQUAL X9LITE OR PCB STREQUAL X9LITES) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} ../../../../../targets/common/arm/stm32/rotary_encoder_driver.cpp diff --git a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp index b849fa8a2..e94ae4756 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/boot.cpp +++ b/radio/src/targets/common/arm/stm32/bootloader/boot.cpp @@ -218,8 +218,13 @@ int main() } } +#if defined(RADIO_T8) && !defined(RADIOMASTER_RELEASE) + // Bind button not pressed + if ((~KEYS_GPIO_REG_BIND & KEYS_GPIO_PIN_BIND) == false) { +#else // LHR & RHL trims not pressed simultanously if (readTrims() != BOOTLOADER_KEYS) { +#endif // Start main application jumpTo(APP_START_ADDRESS); } diff --git a/radio/src/targets/common/arm/stm32/pwr_driver.cpp b/radio/src/targets/common/arm/stm32/pwr_driver.cpp index 220b5fdfd..32ff59195 100644 --- a/radio/src/targets/common/arm/stm32/pwr_driver.cpp +++ b/radio/src/targets/common/arm/stm32/pwr_driver.cpp @@ -80,7 +80,7 @@ void pwrInit() GPIO_Init(SD_PRESENT_GPIO, &GPIO_InitStructure); #endif -#if defined(INTMODULE_USART) && defined(TRAINER_MODULE_CPPM_GPIO_PIN) +#if defined(INTMODULE_USART) && defined(TRAINER_MODULE_CPPM) GPIO_SetBits(TRAINER_MODULE_CPPM_GPIO, TRAINER_MODULE_CPPM_GPIO_PIN); GPIO_InitStructure.GPIO_Pin = TRAINER_MODULE_CPPM_GPIO_PIN; GPIO_Init(TRAINER_MODULE_CPPM_GPIO, &GPIO_InitStructure); diff --git a/radio/src/targets/sky9x/CMakeLists.txt b/radio/src/targets/sky9x/CMakeLists.txt index 842035bde..d09906965 100644 --- a/radio/src/targets/sky9x/CMakeLists.txt +++ b/radio/src/targets/sky9x/CMakeLists.txt @@ -12,6 +12,7 @@ set(PPM_LIMITS_SYMETRICAL YES) set(PXX1 YES) set(AFHDS3 NO) set(GHOST NO) +add_definitions(-DHARDWARE_EXTERNAL_MODULE) add_definitions(-DDISABLE_MULTI_UPDATE) if(PCB STREQUAL 9XRPRO) diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index a287605df..718e3c684 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -6,6 +6,9 @@ option(AFHDS3 "AFHDS3 TX Module" ON) option(GHOST "Ghost TX Module" ON) option(INTERNAL_MODULE_PPM "Support for PPM internal module" OFF) option(AUTOUPDATE "Auto update internal chips from SD" OFF) +option(BIND_KEY "Enable bind button" OFF) + +set(EXTERNAL_MODULE YES) if(PCB STREQUAL X9E) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" ON) @@ -144,6 +147,24 @@ elseif(PCB STREQUAL X7) add_definitions(-DRADIO_TX12) add_definitions(-DEEPROM_VARIANT=0x4002) add_definitions(-DMANUFACTURER_RADIOMASTER) + elseif(PCBREV STREQUAL T8) + option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) + set(EXTERNAL_MODULE NO) + set(AFHDS3 NO) + set(FLAVOUR t8) + set(NAVIGATION_TYPE x7) + set(CPU_TYPE STM32F2) + set(CPU_TYPE_FULL STM32F205xE) # for size report + set(LINKER_SCRIPT targets/taranis/stm32f2_flash.ld) + set(ROTARY_ENCODER NO) + set(LUA_EXPORT lua_export_t8) + set(BLUETOOTH NO) + if(RADIOMASTER_RELEASE) + set(BIND_KEY YES) + endif() + add_definitions(-DRADIO_T8) + add_definitions(-DEEPROM_VARIANT=0x4004) + add_definitions(-DMANUFACTURER_RADIOMASTER) elseif(PCBREV STREQUAL ACCESS) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) @@ -334,6 +355,14 @@ if(ROTARY_ENCODER) ) endif() +if(BIND_KEY) + set(TARGET_SRC + ${TARGET_SRC} + bind_button_driver.cpp + ) + add_definitions(-DBIND_KEY) +endif() + if(USB_CHARGER) set(TARGET_SRC ${TARGET_SRC} usb_charger_driver.cpp) add_definitions(-DUSB_CHARGER) @@ -454,4 +483,8 @@ if(INTERNAL_MODULE_PPM) add_definitions(-DINTERNAL_MODULE_PPM) endif() +if(EXTERNAL_MODULE) + add_definitions(-DHARDWARE_EXTERNAL_MODULE) +endif() + set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET}) diff --git a/radio/src/targets/taranis/backlight_driver.cpp b/radio/src/targets/taranis/backlight_driver.cpp index 63a5bfd1a..7c758f818 100644 --- a/radio/src/targets/taranis/backlight_driver.cpp +++ b/radio/src/targets/taranis/backlight_driver.cpp @@ -20,7 +20,13 @@ #include "opentx.h" -#if defined(PCBX9E) +#if !defined(BACKLIGHT_GPIO) + // no backlight + void backlightInit() {} + void backlightEnable(uint8_t level) {} + void backlightDisable() {} + uint8_t isBacklightEnabled() {return false;} +#elif defined(PCBX9E) void backlightInit() { GPIO_InitTypeDef GPIO_InitStructure; diff --git a/radio/src/targets/taranis/bind_button_driver.cpp b/radio/src/targets/taranis/bind_button_driver.cpp new file mode 100644 index 000000000..2d3ca63fc --- /dev/null +++ b/radio/src/targets/taranis/bind_button_driver.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "opentx.h" + + +bool setBindProtocolSelection() +{ + int16_t xPos = calibratedAnalogs[CONVERT_MODE(0)]; + int16_t yPos = calibratedAnalogs[CONVERT_MODE(1)]; + + // Center: D8 + if (abs(xPos < 50 && abs(yPos) < 50)) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D8; + storageDirty(EE_MODEL); + return true; + } + + // Top left: D16 FCC + if (xPos < -512 && yPos > 512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D16; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + // Top right: D16 LBT + if (xPos > 512 && yPos > 512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D16_LBT; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + // Bottom left: V2.1 D16 FCC + if (xPos < -512 && yPos < -512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKYX2); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKYX2_SUBTYPE_D16; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + // Bottom right: V2.1 D16 LBT + if (xPos > 512 && yPos < -512) { + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKYX2); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKYX2_SUBTYPE_D16_LBT; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; + storageDirty(EE_MODEL); + return true; + } + + return false; +} + +void bindButtonHandler(event_t event) +{ + if( !isModuleMultimodule(INTERNAL_MODULE)) + return; + + if (event == EVT_KEY_LONG(KEY_BIND) && getMultiBindStatus(INTERNAL_MODULE) == MULTI_NORMAL_OPERATION) { + if (setBindProtocolSelection()) { + setMultiBindStatus(INTERNAL_MODULE, MULTI_BIND_INITIATED); + moduleState[INTERNAL_MODULE].mode = MODULE_MODE_BIND; + } + } + + if (getMultiBindStatus(INTERNAL_MODULE) == MULTI_BIND_INITIATED) { + if (FAST_BLINK_ON_PHASE) + LED_BIND(); + else + ledOff(); + } + + if (getMultiBindStatus(INTERNAL_MODULE) == MULTI_BIND_FINISHED) { + setMultiBindStatus(INTERNAL_MODULE, MULTI_NORMAL_OPERATION); + moduleState[INTERNAL_MODULE].mode = MODULE_MODE_NORMAL; + LED_ERROR_END(); + } +} diff --git a/radio/src/targets/taranis/board.cpp b/radio/src/targets/taranis/board.cpp index d86a82f2d..bec585b25 100644 --- a/radio/src/targets/taranis/board.cpp +++ b/radio/src/targets/taranis/board.cpp @@ -272,6 +272,8 @@ void boardOff() #if defined (RADIO_TX12) #define BATTERY_DIVIDER 22830 +#elif defined (RADIO_T8) + #define BATTERY_DIVIDER 50000 #else #define BATTERY_DIVIDER 26214 #endif diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 6ffff9df9..6708b6c89 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -27,8 +27,8 @@ #include "board_common.h" #include "hal.h" -#if defined(RADIO_TX12) - #define NAVIGATION_X7_TX12 +#if defined(RADIO_TX12) || defined(RADIO_TX12) + #define NAVIGATION_X7_TX12 #endif #if defined(ROTARY_ENCODER_NAVIGATION) @@ -152,7 +152,7 @@ void extmoduleSendInvertedByte(uint8_t byte); // Trainer driver #define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE) -#if defined(PCBX9D) || (defined(PCBX9DP) && PCBREV < 2019) +#if defined(TRAINER_DETECT_GPIO) // Trainer detect is a switch on the jack #define TRAINER_CONNECTED() (GPIO_ReadInputDataBit(TRAINER_DETECT_GPIO, TRAINER_DETECT_GPIO_PIN) == Bit_RESET) #elif defined(PCBXLITES) @@ -270,6 +270,10 @@ enum EnumKeys KEY_COUNT, KEY_MAX = KEY_COUNT - 1, +#if defined(KEYS_GPIO_REG_BIND) + KEY_BIND, +#endif + #if defined(ROTARY_ENCODER_NAVIGATION) KEY_PLUS, KEY_MINUS, @@ -355,7 +359,7 @@ enum EnumSwitchesPositions SW_SE1, SW_SE2, #endif -#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || defined(PCBX7) || defined(PCBXLITES) || defined(PCBX9LITES) +#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || defined(PCBX7) || defined(PCBXLITES) || defined(PCBX9LITES) || defined(RADIO_T8) SW_SF0, SW_SF1, SW_SF2, @@ -365,7 +369,7 @@ enum EnumSwitchesPositions SW_SG1, SW_SG2, #endif -#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || (defined(PCBX7) && !defined(RADIO_TX12)) +#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) || (defined(PCBX7) && !defined(RADIO_TX12)) || defined(RADIO_T8) SW_SH0, SW_SH1, SW_SH2, @@ -442,6 +446,11 @@ enum EnumSwitchesPositions #define STORAGE_NUM_SWITCHES NUM_SWITCHES #define DEFAULT_SWITCH_CONFIG (SWITCH_3POS << 10) + (SWITCH_3POS << 8) + (SWITCH_TOGGLE << 6) + (SWITCH_3POS << 4) + (SWITCH_3POS << 2) + (SWITCH_TOGGLE << 0) #define DEFAULT_POTS_CONFIG (POT_WITH_DETENT << 0) + (POT_WITH_DETENT << 2); +#elif defined(RADIO_T8) + #define NUM_SWITCHES 4 + #define STORAGE_NUM_SWITCHES 8 + #define DEFAULT_SWITCH_CONFIG (SWITCH_2POS << 6) + (SWITCH_3POS << 4) + (SWITCH_3POS << 2) + (SWITCH_2POS << 0); + #define DEFAULT_POTS_CONFIG (0) #elif defined(PCBX7ACCESS) #define NUM_SWITCHES 7 #define STORAGE_NUM_SWITCHES 8 @@ -544,6 +553,11 @@ enum Analogs { #define NUM_SLIDERS 0 #define STORAGE_NUM_POTS 1 #define STORAGE_NUM_SLIDERS 0 +#elif defined(RADIO_T8) + #define NUM_POTS 0 + #define NUM_SLIDERS 0 + #define STORAGE_NUM_POTS 2 + #define STORAGE_NUM_SLIDERS 0 #elif defined(PCBXLITE) || defined(PCBX7) #define NUM_POTS 2 #define NUM_SLIDERS 0 @@ -644,6 +658,11 @@ extern uint16_t adcValues[NUM_ANALOGS]; #define BATTERY_WARN 66 // 6.6V #define BATTERY_MIN 67 // 6.7V #define BATTERY_MAX 83 // 8.3V +#elif defined(RADIO_T8) + // 1S Li-ion / Lipo + #define BATTERY_WARN 33 // 3.3V + #define BATTERY_MIN 32 // 3.2V + #define BATTERY_MAX 42 // 4.2V #else // NI-MH 7.2V #define BATTERY_WARN 65 // 6.5V @@ -710,6 +729,10 @@ uint8_t isBacklightEnabled(); #define USB_NAME "Radiomaster TX12" #define USB_MANUFACTURER 'R', 'M', '_', 'T', 'X', ' ', ' ', ' ' /* 8 bytes */ #define USB_PRODUCT 'R', 'M', ' ', 'T', 'X', '1', '2', ' ' /* 8 Bytes */ +#elif defined(RADIO_T8) + #define USB_NAME "Radiomaster T8" + #define USB_MANUFACTURER 'R', 'M', '_', 'T', 'X', ' ', ' ', ' ' /* 8 bytes */ + #define USB_PRODUCT 'R', 'M', ' ', 'T', '8', ' ', ' ', ' ' /* 8 Bytes */ #else #define USB_NAME "FrSky Taranis" #define USB_MANUFACTURER 'F', 'r', 'S', 'k', 'y', ' ', ' ', ' ' /* 8 bytes */ @@ -864,8 +887,10 @@ void bluetoothDisable(); #endif // USB Charger +#if defined(USB_CHARGER) void usbChargerInit(); bool usbChargerLed(); +#endif // LED driver void ledInit(); @@ -889,7 +914,7 @@ void ledBlue(); #define IS_LCD_RESET_NEEDED() true #define LCD_CONTRAST_MIN 10 #define LCD_CONTRAST_MAX 30 -#if defined(RADIO_TX12) +#if defined(RADIO_TX12) || defined(RADIO_T8) #define LCD_CONTRAST_DEFAULT 21 #else #define LCD_CONTRAST_DEFAULT 15 diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index f70e1e690..0647a236a 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -74,6 +74,25 @@ #define KEYS_GPIO_PIN_MDL GPIO_Pin_11 // PE.11 #define KEYS_GPIO_REG_TELE GPIOD->IDR #define KEYS_GPIO_PIN_TELE GPIO_Pin_2 // PD.02 +#elif defined(RADIO_T8) + #define KEYS_GPIO_REG_PAGEUP GPIOD->IDR + #define KEYS_GPIO_PIN_PAGEUP GPIO_Pin_3 // PD.03 + #define KEYS_GPIO_REG_PAGEDN GPIOD->IDR + #define KEYS_GPIO_PIN_PAGEDN GPIO_Pin_7 // PD.07 + #define KEYS_GPIO_REG_EXIT GPIOB->IDR + #define KEYS_GPIO_PIN_EXIT GPIO_Pin_3 // PB.03 + #define KEYS_GPIO_REG_ENTER GPIOA->IDR + #define KEYS_GPIO_PIN_ENTER GPIO_Pin_13 // PA.13 + #define KEYS_GPIO_REG_SYS GPIOB->IDR + #define KEYS_GPIO_PIN_SYS GPIO_Pin_4 // PB.04 + #define KEYS_GPIO_REG_MDL GPIOE->IDR + #define KEYS_GPIO_PIN_MDL GPIO_Pin_11 // PE.11 + #define KEYS_GPIO_REG_PLUS GPIOE->IDR + #define KEYS_GPIO_PIN_PLUS GPIO_Pin_9 // PE.09 + #define KEYS_GPIO_REG_MINUS GPIOE->IDR + #define KEYS_GPIO_PIN_MINUS GPIO_Pin_10 // PE.10 + #define KEYS_GPIO_REG_BIND GPIOA->IDR + #define KEYS_GPIO_PIN_BIND GPIO_Pin_10 // PA.10 #elif defined(PCBX7) #define KEYS_GPIO_REG_PAGE GPIOD->IDR #define KEYS_GPIO_PIN_PAGE GPIO_Pin_3 // PD.03 @@ -332,6 +351,11 @@ #define HARDWARE_SWITCH_A #define SWITCHES_GPIO_REG_A GPIOC->IDR #define SWITCHES_GPIO_PIN_A GPIO_Pin_13 // PC.13 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_A + #define HARDWARE_SWITCH_A + #define SWITCHES_GPIO_REG_A GPIOE->IDR + #define SWITCHES_GPIO_PIN_A GPIO_Pin_14 // PE.14 #elif defined(PCBX7) #define STORAGE_SWITCH_A #define HARDWARE_SWITCH_A @@ -362,6 +386,13 @@ #define SWITCHES_GPIO_PIN_B_L GPIO_Pin_6 // PA.06 #define SWITCHES_GPIO_REG_B_H GPIOA->IDR #define SWITCHES_GPIO_PIN_B_H GPIO_Pin_5 // PA.05 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_B + #define HARDWARE_SWITCH_B + #define SWITCHES_GPIO_REG_B_L GPIOE->IDR + #define SWITCHES_GPIO_PIN_B_L GPIO_Pin_7 // PE.07 + #define SWITCHES_GPIO_REG_B_H GPIOE->IDR + #define SWITCHES_GPIO_PIN_B_H GPIO_Pin_13 // PE.13 #elif defined(PCBX7) #define STORAGE_SWITCH_B #define HARDWARE_SWITCH_B @@ -406,6 +437,13 @@ #define SWITCHES_GPIO_PIN_C_L GPIO_Pin_11 // PD.11 #define SWITCHES_GPIO_REG_C_H GPIOE->IDR #define SWITCHES_GPIO_PIN_C_H GPIO_Pin_0 // PE.00 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_C + #define HARDWARE_SWITCH_C + #define SWITCHES_GPIO_REG_C_L GPIOE->IDR + #define SWITCHES_GPIO_PIN_C_L GPIO_Pin_2 // PE.02 + #define SWITCHES_GPIO_REG_C_H GPIOE->IDR + #define SWITCHES_GPIO_PIN_C_H GPIO_Pin_1 // PE.01 #elif defined(PCBX7) #define STORAGE_SWITCH_C #define HARDWARE_SWITCH_C @@ -448,6 +486,11 @@ #define SWITCHES_GPIO_PIN_D_L GPIO_Pin_4 // PB.04 #define SWITCHES_GPIO_REG_D_H GPIOB->IDR #define SWITCHES_GPIO_PIN_D_H GPIO_Pin_5 // PB.05 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_D + #define HARDWARE_SWITCH_D + #define SWITCHES_GPIO_REG_D GPIOD->IDR + #define SWITCHES_GPIO_PIN_D GPIO_Pin_14 // PD.14 #elif defined(RADIO_TX12) #define STORAGE_SWITCH_D #define HARDWARE_SWITCH_D @@ -539,6 +582,9 @@ #define SWITCHES_GPIO_PIN_F_L GPIO_Pin_1 // PE.01 #define SWITCHES_GPIO_REG_F_H GPIOE->IDR #define SWITCHES_GPIO_PIN_F_H GPIO_Pin_2 // PE.02 +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_F + // no SWF #elif defined(PCBX7) #define STORAGE_SWITCH_F #define HARDWARE_SWITCH_F @@ -563,7 +609,7 @@ #define HARDWARE_SWITCH_G #define SWITCHES_GPIO_REG_G GPIOC->IDR #define SWITCHES_GPIO_PIN_G GPIO_Pin_2 // PC.02 -#elif defined(PCBX7) || defined(PCBXLITE) || defined(PCBX9LITE) +#elif defined(PCBX7) || defined(PCBXLITE) || defined(PCBX9LITE) || defined(RADIO_T8) // no SWG #else #define STORAGE_SWITCH_G @@ -586,6 +632,9 @@ #define SWITCHES_GPIO_PIN_H GPIO_Pin_14 // PD.14 #elif defined(PCBXLITE) || defined(PCBX9LITE) // no SWH +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_H + // no SWH #elif defined(RADIO_TX12) #elif defined(PCBX7) #define STORAGE_SWITCH_H @@ -615,6 +664,10 @@ #define SWITCHES_GPIO_REG_I GPIOC->IDR #define SWITCHES_GPIO_PIN_I GPIO_Pin_13 // PC.13 #define STORAGE_SWITCH_J +#elif defined(RADIO_T8) + #define STORAGE_SWITCH_I + #define STORAGE_SWITCH_J + // no SWI/J #elif defined(RADIO_TX12) #define STORAGE_SWITCH_I #define HARDWARE_SWITCH_I @@ -746,6 +799,13 @@ #define KEYS_GPIOC_PINS (GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3|GPIO_Pin_13) #define KEYS_GPIOD_PINS (GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_7 | GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15) #define KEYS_GPIOE_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8| GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15) +#elif defined(RADIO_T8) + #define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE) + #define KEYS_GPIOA_PINS (KEYS_GPIO_PIN_ENTER | KEYS_GPIO_PIN_BIND) + #define KEYS_GPIOB_PINS (KEYS_GPIO_PIN_EXIT | KEYS_GPIO_PIN_SYS) + #define KEYS_GPIOC_PINS (TRIMS_GPIO_PIN_LHR | TRIMS_GPIO_PIN_RVU | TRIMS_GPIO_PIN_RVD) + #define KEYS_GPIOD_PINS (KEYS_GPIO_PIN_PAGEUP | KEYS_GPIO_PIN_PAGEDN | SWITCHES_GPIO_PIN_D | TRIMS_GPIO_PIN_LHL) + #define KEYS_GPIOE_PINS (SWITCHES_GPIO_PIN_C_H | SWITCHES_GPIO_PIN_C_L | TRIMS_GPIO_PIN_RHL | TRIMS_GPIO_PIN_RHR | TRIMS_GPIO_PIN_LVU | TRIMS_GPIO_PIN_LVD | SWITCHES_GPIO_PIN_B_L | KEYS_GPIO_PIN_PLUS | KEYS_GPIO_PIN_MINUS | KEYS_GPIO_PIN_MDL | SWITCHES_GPIO_PIN_B_H | SWITCHES_GPIO_PIN_A) #elif defined(RADIO_X7ACCESS) #define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE) #define KEYS_GPIOA_PINS GPIO_Pin_5 @@ -900,6 +960,25 @@ #define ADC_CHANNEL_POT2 ADC_Channel_12 // ADC1_IN12 #define ADC_CHANNEL_BATT ADC_Channel_10 // ADC1_IN10 #define ADC_VREF_PREC2 330 +#elif defined(RADIO_T8) + // No pots + #define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_DMA2) + #define ADC_RCC_APB1Periph 0 + #define ADC_RCC_APB2Periph RCC_APB2Periph_ADC1 + #define ADC_GPIO_PIN_STICK_RV GPIO_Pin_0 // PA.00 + #define ADC_GPIO_PIN_STICK_RH GPIO_Pin_1 // PA.01 + #define ADC_GPIO_PIN_STICK_LV GPIO_Pin_2 // PA.02 + #define ADC_GPIO_PIN_STICK_LH GPIO_Pin_3 // PA.03 + #define ADC_CHANNEL_STICK_RV ADC_Channel_0 // ADC1_IN0 + #define ADC_CHANNEL_STICK_RH ADC_Channel_1 // ADC1_IN1 + #define ADC_CHANNEL_STICK_LV ADC_Channel_2 // ADC1_IN2 + #define ADC_CHANNEL_STICK_LH ADC_Channel_3 // ADC1_IN3 + #define ADC_GPIO_PIN_BATT GPIO_Pin_0 // PC.00 + #define ADC_GPIOA_PINS (ADC_GPIO_PIN_STICK_RV | ADC_GPIO_PIN_STICK_RH | ADC_GPIO_PIN_STICK_LH | ADC_GPIO_PIN_STICK_LV) + #define ADC_GPIOB_PINS 0 + #define ADC_GPIOC_PINS ADC_GPIO_PIN_BATT + #define ADC_CHANNEL_BATT ADC_Channel_10 + #define ADC_VREF_PREC2 300 #elif defined(PCBX7) #define HARDWARE_POT1 #define HARDWARE_POT2 @@ -1027,6 +1106,16 @@ #define LED_BLUE_GPIO_PIN GPIO_Pin_1 // PB.01 #define LED_RED_GPIO GPIOC #define LED_RED_GPIO_PIN GPIO_Pin_4 // PC.04 +#elif defined(RADIO_T8) + #define STATUS_LEDS + #define GPIO_LED_GPIO_ON GPIO_SetBits + #define GPIO_LED_GPIO_OFF GPIO_ResetBits + #define LED_RED_GPIO GPIOC + #define LED_RED_GPIO_PIN GPIO_Pin_4 // PC.04 + #define LED_BLUE_GPIO GPIOC + #define LED_BLUE_GPIO_PIN GPIO_Pin_5 // PC.05 + #define LED_GREEN_GPIO GPIOB + #define LED_GREEN_GPIO_PIN GPIO_Pin_1 // PB.01 #elif defined(PCBX7) #define STATUS_LEDS #define GPIO_LED_GPIO_ON GPIO_SetBits @@ -1152,7 +1241,7 @@ #define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream5_IRQHandler #define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF5 #define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#elif defined(RADIO_T12) || defined(RADIO_TX12) +#elif defined(RADIO_T12) || defined(RADIO_TX12) || defined(RADIO_T8) #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1) #define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_USART3 | RCC_APB1Periph_TIM2) #define INTMODULE_RCC_APB2Periph 0 @@ -1401,7 +1490,7 @@ #define TELEMETRY_EXTI_IRQn EXTI9_5_IRQn #define TELEMETRY_EXTI_TRIGGER EXTI_Trigger_Rising -#if defined(RADIO_X7) || defined(RADIO_X7ACCESS) || defined(RADIO_TX12) +#if defined(RADIO_X7) || defined(RADIO_X7ACCESS) || defined(RADIO_TX12) || defined(RADIO_T8) #define TELEMETRY_EXTI_REUSE_INTERRUPT_ROTARY_ENCODER #elif defined(PCBXLITE) || defined(PCBX9LITE) || defined(RADIO_X9DP2019) #define TELEMETRY_EXTI_IRQHandler EXTI9_5_IRQHandler @@ -1477,7 +1566,9 @@ // Heartbeat for iXJT / ISRM synchro #define INTMODULE_HEARTBEAT_TRIGGER EXTI_Trigger_Falling -#if defined(PCBXLITE) +#if !defined(HARDWARE_EXTERNAL_MODULE) + // No heartbeat +#elif defined(PCBXLITE) #define INTMODULE_HEARTBEAT #define INTMODULE_HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD #define INTMODULE_HEARTBEAT_GPIO GPIOD @@ -1507,7 +1598,7 @@ #define INTMODULE_HEARTBEAT_EXTI_PinSource GPIO_PinSource7 #define INTMODULE_HEARTBEAT_EXTI_LINE EXTI_Line7 #define INTMODULE_HEARTBEAT_EXTI_IRQn EXTI9_5_IRQn -#elif defined(RADIO_X7) || defined(RADIO_TX12) +#elif defined(RADIO_X7) || defined(RADIO_TX12) #define INTMODULE_HEARTBEAT #define INTMODULE_HEARTBEAT_REUSE_INTERRUPT_ROTARY_ENCODER #define INTMODULE_HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOC @@ -1577,15 +1668,17 @@ #define TRAINER_MODULE_SBUS_DMA_STREAM EXTMODULE_USART_RX_DMA_STREAM #define TRAINER_MODULE_SBUS_DMA_CHANNEL EXTMODULE_USART_RX_DMA_CHANNEL #else +#if defined(HARDWARE_EXTERNAL_MODULE) #define TRAINER_MODULE_CPPM #define TRAINER_MODULE_SBUS - #define TRAINER_MODULE_RCC_AHB1Periph RCC_AHB1Periph_GPIOC - #define TRAINER_MODULE_RCC_APB2Periph RCC_APB2Periph_USART6 - #define TRAINER_MODULE_RCC_APB1Periph RCC_APB1Periph_TIM3 - #define TRAINER_MODULE_CPPM_TIMER TRAINER_TIMER - #define TRAINER_MODULE_CPPM_GPIO INTMODULE_HEARTBEAT_GPIO - #define TRAINER_MODULE_CPPM_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN - #define TRAINER_MODULE_CPPM_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource +#endif + #define TRAINER_MODULE_RCC_AHB1Periph RCC_AHB1Periph_GPIOC + #define TRAINER_MODULE_RCC_APB2Periph RCC_APB2Periph_USART6 + #define TRAINER_MODULE_RCC_APB1Periph RCC_APB1Periph_TIM3 + #define TRAINER_MODULE_CPPM_TIMER TRAINER_TIMER + #define TRAINER_MODULE_CPPM_GPIO INTMODULE_HEARTBEAT_GPIO + #define TRAINER_MODULE_CPPM_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN + #define TRAINER_MODULE_CPPM_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource #define TRAINER_MODULE_CPPM_INTERRUPT_ENABLE TIM_DIER_CC2IE #define TRAINER_MODULE_CPPM_INTERRUPT_FLAG TIM_SR_CC2IF #define TRAINER_MODULE_CPPM_CCMR1 (TIM_CCMR1_IC2F_0 | TIM_CCMR1_IC2F_1 | TIM_CCMR1_CC2S_0) @@ -1665,6 +1758,10 @@ #define BACKLIGHT_CCMR1 TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 // Channel 1, PWM #define BACKLIGHT_CCER TIM_CCER_CC1E #define BACKLIGHT_COUNTER_REGISTER BACKLIGHT_TIMER->CCR1 +#elif defined(RADIO_T8) + #define BACKLIGHT_RCC_AHB1Periph 0 + #define BACKLIGHT_RCC_APB1Periph 0 + #define BACKLIGHT_RCC_APB2Periph 0 #elif defined(PCBX7) #define BACKLIGHT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD #define BACKLIGHT_RCC_APB1Periph RCC_APB1Periph_TIM4 @@ -1692,7 +1789,7 @@ #define KEYS_BACKLIGHT_RCC_AHB1Periph 0 // LCD driver -#if defined(RADIO_TX12) || defined(RADIO_T12) +#if defined(RADIO_TX12) || defined(RADIO_T12) || defined(RADIO_T8) #define LCD_VERTICAL_INVERT #endif #if defined(PCBX9E) @@ -1887,7 +1984,7 @@ #define AUDIO_SPEAKER_ENABLE_GPIO_PIN GPIO_Pin_14 // PD.14 #define HEADPHONE_TRAINER_SWITCH_GPIO GPIOD #define HEADPHONE_TRAINER_SWITCH_GPIO_PIN GPIO_Pin_9 // PD.09 -#elif defined(RADIO_TX12) +#elif defined(RADIO_TX12) || defined(RADIO_T8) #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #define AUDIO_MUTE_GPIO GPIOE #define AUDIO_MUTE_GPIO_PIN GPIO_Pin_12 // PE.12 @@ -2013,7 +2110,7 @@ #define BT_USART_IRQn USART3_IRQn // #define BT_DMA_Stream_RX DMA1_Stream1 // #define BT_DMA_Channel_RX DMA_Channel_4 -#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T12) || defined(RADIO_TX12) +#elif defined(PCBX9D) || defined(PCBX9DP) || defined(RADIO_T12) || defined(RADIO_TX12) | defined(RADIO_T8) #define STORAGE_BLUETOOTH #define BT_RCC_AHB1Periph 0 #define BT_RCC_APB1Periph 0 diff --git a/radio/src/targets/taranis/keys_driver.cpp b/radio/src/targets/taranis/keys_driver.cpp index 6883740c4..d99851441 100644 --- a/radio/src/targets/taranis/keys_driver.cpp +++ b/radio/src/targets/taranis/keys_driver.cpp @@ -88,6 +88,10 @@ uint32_t readKeys() result |= 1 << KEY_SHIFT; #endif +#if defined(KEYS_GPIO_PIN_BIND) + if (~KEYS_GPIO_REG_BIND & KEYS_GPIO_PIN_BIND) + result |= 1 << KEY_BIND; +#endif // if (result != 0) TRACE("readKeys(): result=0x%02x", result); return result; @@ -200,7 +204,8 @@ uint32_t switchState(uint8_t index) uint32_t xxx = 0; switch (index) { -#if defined(RADIO_TX12) + +#if defined(RADIO_TX12) || defined(RADIO_T8) ADD_2POS_CASE(A); ADD_3POS_CASE(B, 1); ADD_3POS_CASE(C, 2); @@ -235,6 +240,8 @@ uint32_t switchState(uint8_t index) ADD_2POS_CASE(D); ADD_3POS_CASE(E, 4); ADD_3POS_CASE(F, 5); +#elif defined(RADIO_T8) + ADD_2POS_CASE(D); #elif defined(PCBX7) ADD_3POS_CASE(D, 3); ADD_2POS_CASE(F); diff --git a/radio/src/targets/taranis/lcd_driver_spi.cpp b/radio/src/targets/taranis/lcd_driver_spi.cpp index e1efa8e15..97f191c2b 100644 --- a/radio/src/targets/taranis/lcd_driver_spi.cpp +++ b/radio/src/targets/taranis/lcd_driver_spi.cpp @@ -20,7 +20,7 @@ #include "opentx.h" -#if defined(RADIO_T12) || defined(RADIO_TX12) +#if defined(RADIO_T12) || defined(RADIO_TX12) || defined(RADIO_T8) #define LCD_CONTRAST_OFFSET -10 #else #define LCD_CONTRAST_OFFSET 160 diff --git a/radio/src/targets/taranis/usb_charger_driver.cpp b/radio/src/targets/taranis/usb_charger_driver.cpp index 7e92c6dbc..78cc3d6a6 100644 --- a/radio/src/targets/taranis/usb_charger_driver.cpp +++ b/radio/src/targets/taranis/usb_charger_driver.cpp @@ -33,5 +33,9 @@ void usbChargerInit() bool usbChargerLed() { +#if defined(SIMU) + return true; +#else return (GPIO_ReadInputDataBit(USB_CHARGER_GPIO, USB_CHARGER_GPIO_PIN) == Bit_RESET && usbPlugged()); +#endif } diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index b10aaf1fc..87f83a9e9 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -294,6 +294,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Mínus" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index f42ba76d8..0d30316e3 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -300,6 +300,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index f28c96b5c..d0dd3e25c 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -300,6 +300,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 5d4a67fad..cbbe3b591 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -297,6 +297,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index b031eda0f..03c1ecf25 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -318,6 +318,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index a5e5d7180..ce6624b67 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -320,6 +320,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Moins" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 06f51beee..eeecb347a 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -321,6 +321,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 6f1063d3d..087305944 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -298,6 +298,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index f87473ef2..4ff16cc53 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -318,6 +318,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" @@ -1130,17 +1133,17 @@ #define TR_ABOUT_MARTIN_2 "Projekty graficzne" #if defined(MANUFACTURER_FRSKY) - #define TR_ABOUT_HARDWARE_1 "FrSky" - #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" - #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_RADIOMASTER) - #define TR_ABOUT_HARDWARE_1 "Radiomaster" - #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" - #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_JUMPER) - #define TR_ABOUT_HARDWARE_1 "JumperRC" - #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" - #define TR_ABOUT_HARDWARE_3 "Współautor firmware" + #define TR_ABOUT_HARDWARE_1 "FrSky" + #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" + #define TR_ABOUT_HARDWARE_3 "Współautor firmware" +#elif defined(MANUFACTURER_RADI OMASTER) + #define TR_ABOUT_HARDWARE_1 "Radiomaster" + #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" + #define TR_ABOUT_HARDWARE_3 "Współautor firmware" +#elif defined(MANUFACTURER_JUMP ER) + #define TR_ABOUT_HARDWARE_1 "JumperRC" + #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" + #define TR_ABOUT_HARDWARE_3 "Współautor firmware" #else #define TR_ABOUT_HARDWARE_1 "Brent Nelson" #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 472a5585e..7ef85235b 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -311,6 +311,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index ad9606ef1..499fcb41d 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -312,6 +312,9 @@ #elif defined(RADIO_TX12) #define LEN_VKEYS "\005" #define TR_VKEYS "Exit\0""Enter""Up\0 ""Down\0""SYS\0 ""MDL\0 ""TELE\0" +#elif defined(RADIO_T8) + #define LEN_VKEYS "\005" + #define TR_VKEYS "RTN\0 ""ENTER""PGUP\0""PGDN\0""SYS\0 ""MDL\0 ""UP\0 ""DOWN\0" #elif defined(PCBTARANIS) #define LEN_VKEYS "\005" #define TR_VKEYS "Menu\0""Exit\0""Enter""Page\0""Plus\0""Minus" diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index 66d9f053d..b7132d510 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -148,6 +148,11 @@ def main(): cmake_options["PCBREV"] = "TX12" firmware_options = options_radiomaster_tx12 maxsize = 65536 * 8 + elif board_name == "t8": + cmake_options["PCB"] = "X7" + cmake_options["PCBREV"] = "T8" + firmware_options = options_radiomaster_t8 + maxsize = 65536 * 8 elif board_name == "t16": cmake_options["PCB"] = "X10" cmake_options["PCBREV"] = "T16" diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index f4438db44..7b2a01edd 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -258,6 +258,17 @@ options_radiomaster_tx12 = { "afhds3": ("AFHDS3", "YES", "NO") } +options_radiomaster_t8 = { + "noheli": ("HELI", "NO", "YES"), + "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), + "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), + "nogvars": ("GVARS", "NO", "YES"), + "faimode": ("FAI", "YES", None), + "faichoice": ("FAI", "CHOICE", None), + "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), + "bindkey": ("BIND_KEY", "NO", "YES"), +} + options_radiomaster_tx16s = { "noheli": ("HELI", "NO", "YES"), "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), diff --git a/tools/build-radiomaster.py b/tools/build-radiomaster.py index 985bc705a..564463537 100755 --- a/tools/build-radiomaster.py +++ b/tools/build-radiomaster.py @@ -28,6 +28,18 @@ boards = { "PCB": "X7", "PCBREV": "TX12", "DEFAULT_MODE": "2", + }, + "T8_1": { + "PCB": "X7", + "PCBREV": "T8", + "DEFAULT_MODE": "1", + "RADIOMASTER_RTF_RELEASE": "YES", + }, + "T8_2": { + "PCB": "X7", + "PCBREV": "T8", + "DEFAULT_MODE": "2", + "RADIOMASTER_RTF_RELEASE": "YES", } } diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index af2e7f040..ca25ac809 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -139,6 +139,15 @@ if [[ " TX12 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then make -j"${CORES}" tests-radio fi +if [[ " T8 X7 ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on T8 + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=X7 -DPCBREV=TX12 -DHELI=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi + if [[ " XLITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X-Lite rm -rf ./* From e632a335ea63ddb991e143d1d66e80fe80c73e5d Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 17:20:41 +0100 Subject: [PATCH 059/300] Turn off module on model change (#8189) --- radio/src/gui/480x272/model_select.cpp | 1 + radio/src/storage/storage_common.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/radio/src/gui/480x272/model_select.cpp b/radio/src/gui/480x272/model_select.cpp index 661edd814..cd0122ed4 100644 --- a/radio/src/gui/480x272/model_select.cpp +++ b/radio/src/gui/480x272/model_select.cpp @@ -246,6 +246,7 @@ void onModelSelectMenu(const char * result) if (!confirmModelChange()) return; } + // we store the latest changes if any storageFlushCurrentModel(); storageCheck(true); diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index da86cebe8..b21cc007d 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -52,6 +52,9 @@ void preModelLoad() pausePulses(); } pauseMixerCalculations(); +#if defined(EXTMODULE_PWR_GPIO) + EXTERNAL_MODULE_PWR_OFF(); +#endif stopTrainer(); } From 17e272412e2392ea2c0978bc484bd603f282cf31 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 18:01:16 +0100 Subject: [PATCH 060/300] Fixes --- radio/src/storage/storage_common.cpp | 6 +++++- radio/src/targets/taranis/hal.h | 2 +- radio/src/translations/pl.h.txt | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index b21cc007d..40bac6a67 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -52,8 +52,12 @@ void preModelLoad() pausePulses(); } pauseMixerCalculations(); + +#if defined(INTMODULE_PWR_GPIO) + INTERNAL_MODULE_OFF(); +#endif #if defined(EXTMODULE_PWR_GPIO) - EXTERNAL_MODULE_PWR_OFF(); + EXTERNAL_MODULE_OFF(); #endif stopTrainer(); diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 0647a236a..14626fc91 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -1241,7 +1241,7 @@ #define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream5_IRQHandler #define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF5 #define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#elif defined(RADIO_T12) || defined(RADIO_TX12) || defined(RADIO_T8) +#elif (defined(RADIO_T12) && defined(HARDWARE_INTERNAL_MODULE))|| defined(RADIO_TX12) || defined(RADIO_T8) #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1) #define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_USART3 | RCC_APB1Periph_TIM2) #define INTMODULE_RCC_APB2Periph 0 diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 4ff16cc53..367694cde 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -1136,18 +1136,18 @@ #define TR_ABOUT_HARDWARE_1 "FrSky" #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_RADI OMASTER) +#elif defined(MANUFACTURER_RADIOMASTER) #define TR_ABOUT_HARDWARE_1 "Radiomaster" #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" -#elif defined(MANUFACTURER_JUMP ER) +#elif defined(MANUFACTURER_JUMPER) #define TR_ABOUT_HARDWARE_1 "JumperRC" #define TR_ABOUT_HARDWARE_2 "Proucent/projektant Hardware" #define TR_ABOUT_HARDWARE_3 "Współautor firmware" #else - #define TR_ABOUT_HARDWARE_1 "Brent Nelson" - #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" - #define TR_ABOUT_HARDWARE_3 "" + #define TR_ABOUT_HARDWARE_1 "Brent Nelson" + #define TR_ABOUT_HARDWARE_2 "Sky9x designer/výrobce" + #define TR_ABOUT_HARDWARE_3 "" #endif #define TR_ABOUT_PARENTS_1 "Projekty macierzyste" From acb6030d0147e4b05a67bc5d391cc7ebc39d3294 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 15 Jan 2021 18:25:09 +0100 Subject: [PATCH 061/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 41ce9cbb0..85246719c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2097,3 +2097,4 @@ Jens Rainer Hansen Mark Trible Marc Dubois Alan Wilkins +Daniel Hryciuk From c8fa56445de2670ed83ac1cbfdfcbdf2145b79c3 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 15 Jan 2021 19:18:54 +0100 Subject: [PATCH 062/300] Compilation fix --- companion/src/firmwares/sensordata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 753d2eed0..ebcccfa6a 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -217,7 +217,7 @@ bool SensorData::isSourceAvailable(const ModelData * model, const int index) const int sensorcnt = firmware->getCapability(Sensors); const int i = abs(index); - if (model && sensorcnt > 0 and i <= sensorcnt) { + if (model && sensorcnt > 0 && i <= sensorcnt) { if (i > 0) return model->sensorData[i - 1].isAvailable(); else From a9ad94c99d9bb4c975807ae48201b91ed606bf26 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 15 Jan 2021 19:35:27 +0100 Subject: [PATCH 063/300] Improved T8 RTF defaults (no splash, faster power on) --- radio/src/opentx.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 7a222635a..4e31efacb 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -338,6 +338,8 @@ void generalDefault() // Those settings are for headless radio g_eeGeneral.USBMode = USB_JOYSTICK_MODE; g_eeGeneral.disableRtcWarning = 1; + g_eeGeneral.splashMode = 3; // Disable splash + g_eeGeneral.pwrOnSpeed = 1; // 1 second #endif g_eeGeneral.chkSum = 0xFFFF; From 15a77c268917c8414c3b817c7b4590a95490aeb3 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Fri, 15 Jan 2021 19:47:12 +0100 Subject: [PATCH 064/300] T8 swap keys for value edit --- radio/src/gui/navigation/navigation_x7.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/navigation/navigation_x7.cpp b/radio/src/gui/navigation/navigation_x7.cpp index 311a53d9f..7e4255dac 100644 --- a/radio/src/gui/navigation/navigation_x7.cpp +++ b/radio/src/gui/navigation/navigation_x7.cpp @@ -59,7 +59,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(ROTARY_ENCODER_NAVIGATION) if (s_editMode>0 && event==EVT_ROTARY_RIGHT) { #else - if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) { + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) { #endif newval += min(rotencSpeed, i_max-val); while (isValueAvailable && !isValueAvailable(newval) && newval<=i_max) { @@ -73,7 +73,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(ROTARY_ENCODER_NAVIGATION) else if (s_editMode>0 && event==EVT_ROTARY_LEFT) { #else - if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) { + if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) { #endif newval -= min(rotencSpeed, val-i_min); while (isValueAvailable && !isValueAvailable(newval) && newval>=i_min) { From 5321a8afe9f7e59a2138de0aac281de224154d5a Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 14 Jan 2021 17:33:26 +1100 Subject: [PATCH 065/300] Refactor sensor printing --- companion/src/firmwares/rawsource.cpp | 2 +- companion/src/firmwares/sensordata.cpp | 306 ++++++++++++++++--------- companion/src/firmwares/sensordata.h | 5 +- companion/src/modelprinter.cpp | 163 ------------- companion/src/modelprinter.h | 7 - companion/src/multimodelprinter.cpp | 14 +- 6 files changed, 210 insertions(+), 287 deletions(-) diff --git a/companion/src/firmwares/rawsource.cpp b/companion/src/firmwares/rawsource.cpp index 67ad743b6..898c0ca4a 100644 --- a/companion/src/firmwares/rawsource.cpp +++ b/companion/src/firmwares/rawsource.cpp @@ -60,7 +60,7 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting result.min = -30000 * result.step; result.max = +30000 * result.step; result.decimals = sensor.prec; - result.unit = sensor.unitString(); + result.unit = SensorData::unitToString(qr.quot); break; } diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 753d2eed0..152e51a81 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -33,10 +33,206 @@ void SensorData::updateUnit() } } -// TODO depreciated -QString SensorData::unitString() const +QString SensorData::nameToString(int index) const { - return unitToString(unit); + return RadioData::getElementName(tr("TELE"), index + 1, label); +} + +QString SensorData::getOrigin(const ModelData * model) const +{ + if (type != TELEM_TYPE_CUSTOM || !id) + return QString(); + + const ModuleData & module = model->moduleData[moduleIdx]; + if (module.isPxx2Module() && rxIdx <= 2 && module.access.receivers & (1 << rxIdx)) { + return QString(module.access.receiverName[rxIdx]); + } + else { + return QString(); + } +} + +bool SensorData::isEmpty() const +{ + return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); +} + +constexpr int SENSOR_ISCONFIGURABLE { 1 << 1 }; +constexpr int SENSOR_PRINT_GPS { 1 << 2 }; +constexpr int SENSOR_PRINT_CELLS { 1 << 3 }; +constexpr int SENSOR_PRINT_CONSUMPTION { 1 << 4 }; +constexpr int SENSOR_PRINT_RATIO { 1 << 5 }; +constexpr int SENSOR_PRINT_TOTALIZE { 1 << 6 }; +constexpr int SENSOR_PRINT_SOURCES_12 { 1 << 7 }; +constexpr int SENSOR_PRINT_SOURCES_34 { 1 << 8 }; +constexpr char FMT_LABEL[] { "%1: " }; +constexpr char FMT_VALUE[] { "%1 " }; +constexpr char FMT_LABEL_VALUE[] { "%1: %2 " }; + +QString SensorData::paramsToString(const ModelData * model) const +{ + if (!isAvailable()) + return ""; + + QString str; + int mask = 0; + + if (type == TELEM_TYPE_CALCULATED) { + if (formula < TELEM_FORMULA_CELL) + mask |= SENSOR_ISCONFIGURABLE; + if (formula == TELEM_FORMULA_DIST) + mask |= SENSOR_PRINT_GPS; + if (formula == TELEM_FORMULA_CELL) + mask |= SENSOR_PRINT_CELLS; + if (formula == TELEM_FORMULA_CONSUMPTION) + mask |= SENSOR_PRINT_CONSUMPTION; + if (formula <= TELEM_FORMULA_MULTIPLY) + mask |= SENSOR_PRINT_SOURCES_12; + if (formula < TELEM_FORMULA_MULTIPLY) + mask |= SENSOR_PRINT_SOURCES_34; + if (formula == TELEM_FORMULA_TOTALIZE) + mask |= SENSOR_PRINT_TOTALIZE; + + str.append(QString(FMT_LABEL_VALUE).arg(tr("Formula")).arg(formulaToString(formula))); + } + else { + if (unit < UNIT_FIRST_VIRTUAL) + mask |= (SENSOR_ISCONFIGURABLE | SENSOR_PRINT_RATIO); + + str.append(QString(FMT_LABEL_VALUE).arg(tr("Id")).arg(QString::number(id, 16).toUpper())); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Instance")).arg(instance)); + } + + if (mask & SENSOR_PRINT_CELLS) { + str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, source))); + str.append(QString(FMT_VALUE).arg(cellIndexToString(index))); + } + + if (mask & SENSOR_PRINT_SOURCES_12) { + str.append(QString(FMT_LABEL).arg(tr("Sources"))); + for (int i = 0; i < 4; i++) { + if (i < 2 || mask & SENSOR_PRINT_SOURCES_34) { + str.append(QString(FMT_VALUE).arg(sourceToString(model, sources[i]))); + } + } + } + + if (mask & SENSOR_PRINT_CONSUMPTION || mask & SENSOR_PRINT_TOTALIZE) + str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, amps))); + + if (mask & SENSOR_PRINT_GPS) { + str.append(QString(FMT_LABEL_VALUE).arg(tr("GPS")).arg(sourceToString(model, gps))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Alt")).arg(sourceToString(model, alt))); + } + + str.append(QString(FMT_LABEL_VALUE).arg(tr("Unit")).arg(unitToString(unit))); + + if (mask & SENSOR_ISCONFIGURABLE && unit != UNIT_FAHRENHEIT) + str.append(QString(FMT_LABEL_VALUE).arg(tr("Precision")).arg(precToString(prec))); + + if (mask & SENSOR_PRINT_RATIO) { + if (unit != UNIT_RPMS) { + int precsn = prec == 0 ? 1 : pow(10, prec); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Ratio")).arg((float)ratio / 10)); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Offset")).arg(QString::number((float)offset / precsn, 'f', prec))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Auto Offset")).arg(boolToString(autoOffset))); + } + else { + str.append(QString(FMT_LABEL_VALUE).arg(tr("Blades")).arg(ratio)); // TODO refactor to dedicated RPMS field + str.append(QString(FMT_LABEL_VALUE).arg(tr("Multi")).arg(offset)); // TODO refactor to dedicated RPMS field + } + } + + if (mask & SENSOR_ISCONFIGURABLE) + str.append(QString(FMT_LABEL_VALUE).arg(tr("Filter")).arg(boolToString(filter))); + + if (type == TELEM_TYPE_CALCULATED) + str.append(QString(FMT_LABEL_VALUE).arg(tr("Persist")).arg(boolToString(persistent))); + + str.append(QString(FMT_LABEL_VALUE).arg(tr("Positive")).arg(boolToString(onlyPositive))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Log")).arg(boolToString(logs))); + + return str; +} + +// static +QString SensorData::typeToString(const int value) +{ + switch(value) { + case TELEM_TYPE_CUSTOM: + return tr("Custom"); + case TELEM_TYPE_CALCULATED: + return tr("Calculated"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString SensorData::formulaToString(const int value) +{ + switch(value) { + case TELEM_FORMULA_ADD: + return tr("Add"); + case TELEM_FORMULA_AVERAGE: + return tr("Average"); + case TELEM_FORMULA_MIN: + return tr("Minimum"); + case TELEM_FORMULA_MAX: + return tr("Maximum"); + case TELEM_FORMULA_MULTIPLY: + return tr("Multiply"); + case TELEM_FORMULA_TOTALIZE: + return tr("Totalize"); + case TELEM_FORMULA_CELL: + return tr("Cell"); + case TELEM_FORMULA_CONSUMPTION: + return tr("Consumption"); + case TELEM_FORMULA_DIST: + return tr("Distance"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString SensorData::cellIndexToString(const int value) +{ + if (value == TELEM_CELL_INDEX_LOWEST) + return tr("Lowest"); + if (value > TELEM_CELL_INDEX_LOWEST && value < TELEM_CELL_INDEX_HIGHEST) + return tr("Cell %1").arg(value - TELEM_CELL_INDEX_LOWEST); + if (value== TELEM_CELL_INDEX_HIGHEST) + return tr("Highest"); + if (value == TELEM_CELL_INDEX_DELTA) + return tr("Delta"); + return CPN_STR_UNKNOWN_ITEM; +} + +// static +QString SensorData::precToString(const int value) +{ + return QString("0.%1").arg(QString(value, '0')); +} + +// static +QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign) +{ + if (model) { + const QString prfx = sign ? index < 0 ? "-" : "+" : ""; + + if (abs(index) > 0) { + const SensorData &sd = model->sensorData[abs(index) - 1]; + if (sd.type == SensorData::TELEM_TYPE_CUSTOM) + return QString("%1%2 (%3)").arg(prfx).arg(sd.label).arg(sd.getOrigin(model)); + else + return QString("%1%2").arg(prfx).arg(sd.label); + } + else + return CPN_STR_NONE_ITEM; + } + + return ""; } // static @@ -106,110 +302,6 @@ QString SensorData::unitToString(const int value) } } -QString SensorData::nameToString(int index) const -{ - return RadioData::getElementName(tr("TELE"), index + 1, label); -} - -QString SensorData::getOrigin(const ModelData * model) const -{ - if (type != TELEM_TYPE_CUSTOM || !id) - return QString(); - - const ModuleData & module = model->moduleData[moduleIdx]; - if (module.isPxx2Module() && rxIdx <= 2 && module.access.receivers & (1 << rxIdx)) { - return QString(module.access.receiverName[rxIdx]); - } - else { - return QString(); - } -} - -bool SensorData::isEmpty() const -{ - return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); -} - -// static -QString SensorData::typeToString(const int value) -{ - switch(value) { - case TELEM_TYPE_CUSTOM: - return tr("Custom"); - case TELEM_TYPE_CALCULATED: - return tr("Calculated"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} - -// static -QString SensorData::formulaToString(const int value) -{ - switch(value) { - case TELEM_FORMULA_ADD: - return tr("Add"); - case TELEM_FORMULA_AVERAGE: - return tr("Average"); - case TELEM_FORMULA_MIN: - return tr("Minimum"); - case TELEM_FORMULA_MAX: - return tr("Maximum"); - case TELEM_FORMULA_MULTIPLY: - return tr("Multiply"); - case TELEM_FORMULA_TOTALIZE: - return tr("Totalize"); - case TELEM_FORMULA_CELL: - return tr("Cell"); - case TELEM_FORMULA_CONSUMPTION: - return tr("Consumption"); - case TELEM_FORMULA_DIST: - return tr("Distance"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} - -// static -QString SensorData::cellIndexToString(const int value) -{ - if (value == TELEM_CELL_INDEX_LOWEST) - return tr("Lowest"); - if (value > TELEM_CELL_INDEX_LOWEST && value < TELEM_CELL_INDEX_HIGHEST) - return tr("Cell %1").arg(value - TELEM_CELL_INDEX_LOWEST); - if (value== TELEM_CELL_INDEX_HIGHEST) - return tr("Highest"); - if (value == TELEM_CELL_INDEX_DELTA) - return tr("Delta"); - return CPN_STR_UNKNOWN_ITEM; -} - -// static -QString SensorData::precisionToString(const int value) -{ - return QString("0.%1").arg(QString(value, '0')); -} - -// static -QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign) -{ - if (model) { - const QString prfx = sign ? index < 0 ? "-" : "+" : ""; - - if (abs(index) > 0) { - const SensorData &sd = model->sensorData[abs(index) - 1]; - if (sd.type == SensorData::TELEM_TYPE_CUSTOM) - return QString("%1%2 (%3)").arg(prfx).arg(sd.label).arg(sd.getOrigin(model)); - else - return QString("%1%2").arg(prfx).arg(sd.label); - } - else - return CPN_STR_NONE_ITEM; - } - - return ""; -} - // static bool SensorData::isSourceAvailable(const ModelData * model, const int index) { diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index afd9d7891..23d8a7fcc 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -167,11 +167,11 @@ class SensorData { bool isAvailable() const { return strlen(label) > 0; } void updateUnit(); - QString unitString() const; QString nameToString(int index) const; QString getOrigin(const ModelData* model) const; void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; + QString paramsToString(const ModelData * model) const; static QString sourceToString(const ModelData * model, const int index, const bool sign = false); static bool isSourceAvailable(const ModelData * model, const int index); @@ -179,7 +179,8 @@ class SensorData { static QString formulaToString(const int value); static QString cellIndexToString(const int value); static QString unitToString(const int value); - static QString precisionToString(const int value); + static QString precToString(const int value); + static QString boolToString(const bool value) { return value ? tr("Y") : tr("N"); } static AbstractStaticItemModel * typeItemModel(); static AbstractStaticItemModel * formulaItemModel(); diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index 61bfedda9..282f53550 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -1071,17 +1071,6 @@ QString ModelPrinter::printRssiAlarmsDisabled(bool mb) return printBoolean(!mb, BOOLEAN_ENABLEDISABLE); } -QString ModelPrinter::printTelemetrySource(int val) -{ - QStringList strings = QStringList() << tr("None"); - - for (unsigned i=1; i<=CPN_MAX_SENSORS; ++i) { - strings << QString("%1").arg(model.sensorData[i-1].label); - } - - return QString("%1%2").arg((val < 0 ? "-" : "")).arg(strings.value(abs(val))); -} - QString ModelPrinter::printVarioSource(unsigned int val) { switch (val) { @@ -1155,144 +1144,6 @@ QString ModelPrinter::printIgnoreSensorIds(bool mb) return printBoolean(mb, BOOLEAN_ENABLEDISABLE); } -QString ModelPrinter::printSensorType(unsigned int val) -{ - switch (val) { - case SensorData::TELEM_TYPE_CUSTOM: - return tr("Custom"); - case SensorData::TELEM_TYPE_CALCULATED: - return tr("Calculated"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} - -QString ModelPrinter::printSensorFormula(unsigned int val) -{ - switch (val) { - case SensorData::TELEM_FORMULA_ADD: - return tr("Add"); - case SensorData::TELEM_FORMULA_AVERAGE: - return tr("Average"); - case SensorData::TELEM_FORMULA_MIN: - return tr("Min"); - case SensorData::TELEM_FORMULA_MAX: - return tr("Max"); - case SensorData::TELEM_FORMULA_MULTIPLY: - return tr("Multiply"); - case SensorData::TELEM_FORMULA_TOTALIZE: - return tr("Totalise"); - case SensorData::TELEM_FORMULA_CELL: - return tr("Cell"); - case SensorData::TELEM_FORMULA_CONSUMPTION: - return tr("Consumption"); - case SensorData::TELEM_FORMULA_DIST: - return tr("Distance"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} - -QString ModelPrinter::printSensorCells(unsigned int val) -{ - QStringList strings; - - strings << tr("Lowest"); - for (int i=1; i<=6; i++) - strings << tr("Cell %1").arg(i); - strings << tr("Highest") << tr("Delta"); - - return strings.value(val); -} - -QString ModelPrinter::printSensorTypeCond(unsigned int idx) -{ - if (!model.sensorData[idx].isAvailable()) - return ""; - else - return printSensorType(model.sensorData[idx].type); -} - -QString ModelPrinter::printSensorParams(unsigned int idx) -{ - QString str = ""; - SensorData sensor = model.sensorData[idx]; - - if (!sensor.isAvailable()) - return str; - - bool isConfigurable = false; - bool gpsFieldsPrinted = false; - bool cellsFieldsPrinted = false; - bool consFieldsPrinted = false; - bool ratioFieldsPrinted = false; - bool totalizeFieldsPrinted = false; - bool sources12FieldsPrinted = false; - bool sources34FieldsPrinted = false; - - if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) { - isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL); - gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST); - cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL); - consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION); - sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY); - sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY); - totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE); - - str.append(printLabelValue(tr("F"), printSensorFormula(sensor.formula))); - } - else { - isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL; - ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL); - - str.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper())); - str.append(printLabelValue(tr("Inst"), QString::number(sensor.instance))); - } - if (cellsFieldsPrinted) { - str.append(printLabelValue(tr("Sensor"), QString("%1 %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index)))); - } - if (sources12FieldsPrinted) { - QStringList srcs; - for (int i=0;i<4;i++) { - if (i < 2 || sources34FieldsPrinted) { - srcs << printTelemetrySource(sensor.sources[i]); - } - } - str.append(printLabelValues(tr("Sources"), srcs)); - } - if (consFieldsPrinted || totalizeFieldsPrinted) - str.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps))); - if (gpsFieldsPrinted) { - str.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps))); - str.append(printLabelValue(tr("Alt"), printTelemetrySource(sensor.alt))); - } - QString u = sensor.unitString(); - u = u.trimmed() == "" ? "-" : u; - str.append(printLabelValue(tr("Unit"), u)); - if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT) - str.append(printLabelValue(tr("Prec"), printTelemetryPrecision(sensor.prec))); - if (ratioFieldsPrinted) { - if (sensor.unit != SensorData::UNIT_RPMS) { - int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec); - str.append(printLabelValue(tr("Ratio"), QString::number((float)sensor.ratio / 10))); - str.append(printLabelValue(tr("Offset"), QString::number((float)sensor.offset / prec, 'f', sensor.prec))); - } - else if (sensor.unit == SensorData::UNIT_RPMS) { - str.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio))); - str.append(printLabelValue(tr("Multi"), QString::number(sensor.offset))); - } - } - if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable) - str.append(printLabelValue(tr("Auto Offset"), printBoolean(sensor.autoOffset, BOOLEAN_YN))); - if (isConfigurable) - str.append(printLabelValue(tr("Filter"), printBoolean(sensor.filter, BOOLEAN_YN))); - if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) - str.append(printLabelValue(tr("Persist"), printBoolean(sensor.persistent, BOOLEAN_YN))); - str.append(printLabelValue(tr("Positive"), printBoolean(sensor.onlyPositive, BOOLEAN_YN))); - str.append(printLabelValue(tr("Log"), printBoolean(sensor.logs, BOOLEAN_YN), false)); - return str; -} - QString ModelPrinter::printTelemetryScreenType(unsigned int val) { switch (val) { @@ -1373,17 +1224,3 @@ QString ModelPrinter::printChecklist() } return str; } - -QString ModelPrinter::printTelemetryPrecision(unsigned int val) -{ - switch (val) { - case 0: - return tr("0."); - case 1: - return tr("0.0"); - case 2: - return tr("0.00"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} diff --git a/companion/src/modelprinter.h b/companion/src/modelprinter.h index 2cce3585d..639eddb0e 100644 --- a/companion/src/modelprinter.h +++ b/companion/src/modelprinter.h @@ -116,22 +116,15 @@ class ModelPrinter: public QObject QString printLabelValue(const QString & lbl, const QString & val, const bool sep = false); QString printLabelValues(const QString & lbl, const QStringList & vals, const bool sep = false); QString printRssiAlarmsDisabled(bool mb); - QString printTelemetrySource(int val); QString printVarioSource(unsigned int val); QString printVarioCenterSilent(bool mb); QString printVoltsSource(unsigned int val); QString printCurrentSource(unsigned int val); QString printMahPersistent(bool mb); QString printIgnoreSensorIds(bool mb); - QString printSensorType(unsigned int val); - QString printSensorFormula(unsigned int val); - QString printSensorCells(unsigned int val); - QString printSensorTypeCond(unsigned int idx); - QString printSensorParams(unsigned int idx); QString printTelemetryScreenType(unsigned int val); QString printTelemetryScreen(unsigned int idx, unsigned int line, unsigned int width); QString printChecklist(); - QString printTelemetryPrecision(unsigned int val); private: Firmware * firmware; diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 4da889cb1..9686dd944 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -777,7 +777,7 @@ QString MultiModelPrinter::printTelemetry() if (firmware->getCapability(HasVario)) { columns.appendRowStart(tr("Altimetry"), 20); if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { - LABELCOMPARECELL(tr("Vario source"), modelPrinter->printTelemetrySource(model->frsky.varioSource), 80); + LABELCOMPARECELL(tr("Vario source"), SensorData::sourceToString(model, model->frsky.varioSource), 80); } else { LABELCOMPARECELL(tr("Vario source"), modelPrinter->printVarioSource(model->frsky.varioSource), 80); @@ -801,8 +801,8 @@ QString MultiModelPrinter::printTelemetry() if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { columns.appendRowStart(tr("Top Bar"), 20); columns.appendCellStart(80); - COMPARESTRING(tr("Volts source"), modelPrinter->printTelemetrySource(model->frsky.voltsSource), true); - COMPARESTRING(tr("Altitude source"), modelPrinter->printTelemetrySource(model->frsky.altitudeSource), false); + COMPARESTRING(tr("Volts source"), SensorData::sourceToString(model, model->frsky.voltsSource), true); + COMPARESTRING(tr("Altitude source"), SensorData::sourceToString(model, model->frsky.altitudeSource), false); columns.appendCellEnd(); columns.appendRowEnd(); } @@ -822,9 +822,9 @@ QString MultiModelPrinter::printSensors() int count = 0; columns.appendSectionTableStart(); columns.appendRowHeader(QStringList() << tr("Name") << tr("Type") << tr("Parameters")); - for (unsigned i=0; isensorData[i].isAvailable()) { tsEmpty = false; break; @@ -836,8 +836,8 @@ QString MultiModelPrinter::printSensors() columns.appendCellStart(20, true); COMPARE(model->sensorData[i].nameToString(i)); columns.appendCellEnd(true); - COMPARECELLWIDTH(modelPrinter->printSensorTypeCond(i), 15); - COMPARECELLWIDTH(modelPrinter->printSensorParams(i), 65); + COMPARECELLWIDTH(model->sensorData[i].isAvailable() ? SensorData::typeToString(model->sensorData[i].type) : "", 15); + COMPARECELLWIDTH(model->sensorData[i].paramsToString(model), 65); columns.appendRowEnd(); } } From ea6fec611f8cb338e85b8c2f762e597ed7fb1b23 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 15 Jan 2021 18:01:16 +0100 Subject: [PATCH 066/300] Fixes (cherry picked from commit 17e272412e2392ea2c0978bc484bd603f282cf31) --- radio/src/targets/taranis/hal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index d2fba5d07..1d7791bf1 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -1289,7 +1289,7 @@ #define INTMODULE_DMA_STREAM_IRQHandler DMA2_Stream5_IRQHandler #define INTMODULE_DMA_FLAG_TC DMA_IT_TCIF5 #define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) -#elif defined(RADIO_FAMILY_JUMPER_T12) || defined(RADIO_TX12) || defined(RADIO_T8) +#elif (defined(RADIO_FAMILY_JUMPER_T12) && defined(HARDWARE_INTERNAL_MODULE))|| defined(RADIO_TX12) || defined(RADIO_T8) #define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1) #define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_USART3 | RCC_APB1Periph_TIM2) #define INTMODULE_RCC_APB2Periph 0 From f5be251d7bd0a558333b0785d3b25f3bf295b6a4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 Jan 2021 08:38:57 +0100 Subject: [PATCH 067/300] Update CREDITS.txt --- CREDITS.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 85246719c..8e85ef756 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2098,3 +2098,9 @@ Mark Trible Marc Dubois Alan Wilkins Daniel Hryciuk +Thomas Fox +Sidney B Harden +Augustas Sereika +David Belletete +Karl-Heinz Behn +Lena Hjort From 8cd86e6400b3fa8ed9854d3dde36e49465cd4d3d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 Jan 2021 11:15:37 +0100 Subject: [PATCH 068/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8e85ef756..1666c5fce 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2104,3 +2104,4 @@ Augustas Sereika David Belletete Karl-Heinz Behn Lena Hjort +Jan Langhorst From 6eb1ade13543d71240575adf84ff7494900294b5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 18 Jan 2021 13:38:47 +0100 Subject: [PATCH 069/300] Multipos autoswitch --- radio/src/switches.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index 727b05096..9a7fbb2f8 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -520,14 +520,28 @@ swsrc_t getMovedSwitch() swsrc_t result = 0; #if defined(PCBTARANIS) || defined(PCBHORUS) - for (int i=0; i> (i*2); - uint8_t next = (1024+getValue(MIXSRC_SA+i)) / 1024; + swarnstate_t mask = ((swarnstate_t) 0x03 << (i * 2)); + uint8_t prev = (switches_states & mask) >> (i * 2); + uint8_t next = (1024 + getValue(MIXSRC_SA + i)) / 1024; if (prev != next) { - switches_states = (switches_states & (~mask)) | ((swarnstate_t)next << (i*2)); - result = 1+(3*i)+next; + switches_states = (switches_states & (~mask)) | ((swarnstate_t) next << (i * 2)); + result = 1 + (3 * i) + next; + } + } + } + // Multipos + for (int i = 0; i < NUM_XPOTS; i++) { + if (IS_POT_MULTIPOS(POT1 + i)) { + StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1 + i]; + if (IS_MULTIPOS_CALIBRATED(calib)) { + uint8_t prev = potsPos[i] & 0x0F; + uint8_t next = anaIn(POT1 + i) / (2 * RESX / calib->count); + if (prev != next) { + result = SWSRC_LAST_SWITCH + i * XPOTS_MULTIPOS_COUNT + next + 1; + } } } } From 8fcc4f72471b57642504078deab222a7f48d8b1b Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 18 Jan 2021 17:37:50 +0100 Subject: [PATCH 070/300] Cosmetics --- .../src/gui/common/stdlcd/radio_hardware.cpp | 16 +++---- radio/src/targets/taranis/hal.h | 42 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index 09d839676..207e7db04 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -251,14 +251,14 @@ enum { #define EXTERNAL_ANTENNA_ROW 0, void onHardwareAntennaSwitchConfirm(const char * result) { - if (result == STR_OK) { - // Switch to external antenna confirmation - g_eeGeneral.antennaMode = reusableBuffer.radioHardware.antennaMode; - storageDirty(EE_GENERAL); - } - else { - reusableBuffer.radioHardware.antennaMode = g_eeGeneral.antennaMode; - } + if (result == STR_OK) { + // Switch to external antenna confirmation + g_eeGeneral.antennaMode = reusableBuffer.radioHardware.antennaMode; + storageDirty(EE_GENERAL); + } + else { + reusableBuffer.radioHardware.antennaMode = g_eeGeneral.antennaMode; + } } #else #define EXTERNAL_ANTENNA_ROW diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 1d7791bf1..f369f85e2 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -1720,27 +1720,27 @@ #define TRAINER_MODULE_CPPM #define TRAINER_MODULE_SBUS #endif - #define TRAINER_MODULE_RCC_AHB1Periph RCC_AHB1Periph_GPIOC - #define TRAINER_MODULE_RCC_APB2Periph RCC_APB2Periph_USART6 - #define TRAINER_MODULE_RCC_APB1Periph RCC_APB1Periph_TIM3 - #define TRAINER_MODULE_CPPM_TIMER TRAINER_TIMER - #define TRAINER_MODULE_CPPM_GPIO INTMODULE_HEARTBEAT_GPIO - #define TRAINER_MODULE_CPPM_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN - #define TRAINER_MODULE_CPPM_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource - #define TRAINER_MODULE_CPPM_INTERRUPT_ENABLE TIM_DIER_CC2IE - #define TRAINER_MODULE_CPPM_INTERRUPT_FLAG TIM_SR_CC2IF - #define TRAINER_MODULE_CPPM_CCMR1 (TIM_CCMR1_IC2F_0 | TIM_CCMR1_IC2F_1 | TIM_CCMR1_CC2S_0) - #define TRAINER_MODULE_CPPM_CCER TIM_CCER_CC2E - #define TRAINER_MODULE_CPPM_COUNTER_REGISTER TRAINER_TIMER->CCR2 - #define TRAINER_MODULE_CPPM_TIMER_IRQn TRAINER_TIMER_IRQn - #define TRAINER_MODULE_CPPM_GPIO_AF GPIO_AF_TIM3 - #define TRAINER_MODULE_SBUS_GPIO_AF GPIO_AF_USART6 - #define TRAINER_MODULE_SBUS_USART USART6 - #define TRAINER_MODULE_SBUS_GPIO INTMODULE_HEARTBEAT_GPIO - #define TRAINER_MODULE_SBUS_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN - #define TRAINER_MODULE_SBUS_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource - #define TRAINER_MODULE_SBUS_DMA_STREAM DMA2_Stream1 - #define TRAINER_MODULE_SBUS_DMA_CHANNEL DMA_Channel_5 + #define TRAINER_MODULE_RCC_AHB1Periph RCC_AHB1Periph_GPIOC + #define TRAINER_MODULE_RCC_APB2Periph RCC_APB2Periph_USART6 + #define TRAINER_MODULE_RCC_APB1Periph RCC_APB1Periph_TIM3 + #define TRAINER_MODULE_CPPM_TIMER TRAINER_TIMER + #define TRAINER_MODULE_CPPM_GPIO INTMODULE_HEARTBEAT_GPIO + #define TRAINER_MODULE_CPPM_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN + #define TRAINER_MODULE_CPPM_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource + #define TRAINER_MODULE_CPPM_INTERRUPT_ENABLE TIM_DIER_CC2IE + #define TRAINER_MODULE_CPPM_INTERRUPT_FLAG TIM_SR_CC2IF + #define TRAINER_MODULE_CPPM_CCMR1 (TIM_CCMR1_IC2F_0 | TIM_CCMR1_IC2F_1 | TIM_CCMR1_CC2S_0) + #define TRAINER_MODULE_CPPM_CCER TIM_CCER_CC2E + #define TRAINER_MODULE_CPPM_COUNTER_REGISTER TRAINER_TIMER->CCR2 + #define TRAINER_MODULE_CPPM_TIMER_IRQn TRAINER_TIMER_IRQn + #define TRAINER_MODULE_CPPM_GPIO_AF GPIO_AF_TIM3 + #define TRAINER_MODULE_SBUS_GPIO_AF GPIO_AF_USART6 + #define TRAINER_MODULE_SBUS_USART USART6 + #define TRAINER_MODULE_SBUS_GPIO INTMODULE_HEARTBEAT_GPIO + #define TRAINER_MODULE_SBUS_GPIO_PIN INTMODULE_HEARTBEAT_GPIO_PIN + #define TRAINER_MODULE_SBUS_GPIO_PinSource INTMODULE_HEARTBEAT_EXTI_PinSource + #define TRAINER_MODULE_SBUS_DMA_STREAM DMA2_Stream1 + #define TRAINER_MODULE_SBUS_DMA_CHANNEL DMA_Channel_5 #endif // USB From 233d1d26303ec7b982c651445fc9b2905c08c90d Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 08:24:02 +0100 Subject: [PATCH 071/300] Prevent multipos switch to be returned as a source --- radio/src/gui/navigation/navigation_horus.cpp | 2 +- radio/src/opentx.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/navigation/navigation_horus.cpp b/radio/src/gui/navigation/navigation_horus.cpp index d62d07810..07c53ae1d 100644 --- a/radio/src/gui/navigation/navigation_horus.cpp +++ b/radio/src/gui/navigation/navigation_horus.cpp @@ -137,7 +137,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { unsigned int swtch = abs(getMovedSwitch()); - if (swtch) { + if (swtch && !IS_SWITCH_MULTPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index bb756305b..ffabdc9b7 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -268,6 +268,7 @@ void memswap(void * a, void * b, uint8_t size); #define IS_POT_AVAILABLE(x) (IS_POT(x) && POT_CONFIG(x)!=POT_NONE) #define IS_POT_SLIDER_AVAILABLE(x) (IS_POT_AVAILABLE(x) || IS_SLIDER_AVAILABLE(x)) #define IS_MULTIPOS_CALIBRATED(cal) (cal->count>0 && cal->count> (2*((x)-POT1)))&0x03) #define IS_POT_MULTIPOS(x) (IS_POT(x) && POT_CONFIG(x)==POT_MULTIPOS_SWITCH) From ce028e7fe4939b64c5045bf8b87ffe06baa46026 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 08:50:10 +0100 Subject: [PATCH 072/300] Apply to all targets --- radio/src/gui/navigation/navigation_9x.cpp | 2 +- radio/src/gui/navigation/navigation_x7.cpp | 2 +- radio/src/gui/navigation/navigation_x9d.cpp | 2 +- radio/src/gui/navigation/navigation_xlite.cpp | 2 +- radio/src/opentx.h | 2 ++ 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/radio/src/gui/navigation/navigation_9x.cpp b/radio/src/gui/navigation/navigation_9x.cpp index bbc958c63..db9b34d4e 100644 --- a/radio/src/gui/navigation/navigation_9x.cpp +++ b/radio/src/gui/navigation/navigation_9x.cpp @@ -136,7 +136,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { uint8_t swtch = abs(getMovedSwitch()); - if (swtch) { + if (swtch && !IS_SWITCH_MULTPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_x7.cpp b/radio/src/gui/navigation/navigation_x7.cpp index 7e4255dac..3d58c619b 100644 --- a/radio/src/gui/navigation/navigation_x7.cpp +++ b/radio/src/gui/navigation/navigation_x7.cpp @@ -106,7 +106,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { unsigned int swtch = abs(getMovedSwitch()); - if (swtch) { + if (swtch && !IS_SWITCH_MULTPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_x9d.cpp b/radio/src/gui/navigation/navigation_x9d.cpp index a5d6fc332..e0a7a9dae 100644 --- a/radio/src/gui/navigation/navigation_x9d.cpp +++ b/radio/src/gui/navigation/navigation_x9d.cpp @@ -173,7 +173,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { unsigned int swtch = abs(getMovedSwitch()); - if (swtch) { + if (swtch && !IS_SWITCH_MULTPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_xlite.cpp b/radio/src/gui/navigation/navigation_xlite.cpp index 9cbafda8b..4a914a1d3 100644 --- a/radio/src/gui/navigation/navigation_xlite.cpp +++ b/radio/src/gui/navigation/navigation_xlite.cpp @@ -166,7 +166,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { uint8_t swtch = abs(getMovedSwitch()); - if (swtch) { + if (swtch && !IS_SWITCH_MULTPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index ffabdc9b7..ed4a42d43 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -276,11 +276,13 @@ void memswap(void * a, void * b, uint8_t size); #define IS_POT_AVAILABLE(x) (IS_POT(x) && POT_CONFIG(x)!=POT_NONE) #define IS_POT_SLIDER_AVAILABLE(x) (IS_POT_AVAILABLE(x)) #define IS_MULTIPOS_CALIBRATED(cal) (cal->count>0 && cal->count Date: Tue, 19 Jan 2021 09:01:00 +0100 Subject: [PATCH 073/300] Cosmetics --- radio/src/opentx.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index ed4a42d43..652c26409 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -268,7 +268,6 @@ void memswap(void * a, void * b, uint8_t size); #define IS_POT_AVAILABLE(x) (IS_POT(x) && POT_CONFIG(x)!=POT_NONE) #define IS_POT_SLIDER_AVAILABLE(x) (IS_POT_AVAILABLE(x) || IS_SLIDER_AVAILABLE(x)) #define IS_MULTIPOS_CALIBRATED(cal) (cal->count>0 && cal->count> (2*((x)-POT1)))&0x03) #define IS_POT_MULTIPOS(x) (IS_POT(x) && POT_CONFIG(x)==POT_MULTIPOS_SWITCH) @@ -276,12 +275,16 @@ void memswap(void * a, void * b, uint8_t size); #define IS_POT_AVAILABLE(x) (IS_POT(x) && POT_CONFIG(x)!=POT_NONE) #define IS_POT_SLIDER_AVAILABLE(x) (IS_POT_AVAILABLE(x)) #define IS_MULTIPOS_CALIBRATED(cal) (cal->count>0 && cal->count 0 + #define IS_SWITCH_MULTPOS(x) (SWSRC_FIRST_MULTIPOS_SWITCH <= (x) && (x) <= SWSRC_LAST_MULTIPOS_SWITCH) +#else #define IS_SWITCH_MULTPOS(x) (false) #endif From 957631a95b2c0b87625905244bd52e1bf804c670 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 11:00:57 +0100 Subject: [PATCH 074/300] Fix detection step size --- radio/src/opentx.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 4e31efacb..c473fc67a 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -557,6 +557,8 @@ bool isInputRecursive(int index) } #if defined(AUTOSOURCE) +constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / (XPOTS_MULTIPOS_COUNT + 1); + int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) { int8_t result = 0; @@ -565,7 +567,7 @@ int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) static int16_t inputsStates[MAX_INPUTS]; if (min <= MIXSRC_FIRST_INPUT) { for (uint8_t i=0; i 512) { + if (abs(anas[i] - inputsStates[i]) > MULTIPOS_STEP_SIZE) { if (!isInputRecursive(i)) { result = MIXSRC_FIRST_INPUT+i; break; @@ -577,7 +579,7 @@ int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) static int16_t sourcesStates[NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_MOUSE_ANALOGS]; if (result == 0) { for (uint8_t i=0; i 512) { + if (abs(calibratedAnalogs[i] - sourcesStates[i]) > MULTIPOS_STEP_SIZE) { result = MIXSRC_Rud+i; break; } From 42a82d5b7151f90d11f1f4c8203369ffdfe54251 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 19 Jan 2021 12:26:53 +0100 Subject: [PATCH 075/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1666c5fce..591c97d2f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2105,3 +2105,5 @@ David Belletete Karl-Heinz Behn Lena Hjort Jan Langhorst +Alberto Scotti +John Rock From 925af297e5894efbc2af667c12d73992a345bc53 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 14:34:43 +0100 Subject: [PATCH 076/300] Cosmetics --- radio/src/gui/navigation/navigation_9x.cpp | 2 +- radio/src/gui/navigation/navigation_horus.cpp | 2 +- radio/src/gui/navigation/navigation_x7.cpp | 2 +- radio/src/gui/navigation/navigation_x9d.cpp | 2 +- radio/src/gui/navigation/navigation_xlite.cpp | 2 +- radio/src/opentx.cpp | 2 +- radio/src/opentx.h | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/radio/src/gui/navigation/navigation_9x.cpp b/radio/src/gui/navigation/navigation_9x.cpp index db9b34d4e..74380a69b 100644 --- a/radio/src/gui/navigation/navigation_9x.cpp +++ b/radio/src/gui/navigation/navigation_9x.cpp @@ -136,7 +136,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { uint8_t swtch = abs(getMovedSwitch()); - if (swtch && !IS_SWITCH_MULTPOS(swtch)) { + if (swtch && !IS_SWITCH_MULTIPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_horus.cpp b/radio/src/gui/navigation/navigation_horus.cpp index 07c53ae1d..9679ab6e7 100644 --- a/radio/src/gui/navigation/navigation_horus.cpp +++ b/radio/src/gui/navigation/navigation_horus.cpp @@ -137,7 +137,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { unsigned int swtch = abs(getMovedSwitch()); - if (swtch && !IS_SWITCH_MULTPOS(swtch)) { + if (swtch && !IS_SWITCH_MULTIPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_x7.cpp b/radio/src/gui/navigation/navigation_x7.cpp index 3d58c619b..ecf00a02b 100644 --- a/radio/src/gui/navigation/navigation_x7.cpp +++ b/radio/src/gui/navigation/navigation_x7.cpp @@ -106,7 +106,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { unsigned int swtch = abs(getMovedSwitch()); - if (swtch && !IS_SWITCH_MULTPOS(swtch)) { + if (swtch && !IS_SWITCH_MULTIPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_x9d.cpp b/radio/src/gui/navigation/navigation_x9d.cpp index e0a7a9dae..2b3587cba 100644 --- a/radio/src/gui/navigation/navigation_x9d.cpp +++ b/radio/src/gui/navigation/navigation_x9d.cpp @@ -173,7 +173,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { unsigned int swtch = abs(getMovedSwitch()); - if (swtch && !IS_SWITCH_MULTPOS(swtch)) { + if (swtch && !IS_SWITCH_MULTIPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/gui/navigation/navigation_xlite.cpp b/radio/src/gui/navigation/navigation_xlite.cpp index 4a914a1d3..974308f1b 100644 --- a/radio/src/gui/navigation/navigation_xlite.cpp +++ b/radio/src/gui/navigation/navigation_xlite.cpp @@ -166,7 +166,7 @@ int checkIncDec(event_t event, int val, int i_min, int i_max, unsigned int i_fla #if defined(AUTOSWITCH) else { uint8_t swtch = abs(getMovedSwitch()); - if (swtch && !IS_SWITCH_MULTPOS(swtch)) { + if (swtch && !IS_SWITCH_MULTIPOS(swtch)) { newval = switchToMix(swtch); } } diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index c473fc67a..64e5e5419 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -557,7 +557,7 @@ bool isInputRecursive(int index) } #if defined(AUTOSOURCE) -constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / (XPOTS_MULTIPOS_COUNT + 1); +constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / (XPOTS_MULTIPOS_COUNT - 1); int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) { diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 652c26409..2580aa653 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -283,9 +283,9 @@ void memswap(void * a, void * b, uint8_t size); #endif #if NUM_XPOTS > 0 - #define IS_SWITCH_MULTPOS(x) (SWSRC_FIRST_MULTIPOS_SWITCH <= (x) && (x) <= SWSRC_LAST_MULTIPOS_SWITCH) + #define IS_SWITCH_MULTIPOS(x) (SWSRC_FIRST_MULTIPOS_SWITCH <= (x) && (x) <= SWSRC_LAST_MULTIPOS_SWITCH) #else - #define IS_SWITCH_MULTPOS(x) (false) + #define IS_SWITCH_MULTIPOS(x) (false) #endif #if defined(PWR_BUTTON_PRESS) From ab067249702187ed1b3c1b538c9c1dfce8d48462 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 14:43:36 +0100 Subject: [PATCH 077/300] Consistency --- radio/src/opentx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 64e5e5419..9cdfa7b59 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -557,7 +557,7 @@ bool isInputRecursive(int index) } #if defined(AUTOSOURCE) -constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / (XPOTS_MULTIPOS_COUNT - 1); +constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / (XPOTS_MULTIPOS_COUNT); int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) { From 7b05b086e495b60bc004caaef282ded6e2c0a1ca Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 14:48:41 +0100 Subject: [PATCH 078/300] Cosmetics --- radio/src/opentx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 9cdfa7b59..7e89312dc 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -557,7 +557,7 @@ bool isInputRecursive(int index) } #if defined(AUTOSOURCE) -constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / (XPOTS_MULTIPOS_COUNT); +constexpr int MULTIPOS_STEP_SIZE = (2 * RESX) / XPOTS_MULTIPOS_COUNT; int8_t getMovedSource(GET_MOVED_SOURCE_PARAMS) { From c38e513c7e3b8cf33d2c48531f6a491ea9aa065a Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 14:57:45 +0100 Subject: [PATCH 079/300] Allow setting serial baurate for LUA (#8228) --- radio/src/dataconstants.h | 2 +- radio/src/lua/api_general.cpp | 42 +++++++++++++++---- .../common/arm/stm32/aux_serial_driver.cpp | 10 ++--- radio/src/targets/horus/board.h | 8 ++++ radio/src/targets/simu/simpgmspace.cpp | 16 +++++++ radio/src/targets/taranis/board.h | 4 ++ 6 files changed, 67 insertions(+), 15 deletions(-) diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 005fbec56..dae853d12 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -674,7 +674,7 @@ enum MixSources { MIXSRC_GYRO2, LUA_EXPORT("gyry", "Gyro Y") #endif - MIXSRC_MAX, + MIXSRC_MAX, LUA_EXPORT("max", "MAX") MIXSRC_FIRST_HELI, MIXSRC_CYC1 = MIXSRC_FIRST_HELI, LUA_EXPORT("cyc1", "Cyclic 1") diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index b3c3f5ee8..e9d0a7a25 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1598,13 +1598,38 @@ static int luaMultiBuffer(lua_State * L) } #endif +/*luadoc +@function setSerialBaudrate(baudrate) +@param baudrate Desired baurate + +Set baudrate for serial port(s) affected to LUA + +@status current Introduced in 2.3.12 +*/ +static int luaSetSerialBaudrate(lua_State * L) +{ + unsigned int baudrate = luaL_checkunsigned(L, 1); + +#if defined(AUX_SERIAL) + if (auxSerialMode == UART_MODE_LUA) { + auxSerialSetup(baudrate, true); + } +#endif +#if defined(AUX2_SERIAL) + if (aux2SerialMode == UART_MODE_LUA) { + aux2SerialSetup(baudrate, true); + } +#endif + return 1; +} + /*luadoc @function serialWrite(str) @param str (string) String to be written to the serial port. Writes a string to the serial port. The string is allowed to contain any character, including 0. -@status current Introduced in TODO +@status current Introduced in 2.3.10 */ static int luaSerialWrite(lua_State * L) { @@ -1614,21 +1639,22 @@ static int luaSerialWrite(lua_State * L) if (!str || len < 1) return 0; -#if !defined(SIMU) - #if defined(USB_SERIAL) +#if defined(USB_SERIAL) if (getSelectedUsbMode() == USB_SERIAL_MODE) { size_t wr_len = len; const char* p = str; while(wr_len--) usbSerialPutc(*p++); } - #endif - #if defined(AUX_SERIAL) +#endif + +#if defined(AUX_SERIAL) if (auxSerialMode == UART_MODE_LUA) { size_t wr_len = len; const char* p = str; while(wr_len--) auxSerialPutc(*p++); } - #endif +#endif + #if defined(AUX2_SERIAL) if (aux2SerialMode == UART_MODE_LUA) { size_t wr_len = len; @@ -1636,9 +1662,6 @@ static int luaSerialWrite(lua_State * L) while(wr_len--) aux2SerialPutc(*p++); } #endif -#else - debugPrintf("luaSerialWrite: %.*s",len,str); -#endif return 0; } @@ -1742,6 +1765,7 @@ const luaL_Reg opentxLib[] = { #if defined(MULTIMODULE) { "multiBuffer", luaMultiBuffer }, #endif + { "setSerialBaudrate", luaSetSerialBaudrate }, { "serialWrite", luaSerialWrite }, { "serialRead", luaSerialRead }, { nullptr, nullptr } /* sentinel */ 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 b2c528dc0..a9ec2b155 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -30,7 +30,7 @@ uint8_t auxSerialMode = UART_MODE_COUNT; // Prevent debug output before port is Fifo auxSerialTxFifo; AuxSerialRxFifo auxSerialRxFifo __DMA (AUX_SERIAL_DMA_Stream_RX); -void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t lenght = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1) +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length, uint16_t parity, uint16_t stop) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; @@ -53,7 +53,7 @@ void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t lenght = USART_Wor #endif USART_InitStructure.USART_BaudRate = baudrate; - USART_InitStructure.USART_WordLength = lenght; + USART_InitStructure.USART_WordLength = length; USART_InitStructure.USART_StopBits = stop; USART_InitStructure.USART_Parity = parity; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; @@ -133,7 +133,7 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) break; case UART_MODE_LUA: - auxSerialSetup(DEBUG_BAUDRATE, false); + auxSerialSetup(LUA_DEFAULT_BAUDRATE, true); AUX_SERIAL_POWER_ON(); } } @@ -226,7 +226,7 @@ uint8_t aux2SerialMode = UART_MODE_COUNT; // Prevent debug output before port i Fifo aux2SerialTxFifo; AuxSerialRxFifo aux2SerialRxFifo __DMA (AUX2_SERIAL_DMA_Stream_RX); -void aux2SerialSetup(unsigned int baudrate, bool dma, uint16_t lenght = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1) +void aux2SerialSetup(unsigned int baudrate, bool dma, uint16_t length, uint16_t parity, uint16_t stop) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; @@ -249,7 +249,7 @@ void aux2SerialSetup(unsigned int baudrate, bool dma, uint16_t lenght = USART_Wo #endif USART_InitStructure.USART_BaudRate = baudrate; - USART_InitStructure.USART_WordLength = lenght; + USART_InitStructure.USART_WordLength = length; USART_InitStructure.USART_StopBits = stop; USART_InitStructure.USART_Parity = parity; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index cffc553dc..8550d1517 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -637,11 +637,16 @@ void sportUpdatePowerInit(); // Aux serial port driver #if defined(RADIO_TX16S) #define DEBUG_BAUDRATE 400000 + #define LUA_DEFAULT_BAUDRATE 115200 #else #define DEBUG_BAUDRATE 115200 + #define LUA_DEFAULT_BAUDRATE 115200 #endif #if defined(AUX_SERIAL_GPIO) extern uint8_t auxSerialMode; +#if defined __cplusplus +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1); +#endif void auxSerialInit(unsigned int mode, unsigned int protocol); void auxSerialPutc(char c); #define auxSerialTelemetryInit(protocol) auxSerialInit(UART_MODE_TELEMETRY, protocol) @@ -661,6 +666,9 @@ void auxSerialPowerOff(); // Aux2 serial port driver #if defined(AUX2_SERIAL) extern uint8_t aux2SerialMode; +#if defined __cplusplus +void aux2SerialSetup(unsigned int baudrate, bool dma, uint16_t length = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1); +#endif void aux2SerialInit(unsigned int mode, unsigned int protocol); void aux2SerialPutc(char c); #define aux2SerialTelemetryInit(protocol) aux2SerialInit(UART_MODE_TELEMETRY, protocol) diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index 8376d786c..8402cf4fd 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -811,9 +811,20 @@ void rtcSetTime(const struct gtm * t) { } +#if defined(USB_SERIAL) +void usbSerialPutc(uint8_t c) +{ +} +#endif + #if defined(AUX_SERIAL) AuxSerialRxFifo auxSerialRxFifo(nullptr); uint8_t auxSerialMode; + +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length, uint16_t parity, uint16_t stop) +{ +} + void auxSerialInit(unsigned int mode, unsigned int protocol) { } @@ -834,6 +845,11 @@ void auxSerialStop() #if defined(AUX2_SERIAL) AuxSerialRxFifo aux2SerialRxFifo(nullptr); uint8_t aux2SerialMode; + +void aux2SerialSetup(unsigned int baudrate, bool dma, uint16_t length, uint16_t parity, uint16_t stop) +{ +} + void aux2SerialInit(unsigned int mode, unsigned int protocol) { } diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 6708b6c89..383ac7dff 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -850,8 +850,12 @@ void hapticOff(); // Aux serial port driver #if defined(AUX_SERIAL_GPIO) #define DEBUG_BAUDRATE 115200 +#define LUA_DEFAULT_BAUDRATE 115200 #define AUX_SERIAL extern uint8_t auxSerialMode; +#if defined __cplusplus +void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length = USART_WordLength_8b, uint16_t parity = USART_Parity_No, uint16_t stop = USART_StopBits_1); +#endif void auxSerialInit(unsigned int mode, unsigned int protocol); void auxSerialPutc(char c); #define auxSerialTelemetryInit(protocol) auxSerialInit(UART_MODE_TELEMETRY, protocol) From 24113cd8fe1d7c38ee789a73916eaa76678997d5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 15:01:40 +0100 Subject: [PATCH 080/300] Colorlcd multirotor wizard (#8221) --- .../horus/SCRIPTS/WIZARD/multirotor/icon.png | Bin 0 -> 8087 bytes .../SCRIPTS/WIZARD/multirotor/img/arm.png | Bin 0 -> 73029 bytes .../WIZARD/multirotor/img/background.png | Bin 0 -> 78456 bytes .../SCRIPTS/WIZARD/multirotor/img/beeper.png | Bin 0 -> 71079 bytes .../SCRIPTS/WIZARD/multirotor/img/mark_bg.png | Bin 0 -> 506 bytes .../SCRIPTS/WIZARD/multirotor/img/mode.png | Bin 0 -> 76148 bytes .../SCRIPTS/WIZARD/multirotor/img/pagedn.png | Bin 0 -> 1885 bytes .../SCRIPTS/WIZARD/multirotor/img/pageup.png | Bin 0 -> 1897 bytes .../SCRIPTS/WIZARD/multirotor/img/pitch.png | Bin 0 -> 78003 bytes .../SCRIPTS/WIZARD/multirotor/img/roll.png | Bin 0 -> 74861 bytes .../WIZARD/multirotor/img/throttle.png | Bin 0 -> 71407 bytes .../SCRIPTS/WIZARD/multirotor/img/yaw.png | Bin 0 -> 78662 bytes .../SCRIPTS/WIZARD/multirotor/wizard.lua | 411 ++++++++++++++++++ radio/src/gui/480x272/model_select.cpp | 1 - 14 files changed, 411 insertions(+), 1 deletion(-) create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/icon.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/arm.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/background.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/beeper.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/mark_bg.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/mode.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/pagedn.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/pageup.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/pitch.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/roll.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/throttle.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/yaw.png create mode 100755 radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/wizard.lua diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/icon.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..b6b7370479181571318473293047fa26ca170d63 GIT binary patch literal 8087 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/arm.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/arm.png new file mode 100755 index 0000000000000000000000000000000000000000..33dcd7d91bf6f54e8c8111bdc1b0236c38b9ca91 GIT binary patch literal 73029 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/background.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/background.png new file mode 100755 index 0000000000000000000000000000000000000000..e21f7bc812c71bb14c9e572aee123bb9fe8a151d GIT binary patch literal 78456 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/mode.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/mode.png new file mode 100755 index 0000000000000000000000000000000000000000..37a1830208b5bc40ea83f7352d89e2d75fe44c90 GIT binary patch literal 76148 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 z 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ⅇRN*O#o%>EHp++(m*fM-N(ZjOBsZY>?&|3YnQZG{ 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/pitch.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/pitch.png new file mode 100755 index 0000000000000000000000000000000000000000..e691fded263b1f1e03cbb57d9b61e57c4874a24b GIT binary patch literal 78003 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�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 TCj#~400000NkvXXu0mjfpCWi7 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/yaw.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/yaw.png new file mode 100755 index 0000000000000000000000000000000000000000..3fc055bb34050dfbe464de10bab56a967720ac59 GIT binary patch literal 78662 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 min) or (step > 0 and field[5] < max) then + field[5] = field[5] + step + end +end + +-- Select the next or previous page +local function selectPage(step) + page = 1 + ((page + step - 1 + #pages) % #pages) + edit = false + current = 1 +end + +-- Select the next or previous editable field +local function selectField(step) + repeat + current = 1 + ((current + step - 1 + #fields) % #fields) + until fields[current][4]==1 +end + +-- Redraw the current page +local function redrawFieldsPage(event) + + for index = 1, 10, 1 do + local field = fields[index] + if field == nil then + break + end + + local attr = current == (index) and ((edit == true and BLINK or 0) + INVERS) or 0 + attr = attr + TEXT_COLOR + + if field[4] == 1 then + if field[3] == VALUE then + lcd.drawNumber(field[1], field[2], field[5], LEFT + attr) + elseif field[3] == COMBO then + if field[5] >= 0 and field[5] < #(field[6]) then + lcd.drawText(field[1],field[2], field[6][1+field[5]], attr) + end + end + end + end +end + +local function updateField(field) + local value = field[5] +end + +-- Main +local function runFieldsPage(event) + if event == EVT_VIRTUAL_EXIT then -- exit script + return 2 + elseif event == EVT_VIRTUAL_ENTER then -- toggle editing/selecting current field + if fields[current][5] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_VIRTUAL_INC or event == EVT_VIRTUAL_INC_REPT then + addField(1) + elseif event == EVT_VIRTUAL_DEC or event == EVT_VIRTUAL_DEC_REPT then + addField(-1) + end + else + if event == EVT_VIRTUAL_NEXT then + selectField(1) + elseif event == EVT_VIRTUAL_PREV then + selectField(-1) + end + end + redrawFieldsPage(event) + return 0 +end + +-- set visibility flags starting with SECOND field of fields +local function setFieldsVisible(...) + local arg={...} + local cnt = 2 + for i,v in ipairs(arg) do + fields[cnt][4] = v + cnt = cnt + 1 + end +end + +-- draws one letter mark +local function drawMark(x, y, name) + lcd.drawBitmap(ImgMarkBg, x, y) + lcd.drawText(x+8, y+3, name, TEXT_COLOR) +end + + +local ThrottleFields = { + {50, 50, COMBO, 1, 2, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local ThrottleBackground + +local function runThrottleConfig(event) + lcd.clear() + if ThrottleBackground == nil then + ThrottleBackground = Bitmap.open("img/throttle.png") + end + lcd.drawBitmap(ThrottleBackground, 0, 0) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = ThrottleFields + lcd.drawText(40, 20, "Assign Throttle channel", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local RollFields = { + {50, 50, COMBO, 1, 0, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local RollBackground + +local function runRollConfig(event) + lcd.clear() + if RollBackground == nil then + RollBackground = Bitmap.open("img/roll.png") + end + lcd.drawBitmap(RollBackground, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = RollFields + lcd.drawText(40, 20, "Assign Roll channel", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local PitchFields = { + {50, 50, COMBO, 1, 1, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local PitchBackground + +local function runPitchConfig(event) + lcd.clear() + if PitchBackground == nil then + PitchBackground = Bitmap.open("img/pitch.png") + end + lcd.drawBitmap(PitchBackground, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = PitchFields + lcd.drawText(40, 20, "Assign Pitch channel", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local YawFields = { + {50, 50, COMBO, 1, 3, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local YawBackground + +local function runYawConfig(event) + lcd.clear() + if YawBackground == nil then + YawBackground = Bitmap.open("img/yaw.png") + end + lcd.drawBitmap(YawBackground, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = YawFields + lcd.drawText(40, 20, "Assign Yaw channel", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local ArmFields = { + {50, 50, COMBO, 1, 5, { "SA", "SB", "SC", "SD", "SE", "SF"} }, +} + +local ArmBackground + +local function runArmConfig(event) + lcd.clear() + if ArmBackground == nil then + ArmBackground = Bitmap.open("img/arm.png") + end + lcd.drawBitmap(ArmBackground, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = ArmFields + lcd.drawText(40, 20, "Assign Arm switch", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local BeeperFields = { + {50, 50, COMBO, 1, 3, { "SA", "SB", "SC", "SD", "SE", "SF"} }, +} + +local BeeperBackground + +local function runBeeperConfig(event) + lcd.clear() + if BeeperBackground == nil then + BeeperBackground = Bitmap.open("img/beeper.png") + end + lcd.drawBitmap(BeeperBackground, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = BeeperFields + lcd.drawText(40, 20, "Assign Beeper switch", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local ModeFields = { + {50, 50, COMBO, 1, 0, { "SA", "SB", "SC", "SD", "SE", "SF"} }, +} + +local ModeBackground + +local function runModeConfig(event) + lcd.clear() + if ModeBackground == nil then + ModeBackground = Bitmap.open("img/mode.png") + end + lcd.drawBitmap(ModeBackground, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = ModeFields + lcd.drawText(40, 20, "Assign Mode switch", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 100, 30, TEXT_BGCOLOR) + fields[1][4]=1 + local result = runFieldsPage(event) + return result +end + +local lineIndex +local function drawNextChanelLine(text, text2) + lcd.drawText(40, lineIndex, text, TEXT_COLOR) + lcd.drawText(242, lineIndex, ": CH" .. text2 + 1, TEXT_COLOR) + lineIndex = lineIndex + 20 +end + +local function drawNextSwitchLine(text, text2) + lcd.drawText(40, lineIndex, text, TEXT_COLOR) + lcd.drawText(242, lineIndex, ": " ..switches[text2 + 1], TEXT_COLOR) + lineIndex = lineIndex + 20 +end + + +local ConfigSummaryFields = { + {110, 250, COMBO, 1, 0, { "No, I need to change something", "Yes, all is well, create the model !"} }, +} + +local ImgSummary + +local function runConfigSummary(event) + lcd.clear() + if ImgSummary == nil then + ImgSummary = Bitmap.open("img/summary.png") + end + fields = ConfigSummaryFields + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgSummary, 300, 60) + lineIndex = 40 + -- throttle + drawNextChanelLine("Throttle channel", ThrottleFields[1][5]) + -- roll + drawNextChanelLine("Roll channel",RollFields[1][5]) + -- pitch + drawNextChanelLine("Pitch channel",PitchFields[1][5]) + -- yaw + drawNextChanelLine("Yaw channel",YawFields[1][5]) + -- arm + drawNextSwitchLine("Arm switch",ArmFields[1][5]) + -- beeper + drawNextSwitchLine("Beeper switch",BeeperFields[1][5]) + -- mode + drawNextSwitchLine("Mode switch",ModeFields[1][5]) + + local result = runFieldsPage(event) + if(fields[1][5] == 1 and edit == false) then + selectPage(1) + end + return result +end + +local function addMix(channel, input, name, weight, index) + local mix = { source=input, name=name } + if weight ~= nil then + mix.weight = weight + end + if index == nil then + index = 0 + end + model.insertMix(channel, index, mix) +end + +local function createModel(event) + lcd.clear() + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgSummary, 300, 60) + model.defaultInputs() + model.deleteMixes() + -- throttle + addMix(ThrottleFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Thr") + -- roll + addMix(RollFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + -- pitch + addMix(PitchFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + -- yaw + addMix(YawFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + addMix(4, MIXSRC_SA + ArmFields[1][5], "Arm") + addMix(5, MIXSRC_SA + BeeperFields[1][5], "Beeper") + addMix(6, MIXSRC_SA + ModeFields[1][5], "Mode") + + lcd.drawText(70, 90, "Model successfully created !", TEXT_COLOR) + lcd.drawText(100, 130, "Press RTN to exit", TEXT_COLOR) + return 2 +end + +-- Init +local function init() + current, edit = 1, false + pages = { + runThrottleConfig, + runRollConfig, + runPitchConfig, + runYawConfig, + runArmConfig, + runBeeperConfig, + runModeConfig, + runConfigSummary, + createModel, + } +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_VIRTUAL_NEXT_PAGE and page < #pages-1 then + selectPage(1) + elseif event == EVT_VIRTUAL_PREV_PAGE and page > 1 then + killEvents(event); + selectPage(-1) + end + + local result = pages[page](event) + return result +end + +return { init=init, run=run } diff --git a/radio/src/gui/480x272/model_select.cpp b/radio/src/gui/480x272/model_select.cpp index cd0122ed4..33c9453fe 100644 --- a/radio/src/gui/480x272/model_select.cpp +++ b/radio/src/gui/480x272/model_select.cpp @@ -191,7 +191,6 @@ bool menuModelWizard(event_t event) uint16_t x = WIZARD_LEFT_SPACING + (wizidx - first) * (WIZARD_SPACING + WIZARD_ICON_X); strcpy(&wizpath[sizeof(WIZARD_PATH)], fno.fname); strcpy(&wizpath[sizeof(WIZARD_PATH) + strlen(fno.fname)], "/icon.png"); - lcdDrawText(x + 10, WIZARD_TEXT_Y, fno.fname); BitmapBuffer * background = BitmapBuffer::load(wizpath); lcd->drawBitmap(x, WIZARD_ICON_Y, background); if(wizidx == wizardSelected ) { From b90cfb767506b9e85e0aeb7558047c767e86edbc Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 16:14:46 +0100 Subject: [PATCH 081/300] Cosmetics --- companion/src/firmwares/boards.h | 7 ++--- .../src/gui/common/stdlcd/radio_hardware.cpp | 31 ++++++++----------- .../targets/common/arm/stm32/adc_driver.cpp | 4 +-- radio/src/targets/taranis/board.h | 2 +- radio/src/targets/taranis/hal.h | 10 +++--- 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 4d2f03b9d..1f5fc0358 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -54,12 +54,11 @@ namespace Board { BOARD_JUMPER_T18, BOARD_RADIOMASTER_TX12, BOARD_RADIOMASTER_T8, - BOARD_JUMPER_TLITE + BOARD_JUMPER_TLITE, + BOARD_TYPE_COUNT, + BOARD_TYPE_MAX = BOARD_TYPE_COUNT-1 }; - - constexpr int BOARD_TYPE_MAX = BOARD_JUMPER_TLITE; - enum PotType { POT_NONE, diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index 207e7db04..7c4d0c1e5 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -98,8 +98,10 @@ enum { ITEM_RADIO_HARDWARE_STICK2, ITEM_RADIO_HARDWARE_STICK3, ITEM_RADIO_HARDWARE_STICK4, +#if (NUM_POTS + NUM_SLIDERS) > 0 ITEM_RADIO_HARDWARE_LABEL_POTS, ITEM_RADIO_HARDWARE_POT1, +#endif #if defined(HARDWARE_POT2) ITEM_RADIO_HARDWARE_POT2, #endif @@ -196,29 +198,21 @@ enum { }; #if (NUM_POTS + NUM_SLIDERS) == 0 - #define POT_LABEL_ROW HIDDEN_ROW - #define POTS_ROWS HIDDEN_ROW + #define POTS_ROWS #elif (NUM_POTS + NUM_SLIDERS) == 1 - #define POT_LABEL_ROW LABEL(Pots) - #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 2 - #define POT_LABEL_ROW LABEL(Pots) - #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 3 - #define POT_LABEL_ROW LABEL(Pots) - #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif defined(PCBX9D) // TODO defined(STORAGE_POT3) && !defined(STORAGE_POT3) - #define POT_LABEL_ROW LABEL(Pots) - #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, HIDDEN_ROW, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, HIDDEN_ROW, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 4 - #define POT_LABEL_ROW LABEL(Pots) - #define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 #elif (NUM_POTS + NUM_SLIDERS) == 5 - #define POT_LABEL_ROW LABEL(Pots) - #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 + #define POTS_ROWS LABEL(Pots), 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 #elif (NUM_POTS + NUM_SLIDERS) == 8 - #define POT_LABEL_ROW LABEL(Pots) - #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, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), 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 #endif #if defined(PCBX9E) @@ -336,8 +330,7 @@ void menuRadioHardware(event_t event) 0 /* stick 2 */, 0 /* stick 3 */, 0 /* stick 4 */, - POT_LABEL_ROW, - POTS_ROWS, + POTS_ROWS LABEL(Switches), SWITCHES_ROWS, 0 /* battery calib */, @@ -401,6 +394,7 @@ void menuRadioHardware(event_t event) editStickHardwareSettings(HW_SETTINGS_COLUMN1, y, k - ITEM_RADIO_HARDWARE_STICK1, event, attr); break; +#if (NUM_POTS + NUM_SLIDERS) > 0 case ITEM_RADIO_HARDWARE_LABEL_POTS: lcdDrawTextAlignedLeft(y, STR_POTS); break; @@ -452,6 +446,7 @@ void menuRadioHardware(event_t event) g_eeGeneral.slidersConfig |= (potType << idx); break; } +#endif #endif case ITEM_RADIO_HARDWARE_LABEL_SWITCHES: diff --git a/radio/src/targets/common/arm/stm32/adc_driver.cpp b/radio/src/targets/common/arm/stm32/adc_driver.cpp index 65879f44f..6a1214207 100644 --- a/radio/src/targets/common/arm/stm32/adc_driver.cpp +++ b/radio/src/targets/common/arm/stm32/adc_driver.cpp @@ -139,8 +139,8 @@ void adcInit() ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH << 0) + (ADC_CHANNEL_STICK_LV << 5) + (ADC_CHANNEL_STICK_RV << 10) + (ADC_CHANNEL_STICK_RH << 15) + (ADC_CHANNEL_POT1 << 20) + (ADC_CHANNEL_POT2 << 25); #endif - ADC_MAIN->SMPR1 = (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (VBAT_ADC_SAMPTIME << 24); - ADC_MAIN->SMPR2 = (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); + ADC_MAIN->SMPR1 = ADC_MAIN_SMPR1; + ADC_MAIN->SMPR2 = ADC_MAIN_SMPR2; ADC->CCR = ADC_CCR_VBATE; // Enable vbat sensor diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 24b709b3f..92df79717 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -28,7 +28,7 @@ #include "hal.h" #if defined(RADIO_TX12) || defined(RADIO_TX12) - #define NAVIGATION_X7_TX12 + #define NAVIGATION_X7_TX12 #endif #if defined(ROTARY_ENCODER_NAVIGATION) diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index f369f85e2..853100898 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -875,9 +875,11 @@ #define ADC_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) #define ADC_SAMPTIME 2 // sample time = 28 cycles #if defined(RADIO_TLITE) - #define VBAT_ADC_SAMPTIME (ADC_SAMPTIME + 1) + #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + ((ADC_SAMPTIME + 1) << 24); + #define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); #else - #define VBAT_ADC_SAMPTIME ADC_SAMPTIME + #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + ((ADC_SAMPTIME + 1) << 24); + #define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); #endif #if defined(PCBX9E) #define HARDWARE_POT1 @@ -2032,16 +2034,16 @@ #define AUDIO_SPEAKER_ENABLE_GPIO_PIN GPIO_Pin_14 // PD.14 #define HEADPHONE_TRAINER_SWITCH_GPIO GPIOD #define HEADPHONE_TRAINER_SWITCH_GPIO_PIN GPIO_Pin_9 // PD.09 -#elif defined(RADIO_TX12) || defined(RADIO_T8) +#elif defined(AUDIO_MUTE_GPIO) #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #define AUDIO_MUTE_GPIO GPIOE #define AUDIO_MUTE_GPIO_PIN GPIO_Pin_12 // PE.12 + #define AUDIO_MUTE_DELAY 500 // ms #if defined(RADIO_TLITE) #define AUDIO_UNMUTE_DELAY 250 // ms #else #define AUDIO_UNMUTE_DELAY 150 // ms #endif - #define AUDIO_MUTE_DELAY 500 // ms #else #define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA1) #endif From 40506ac8583bf35867dd801a35f01612b2f26151 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 16:24:39 +0100 Subject: [PATCH 082/300] Compilation fix --- radio/src/gui/common/stdlcd/radio_hardware.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index 7c4d0c1e5..cd4cfb905 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -200,19 +200,19 @@ enum { #if (NUM_POTS + NUM_SLIDERS) == 0 #define POTS_ROWS #elif (NUM_POTS + NUM_SLIDERS) == 1 - #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, #elif (NUM_POTS + NUM_SLIDERS) == 2 - #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, #elif (NUM_POTS + NUM_SLIDERS) == 3 - #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, #elif defined(PCBX9D) // TODO defined(STORAGE_POT3) && !defined(STORAGE_POT3) - #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, HIDDEN_ROW, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, HIDDEN_ROW, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, #elif (NUM_POTS + NUM_SLIDERS) == 4 - #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 + #define POTS_ROWS LABEL(Pots), NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, #elif (NUM_POTS + NUM_SLIDERS) == 5 - #define POTS_ROWS LABEL(Pots), 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 POTS_ROWS LABEL(Pots), 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, #elif (NUM_POTS + NUM_SLIDERS) == 8 - #define POTS_ROWS LABEL(Pots), 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 POTS_ROWS LABEL(Pots), 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, #endif #if defined(PCBX9E) From be4c3c2824b1de16eab9eb0c822f86cf017567fc Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 19 Jan 2021 16:35:55 +0100 Subject: [PATCH 083/300] Compilation fix --- radio/src/targets/horus/hal.h | 3 +++ radio/src/targets/taranis/hal.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index e63e13e14..b810f81e2 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -331,6 +331,9 @@ #endif #endif +#define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); +#define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); + // Power #if defined(RADIO_T18) #define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_GPIOB diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 853100898..fb973a06b 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -878,7 +878,7 @@ #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + ((ADC_SAMPTIME + 1) << 24); #define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); #else - #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + ((ADC_SAMPTIME + 1) << 24); + #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); #define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); #endif #if defined(PCBX9E) From 274a7e96d4b5f31b03c22fa971ebb244279258fb Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 20 Jan 2021 08:12:15 +0100 Subject: [PATCH 084/300] Cosmetics --- radio/src/targets/horus/hal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index b810f81e2..61620cc60 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -331,8 +331,8 @@ #endif #endif -#define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); -#define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); +#define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); +#define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); // Power #if defined(RADIO_T18) From 48907600efb66f0306fbbbe32ddf4227722225e2 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 20 Jan 2021 08:30:25 +0100 Subject: [PATCH 085/300] Fix quad wiz and bump SDCard --- CMakeLists.txt | 2 +- radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/wizard.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e6ccca0..8d23900bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(VERSION_REVISION "12") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) -set(SDCARD_REVISION "0035") +set(SDCARD_REVISION "0036") set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION}) cmake_minimum_required(VERSION 2.8) diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/wizard.lua b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/wizard.lua index 9d052c971..baf8461b5 100755 --- a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/wizard.lua +++ b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/wizard.lua @@ -362,11 +362,11 @@ local function createModel(event) -- throttle addMix(ThrottleFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Thr") -- roll - addMix(RollFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + addMix(RollFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "Roll") -- pitch - addMix(PitchFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + addMix(PitchFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "Pitch") -- yaw - addMix(YawFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Roll") + addMix(YawFields[1][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Yaw") addMix(4, MIXSRC_SA + ArmFields[1][5], "Arm") addMix(5, MIXSRC_SA + BeeperFields[1][5], "Beeper") addMix(6, MIXSRC_SA + ModeFields[1][5], "Mode") From 27d6e5faa7dc50b53ee49958c1a1562c8f16bc1b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 20 Jan 2021 08:45:43 +0100 Subject: [PATCH 086/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 591c97d2f..a20d6e03b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2107,3 +2107,4 @@ Lena Hjort Jan Langhorst Alberto Scotti John Rock +Adam Kouse From d7c53fa3a4d1169d2e6c679efed0269e7081d3a2 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 20 Jan 2021 18:14:28 +0100 Subject: [PATCH 087/300] Improve serial LUA --- radio/src/targets/common/arm/stm32/aux_serial_driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index a9ec2b155..f2257a953 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -133,7 +133,7 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) break; case UART_MODE_LUA: - auxSerialSetup(LUA_DEFAULT_BAUDRATE, true); + auxSerialSetup(LUA_DEFAULT_BAUDRATE, false); AUX_SERIAL_POWER_ON(); } } @@ -329,7 +329,7 @@ void aux2SerialInit(unsigned int mode, unsigned int protocol) break; case UART_MODE_LUA: - aux2SerialSetup(DEBUG_BAUDRATE, false); + aux2SerialSetup(LUA_DEFAULT_BAUDRATE, false); AUX2_SERIAL_POWER_ON(); } } From 43129ca9e2f51afd23ebff62112dec547c391fa7 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 20 Jan 2021 19:25:22 +0100 Subject: [PATCH 088/300] Improve serial LUA --- radio/src/lua/api_general.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index e9d0a7a25..827734642 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1612,12 +1612,14 @@ static int luaSetSerialBaudrate(lua_State * L) #if defined(AUX_SERIAL) if (auxSerialMode == UART_MODE_LUA) { - auxSerialSetup(baudrate, true); + auxSerialStop(); + auxSerialSetup(baudrate, false); } #endif #if defined(AUX2_SERIAL) if (aux2SerialMode == UART_MODE_LUA) { - aux2SerialSetup(baudrate, true); + aux2SerialStop(); + aux2SerialSetup(baudrate, false); } #endif return 1; From ef1fea95f2bfd7d6709c2643df39e17deb83e195 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 21 Jan 2021 09:24:12 +0100 Subject: [PATCH 089/300] Remove retry timer for set trim --- radio/src/targets/simu/opentxsimulator.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index 180709484..df763c8fa 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -213,17 +213,7 @@ void OpenTxSimulator::setTrim(unsigned int idx, int value) if (i < 4) // swap axes i = modn12x3[4 * getStickMode() + idx]; uint8_t phase = getTrimFlightMode(getFlightMode(), i); - - if (!setTrimValue(phase, i, value)) { - QTimer *timer = new QTimer(this); - timer->setSingleShot(true); - connect(timer, &QTimer::timeout, [=]() { - emit trimValueChange(idx, 0); - emit outputValueChange(OUTPUT_SRC_TRIM_VALUE, idx, 0); - timer->deleteLater(); - }); - timer->start(50); - } + setTrimValue(phase, i, value); } void OpenTxSimulator::setTrainerInput(unsigned int inputNumber, int16_t value) From d3c4f611eeb8dc346fafefc74d43e82589ad2144 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 21 Jan 2021 10:09:32 +0100 Subject: [PATCH 090/300] Add ETX1 and EXT1 to calibration screen --- radio/src/gui/480x272/radio_calibration.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/radio/src/gui/480x272/radio_calibration.cpp b/radio/src/gui/480x272/radio_calibration.cpp index a81355b89..bdf623cfd 100644 --- a/radio/src/gui/480x272/radio_calibration.cpp +++ b/radio/src/gui/480x272/radio_calibration.cpp @@ -57,6 +57,14 @@ void drawPots() // The 2 main front sliders drawVerticalSlider(125, 120, 120, calibratedAnalogs[CALIBRATED_SLIDER_FRONT_LEFT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); drawVerticalSlider(LCD_W-125-12, 120, 120, calibratedAnalogs[CALIBRATED_SLIDER_FRONT_RIGHT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); +#else + if (IS_POT_AVAILABLE(EXT1)) { + drawVerticalSlider(125, 120, 120, calibratedAnalogs[CALIBRATED_POT_EXT1], -RESX, RESX, 40, + OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + } + if (IS_POT_AVAILABLE(EXT2)) { + drawVerticalSlider(LCD_W-125-12, 120, 120, calibratedAnalogs[CALIBRATED_POT_EXT2], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + } #endif } From 5c98eca43a095dfe54a6b63f450d311f4bc7c1ec Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 21 Jan 2021 10:10:52 +0100 Subject: [PATCH 091/300] Cosmetics --- radio/src/gui/480x272/radio_calibration.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/radio_calibration.cpp b/radio/src/gui/480x272/radio_calibration.cpp index bdf623cfd..ab5576f44 100644 --- a/radio/src/gui/480x272/radio_calibration.cpp +++ b/radio/src/gui/480x272/radio_calibration.cpp @@ -59,8 +59,7 @@ void drawPots() drawVerticalSlider(LCD_W-125-12, 120, 120, calibratedAnalogs[CALIBRATED_SLIDER_FRONT_RIGHT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); #else if (IS_POT_AVAILABLE(EXT1)) { - drawVerticalSlider(125, 120, 120, calibratedAnalogs[CALIBRATED_POT_EXT1], -RESX, RESX, 40, - OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + drawVerticalSlider(125, 120, 120, calibratedAnalogs[CALIBRATED_POT_EXT1], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); } if (IS_POT_AVAILABLE(EXT2)) { drawVerticalSlider(LCD_W-125-12, 120, 120, calibratedAnalogs[CALIBRATED_POT_EXT2], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); From 9559f34630259d2217f405f24f39915ece44c4d3 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 21 Jan 2021 13:02:06 +0100 Subject: [PATCH 092/300] Cosmetics --- companion/src/simulation/simulateduiwidget.h | 2 +- radio/src/targets/horus/hal.h | 2 -- radio/src/targets/taranis/hal.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/companion/src/simulation/simulateduiwidget.h b/companion/src/simulation/simulateduiwidget.h index aee2c5f87..f9d92ecd7 100644 --- a/companion/src/simulation/simulateduiwidget.h +++ b/companion/src/simulation/simulateduiwidget.h @@ -112,7 +112,7 @@ namespace Ui { class SimulatedUIWidgetX10; class SimulatedUIWidgetX12; class SimulatedUIWidgetJumperT12; - class SimulatedUIWidgetJumperTLITE; + class SimulatedUIWidgetJumperTLITE; class SimulatedUIWidgetJumperT16; class SimulatedUIWidgetJumperT18; class SimulatedUIWidgetTX16S; diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 61620cc60..81639e73a 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -275,7 +275,6 @@ #define ADC_DMA DMA2 #define ADC_DMA_Stream DMA2_Stream0 #define ADC_SAMPTIME 3 - #define VBAT_ADC_SAMPTIME ADC_SAMPTIME #define ADC_VREF_PREC2 300 #elif defined(PCBX10) #define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_DMA2) @@ -316,7 +315,6 @@ #define ADC_CHANNEL_EXT2 ADC_Channel_7 // ADC3_IN7 #define ADC_MAIN ADC3 #define ADC_SAMPTIME 3 - #define VBAT_ADC_SAMPTIME ADC_SAMPTIME #define ADC_DMA DMA2 #define ADC_DMA_SxCR_CHSEL DMA_SxCR_CHSEL_1 #define ADC_DMA_Stream DMA2_Stream0 diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index fb973a06b..d80f9e197 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -875,7 +875,7 @@ #define ADC_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) #define ADC_SAMPTIME 2 // sample time = 28 cycles #if defined(RADIO_TLITE) - #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + ((ADC_SAMPTIME + 1) << 24); + #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + ((ADC_SAMPTIME + 1) << 24); // TLite needs +1 for proper RTC Bat measurement. #define ADC_MAIN_SMPR2 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24) + (ADC_SAMPTIME << 27); #else #define ADC_MAIN_SMPR1 (ADC_SAMPTIME << 0) + (ADC_SAMPTIME << 3) + (ADC_SAMPTIME << 6) + (ADC_SAMPTIME << 9) + (ADC_SAMPTIME << 12) + (ADC_SAMPTIME << 15) + (ADC_SAMPTIME << 18) + (ADC_SAMPTIME << 21) + (ADC_SAMPTIME << 24); From 12a8479a94a4490332c18bb43793f3bb7974a561 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 21 Jan 2021 17:44:24 +0100 Subject: [PATCH 093/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index a20d6e03b..e8e9260f0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2108,3 +2108,4 @@ Jan Langhorst Alberto Scotti John Rock Adam Kouse +Ricardo Martins From 8475a45b3a1c695ca6aef0b58aa481c0d5b4a401 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 21 Jan 2021 20:23:50 +0100 Subject: [PATCH 094/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index e8e9260f0..146a46428 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2109,3 +2109,4 @@ Alberto Scotti John Rock Adam Kouse Ricardo Martins +Noel Irwin From 3cf86d9e0fd95c3acce49ced883cba7f19cebdf4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 22 Jan 2021 14:36:07 +0100 Subject: [PATCH 095/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 146a46428..4234551ac 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2110,3 +2110,4 @@ John Rock Adam Kouse Ricardo Martins Noel Irwin +Christoph Rausch From 18ad0fbf4997e6ffd781c822629c261b18102e67 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 22 Jan 2021 18:17:15 +0100 Subject: [PATCH 096/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4234551ac..51f0d458d 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2111,3 +2111,4 @@ Adam Kouse Ricardo Martins Noel Irwin Christoph Rausch +Anthony Hrabusicky From 5e00666dbfaeeca8e4e624d5bd8e144f25d25e2f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 23 Jan 2021 10:41:37 +0100 Subject: [PATCH 097/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 51f0d458d..8e6ba136a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2112,3 +2112,5 @@ Ricardo Martins Noel Irwin Christoph Rausch Anthony Hrabusicky +Sergio Tondini +Aldo Visentin From 139392bc74f453318570197e711285fe71f4eb43 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 23 Jan 2021 11:58:37 +0100 Subject: [PATCH 098/300] Fix i in dblsize --- radio/src/fonts/480x272/font_dblsize.png | Bin 37236 -> 37235 bytes radio/util/font2png.py | 3 +++ 2 files changed, 3 insertions(+) diff --git a/radio/src/fonts/480x272/font_dblsize.png b/radio/src/fonts/480x272/font_dblsize.png index b8d7d47173872ad24545b83e35f201a0a798fab3..1be293a2d2b3d6f98d09138a21a35e73f17342e8 100644 GIT binary patch delta 30506 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 delta 30487 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 Date: Sat, 23 Jan 2021 13:41:27 +0100 Subject: [PATCH 099/300] Fix i in xxlsize --- radio/src/fonts/480x272/font_xxlsize.png | Bin 82882 -> 82882 bytes radio/src/tests/fonts_480x272.png | Bin 14769 -> 14748 bytes radio/util/font2png.py | 3 +++ 3 files changed, 3 insertions(+) diff --git a/radio/src/fonts/480x272/font_xxlsize.png b/radio/src/fonts/480x272/font_xxlsize.png index cd38d5e6d40918886329bf63a8bd1df75a9e01d7..24a3033094343d4d745c86fdb59359a4a3a7043c 100644 GIT binary patch delta 70739 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 IXcG|oKWrU!4FCWD delta 70778 zcmZtu2RN30_y!JV&yX!-Z^_aQH7h=__N#m5)v4^$b~xw*T`tr5~oqLgAA z8ybF0R$w$3ws$FX<4M4sGWD`rJgu?gq5s=!}p02b$WVwrXmbB z_qnDvzfDph1tjlIn2*0d`*YKF{~Z*H+~;(4)lTiBHVzI>b7J4w$syPI{Wc#q`_rqm zqB{r?azj73JXIfI?;`CMM(_VIwJ@)LB!*{{EJ`L8STZ+qZ#e z9&2v_p`eONZe3m7EJ|F7=7EKUoP+dIJRXb3us{;x;>qfJ`}=pbwK>ZE9jp=9b56|8 zo_uNXOxWtu-5D@{_t3&L>BWmLnL>^S2M2!J05=i`qtJAxtH%Ao-=1Fc)GcbJm~+qztT?e$;f2`G`L@G23ZCPmG#qNo#1#Cp>uT!ix9-1{et35L z?B+dEqSjfG6=t=EKvgs9X&XCE zbU~~dofbY?;l<9*E^k!Gt3Wn<%)CTBAGc-3gK|33NlQgq=$o0n_E>mbs`=ve>({5p$0J!HTVr`L z$w$xmEPl+qEQ*bdeg6D2+!0bTvXcXp2yAkPkqq5B%iG4r#t8BccB=g8vQJxRe-iyo zvnDTJ-^2ZVI1gI>{-Sw{F4{gmK7oOOHD=B8^T@nevkL_+Ex}!L1A``?H8mL-8F&*J znRn?t#*`Ek0{p>88@dCQ_emb%5Ig)1ltC`cn_UcjBBm-0Svb!lgwbwGOE11dwEL&O zBL6furGu=RZF=#CQ2J$Bh9S5^DJ&|Sj|?!d2^l+T;cAN9#@ZijDF?ZA!@6;<$lpZF zni!?eU{mO;iT*t~IY0dsZ|>vmEnpf$+|5r$miDPkyQ-9zi)(YLib|p+CFSGA`QK&j z7$PMlB@x%@;?QI2n3t`dmHGMM2fg}NKAV-;eSI62KjG%))+7;|+soGjZ=F8DS~*#1 zxYhrZ0kg=%!($bdo12>_gE*K|X(6sE`L_K@aq_U0a&L!}2J?uD8adlcSDTs{qYu>F zYvJq-6(AH66ny>gTr!~4Th35?FogBT=ejxz_y5I*LAbXAk9wwS?&%K+i8xOvip;)` zz&HuWkTPLVd5pRHdTs9g0;9V0{CGh_u(Tc*^8zjqf11nq2W4wk!dDBYjL0!D

?|$4_b65mIo4ec{q=*wE_Z(* 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 i#~jz#UuS{jkA99d7c4@QvmJWySoP+Q0~ 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 D17HW` delta 3609 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 a+9yw(k=#=dS5PAaK28p2?W=A5;{OBUJsEib diff --git a/radio/util/font2png.py b/radio/util/font2png.py index 71977dcb2..b83505a0c 100755 --- a/radio/util/font2png.py +++ b/radio/util/font2png.py @@ -80,6 +80,9 @@ def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, backg elif c == "j" and fontsize == 32: w, h = font.getsize(c) draw.text((width + 2, fontoffset), c, fill=foreground, font=font) + elif c == "j" and fontsize == 64: + aw, h = font.getsize(c) + draw.text((width + 3, fontoffset), c, fill=foreground, font=font) else: w, h = font.getsize(c) draw.text((width + 1, fontoffset), c, fill=foreground, font=font) From 9fb62190a7d664a3c6c57f5e66ebac9db3f884c2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 23 Jan 2021 17:09:02 +0100 Subject: [PATCH 100/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8e6ba136a..761bc23bf 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2114,3 +2114,4 @@ Christoph Rausch Anthony Hrabusicky Sergio Tondini Aldo Visentin +Greg Wilson From 14343ea37cb73a9df1805291f568931213b2834a Mon Sep 17 00:00:00 2001 From: Kilrah Date: Sun, 24 Jan 2021 10:06:12 +0100 Subject: [PATCH 101/300] Better battery settings for T-Lite/T8 based on design --- radio/src/targets/taranis/board.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 92df79717..e7d32f4bf 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -664,9 +664,9 @@ extern uint16_t adcValues[NUM_ANALOGS]; #define BATTERY_MIN 67 // 6.7V #define BATTERY_MAX 83 // 8.3V #elif defined(RADIO_T8) || defined(RADIO_TLITE) - // 1S Li-ion / Lipo - #define BATTERY_WARN 33 // 3.3V - #define BATTERY_MIN 32 // 3.2V + // 1S Li-ion / Lipo, LDO for 3.3V + #define BATTERY_WARN 35 // 3.5V + #define BATTERY_MIN 34 // 3.4V #define BATTERY_MAX 42 // 4.2V #else // NI-MH 7.2V From fd3240ee8ee5f068ca838b0ea3a3f577f93048b4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 24 Jan 2021 12:55:58 +0100 Subject: [PATCH 102/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 761bc23bf..34c8482ad 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2115,3 +2115,4 @@ Anthony Hrabusicky Sergio Tondini Aldo Visentin Greg Wilson +Mark Sutton From 84bb693a53490b7e9b9ae33487da30629f512447 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 24 Jan 2021 16:13:45 +0100 Subject: [PATCH 103/300] Fix #if/#endif position (thx Olliw42) --- .../targets/common/arm/stm32/aux_serial_driver.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index f2257a953..965ae0daf 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -217,9 +217,9 @@ extern "C" void AUX_SERIAL_USART_IRQHandler(void) } status = AUX_SERIAL_USART->SR; } -#endif } -#endif +#endif // SIMU +#endif // AUX_SERIAL #if defined(AUX2_SERIAL) uint8_t aux2SerialMode = UART_MODE_COUNT; // Prevent debug output before port is setup @@ -400,7 +400,6 @@ extern "C" void AUX2_SERIAL_USART_IRQHandler(void) } } #endif -#if defined(AUX2_SERIAL) // Receive uint32_t status = AUX2_SERIAL_USART->SR; while (status & (USART_FLAG_RXNE | USART_FLAG_ERRORS)) { @@ -416,7 +415,5 @@ extern "C" void AUX2_SERIAL_USART_IRQHandler(void) status = AUX2_SERIAL_USART->SR; } } -#endif - -#endif -#endif // AUX_SERIAL \ No newline at end of file +#endif // SIMU +#endif // AUX2_SERIAL \ No newline at end of file From 92479949a9bb4ea9570f058886896a2d7395423c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 24 Jan 2021 22:25:33 +0100 Subject: [PATCH 104/300] Warning removed --- radio/src/lua/api_general.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 827734642..014d2503e 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1608,7 +1608,9 @@ Set baudrate for serial port(s) affected to LUA */ static int luaSetSerialBaudrate(lua_State * L) { +#if defined(AUX_SERIAL) || defined(AUX2_SERIAL) unsigned int baudrate = luaL_checkunsigned(L, 1); +#endif #if defined(AUX_SERIAL) if (auxSerialMode == UART_MODE_LUA) { From 27f3adeeaaa93f6ae22098054297024e7587f370 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 24 Jan 2021 22:34:00 +0100 Subject: [PATCH 105/300] Grrrrrr. --- radio/src/gui/common/stdlcd/model_receiver_options.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/common/stdlcd/model_receiver_options.cpp b/radio/src/gui/common/stdlcd/model_receiver_options.cpp index fe5affb15..8516f61cf 100644 --- a/radio/src/gui/common/stdlcd/model_receiver_options.cpp +++ b/radio/src/gui/common/stdlcd/model_receiver_options.cpp @@ -141,11 +141,11 @@ void menuModelReceiverOptions(event_t event) lcdInvertLine(0); if (reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_SETTINGS_OK) { - for (uint8_t k=0; k Date: Mon, 25 Jan 2021 14:50:19 +0100 Subject: [PATCH 106/300] Remove uneeded capabilities --- companion/src/firmwares/boards.cpp | 20 ++++++-------------- companion/src/firmwares/boards.h | 3 +-- companion/src/generaledit/hardware.cpp | 4 ++-- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 779712b65..28137b3ba 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -339,15 +339,11 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else return 3; - case AvailablePots: - if (IS_JUMPER_TLITE(board)) - return 0; - else - return getCapability(board, Pots); - case FactoryInstalledPots: if (IS_TARANIS_X9(board)) return 2; + else if (IS_JUMPER_TLITE(board)) + return 0; else return getCapability(board, Pots); @@ -391,6 +387,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) return 7; else if (board == BOARD_TARANIS_X7) return 8; + else if (board == BOARD_JUMPER_TLITE) + return 4; else if (IS_FAMILY_T12(board)) return 8; else if (IS_TARANIS_XLITE(board)) @@ -404,20 +402,14 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) else return 7; - case AvailableSwitches: - if (IS_JUMPER_TLITE(board)) - return 4; - else - return getCapability(board, Switches); - case FactoryInstalledSwitches: if (IS_TARANIS_X9E(board)) return 8; else if (IS_JUMPER_TLITE(board)) return 4; - if (IS_FAMILY_T12(board)) + else if (IS_FAMILY_T12(board)) return 6; - if (IS_HORUS_X12S(board)) + else if (IS_HORUS_X12S(board)) return 8; else return getCapability(board, Switches); diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 1f5fc0358..e871e323d 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -119,7 +119,6 @@ namespace Board { enum Capability { Sticks, Pots, - AvailablePots, FactoryInstalledPots, Sliders, MouseAnalogs, @@ -129,7 +128,7 @@ namespace Board { MultiposPotsPositions, Switches, SwitchPositions, - AvailableSwitches, + FactoryInstalledSwitches, NumTrims, NumTrimSwitches diff --git a/companion/src/generaledit/hardware.cpp b/companion/src/generaledit/hardware.cpp index 1d211d280..9d2e8e452 100644 --- a/companion/src/generaledit/hardware.cpp +++ b/companion/src/generaledit/hardware.cpp @@ -24,7 +24,7 @@ void HardwarePanel::setupSwitchType(int index, QLabel * label, AutoLineEdit * name, AutoComboBox * type, bool threePos) { Board::Type board = getCurrentBoard(); - if (IS_STM32(board) && index < getBoardCapability(board, Board::AvailableSwitches)) { + if (IS_STM32(board) && index < getBoardCapability(board, Board::Switches)) { type->addItem(tr("None"), Board::SWITCH_NOT_AVAILABLE); type->addItem(tr("2 Positions Toggle"), Board::SWITCH_TOGGLE); type->addItem(tr("2 Positions"), Board::SWITCH_2POS); @@ -75,7 +75,7 @@ void HardwarePanel::setupPotType(int index, QLabel * label, AutoLineEdit * name, { Board::Type board = firmware->getBoard(); - if (IS_STM32(board) && index < getBoardCapability(board, Board::AvailablePots)) { + if (IS_STM32(board) && index < getBoardCapability(board, Board::Pots)) { label->setText(RawSource(SOURCE_TYPE_STICK, CPN_MAX_STICKS+index).toString()); type->addItem(tr("None"), Board::POT_NONE); type->addItem(tr("Pot with detent"), Board::POT_WITH_DETENT); From 1d73c76dfbba243e6997c9bedacb9081bb653494 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 25 Jan 2021 14:52:53 +0100 Subject: [PATCH 107/300] Fix --- companion/src/firmwares/boards.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 28137b3ba..4a96cd601 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -325,9 +325,11 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case Sticks: return 4; - case Pots: // Storage pots + case Pots: if (IS_TARANIS_X9LITE(board)) return 1; + else if (IS_JUMPER_TLITE(board)) + return 0; else if (IS_TARANIS_SMALL(board)) return 2; else if (IS_TARANIS_X9E(board)) From 4b9b2c730654e52b4cfa00ebf5f10a528dceecbc Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 25 Jan 2021 15:09:05 +0100 Subject: [PATCH 108/300] Fix --- companion/src/firmwares/boards.cpp | 2 -- companion/src/firmwares/opentx/opentxeeprom.cpp | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 4a96cd601..cfeabc918 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -344,8 +344,6 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) case FactoryInstalledPots: if (IS_TARANIS_X9(board)) return 2; - else if (IS_JUMPER_TLITE(board)) - return 0; else return getCapability(board, Pots); diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 264a3ad6a..bc658bfdf 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -76,6 +76,8 @@ inline int MAX_POTS(Board::Type board, int version) { if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board)) return 3; + if (IS_FAMILY_T12(board)) + return 2; return Boards::getCapability(board, Board::Pots); } From 7333de5c8b3c6b6de3f2a8435c12ce4f6be192c5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 25 Jan 2021 15:15:13 +0100 Subject: [PATCH 109/300] Fix switches --- companion/src/firmwares/opentx/opentxeeprom.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index bc658bfdf..2a07fc75a 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -94,6 +94,8 @@ inline int MAX_POTS_SOURCES(Board::Type board, int version) { if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board)) return 5; + if (IS_FAMILY_T12(board)) + return 2; return Boards::getCapability(board, Board::Pots); } From ae83086cbaa58626b537689dd1db301616478c3b Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 25 Jan 2021 15:32:13 +0100 Subject: [PATCH 110/300] Fix switches --- companion/src/firmwares/opentx/opentxeeprom.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 2a07fc75a..111e3c391 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -87,6 +87,8 @@ inline int MAX_POTS_STORAGE(Board::Type board, int version) return 3; if (version >= 219 && IS_FAMILY_HORUS_OR_T16(board)) return 5; + if (IS_FAMILY_T12(board)) + return 2; return Boards::getCapability(board, Board::Pots); } From 96dc669336749c321fa1b46e62449c550a5cc193 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 25 Jan 2021 15:34:18 +0100 Subject: [PATCH 111/300] Cosmetics --- companion/src/firmwares/boards.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index e871e323d..e55aeae37 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -56,7 +56,7 @@ namespace Board { BOARD_RADIOMASTER_T8, BOARD_JUMPER_TLITE, BOARD_TYPE_COUNT, - BOARD_TYPE_MAX = BOARD_TYPE_COUNT-1 + BOARD_TYPE_MAX = BOARD_TYPE_COUNT - 1 }; enum PotType @@ -128,7 +128,6 @@ namespace Board { MultiposPotsPositions, Switches, SwitchPositions, - FactoryInstalledSwitches, NumTrims, NumTrimSwitches From 6c74dc75dd6be4570a3dde7c2189b80982671d94 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 25 Jan 2021 18:01:33 +0100 Subject: [PATCH 112/300] Test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 332a2a874..6ddb599b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ before_install: - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size - sudo apt-get install --yes --force-yes -qq qt${QT_BASE}base qt${QT_BASE}multimedia qt${QT_BASE}svg qt${QT_BASE}tools; source /opt/qt${QT_BASE}/bin/qt${QT_BASE}-env.sh - sudo ln -sf /usr/bin/python3 /usr/bin/python - - python3 -m pip install --upgrade pip + - python3 -m pip install pip==20.3.3 - sudo apt-get install python3-setuptools - python3 -m pip install pillow clang From 40c18a07fc5573a36b580c4639e9914e49148fff Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 25 Jan 2021 19:08:30 +0100 Subject: [PATCH 113/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 34c8482ad..5e7672e1f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2116,3 +2116,4 @@ Sergio Tondini Aldo Visentin Greg Wilson Mark Sutton +Lukas Kana From 514d7cd9d2d4103b6442f2c5d9f9821a6e100060 Mon Sep 17 00:00:00 2001 From: olliw42 Date: Tue, 26 Jan 2021 07:59:56 +0100 Subject: [PATCH 114/300] TaskIndex enum is not used, and had a superfluous bluetooth entry also --- radio/src/tasks.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 16e120784..fc2df74c7 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -32,16 +32,6 @@ RTOS_DEFINE_STACK(audioStack, AUDIO_STACK_SIZE); RTOS_MUTEX_HANDLE audioMutex; RTOS_MUTEX_HANDLE mixerMutex; -enum TaskIndex { - MENU_TASK_INDEX, - MIXER_TASK_INDEX, - AUDIO_TASK_INDEX, - CLI_TASK_INDEX, - BLUETOOTH_TASK_INDEX, - TASK_INDEX_COUNT, - MAIN_TASK_INDEX = 255 -}; - void stackPaint() { menusStack.paint(); From e1e097d4294a3ce4e478d4ec4ef487caf48197e0 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 26 Jan 2021 11:50:47 +0100 Subject: [PATCH 115/300] Disable commit-tests for AR9X, 9XPRO and SKY9X in synced branch for now --- tools/commit-tests.sh | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index be5940308..9ecd88769 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -58,32 +58,32 @@ COMMON_OPTIONS+=${EXTRA_OPTIONS} mkdir build || true cd build -if [[ " SKY9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then - # OpenTX on Sky9x - rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=SKY9X -DHELI=YES DLUA=YES -DTELEMETRY=FRSKY -DPPM_LIMITS_SYMETRICAL=YES -DVARIO=YES -DAUTOSWITCH=YES -DAUTOSOURCE=YES -DAUDIO=YES -DGPS=YES -DPPM_CENTER_ADJUSTABLE=YES -DFLIGHT_MODES=YES -DOVERRIDE_CHANNEL_FUNCTION=YES -DFRSKY_STICKS=YES -DGVARS=YES "${SRCDIR}" - make -j"${CORES}" ${FIRMARE_TARGET} - make -j"${CORES}" libsimulator - make -j"${CORES}" tests-radio -fi - -if [[ " AR9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then - # OpenTX on AR9X - rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=AR9X -DHELI=YES -DGVARS=YES "${SRCDIR}" - make -j"${CORES}" ${FIRMARE_TARGET} - make -j"${CORES}" libsimulator - make -j"${CORES}" tests-radio -fi - -if [[ " 9XRPRO ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then - # OpenTX on Sky9x - rm -rf ./* - cmake "${COMMON_OPTIONS}" -DPCB=9XRPRO -DHELI=YES -DGVARS=YES "${SRCDIR}" - make -j"${CORES}" ${FIRMARE_TARGET} - make -j"${CORES}" libsimulator - make -j"${CORES}" tests-radio -fi +#if [[ " SKY9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then +# # OpenTX on Sky9x +# rm -rf ./* +# cmake "${COMMON_OPTIONS}" -DPCB=SKY9X -DHELI=YES DLUA=YES -DTELEMETRY=FRSKY -DPPM_LIMITS_SYMETRICAL=YES -DVARIO=YES -DAUTOSWITCH=YES -DAUTOSOURCE=YES -DAUDIO=YES -DGPS=YES -DPPM_CENTER_ADJUSTABLE=YES -DFLIGHT_MODES=YES -DOVERRIDE_CHANNEL_FUNCTION=YES -DFRSKY_STICKS=YES -DGVARS=YES "${SRCDIR}" +# make -j"${CORES}" ${FIRMARE_TARGET} +# make -j"${CORES}" libsimulator +# make -j"${CORES}" tests-radio +#fi +# +#if [[ " AR9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then +# # OpenTX on AR9X +# rm -rf ./* +# cmake "${COMMON_OPTIONS}" -DPCB=AR9X -DHELI=YES -DGVARS=YES "${SRCDIR}" +# make -j"${CORES}" ${FIRMARE_TARGET} +# make -j"${CORES}" libsimulator +# make -j"${CORES}" tests-radio +#fi +# +#if [[ " 9XRPRO ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then +# # OpenTX on Sky9x +# rm -rf ./* +# cmake "${COMMON_OPTIONS}" -DPCB=9XRPRO -DHELI=YES -DGVARS=YES "${SRCDIR}" +# make -j"${CORES}" ${FIRMARE_TARGET} +# make -j"${CORES}" libsimulator +# make -j"${CORES}" tests-radio +#fi if [[ " X9LITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9LITE From f573c4886257d9a0d827d8dc970ef3309cacc05d Mon Sep 17 00:00:00 2001 From: LapinFou Date: Tue, 26 Jan 2021 12:05:23 +0100 Subject: [PATCH 116/300] Initial version. --- companion/src/translations/companion_fr.ts | 990 +++++++++++---------- 1 file changed, 508 insertions(+), 482 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 593231b91..d052a6e2c 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -743,58 +743,58 @@ Manche Droit: Profondeur, Direction Boards - + Left Horizontal Gauche Horizontal - + Left Vertical Gauche Vertical - + Right Vertical Droit Vertical - + Right Horizontal Droit Horizontal - + Aux. 1 - + Aux. 2 - - + + Unknown Inconnu - + Rud Dir - + Ele Prf - + Thr Gaz - + Ail @@ -843,62 +843,62 @@ Manche Droit: Profondeur, Direction ChannelsPanel - + Name Nom - + Subtrim Subtrim - + Min - + Max - + Direction - + Curve Courbe - + PPM Center Neutre PPM - + Linear Subtrim Subtrim linéaire - + CH%1 VOIE%1 - + Popup menu available Menu contextuel disponible - + --- - + INV @@ -1386,6 +1386,29 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import CB + + CurveReference + + + Diff + + + + + Expo + + + + + Func + + + + + Curve + Courbe + + Curves @@ -1478,112 +1501,112 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import CurvesPanel - + Curve %1 Courbe %1 - + Popup menu available Menu contextuel disponible - + %1 points - + Linear Linéaire - + Single Expo Expo simple - + Symmetrical f(x)=-f(-x) Symétrique f(x)=-f(-x) - + Symmetrical f(x)=f(-x) Symétrique f(x)=f(-x) - + Editing curve %1 Édition de la courbe %1 - + Not enough free points in EEPROM to store the curve. Pas assez de points disponibles en mémoire pour enregistrer la courbe. - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Curve. Are you sure? Effacer la Courbe. Êtes-vous sûr? - + Clear all Curves. Are you sure? Effacer toutes les Courbes. Êtes-vous sûr? - + Cut Curve. Are you sure? Couper la Courbe. Êtes-vous sûr? - + Delete Curve. Are you sure? Supprimer la Courbe. Êtes-vous sûr? @@ -1657,8 +1680,8 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import - Set Timer %1 - Définir chrono %1 + Set %1 + @@ -1746,79 +1769,64 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Bind module externe - - Timer1 - Chrono 1 - - - - Timer2 - Chrono 2 - - - - Timer3 - Chrono 3 - - - + Flight Vol - + Telemetry Télémétrie - + Rotary Encoder Sélecteur Rotatif - + REa - + REb - + s - - + + <font color=red><b>Inconsistent parameter</b></font> <font color=red><b>Paramètre inconsistant</b></font> - + Value Valeur - + played once, not during startup !1x Lu une fois, mais pas à la mise en route - + repeat(%1s) Répéter chaque %1s - + DISABLED DESACTIVE - + CFN FPN @@ -1826,139 +1834,139 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import CustomFunctionsPanel - + Switch Interrupteur - + Action - + Parameters Paramètres - + Enable Actif - + Popup menu available Menu contextuel disponible - + SF%1 FS%1 - + GF%1 FG%1 - + ON - + Error occurred while trying to play sound, possibly the file is already opened. (Err: %1 [%2]) Une erreur s'est produite lors de la lecture du son, le fichier est peut-être déjà ouvert. (Err: %1 [%2]) - + Unable to find or open sound file: %1 Impossible de trouver ou d'ouvrir le fichier son: %1 - + Delete Function. Are you sure? Supprimer la Fonction. Êtes-vous sûr? - + Cut Special Function. Are you sure? Couper la Fonction. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Value Valeur - + Source Source - + GVAR VG - + Increment Incrément - + Clear Function. Are you sure? Effacer la Fonction. Êtes-vous sûr? - + Clear all Functions. Are you sure? Effacer toutes les Fonctions. Êtes-vous sûr? @@ -2126,48 +2134,48 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Erreur de conversion du champ %1 - + Switch Interrupteur - + Switch L'interrupteur - + cannot be exported on this board! n'est pas supporté sur cette carte ! - + Source - + Source %1 cannot be exported on this board! La source %1 n'est pas supportée sur cette plateforme ! - + OpenTX only accepts %1 points in all curves OpenTX n'accepte que %1 points au maximum entre toutes les courbes - + OpenTx only accepts %1 points in all curves OpenTX n'accepte que %1 points au maximum entre toutes les courbes - - + + OpenTX on this board doesn't accept this function OpenTX ne supporte pas cette fonction sur cette carte - + OpenTX doesn't accept this radio protocol OpenTX ne supporte pas ce protocole radio @@ -2500,27 +2508,27 @@ Si vide, la ligne est toujours active. Les 2 - + Edit %1 Éditer %1 - + Popup menu available Menu contextuel disponible - + Clear All Effacer Tout - + Set All Définir Tout - + Invert All Inverser Tout @@ -2889,272 +2897,293 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Firmware - + Channel values displayed in us Positions de voies affichées en µs - + No OverrideCH functions available Désactive la fonction spéciale "Remplacer VOIExx" - + Possibility to enable FAI MODE (no telemetry) at field Possibilité d'activer le mode FAI (télémétrie désactivée) sur le terrain - + FAI MODE (no telemetry) always enabled Mode FAI (télémétrie toujours désactivée) - + Removes D8 FrSky protocol support which is not legal for use in the EU on radios sold after Jan 1st, 2015 Masque le protocole D8. Légalement obligatoire sur les radios Européennes importées après le 1er janvier 2015 - - - + + + + + Disable HELI menu and cyclic mix support Supprimer le menu HELICO et les mixages cycliques - - - + + + + + Disable Global variables Supprimer le support des variables globales - - - + + + + + Enable Lua custom scripts screen Activer l'écran des scripts Lua personnalisés - + Use alternative SQT5 font Utiliser la police alternative SQT5 - - + + Disable RAS (SWR) Désactiver le RAS (SWR). Utile uniquement pour les X9D+ avec un RAS/SWR non fonctionnel - + Pots use in menus navigation Support de la navigation dans les menus avec les POTS - + Enable AFHDS3 support Activer le support du AFHDS3 - + FrSky Taranis X9D+ - + FrSky Taranis X9D+ 2019 - + FrSky Taranis X9D - + Haptic module installed Module vibreur installé - + FrSky Taranis X9E - + Confirmation before radio shutdown Confirmation avant l'arrêt de la radio - + Horus gimbals installed (Hall sensors) Manches Horus à effet HALL installés - + FrSky Taranis X9-Lite - + FrSky Taranis X9-Lite S - + FrSky Taranis X7 / X7S - + FrSky Taranis X7 / X7S Access - + FrSky Taranis X-Lite S/PRO - + FrSky Taranis X-Lite - + FrSky Horus X10 / X10S - - + + Support for ACCESS internal module replacement Prise en charge du module de remplacement interne ACCESS - - - - - + + + + + Support hardware mod: R9M ACCESS Prise en charge du mod hardware: R9M ACCESS - + FrSky Horus X10 Express / X10S Express - + FrSky Horus X12S - + Use ONLY with first DEV pcb version Exclusivement pour les Horus DEV - - + + Support for MULTI internal module Prise en charge du module MULTI interne - - - + + Jumper T-Lite + + + + + + Support for bluetooth module Prise en charge du module Bluetooth - + + Radiomaster T8 + + + + + Allow bind using bind key + + + + Support internal GPS Prise en charge du GPS interne - + Jumper T18 - + Turnigy 9XR-PRO - + Enable HELI menu and cyclic mix support Activer le menu hélico et les mixages CCPM - + Global variables Variables globales - + Enable non certified firmwares Activer les firmwares non certifiés - + In model setup menus automatically set source by moving the control Sélectionner les sources des mixeurs en bougeant le contrôle désiré - + In model setup menus automatically set switch by moving the control Sélectionner les interrupteurs en bougeant le contrôle désiré - + No graphical check boxes and sliders Pas de cases à cocher et de curseurs graphiques - + Battery graph Graphique de la batterie - + Don't use bold font for highlighting active items Ne pas mettre les lignes actives en gras - + Enable resetting values by pressing up and down at the same time Permet la remise à zéro des valeurs en pressant haut-bas en même temps, valeur min avec gauche/bas, valeur max avec haut/droite, inversion avec gauche/droite - + Radiomaster TX12 - + Radiomaster TX16S / SE / Hall / Masterfire - + 9X with AR9X board 9X avec carte AR9X - + Jumper T12 / T12 Pro - + Jumper T16 / T16+ / T16 Pro - + 9X with Sky9x board 9X avec carte Sky9x @@ -3864,17 +3893,17 @@ Vous utilisez actuellement: FlightModesPanel - + Flight Mode %1 Phase de vol %1 - + (%1) - + (default) (par défaut) @@ -4127,7 +4156,7 @@ p, li { white-space: pre-wrap; } GeneralEdit - + Setup Configuration @@ -4144,7 +4173,7 @@ These will be relevant for all models in the same EEPROM. Communs à tous les modèles d'une même EEPROM. - + Trainer Écolage @@ -4159,42 +4188,42 @@ Communs à tous les modèles d'une même EEPROM. Lire étalonnage et paramètres hw du profil - + Global Functions Fonctions globales - + Hardware Matériel - + Calibration - + Wrong data in profile, radio calibration was not retrieved Données du profil erronnées, l'étalonnage n'a pas été lu - + Wrong data in profile, Switch/pot config not retrieved Données incorecte, Interrupteurs/Pot non trouvés - + Wrong data in profile, hw related parameters were not retrieved Données du profil erronnées, les paramètres hw n'ont pas été lus - + Do you want to store calibration in %1 profile<br>overwriting existing calibration? Voulez-vous stocker l'étalonnage dans le profil %1<br> et écraser l'étalonnage existant ? - + Calibration and HW parameters saved. Etalonnage et paramètres hw enregistrés avec succès. @@ -4202,7 +4231,7 @@ Communs à tous les modèles d'une même EEPROM. GeneralSettings - + Radio Settings Paramètres de la radio @@ -5709,159 +5738,159 @@ Cette fonction ne peut pas être désactivée sur la radio. InputsPanel - + + - Move Up Monter - - + + Ctrl+Up Ctrl+Haut - + + - Move Down Descendre - - + + Ctrl+Down Ctrl+Bas - + Clear All Inputs Effacer toutes les entrées - + Not enough available Inputs! Pas assez d'Entrées disponibles! - + Delete selected Input lines. Are you sure? Supprimer les lignes Entrée sélectionnées. Êtes-vous sûr? - + Cut selected Input lines. Are you sure? Couper les lignes Entrée sélectionnées. Êtes-vous sûr? - + Lines Linéaire - + &Add &Ajouter - + Ctrl+A Ctrl+A - + &Edit &Édition - + Enter Entrée - + &Delete &Supprimer - - + + Delete Supprimer - + &Copy &Copier - + Ctrl+C - + &Cut &Couper - + Ctrl+X - + &Paste &Coller - + Ctrl+V - + Du&plicate Du&pliquer - + Ctrl+U - + Input Entrée - + Insert Insérer - + Clear Effacer - + Clear All Effacer Tout - + Clear all Input lines. Are you sure? Effacer toutes les lignes Entrée. Êtes-vous sûr? - + Clear all lines for the selected Inputs. Are you sure? Effacer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? - + Delete all lines for the selected Inputs. Are you sure? Supprimer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? @@ -6058,72 +6087,72 @@ Cette fonction ne peut pas être désactivée sur la radio. - + (infinite) (infini) - + Delete Logical Switch. Are you sure? Supprimer l'Interrupteur Logique. Êtes-vous sûr? - + Cut Logical Switch. Are you sure? Couper l'Interrupteur Logique. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Logical Switch. Are you sure? Effacer l'Interrupteur Logique. Êtes-vous sûr? - + Clear all Logical Switches. Are you sure? Effacer tous les Interrupteurs Logiques. Êtes-vous sûr? @@ -7850,27 +7879,27 @@ Mixage actif par défaut si non-renseigné. Nom - + DEST -> %1 - + Click to access popup menu Cliquez pour accéder au menu contextuel - + Clear All Effacer Tout - + Set All Définir Tout - + Invert All Inverser Tout @@ -7916,136 +7945,136 @@ Mixage actif par défaut si non-renseigné. MixesPanel - - + + Move Up Monter - - + + Ctrl+Up Ctrl+Haut - - + + Move Down Descendre - - + + Ctrl+Down Ctrl+Bas - + Clear Mixes Effacer tous les mixages - + Not enough available mixers! Plus de mixages disponibles ! - + Delete selected Mix lines. Are you sure? Supprimer les lignes Mixeur sélectionnées. Êtes-vous sûr? - + Cut selected Mix lines. Are you sure? Couper les lignes Mixeur sélectionnées. Êtes-vous sûr? - + &Add &Ajouter - + Ctrl+A - + &Edit &Édition - + Enter Entrée - + Ctrl+T - + &Toggle highlight &(Dé)sélectionner - + &Delete &Supprimer - + Delete Supprimer - + &Copy &Copier - + Ctrl+C - + Ctrl+X - + C&ut &Couper - + &Paste &Coller - + Ctrl+V - + Du&plicate &Dupliquer - + Ctrl+U - + Clear Mixes? Effacer tous les mixages ? - + Really clear all the mixes? Êtes-vous sûr de vraiment vouloir effacer tous les mixages ? @@ -8062,6 +8091,11 @@ Mixage actif par défaut si non-renseigné. Throttle Source Source des gaz + + + THR + GAZ + ModelEdit @@ -8071,32 +8105,32 @@ Mixage actif par défaut si non-renseigné. Simulation - + Heli Paramètres hélico - + Inputs Entrées - + Mixes Mixages - + Logical Switches Inters logiques - + Setup Configuration - + Curves Courbes @@ -8106,22 +8140,22 @@ Mixage actif par défaut si non-renseigné. Dialogue - + Flight Modes Phases de vol - + Outputs Sorties - + Special Functions Fonctions spéciales - + Telemetry Télémesure @@ -9208,12 +9242,12 @@ Mixage actif par défaut si non-renseigné. ModuleData - + Positive - + Negative Négative @@ -9319,42 +9353,42 @@ Mixage actif par défaut si non-renseigné. ModulePanel - + Value Valeur - + Hold Maintien - + No Pulse Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - + Bind on channel Bind avec VOIE @@ -9830,22 +9864,22 @@ Mixage actif par défaut si non-renseigné. OpenTxEepromInterface - + Unknown error Erreur inconnue - + ... plus %1 errors ... plus %1 erreurs - + Cannot write radio settings Impossible d'écrire les paramètres de la radio - + Cannot write model %1 Impossible d'écrire le modèle %1 @@ -10350,17 +10384,6 @@ E REb - - - - ??? - - - - - ---- - - I @@ -10389,7 +10412,7 @@ E - + SRC @@ -10637,146 +10660,226 @@ E SensorData - - + + Raw (-) + Chiffres bruts (-) + + + + V - + A - + mA - + kts - + m/s - + km/h - + mph - + m - + f - + °C - + °F - + % - + mAh - + W - + mW - + dB - + rpms - + g - + ° - + Rad - + hours heures - + minutes - + seconds secondes - + ml/minute - + Hertz - + mS - + uS - + TELE + + + Custom + Prédéfini + + + + Calculated + Calculé + + + + Add + Addition + + + + Average + Moyenne + + + + Minimum + + + + + Maximum + + + + + Multiply + Multiplication + + + + Totalize + Totalisation + + + + Cell + Element LiPo + + + + Consumption + Consommation + + + + Distance + + + + + Lowest + Min + + + + Cell %1 + Elém. %1 + + + + Highest + Max + + + + Delta + + Setup @@ -10960,92 +11063,92 @@ Si cette option est cochée, la voie des gaz est inversée: le ralenti est &apo SetupPanel - + Popup menu available Menu contextuel disponible - + Timer %1 Chrono %1 - + THR GAZ - + Profile Settings Paramètres du profil - + SD structure path not specified or invalid Chemin de la structure SD non spécifié ou invalide - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Timer. Are you sure? Effacer le Chrono. Êtes-vous sûr? - + Clear all Timers. Are you sure? Effacer tous les Chronos. Êtes-vous sûr? - + Cut Timer. Are you sure? Couper le Chrono. Êtes-vous sûr? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? @@ -11769,62 +11872,62 @@ La valeur par défaut est configurée dans le profil radio sélectionné.Simulateur Radio (%1) - + Could not determine startup data source. Impossible de déterminer les données source. - + Could not load data, possibly wrong format. Impossible de charger les données, mauvais format. - + Data Load Error Erreur de lecture des données - + Invalid startup data provided. Plese specify a proper file/path. Données de démarrage fournies non valides. Veuillez spécifier un fichier/chemin approprié. - + Simulator Startup Error Erreur de démarrage du simulateur - + Error saving data: could open file for writing: '%1' Erreur de sauvegarde des données: impossible d'écrire le fichier '%1' - + Error saving data: could not get data from simulator interface. Erreur de sauvegarde des données: impossible de récupérer les données depuis l'interface de simulation. - + An unexpected error occurred while attempting to save radio data to file '%1'. Une erreur inattendue s'est produite lors de l'enregistrement des données radio dans le fichier '%1'. - + Data Save Error Erreur de sauvegarde des données - + Cannot open joystick, joystick disabled Impossible d'accéder au Joystick, Joystick désactivé - + Radio firmware error: %1 Erreur firmware radio: %1 - + - Flight Mode %1 (#%2) - Phase de Vol %1 (#%2) @@ -12479,22 +12582,22 @@ Trop d'erreurs, abandon. Type d'écran personnalisé - + None Aucun - + Numbers Chiffres - + Bars Barres - + Script @@ -12522,109 +12625,52 @@ Trop d'erreurs, abandon. TelemetryPanel - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Telemetry screen %1 Ecran de télémesure %1 - + Source - + Low Alarm Alarme basse - + Critical Alarm Alarme critique - + Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (non supporté) - - Alti - Alt - - - - Alti+ - Alt+ - - - - VSpeed - Vitesse verticale - - - - - - A1 - - - - - - - A2 - - - - - - A3 - - - - - - A4 - - - - - - FAS - - - - - Cells - Velm - - - - --- - - - - + Delete Sensor. Are you sure? Supprimer le Capteur. Êtes-vous sûr? @@ -12922,92 +12968,72 @@ Trop d'erreurs, abandon. TelemetrySensorPanel - + TELE%1 - + Popup menu available Menu contextuel disponible - - Lowest - Min - - - - Cell %1 - Elém. %1 - - - - Highest - Max - - - - Delta - - - - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Cut Telemetry Sensor. Are you sure? Couper le capteur de télémétrie. Êtes-vous sûr? - + Clear Telemetry Sensor. Are you sure? Effacer le capteur de télémétrie. Êtes-vous sûr? - + Clear all Telemetry Sensors. Are you sure? Effacez tous les capteurs de télémétrie. Êtes-vous sûr? From f10eaa48ee16434d2f9c34fdaaa0556512d069c5 Mon Sep 17 00:00:00 2001 From: Kilrah Date: Tue, 26 Jan 2021 12:17:06 +0100 Subject: [PATCH 117/300] Companion countdown start --- companion/src/firmwares/modeldata.h | 2 ++ .../src/firmwares/opentx/opentxeeprom.cpp | 3 ++- companion/src/modeledit/setup.cpp | 18 +++++++++++++++++- companion/src/modeledit/setup.h | 1 + companion/src/modeledit/setup_timer.ui | 14 +++++++++++++- 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 2b277568f..847997d94 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -70,6 +70,8 @@ class TimerData { unsigned int countdownBeep; unsigned int val; unsigned int persistent; + int countdownStart; + unsigned int direction; int pvalue; void clear() { memset(reinterpret_cast(this), 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } void convert(RadioDataConversionState & cstate); diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 111e3c391..f9fc8c8f5 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -2311,7 +2311,8 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig internalField.Append(new UnsignedField<2>(this, modelData.timers[i].countdownBeep)); internalField.Append(new BoolField<1>(this, modelData.timers[i].minuteBeep)); internalField.Append(new UnsignedField<2>(this, modelData.timers[i].persistent)); - internalField.Append(new SpareBitsField<3>(this)); + internalField.Append(new SignedField<2>(this, modelData.timers[i].countdownStart)); + internalField.Append(new UnsignedField<1>(this, modelData.timers[i].direction)); if (HAS_LARGE_LCD(board)) internalField.Append(new ZCharField<8>(this, modelData.timers[i].name, "Timer name")); else diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 25cd576ec..093c8acf7 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -73,13 +73,20 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui->persistent->addItem(tr("Not persistent"), 0); ui->persistent->addItem(tr("Persistent (flight)"), 1); ui->persistent->addItem(tr("Persistent (manual reset)"), 2); + + ui->countdownStart->setField(timer.countdownStart, this); + ui->countdownStart->addItem("5s", 1); + ui->countdownStart->addItem("10s", 0); + ui->countdownStart->addItem("20s", -1); + ui->countdownStart->addItem("30s", 2); disableMouseScrolling(); QWidget::setTabOrder(prevFocus, ui->name); QWidget::setTabOrder(ui->name, ui->value); QWidget::setTabOrder(ui->value, ui->mode); QWidget::setTabOrder(ui->mode, ui->countdownBeep); - QWidget::setTabOrder(ui->countdownBeep, ui->minuteBeep); + QWidget::setTabOrder(ui->countdownBeep, ui->countdownStart); + QWidget::setTabOrder(ui->countdownStart, ui->minuteBeep); QWidget::setTabOrder(ui->minuteBeep, ui->persistent); update(); @@ -121,6 +128,7 @@ void TimerPanel::update() ui->countdownBeep->updateValue(); ui->minuteBeep->setChecked(timer.minuteBeep); ui->persistent->updateValue(); + ui->countdownStart->updateValue(); lock = false; } @@ -129,6 +137,14 @@ QWidget * TimerPanel::getLastFocus() return ui->persistent; } +void TimerPanel::on_countdownBeep_currentIndexChanged(int index) +{ + if(index == TimerData::COUNTDOWN_SILENT) + ui->countdownStart->hide(); + else + ui->countdownStart->show(); +} + void TimerPanel::on_value_editingFinished() { if (!lock) { diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index c931f9dd7..7a5cddba9 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -51,6 +51,7 @@ class TimerPanel : public ModelPanel void onModeChanged(int index); void on_value_editingFinished(); void on_minuteBeep_toggled(bool checked); + void on_countdownBeep_currentIndexChanged(int index); void on_name_editingFinished(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); diff --git a/companion/src/modeledit/setup_timer.ui b/companion/src/modeledit/setup_timer.ui index b6b920a10..11f566b45 100644 --- a/companion/src/modeledit/setup_timer.ui +++ b/companion/src/modeledit/setup_timer.ui @@ -20,7 +20,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -59,6 +68,9 @@ + + + From 00b2b295ca94fb1e327d6ef616bcf8b6accde2c6 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 26 Jan 2021 13:01:20 +0100 Subject: [PATCH 118/300] 128 GUI --- radio/src/gui/128x64/model_setup.cpp | 109 +++++++++++++++------------ 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 468597b29..ec199df66 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -236,7 +236,8 @@ inline uint8_t MODULE_SUBTYPE_ROWS(int moduleIdx) } #define POT_WARN_ROWS ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0) -#define TIMER_ROWS 2, 0, 0, 0, 0 +#define TIMER_ROWS(x) 2, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t) 1 : (uint8_t)0 +#define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2) #if defined(PCBSKY9X) #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2, @@ -306,6 +307,63 @@ void onBluetoothConnectMenu(const char * result) } #endif +void editTimerMode(int timerIdx, coord_t y, LcdFlags attr, event_t event) +{ + TimerData & timer = g_model.timers[timerIdx]; + drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1); + drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer.mode, menuHorizontalPosition==0 ? attr : 0); + drawTimer(MODEL_SETUP_2ND_COLUMN + 5 * FW - 2 + 5 * FWNUM + 1, y, timer.start, RIGHT | (menuHorizontalPosition == 1 ? attr : 0),menuHorizontalPosition == 2 ? attr : 0); + if (attr && s_editMode>0) { + div_t qr = div(timer.start, 60); + switch (menuHorizontalPosition) { + case 0: + { + swsrc_t timerMode = timer.mode; + if (timerMode < 0) timerMode -= TMRMODE_COUNT-1; + CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); + if (timerMode < 0) timerMode += TMRMODE_COUNT-1; + timer.mode = timerMode; +#if defined(AUTOSWITCH) + if (s_editMode>0) { + swsrc_t val = timer.mode - (TMRMODE_COUNT-1); + swsrc_t switchVal = checkIncDecMovedSwitch(val); + if (val != switchVal) { + timer.mode = switchVal + (TMRMODE_COUNT-1); + storageDirty(EE_MODEL); + } + } +#endif + break; + } + case 1: + qr.quot = checkIncDec(event, qr.quot, 0, 1439, EE_MODEL | NO_INCDEC_MARKS); // 23h59 + timer.start = qr.rem + qr.quot*60; + break; + } + } +} + +void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) +{ + TimerData & timer = g_model.timers[timerIdx]; + lcdDrawTextAlignedLeft(y, STR_BEEPCOUNTDOWN); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VBEEPCOUNTDOWN, timer.countdownBeep, (menuHorizontalPosition==0 ? attr : 0)); + if (timer.countdownBeep != COUNTDOWN_SILENT) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 6 * FW, y, TIMER_COUNTDOWN_START(timerIdx), (menuHorizontalPosition == 1 ? attr : 0) | LEFT); + lcdDrawChar(lcdLastRightPos, y, 's'); + } + if (attr && s_editMode>0) { + switch (menuHorizontalPosition) { + case 0: + CHECK_INCDEC_MODELVAR(event, timer.countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1); + break; + case 1: + timer.countdownStart = -checkIncDecModel(event, -timer.countdownStart, -1, +2); + break; + } + } +} + #include "common/stdlcd/model_setup_pxx1.cpp" #if defined(PXX2) @@ -375,9 +433,7 @@ void menuModelSetup(event_t event) MENU_TAB({ HEADER_LINE_COLUMNS 0, - TIMER_ROWS, - TIMER_ROWS, - TIMER_ROWS, + TIMERS_ROWS, 0, // Extended limits 1, // Extended trims 0, // Show trims @@ -502,47 +558,7 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_TIMER2: case ITEM_MODEL_SETUP_TIMER3: { - unsigned int timerIdx = (k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)); - TimerData * timer = &g_model.timers[timerIdx]; - drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1); - drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0); - drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, RIGHT | (menuHorizontalPosition==1 ? attr : 0), menuHorizontalPosition==2 ? attr : 0); - if (attr && s_editMode > 0) { - div_t qr = div(timer->start, 60); - switch (menuHorizontalPosition) { - case 0: - { - swsrc_t timerMode = timer->mode; - if (timerMode < 0) - timerMode -= TMRMODE_COUNT-1; - CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); - if (timerMode < 0) - timerMode += TMRMODE_COUNT-1; - timer->mode = timerMode; -#if defined(AUTOSWITCH) - if (s_editMode>0) { - int8_t val = timer->mode - (TMRMODE_COUNT-1); - int8_t switchVal = checkIncDecMovedSwitch(val); - if (val != switchVal) { - timer->mode = switchVal + (TMRMODE_COUNT-1); - storageDirty(EE_MODEL); - } - } -#endif - break; - } - case 1: - CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 539); // 8:59 - timer->start = qr.rem + qr.quot*60; - break; - case 2: - qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; - timer->start -= qr.rem; - if ((int16_t)timer->start < 0) timer->start=0; - if ((int16_t)timer->start > 5999) timer->start=32399; // 8:59:59 - break; - } - } + editTimerMode(k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0), y, attr, event); break; } @@ -568,8 +584,7 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP: case ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP: { - TimerData * timer = &g_model.timers[k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)]; - timer->countdownBeep = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT-1, attr, event); + editTimerCountdown(k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0), y, attr, event); break; } From fcb3f3de4c6333187ef604fc39d01286e00cb70c Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 26 Jan 2021 13:24:25 +0100 Subject: [PATCH 119/300] Fix --- radio/src/gui/128x64/model_setup.cpp | 78 +++++++++++++++------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index ec199df66..b2c39b63b 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -307,42 +307,6 @@ void onBluetoothConnectMenu(const char * result) } #endif -void editTimerMode(int timerIdx, coord_t y, LcdFlags attr, event_t event) -{ - TimerData & timer = g_model.timers[timerIdx]; - drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1); - drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer.mode, menuHorizontalPosition==0 ? attr : 0); - drawTimer(MODEL_SETUP_2ND_COLUMN + 5 * FW - 2 + 5 * FWNUM + 1, y, timer.start, RIGHT | (menuHorizontalPosition == 1 ? attr : 0),menuHorizontalPosition == 2 ? attr : 0); - if (attr && s_editMode>0) { - div_t qr = div(timer.start, 60); - switch (menuHorizontalPosition) { - case 0: - { - swsrc_t timerMode = timer.mode; - if (timerMode < 0) timerMode -= TMRMODE_COUNT-1; - CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); - if (timerMode < 0) timerMode += TMRMODE_COUNT-1; - timer.mode = timerMode; -#if defined(AUTOSWITCH) - if (s_editMode>0) { - swsrc_t val = timer.mode - (TMRMODE_COUNT-1); - swsrc_t switchVal = checkIncDecMovedSwitch(val); - if (val != switchVal) { - timer.mode = switchVal + (TMRMODE_COUNT-1); - storageDirty(EE_MODEL); - } - } -#endif - break; - } - case 1: - qr.quot = checkIncDec(event, qr.quot, 0, 1439, EE_MODEL | NO_INCDEC_MARKS); // 23h59 - timer.start = qr.rem + qr.quot*60; - break; - } - } -} - void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) { TimerData & timer = g_model.timers[timerIdx]; @@ -558,7 +522,47 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_TIMER2: case ITEM_MODEL_SETUP_TIMER3: { - editTimerMode(k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0), y, attr, event); + unsigned int timerIdx = (k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)); + TimerData * timer = &g_model.timers[timerIdx]; + drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1); + drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0); + drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, RIGHT | (menuHorizontalPosition==1 ? attr : 0), menuHorizontalPosition==2 ? attr : 0); + if (attr && s_editMode > 0) { + div_t qr = div(timer->start, 60); + switch (menuHorizontalPosition) { + case 0: + { + swsrc_t timerMode = timer->mode; + if (timerMode < 0) + timerMode -= TMRMODE_COUNT-1; + CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); + if (timerMode < 0) + timerMode += TMRMODE_COUNT-1; + timer->mode = timerMode; +#if defined(AUTOSWITCH) + if (s_editMode>0) { + int8_t val = timer->mode - (TMRMODE_COUNT-1); + int8_t switchVal = checkIncDecMovedSwitch(val); + if (val != switchVal) { + timer->mode = switchVal + (TMRMODE_COUNT-1); + storageDirty(EE_MODEL); + } + } +#endif + break; + } + case 1: + CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 539); // 8:59 + timer->start = qr.rem + qr.quot*60; + break; + case 2: + qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; + timer->start -= qr.rem; + if ((int16_t)timer->start < 0) timer->start=0; + if ((int16_t)timer->start > 5999) timer->start=32399; // 8:59:59 + break; + } + } break; } From a908efffa03763b4a887b5defb398cf3937aa75f Mon Sep 17 00:00:00 2001 From: Kilrah Date: Tue, 26 Jan 2021 13:31:47 +0100 Subject: [PATCH 120/300] Fix 30s value --- companion/src/modeledit/setup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 093c8acf7..d1756aa48 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -78,7 +78,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui->countdownStart->addItem("5s", 1); ui->countdownStart->addItem("10s", 0); ui->countdownStart->addItem("20s", -1); - ui->countdownStart->addItem("30s", 2); + ui->countdownStart->addItem("30s", -2); disableMouseScrolling(); QWidget::setTabOrder(prevFocus, ui->name); From b5d1d4d57924d89fa78e3592f6aa0c672bded274 Mon Sep 17 00:00:00 2001 From: LapinFou Date: Tue, 26 Jan 2021 13:32:01 +0100 Subject: [PATCH 121/300] Updated French Translation --- companion/src/translations/companion_fr.ts | 54 +++++++++++----------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index d052a6e2c..89a6734e2 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -1391,22 +1391,22 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Diff - + Expo - + Func - + Fonc Curve - Courbe + Courbe @@ -1681,7 +1681,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import Set %1 - + Activer %1 @@ -3078,7 +3078,7 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Jumper T-Lite - + @@ -3090,12 +3090,12 @@ Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont Radiomaster T8 - + Allow bind using bind key - + Autoriser l'appairage avec le bouton"bind" @@ -4180,12 +4180,12 @@ Communs à tous les modèles d'une même EEPROM. Store calib. and hw settings in selected profile - Sauver étalonnage et paramètres hw dans profil + Sauver paramètres Radio dans profil Retrieve calib. and hw settings from profile - Lire étalonnage et paramètres hw du profil + Lire paramètres Radio depuis profil @@ -8094,7 +8094,7 @@ Mixage actif par défaut si non-renseigné. THR - GAZ + GAZ @@ -10662,7 +10662,7 @@ E Raw (-) - Chiffres bruts (-) + Chiffres bruts (-) @@ -10808,77 +10808,77 @@ E Custom - Prédéfini + Prédéfini Calculated - Calculé + Calculé Add - Addition + Addition Average - Moyenne + Moyenne Minimum - + Maximum - + Multiply - Multiplication + Multiplication Totalize - Totalisation + Totalisation Cell - Element LiPo + Element LiPo Consumption - Consommation + Consommation Distance - + Lowest - Min + Min Cell %1 - Elém. %1 + Elément %1 Highest - Max + Max Delta - + From f6c4da918ad8e7dad7469fc8d7d047edd7f7d746 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 26 Jan 2021 15:46:18 +0100 Subject: [PATCH 122/300] Enable compilation on servers --- companion/src/firmwares/opentx/opentxinterface.cpp | 2 +- radio/util/build-firmware.py | 5 +++++ radio/util/fwoptions.py | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index a9b3f69e9..1190062c5 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -1307,7 +1307,7 @@ void registerOpenTxFirmwares() firmware->addOption("lua", Firmware::tr("Enable Lua custom scripts screen")); addOpenTxFontOptions(firmware); registerOpenTxFirmware(firmware); - addOpenTxRfOptions(firmware, FLEX + AFHDS3); + addOpenTxRfOptions(firmware, FLEX); /* Jumper T16 board */ firmware = new OpenTxFirmware("opentx-t16", Firmware::tr("Jumper T16 / T16+ / T16 Pro"), BOARD_JUMPER_T16); diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index b7132d510..5d577092c 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -138,6 +138,11 @@ def main(): cmake_options["PCB"] = "X12S" firmware_options = options_horus_x12s maxsize = 2 * 1024 * 1024 + elif board_name == "tlite": + cmake_options["PCB"] = "X7" + cmake_options["PCBREV"] = "TLITE" + firmware_options = options_jumper_tlite + maxsize = 65536 * 8 elif board_name == "t12": cmake_options["PCB"] = "X7" cmake_options["PCBREV"] = "T12" diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index 7b2a01edd..4ce915426 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -207,6 +207,18 @@ options_horus_x10express = { "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), } +options_jumper_tlite = { + "noheli": ("HELI", "NO", "YES"), + "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), + "lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"), + "nogvars": ("GVARS", "NO", "YES"), + "faimode": ("FAI", "YES", None), + "faichoice": ("FAI", "CHOICE", None), + "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), + "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), +} + + options_jumper_t12 = { "noheli": ("HELI", "NO", "YES"), "ppmus": ("PPM_UNIT", "US", "PERCENT_PREC1"), From 20e8c5ac8c802ffe82a848642b3c185ef3e5c140 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 27 Jan 2021 18:57:47 +0100 Subject: [PATCH 123/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5e7672e1f..c4015a865 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2117,3 +2117,4 @@ Aldo Visentin Greg Wilson Mark Sutton Lukas Kana +Peter Newman From 51049e89761b4653736f856631a73aa718e71313 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 27 Jan 2021 18:58:10 +0100 Subject: [PATCH 124/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c4015a865..47335f694 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2118,3 +2118,4 @@ Greg Wilson Mark Sutton Lukas Kana Peter Newman +Kevin Cave From 582f84435ff01959b6decea18372b21f4185141a Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 28 Jan 2021 08:02:41 +0100 Subject: [PATCH 125/300] Display only on debug on capable radios --- radio/src/gui/480x272/radio_hardware.cpp | 2 +- radio/src/telemetry/crossfire.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp index 419fedbf5..8e63a79c6 100644 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ b/radio/src/gui/480x272/radio_hardware.cpp @@ -126,7 +126,7 @@ void onHardwareAntennaSwitchConfirm(const char * result) #define EXTERNAL_ANTENNA_ROW #endif -#if (defined(CROSSFIRE) || defined(GHOST)) +#if (defined(CROSSFIRE) || defined(GHOST)) && (SPORT_MAX_BAUDRATE < 400000 || defined(DEBUG)) #define MAX_BAUDRATE_ROW 0 #else #define MAX_BAUDRATE_ROW HIDDEN_ROW diff --git a/radio/src/telemetry/crossfire.h b/radio/src/telemetry/crossfire.h index 9855b78f4..5e7d59c78 100644 --- a/radio/src/telemetry/crossfire.h +++ b/radio/src/telemetry/crossfire.h @@ -102,7 +102,7 @@ const uint8_t CROSSFIRE_PERIODS[] = { 4, 16, }; -#if SPORT_MAX_BAUDRATE < 400000 +#if SPORT_MAX_BAUDRATE < 400000 || defined(DEBUG) #define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate] #define CROSSFIRE_PERIOD CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate] #else From 3a2a51e35365376788c1f4db5e7f1192ed9cc89a Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 28 Jan 2021 08:05:33 +0100 Subject: [PATCH 126/300] b&w too --- radio/src/gui/common/stdlcd/radio_hardware.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index cd4cfb905..7f38f692e 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -284,7 +284,7 @@ enum { #define TX_CAPACITY_MEASUREMENT_ROWS #endif -#if (defined(CROSSFIRE) || defined(GHOST)) && SPORT_MAX_BAUDRATE < 400000 +#if (defined(CROSSFIRE) || defined(GHOST)) && (SPORT_MAX_BAUDRATE < 400000 || defined(DEBUG)) #define MAX_BAUD_ROWS 0, #else #define MAX_BAUD_ROWS From a933903e6b86310e1795acba6e8fa830aaea308b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 28 Jan 2021 09:31:17 +0100 Subject: [PATCH 127/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 47335f694..a47100b3b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2119,3 +2119,4 @@ Mark Sutton Lukas Kana Peter Newman Kevin Cave +Arold Reinders From a706622d994b95e5968331e354cea0be4fb1fbb7 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 29 Jan 2021 14:55:57 +0100 Subject: [PATCH 128/300] Fix multi per channel failsafe --- radio/src/pulses/multi.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/pulses/multi.cpp b/radio/src/pulses/multi.cpp index dcc7c7f9c..6c6471c9c 100644 --- a/radio/src/pulses/multi.cpp +++ b/radio/src/pulses/multi.cpp @@ -108,15 +108,15 @@ static void sendFailsafeChannels(uint8_t moduleIdx) int16_t failsafeValue = g_model.failsafeChannels[i]; int pulseValue; - if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_HOLD) { + if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_HOLD || failsafeValue == FAILSAFE_CHANNEL_HOLD) { pulseValue = 2047; } - else if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_NOPULSES) { + else if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_NOPULSES || failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { pulseValue = 0; } else { failsafeValue += 2 * PPM_CH_CENTER(g_model.moduleData[moduleIdx].channelsStart + i) - 2 * PPM_CENTER; - pulseValue = limit(1, (failsafeValue * 800 / 1000) + 1024, 2047); + pulseValue = limit(1, (failsafeValue * 800 / 1000) + 1024, 2046); } bits |= pulseValue << bitsavailable; From 452f7b6c6a3c108412d228e316c81b56e4f73531 Mon Sep 17 00:00:00 2001 From: olliw42 Date: Sun, 31 Jan 2021 07:51:41 +0100 Subject: [PATCH 129/300] remove superfluous trainerSbusFifo in aux_serial_driver & horus/trainer_driver --- radio/src/targets/common/arm/stm32/aux_serial_driver.cpp | 6 +----- radio/src/targets/horus/trainer_driver.cpp | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index 965ae0daf..9cc558b51 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -21,10 +21,6 @@ #include "opentx.h" #include "targets/horus/board.h" -#if defined(SBUS) -extern Fifo trainerSbusFifo; -#endif - #if defined(AUX_SERIAL) uint8_t auxSerialMode = UART_MODE_COUNT; // Prevent debug output before port is setup Fifo auxSerialTxFifo; @@ -416,4 +412,4 @@ extern "C" void AUX2_SERIAL_USART_IRQHandler(void) } } #endif // SIMU -#endif // AUX2_SERIAL \ No newline at end of file +#endif // AUX2_SERIAL diff --git a/radio/src/targets/horus/trainer_driver.cpp b/radio/src/targets/horus/trainer_driver.cpp index ce649c688..38d25cb45 100644 --- a/radio/src/targets/horus/trainer_driver.cpp +++ b/radio/src/targets/horus/trainer_driver.cpp @@ -20,10 +20,6 @@ #include "opentx.h" -#if defined(SBUS_TRAINER) -Fifo trainerSbusFifo; -#endif - void trainerSendNextFrame(); void init_trainer_ppm() @@ -174,4 +170,4 @@ int sbusGetByte(uint8_t * byte) default: return false; } -} \ No newline at end of file +} From 68e0d130d0116f2e2d09fadd957d55eba4455545 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 31 Jan 2021 09:00:32 +0100 Subject: [PATCH 130/300] Fix some trainer modes missing on Horus --- radio/src/targets/horus/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 2a7f14ec5..6f64dae51 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -21,6 +21,7 @@ set(RTC_BACKUP_RAM YES) set(PPM_LIMITS_SYMETRICAL YES) set(USB_SERIAL ON CACHE BOOL "Enable USB serial (CDC)") set(ROTARY_ENCODER YES) +set(EXTERNAL_MODULE YES) # for size report script set(CPU_TYPE_FULL STM32F429xI) @@ -173,6 +174,10 @@ if(INTERNAL_MODULE_MULTI) add_definitions(-DINTERNAL_MODULE_MULTI) endif() +if(EXTERNAL_MODULE) + add_definitions(-DHARDWARE_EXTERNAL_MODULE) +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) From 5ac380360dcda0d9b9c0bef4930cb2554f69c68b Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 29 Jan 2021 16:54:45 +1100 Subject: [PATCH 131/300] Sensor ui refactoring --- companion/src/firmwares/CMakeLists.txt | 1 + companion/src/firmwares/helpersdata.h | 53 +++++ companion/src/firmwares/sensordata.cpp | 175 +++++++++++++---- companion/src/firmwares/sensordata.h | 28 ++- companion/src/modeledit/telemetry.cpp | 206 ++++++++------------ companion/src/modeledit/telemetry.h | 9 +- companion/src/modeledit/telemetry_sensor.ui | 198 +------------------ companion/src/multimodelprinter.cpp | 2 +- companion/src/shared/autocheckbox.h | 5 + companion/src/shared/autolineedit.h | 7 +- 10 files changed, 319 insertions(+), 365 deletions(-) create mode 100644 companion/src/firmwares/helpersdata.h diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index e50d9241b..bf05bc44d 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -30,6 +30,7 @@ string(REPLACE ".cpp" ".h" firmwares_HDRS "${firmwares_SRCS}") list(APPEND firmwares_HDRS eepromimportexport.h moduledata.h + helpersdata.h ) set(firmwares_QT diff --git a/companion/src/firmwares/helpersdata.h b/companion/src/firmwares/helpersdata.h new file mode 100644 index 000000000..850bcb8a7 --- /dev/null +++ b/companion/src/firmwares/helpersdata.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _HELPERS_DATA_H_ +#define _HELPERS_DATA_H_ + +#include + +class FieldRange +{ + Q_DECLARE_TR_FUNCTIONS(FieldRange) + + public: + FieldRange(): + decimals(0), + min(0.0), + max(0.0), + step(1.0), + offset(0.0), + prefix(""), + unit("") + { + } + + float getValue(int value) { return float(value) * step; } + + int decimals; + double min; + double max; + double step; + double offset; + QString prefix; + QString unit; +}; + +#endif // _HELPERS_DATA_H_ diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index b5cce1809..f9660f1db 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -57,17 +57,65 @@ bool SensorData::isEmpty() const return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); } -constexpr int SENSOR_ISCONFIGURABLE { 1 << 1 }; -constexpr int SENSOR_PRINT_GPS { 1 << 2 }; -constexpr int SENSOR_PRINT_CELLS { 1 << 3 }; -constexpr int SENSOR_PRINT_CONSUMPTION { 1 << 4 }; -constexpr int SENSOR_PRINT_RATIO { 1 << 5 }; -constexpr int SENSOR_PRINT_TOTALIZE { 1 << 6 }; -constexpr int SENSOR_PRINT_SOURCES_12 { 1 << 7 }; -constexpr int SENSOR_PRINT_SOURCES_34 { 1 << 8 }; -constexpr char FMT_LABEL[] { "%1: " }; -constexpr char FMT_VALUE[] { "%1 " }; -constexpr char FMT_LABEL_VALUE[] { "%1: %2 " }; +QString SensorData::typeToString() const +{ + return typeToString(type); +} + +QString SensorData::formulaToString() const +{ + return formulaToString(formula); +} + +QString SensorData::cellIndexToString() const +{ + return cellIndexToString(index); +} + +QString SensorData::unitToString() const +{ + return unitToString(unit); +} + +QString SensorData::precToString() const +{ + return precToString(prec); +} + +int SensorData::getMask() const +{ + int mask = 0; + + if (type == TELEM_TYPE_CALCULATED) { + if (formula < TELEM_FORMULA_CELL) + mask |= SENSOR_ISCONFIGURABLE | SENSOR_HAS_POSITIVE; + if (formula == TELEM_FORMULA_DIST) + mask |= SENSOR_HAS_GPS; + if (formula == TELEM_FORMULA_CELL) + mask |= SENSOR_HAS_CELLS; + if (formula == TELEM_FORMULA_CONSUMPTION) + mask |= SENSOR_HAS_CONSUMPTION; + if (formula <= TELEM_FORMULA_MULTIPLY) + mask |= SENSOR_HAS_SOURCES_12; + if (formula < TELEM_FORMULA_MULTIPLY) + mask |= SENSOR_HAS_SOURCES_34; + if (formula == TELEM_FORMULA_TOTALIZE) + mask |= SENSOR_HAS_TOTALIZE; + } + else { + if (unit < UNIT_FIRST_VIRTUAL) + mask |= (SENSOR_ISCONFIGURABLE | SENSOR_HAS_RATIO | SENSOR_HAS_POSITIVE); + } + + if (mask & SENSOR_ISCONFIGURABLE && unit != UNIT_FAHRENHEIT) + mask |= SENSOR_HAS_PRECISION; + + return mask; +} + +constexpr char FMT_LABEL[] { "%1: " }; +constexpr char FMT_VALUE[] { "%1 " }; +constexpr char FMT_LABEL_VALUE[] { "%1: %2 " }; QString SensorData::paramsToString(const ModelData * model) const { @@ -75,62 +123,45 @@ QString SensorData::paramsToString(const ModelData * model) const return ""; QString str; - int mask = 0; + int mask = getMask(); if (type == TELEM_TYPE_CALCULATED) { - if (formula < TELEM_FORMULA_CELL) - mask |= SENSOR_ISCONFIGURABLE; - if (formula == TELEM_FORMULA_DIST) - mask |= SENSOR_PRINT_GPS; - if (formula == TELEM_FORMULA_CELL) - mask |= SENSOR_PRINT_CELLS; - if (formula == TELEM_FORMULA_CONSUMPTION) - mask |= SENSOR_PRINT_CONSUMPTION; - if (formula <= TELEM_FORMULA_MULTIPLY) - mask |= SENSOR_PRINT_SOURCES_12; - if (formula < TELEM_FORMULA_MULTIPLY) - mask |= SENSOR_PRINT_SOURCES_34; - if (formula == TELEM_FORMULA_TOTALIZE) - mask |= SENSOR_PRINT_TOTALIZE; - str.append(QString(FMT_LABEL_VALUE).arg(tr("Formula")).arg(formulaToString(formula))); } else { - if (unit < UNIT_FIRST_VIRTUAL) - mask |= (SENSOR_ISCONFIGURABLE | SENSOR_PRINT_RATIO); - str.append(QString(FMT_LABEL_VALUE).arg(tr("Id")).arg(QString::number(id, 16).toUpper())); str.append(QString(FMT_LABEL_VALUE).arg(tr("Instance")).arg(instance)); } - if (mask & SENSOR_PRINT_CELLS) { + if (mask & SENSOR_HAS_CELLS) { str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, source))); str.append(QString(FMT_VALUE).arg(cellIndexToString(index))); } - if (mask & SENSOR_PRINT_SOURCES_12) { + if (mask & SENSOR_HAS_SOURCES_12) { str.append(QString(FMT_LABEL).arg(tr("Sources"))); for (int i = 0; i < 4; i++) { - if (i < 2 || mask & SENSOR_PRINT_SOURCES_34) { + if (i < 2 || mask & SENSOR_HAS_SOURCES_34) { str.append(QString(FMT_VALUE).arg(sourceToString(model, sources[i]))); } } } - if (mask & SENSOR_PRINT_CONSUMPTION || mask & SENSOR_PRINT_TOTALIZE) + if (mask & SENSOR_HAS_CONSUMPTION || mask & SENSOR_HAS_TOTALIZE) str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, amps))); - if (mask & SENSOR_PRINT_GPS) { + if (mask & SENSOR_HAS_GPS) { str.append(QString(FMT_LABEL_VALUE).arg(tr("GPS")).arg(sourceToString(model, gps))); str.append(QString(FMT_LABEL_VALUE).arg(tr("Alt")).arg(sourceToString(model, alt))); } - str.append(QString(FMT_LABEL_VALUE).arg(tr("Unit")).arg(unitToString(unit))); + if (mask & SENSOR_ISCONFIGURABLE) + str.append(QString(FMT_LABEL_VALUE).arg(tr("Unit")).arg(unitToString(unit))); - if (mask & SENSOR_ISCONFIGURABLE && unit != UNIT_FAHRENHEIT) + if (mask & SENSOR_HAS_PRECISION) str.append(QString(FMT_LABEL_VALUE).arg(tr("Precision")).arg(precToString(prec))); - if (mask & SENSOR_PRINT_RATIO) { + if (mask & SENSOR_HAS_RATIO) { if (unit != UNIT_RPMS) { int precsn = prec == 0 ? 1 : pow(10, prec); str.append(QString(FMT_LABEL_VALUE).arg(tr("Ratio")).arg((float)ratio / 10)); @@ -149,12 +180,78 @@ QString SensorData::paramsToString(const ModelData * model) const if (type == TELEM_TYPE_CALCULATED) str.append(QString(FMT_LABEL_VALUE).arg(tr("Persist")).arg(boolToString(persistent))); - str.append(QString(FMT_LABEL_VALUE).arg(tr("Positive")).arg(boolToString(onlyPositive))); + if (mask & SENSOR_HAS_POSITIVE) + str.append(QString(FMT_LABEL_VALUE).arg(tr("Positive")).arg(boolToString(onlyPositive))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Log")).arg(boolToString(logs))); return str; } +FieldRange SensorData::getRatioRange() const +{ + FieldRange result; + + if (unit == SensorData::UNIT_RPMS) { + result.decimals = 0; + result.max = 30000; + result.min = 1; + result.step = 1; + } + else { + result.decimals = 1; + result.max = 30000; + result.min = 0; + result.step = 0.1; + } + + return result; +} + +FieldRange SensorData::getOffsetRange() const +{ + FieldRange result; + + if (unit == SensorData::UNIT_RPMS) { + result.decimals = 0; + result.max = 30000; + result.min = 1; + result.step = 1; + } + else { + result.decimals = prec; + result.max = 30000.0f / powf(10.0f, prec); + result.min = -result.max; + result.step = pow(0.1, prec); + } + + return result; +} + +void SensorData::formulaChanged() +{ + switch (formula) { + case TELEM_FORMULA_CELL: + prec = 2; + unit = UNIT_VOLTS; + break; + case TELEM_FORMULA_CONSUMPTION: + prec = 0; + unit = UNIT_MAH; + break; + case TELEM_FORMULA_DIST: + prec = 0; + unit = UNIT_METERS; + break; + } +} + +void SensorData::unitChanged() +{ + if (unit == UNIT_FAHRENHEIT) + prec = 0; +} + // static QString SensorData::typeToString(const int value) { diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 23d8a7fcc..0a24cd301 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -21,16 +21,30 @@ #ifndef SENSORDATA_H #define SENSORDATA_H +#include "helpersdata.h" + #include -constexpr int CPN_MAX_SENSORS = 60; -constexpr int SENSOR_LABEL_LEN = 4; +constexpr int CPN_MAX_SENSORS { 60 }; +constexpr int SENSOR_LABEL_LEN { 4 }; + +constexpr int SENSOR_ISCONFIGURABLE { 1 << 1 }; +constexpr int SENSOR_HAS_GPS { 1 << 2 }; +constexpr int SENSOR_HAS_CELLS { 1 << 3 }; +constexpr int SENSOR_HAS_CONSUMPTION { 1 << 4 }; +constexpr int SENSOR_HAS_RATIO { 1 << 5 }; +constexpr int SENSOR_HAS_TOTALIZE { 1 << 6 }; +constexpr int SENSOR_HAS_SOURCES_12 { 1 << 7 }; +constexpr int SENSOR_HAS_SOURCES_34 { 1 << 8 }; +constexpr int SENSOR_HAS_POSITIVE { 1 << 9 }; +constexpr int SENSOR_HAS_PRECISION { 1 << 10 }; class ModelData; class AbstractStaticItemModel; class PrecisionItemModel; class SensorData { + Q_DECLARE_TR_FUNCTIONS(SensorData) public: @@ -171,7 +185,17 @@ class SensorData { QString getOrigin(const ModelData* model) const; void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; + QString typeToString() const; + QString formulaToString() const; + QString cellIndexToString() const; + QString unitToString() const; + QString precToString() const; + int getMask() const; QString paramsToString(const ModelData * model) const; + FieldRange getRatioRange() const; + FieldRange getOffsetRange() const; + void formulaChanged(); + void unitChanged(); static QString sourceToString(const ModelData * model, const int index, const bool sign = false); static bool isSourceAvailable(const ModelData * model, const int index); diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 423e10e5d..8cda220a9 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -27,13 +27,13 @@ #include -constexpr char FIM_RAWSOURCE[] {"Raw Source"}; -constexpr char FIM_TELEALLSRC[] {"Tele All Source"}; -constexpr char FIM_TELEPOSSRC[] {"Tele Pos Source"}; -constexpr char FIM_SENSORTYPE[] {"Sensor.Type"}; -constexpr char FIM_SENSORFORMULA[] {"Sensor.Formula"}; +constexpr char FIM_RAWSOURCE[] {"Raw Source"}; +constexpr char FIM_TELEALLSRC[] {"Tele All Source"}; +constexpr char FIM_TELEPOSSRC[] {"Tele Pos Source"}; +constexpr char FIM_SENSORTYPE[] {"Sensor.Type"}; +constexpr char FIM_SENSORFORMULA[] {"Sensor.Formula"}; constexpr char FIM_SENSORCELLINDEX[] {"Sensor.CellIndex"}; -constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"}; +constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"}; constexpr char FIM_SENSORPRECISION[] {"Sensor.Precision"}; TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, @@ -336,9 +336,6 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, modelsUpdateCnt(0), parentLock(parentLock) { - // TODO ui definition housekeeping - // - remove all predefined combobox lists leaving to code - // - use Auto types for consistency ui->setupUi(this); FilteredItemModel * fltmdl; @@ -358,11 +355,19 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->numLabel->setToolTip(tr("Popup menu available")); ui->numLabel->setMouseTracking(true); connect(ui->numLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint))); + ui->name->setField(sensor.label, SENSOR_LABEL_LEN); + connect(ui->name, SIGNAL(currentDataChanged()), this, SLOT(on_nameDataChanged())); ui->id->setField(sensor.id, this); ui->instance->setField(sensor.instance, this); ui->type->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORTYPE)); + ui->type->setField(sensor.type); + connect(ui->type, SIGNAL(currentDataChanged(int)), this, SLOT(update())); ui->formula->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORFORMULA)); + ui->formula->setField(sensor.formula); + connect(ui->formula, SIGNAL(currentDataChanged(int)), this, SLOT(on_formulaDataChanged())); ui->unit->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORUNIT)); + ui->unit->setField(sensor.unit); + connect(ui->unit, SIGNAL(currentDataChanged(int)), this, SLOT(on_unitDataChanged())); ui->ratio->setField(sensor.ratio, this); ui->offset->setField(sensor.offset, this); ui->autoOffset->setField(sensor.autoOffset, this); @@ -390,7 +395,9 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->source3->setField(sensor.sources[2], this); ui->source4->setModel(fltmdl); ui->source4->setField(sensor.sources[3], this); - ui->prec->setField(sensor.prec, 0, 2, false, "", this); // TODO replace autoprecisioncombobox with autocombobox and filtereditemmodel + ui->prec->setModel(panelFilteredItemModels->getItemModel(FIM_SENSORPRECISION)); + ui->prec->setField(sensor.prec); + connect(ui->prec, SIGNAL(currentDataChanged(int)), this, SLOT(on_precDataChanged())); lock = false; update(); @@ -403,20 +410,13 @@ TelemetrySensorPanel::~TelemetrySensorPanel() void TelemetrySensorPanel::update() { - // TODO use bit mask - bool isConfigurable = false; - bool gpsFieldsDisplayed = false; - bool cellsFieldsDisplayed = false; - bool consFieldsDisplayed = false; - bool ratioFieldsDisplayed = false; - bool totalizeFieldsDisplayed = false; - bool sources12FieldsDisplayed = false; - bool sources34FieldsDisplayed = false; - lock = true; - ui->name->setText(sensor.label); - ui->type->setCurrentIndex(ui->type->findData(sensor.type)); - ui->unit->setCurrentIndex(ui->unit->findData(sensor.unit)); + int mask = sensor.getMask(); + + ui->name->updateValue(); + ui->type->updateValue(); + ui->formula->updateValue(); + ui->unit->updateValue(); ui->id->updateValue(); ui->instance->updateValue(); ui->ratio->updateValue(); @@ -443,13 +443,6 @@ void TelemetrySensorPanel::update() ui->origin->hide(); ui->formula->show(); ui->formula->setCurrentIndex(ui->formula->findData(sensor.formula)); - isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL); - gpsFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_DIST); - cellsFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_CELL); - consFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION); - sources12FieldsDisplayed = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY); - sources34FieldsDisplayed = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY); - totalizeFieldsDisplayed = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE); ui->source1->updateValue(); ui->source2->updateValue(); ui->source3->updateValue(); @@ -464,121 +457,78 @@ void TelemetrySensorPanel::update() ui->originLabel->setVisible(!origin.isEmpty()); ui->origin->setVisible(!origin.isEmpty()); ui->origin->setText(origin); - ui->formula->hide(); - isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL; - ratioFieldsDisplayed = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL); - if (sensor.unit == SensorData::UNIT_RPMS) { - if (ui->ratio->decimals()) { - ui->ratio->setDecimals(0); - ui->ratio->setMaximum(30000); - ui->ratio->setMinimum(1); - ui->ratio->setSingleStep(1); - ui->offset->setDecimals(0); - ui->offset->setMaximum(30000); - ui->offset->setMinimum(1); - ui->offset->setSingleStep(1); - } - } - else { - if (!ui->ratio->decimals()) { - ui->ratio->setDecimals(1); - ui->ratio->setMaximum(3000); - ui->ratio->setMinimum(0); - ui->ratio->setSingleStep(0.1); - } - if (ui->offset->decimals() != (int)sensor.prec) { - ui->offset->setDecimals(sensor.prec); - ui->offset->setMaximum(30000.0f / powf(10.0f, sensor.prec)); - ui->offset->setMinimum(-ui->offset->maximum()); - ui->offset->setSingleStep(pow(0.1, sensor.prec)); - } - } + FieldRange rng = sensor.getRatioRange(); + ui->ratio->setDecimals(rng.decimals); + ui->ratio->setMaximum(rng.max); + ui->ratio->setMinimum(rng.min); + ui->ratio->setSingleStep(rng.step); + rng = sensor.getOffsetRange(); + ui->offset->setDecimals(rng.decimals); + ui->offset->setMaximum(rng.max); + ui->offset->setMinimum(rng.min); + ui->offset->setSingleStep(rng.step); } - ui->ratioLabel->setVisible(ratioFieldsDisplayed && sensor.unit != SensorData::UNIT_RPMS); - ui->bladesLabel->setVisible(sensor.unit == SensorData::UNIT_RPMS); - ui->ratio->setVisible(ratioFieldsDisplayed); - ui->offsetLabel->setVisible(ratioFieldsDisplayed && sensor.unit != SensorData::UNIT_RPMS); - ui->multiplierLabel->setVisible(sensor.unit == SensorData::UNIT_RPMS); - ui->offset->setVisible(ratioFieldsDisplayed); - ui->precLabel->setVisible(isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT); - ui->prec->setVisible(isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT); - ui->unit->setVisible((sensor.type == SensorData::TELEM_TYPE_CALCULATED && (sensor.formula == SensorData::TELEM_FORMULA_DIST)) || isConfigurable); - ui->gpsSensorLabel->setVisible(gpsFieldsDisplayed); - ui->gpsSensor->setVisible(gpsFieldsDisplayed); - ui->altSensorLabel->setVisible(gpsFieldsDisplayed); - ui->altSensor->setVisible(gpsFieldsDisplayed); - ui->ampsSensorLabel->setVisible(consFieldsDisplayed || totalizeFieldsDisplayed); - ui->ampsSensor->setVisible(consFieldsDisplayed || totalizeFieldsDisplayed); - ui->cellsSensorLabel->setVisible(cellsFieldsDisplayed); - ui->cellsSensor->setVisible(cellsFieldsDisplayed); - ui->cellsIndex->setVisible(cellsFieldsDisplayed); - ui->source1->setVisible(sources12FieldsDisplayed); - ui->source2->setVisible(sources12FieldsDisplayed); - ui->source3->setVisible(sources34FieldsDisplayed); - ui->source4->setVisible(sources34FieldsDisplayed); - ui->autoOffset->setVisible(sensor.unit != SensorData::UNIT_RPMS && isConfigurable); - ui->filter->setVisible(isConfigurable); - ui->persistent->setVisible(sensor.type == SensorData::TELEM_TYPE_CALCULATED); + ui->ratioLabel->setVisible(mask & SENSOR_HAS_RATIO && sensor.unit != SensorData::UNIT_RPMS); + ui->bladesLabel->setVisible(mask & SENSOR_HAS_RATIO && sensor.unit == SensorData::UNIT_RPMS); + ui->ratio->setVisible(mask & SENSOR_HAS_RATIO); + + ui->offsetLabel->setVisible(mask & SENSOR_HAS_RATIO && sensor.unit != SensorData::UNIT_RPMS); + ui->multiplierLabel->setVisible(mask & SENSOR_HAS_RATIO && sensor.unit == SensorData::UNIT_RPMS); + ui->offset->setVisible(mask & SENSOR_HAS_RATIO); + + ui->precLabel->setVisible(mask & SENSOR_HAS_PRECISION); + ui->prec->setVisible(mask & SENSOR_HAS_PRECISION); + + ui->unit->setVisible(mask & SENSOR_ISCONFIGURABLE); + + ui->gpsSensorLabel->setVisible(mask & SENSOR_HAS_GPS); + ui->gpsSensor->setVisible(mask & SENSOR_HAS_GPS); + + ui->altSensorLabel->setVisible(mask & SENSOR_HAS_GPS); + ui->altSensor->setVisible(mask & SENSOR_HAS_GPS); + + ui->ampsSensorLabel->setVisible(mask & SENSOR_HAS_CONSUMPTION || mask & SENSOR_HAS_TOTALIZE); + ui->ampsSensor->setVisible(mask & SENSOR_HAS_CONSUMPTION || mask & SENSOR_HAS_TOTALIZE); + + ui->cellsSensorLabel->setVisible(mask & SENSOR_HAS_CELLS); + ui->cellsSensor->setVisible(mask & SENSOR_HAS_CELLS); + ui->cellsIndex->setVisible(mask & SENSOR_HAS_CELLS); + + ui->source1->setVisible(mask & SENSOR_HAS_SOURCES_12); + ui->source2->setVisible(mask & SENSOR_HAS_SOURCES_12); + ui->source3->setVisible(mask & SENSOR_HAS_SOURCES_34); + ui->source4->setVisible(mask & SENSOR_HAS_SOURCES_34); + + ui->autoOffset->setEnabled(mask & SENSOR_HAS_RATIO && sensor.unit != SensorData::UNIT_RPMS); + ui->onlyPositive->setEnabled(mask & SENSOR_HAS_POSITIVE); + ui->filter->setEnabled(mask & SENSOR_ISCONFIGURABLE); + ui->persistent->setEnabled(sensor.type == SensorData::TELEM_TYPE_CALCULATED); lock = false; } -void TelemetrySensorPanel::on_name_editingFinished() +void TelemetrySensorPanel::on_nameDataChanged() { - if (!isLocked() && (sensor.label != ui->name->text().toLatin1())) { - strcpy(sensor.label, ui->name->text().toLatin1()); - emit dataModified(); - } + emit dataModified(); } -void TelemetrySensorPanel::on_type_currentIndexChanged(int index) +void TelemetrySensorPanel::on_formulaDataChanged() { - if (!isLocked()) { - sensor.type = ui->type->itemData(index).toInt(); - update(); - emit modified(); - } + sensor.formulaChanged(); + update(); } -void TelemetrySensorPanel::on_formula_currentIndexChanged(int index) +void TelemetrySensorPanel::on_unitDataChanged() { - if (!isLocked()) { - sensor.formula = ui->formula->itemData(index).toInt(); - if (sensor.formula == SensorData::TELEM_FORMULA_CELL) { - sensor.prec = 2; - sensor.unit = SensorData::UNIT_VOLTS; - } - else if (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION) { - sensor.prec = 0; - sensor.unit = SensorData::UNIT_MAH; - } - else if (sensor.formula == SensorData::TELEM_FORMULA_DIST) { - sensor.prec = 0; - sensor.unit = SensorData::UNIT_METERS; - } - emit dataModified(); - } + sensor.unitChanged(); + update(); } -void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) +void TelemetrySensorPanel::on_precDataChanged() { - if (!isLocked()) { - sensor.unit = ui->unit->itemData(index).toInt(); - if (sensor.unit == SensorData::UNIT_FAHRENHEIT) { - sensor.prec = 0; - } - update(); - emit modified(); - } -} - -void TelemetrySensorPanel::on_prec_valueChanged() -{ - if (!isLocked()) { - update(); - } + update(); } void TelemetrySensorPanel::on_customContextMenuRequested(QPoint pos) diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 224f88020..2ed0ab2e8 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -94,11 +94,6 @@ class TelemetrySensorPanel: public ModelPanel void moveDownSensor(int index); protected slots: - void on_name_editingFinished(); - void on_type_currentIndexChanged(int index); - void on_formula_currentIndexChanged(int index); - void on_unit_currentIndexChanged(int index); - void on_prec_valueChanged(); void on_customContextMenuRequested(QPoint pos); bool hasClipboardData(QByteArray * data = nullptr) const; void cmCopy(); @@ -112,6 +107,10 @@ class TelemetrySensorPanel: public ModelPanel void cmMoveDown(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); + void on_nameDataChanged(); + void on_formulaDataChanged(); + void on_unitDataChanged(); + void on_precDataChanged(); private: Ui::TelemetrySensor * ui; diff --git a/companion/src/modeledit/telemetry_sensor.ui b/companion/src/modeledit/telemetry_sensor.ui index 12af2cf06..e70cc7aa2 100644 --- a/companion/src/modeledit/telemetry_sensor.ui +++ b/companion/src/modeledit/telemetry_sensor.ui @@ -40,7 +40,7 @@ - + 0 @@ -59,23 +59,13 @@ - + 0 0 - - - Custom - - - - - Calculated - - @@ -147,7 +137,7 @@ - + 0 @@ -175,58 +165,13 @@ - + 0 0 - - - Add - - - - - Average - - - - - Min - - - - - Max - - - - - Multiply - - - - - Totalize - - - - - Cell - - - - - Consumption - - - - - Dist - - @@ -412,138 +357,13 @@ - + 0 0 - - - Raw (-) - - - - - V - - - - - A - - - - - mA - - - - - kt - - - - - m/s - - - - - ft/s - - - - - km/h - - - - - mph - - - - - m - - - - - ft - - - - - °C - - - - - °F - - - - - % - - - - - mAh - - - - - W - - - - - mW - - - - - dBm - - - - - RPM - - - - - g - - - - - ° - - - - - Rad - - - - - mL - - - - - US fl.Oz. - - - - - ml/min - - @@ -560,7 +380,7 @@ - + @@ -720,9 +540,9 @@

autohexspinbox.h
- AutoPrecisionComboBox - QComboBox -
autoprecisioncombobox.h
+ AutoLineEdit + QLineEdit +
autolineedit.h
diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 9686dd944..17cb30be4 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -836,7 +836,7 @@ QString MultiModelPrinter::printSensors() columns.appendCellStart(20, true); COMPARE(model->sensorData[i].nameToString(i)); columns.appendCellEnd(true); - COMPARECELLWIDTH(model->sensorData[i].isAvailable() ? SensorData::typeToString(model->sensorData[i].type) : "", 15); + COMPARECELLWIDTH(model->sensorData[i].isAvailable() ? model->sensorData[i].typeToString() : "", 15); COMPARECELLWIDTH(model->sensorData[i].paramsToString(model), 65); columns.appendRowEnd(); } diff --git a/companion/src/shared/autocheckbox.h b/companion/src/shared/autocheckbox.h index df99278a2..d65753168 100644 --- a/companion/src/shared/autocheckbox.h +++ b/companion/src/shared/autocheckbox.h @@ -45,6 +45,11 @@ class AutoCheckBox: public QCheckBox updateValue(); } + void setEnabled(bool enabled) + { + QCheckBox::setEnabled(enabled); + } + void updateValue() { lock = true; diff --git a/companion/src/shared/autolineedit.h b/companion/src/shared/autolineedit.h index 36d848972..e8567c803 100644 --- a/companion/src/shared/autolineedit.h +++ b/companion/src/shared/autolineedit.h @@ -71,10 +71,13 @@ class AutoLineEdit: public QLineEdit lock = false; } + signals: + void currentDataChanged(); + protected slots: void onEdited() { - if (lock) + if ((panel && panel->lock) || lock) return; if (field) @@ -86,6 +89,8 @@ class AutoLineEdit: public QLineEdit if (panel) emit panel->modified(); + + emit currentDataChanged(); } protected: From 9a7c7cd303894f512e5c40411a57e9f886daf3c6 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sat, 30 Jan 2021 09:14:02 +1100 Subject: [PATCH 132/300] Another ToString function --- companion/src/firmwares/sensordata.cpp | 23 +++++++++++++++++------ companion/src/firmwares/sensordata.h | 2 ++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index f9660f1db..a87dc6d21 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -57,6 +57,11 @@ bool SensorData::isEmpty() const return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0); } +QString SensorData::idToString() const +{ + return idToString(id); +} + QString SensorData::typeToString() const { return typeToString(type); @@ -126,16 +131,16 @@ QString SensorData::paramsToString(const ModelData * model) const int mask = getMask(); if (type == TELEM_TYPE_CALCULATED) { - str.append(QString(FMT_LABEL_VALUE).arg(tr("Formula")).arg(formulaToString(formula))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Formula")).arg(formulaToString())); } else { - str.append(QString(FMT_LABEL_VALUE).arg(tr("Id")).arg(QString::number(id, 16).toUpper())); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Id")).arg(idToString())); str.append(QString(FMT_LABEL_VALUE).arg(tr("Instance")).arg(instance)); } if (mask & SENSOR_HAS_CELLS) { str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, source))); - str.append(QString(FMT_VALUE).arg(cellIndexToString(index))); + str.append(QString(FMT_VALUE).arg(cellIndexToString())); } if (mask & SENSOR_HAS_SOURCES_12) { @@ -156,10 +161,10 @@ QString SensorData::paramsToString(const ModelData * model) const } if (mask & SENSOR_ISCONFIGURABLE) - str.append(QString(FMT_LABEL_VALUE).arg(tr("Unit")).arg(unitToString(unit))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Unit")).arg(unitToString())); if (mask & SENSOR_HAS_PRECISION) - str.append(QString(FMT_LABEL_VALUE).arg(tr("Precision")).arg(precToString(prec))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Precision")).arg(precToString())); if (mask & SENSOR_HAS_RATIO) { if (unit != UNIT_RPMS) { @@ -170,7 +175,7 @@ QString SensorData::paramsToString(const ModelData * model) const } else { str.append(QString(FMT_LABEL_VALUE).arg(tr("Blades")).arg(ratio)); // TODO refactor to dedicated RPMS field - str.append(QString(FMT_LABEL_VALUE).arg(tr("Multi")).arg(offset)); // TODO refactor to dedicated RPMS field + str.append(QString(FMT_LABEL_VALUE).arg(tr("Multiplier")).arg(offset)); // TODO refactor to dedicated RPMS field } } @@ -252,6 +257,12 @@ void SensorData::unitChanged() prec = 0; } +// static +QString SensorData::idToString(const int value) +{ + return QString::number(value, 16).toUpper(); +} + // static QString SensorData::typeToString(const int value) { diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 0a24cd301..5870d9649 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -185,6 +185,7 @@ class SensorData { QString getOrigin(const ModelData* model) const; void clear() { memset(this, 0, sizeof(SensorData)); } bool isEmpty() const; + QString idToString() const; QString typeToString() const; QString formulaToString() const; QString cellIndexToString() const; @@ -199,6 +200,7 @@ class SensorData { static QString sourceToString(const ModelData * model, const int index, const bool sign = false); static bool isSourceAvailable(const ModelData * model, const int index); + static QString idToString(const int value); static QString typeToString(const int value); static QString formulaToString(const int value); static QString cellIndexToString(const int value); From c35301ec053921fbd86d6c09cc00924f5db2e9ef Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 31 Jan 2021 07:11:53 +1100 Subject: [PATCH 133/300] Modernise header guards --- companion/src/firmwares/helpersdata.h | 5 +---- companion/src/firmwares/sensordata.h | 5 +---- companion/src/modeledit/telemetry.h | 5 +---- companion/src/modelprinter.h | 5 +---- companion/src/shared/autobitsetcheckbox.h | 5 +---- companion/src/shared/autocheckbox.h | 5 +---- companion/src/shared/autocombobox.h | 5 +---- companion/src/shared/autodoublespinbox.h | 5 +---- companion/src/shared/autohexspinbox.h | 5 +---- companion/src/shared/autolineedit.h | 5 +---- companion/src/shared/autoprecisioncombobox.h | 5 +---- companion/src/shared/genericpanel.h | 5 +---- companion/src/shared/hexspinbox.h | 5 +---- companion/src/shared/verticalscrollarea.h | 6 +----- 14 files changed, 14 insertions(+), 57 deletions(-) diff --git a/companion/src/firmwares/helpersdata.h b/companion/src/firmwares/helpersdata.h index 850bcb8a7..29273e5f0 100644 --- a/companion/src/firmwares/helpersdata.h +++ b/companion/src/firmwares/helpersdata.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _HELPERS_DATA_H_ -#define _HELPERS_DATA_H_ +#pragma once #include @@ -49,5 +48,3 @@ class FieldRange QString prefix; QString unit; }; - -#endif // _HELPERS_DATA_H_ diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 5870d9649..5e888d749 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef SENSORDATA_H -#define SENSORDATA_H +#pragma once #include "helpersdata.h" @@ -214,5 +213,3 @@ class SensorData { static AbstractStaticItemModel * unitItemModel(); static PrecisionItemModel * precisionItemModel(); }; - -#endif // SENSORDATA_H diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 2ed0ab2e8..3f04bebf6 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _TELEMETRY_H_ -#define _TELEMETRY_H_ +#pragma once #include "modeledit.h" #include "eeprominterface.h" @@ -180,5 +179,3 @@ class TelemetryPanel : public ModelPanel void connectItemModelEvents(const int id); inline bool isLocked() { return lock; } }; - -#endif // _TELEMETRY_H_ diff --git a/companion/src/modelprinter.h b/companion/src/modelprinter.h index 639eddb0e..ea2eab2d8 100644 --- a/companion/src/modelprinter.h +++ b/companion/src/modelprinter.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _MODELPRINTER_H_ -#define _MODELPRINTER_H_ +#pragma once #include #include @@ -132,5 +131,3 @@ class ModelPrinter: public QObject const ModelData & model; }; - -#endif // _MODELPRINTER_H_ diff --git a/companion/src/shared/autobitsetcheckbox.h b/companion/src/shared/autobitsetcheckbox.h index 6274e0117..ea86db956 100644 --- a/companion/src/shared/autobitsetcheckbox.h +++ b/companion/src/shared/autobitsetcheckbox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef AUTOBITSETCHECKBOX_H -#define AUTOBITSETCHECKBOX_H +#pragma once #include #include "genericpanel.h" @@ -174,5 +173,3 @@ class AutoBitsetCheckBox: public QCheckBox bool m_lock = false; GenericPanel * m_panel = nullptr; }; - -#endif // AUTOBITSETCHECKBOX_H diff --git a/companion/src/shared/autocheckbox.h b/companion/src/shared/autocheckbox.h index d65753168..f5b233fed 100644 --- a/companion/src/shared/autocheckbox.h +++ b/companion/src/shared/autocheckbox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _AUTOCHECKBOX_H_ -#define _AUTOCHECKBOX_H_ +#pragma once #include #include "modeledit/modeledit.h" @@ -75,5 +74,3 @@ class AutoCheckBox: public QCheckBox ModelPanel * panel; bool lock; }; - -#endif // _AUTOCHECKBOX_H_ diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index 158352f97..784abaefa 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _AUTOCOMBOBOX_H_ -#define _AUTOCOMBOBOX_H_ +#pragma once #include #include "genericpanel.h" @@ -137,5 +136,3 @@ class AutoComboBox: public QComboBox bool lock = false; bool hasModel = false; }; - -#endif // _AUTOCOMBOBOX_H_ diff --git a/companion/src/shared/autodoublespinbox.h b/companion/src/shared/autodoublespinbox.h index 17188243b..6560b6ea2 100644 --- a/companion/src/shared/autodoublespinbox.h +++ b/companion/src/shared/autodoublespinbox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _AUTODOUBLESPINBOX_H_ -#define _AUTODOUBLESPINBOX_H_ +#pragma once #include #include "modeledit/modeledit.h" @@ -97,5 +96,3 @@ class AutoDoubleSpinBox: public QDoubleSpinBox ModelPanel * panel; bool lock; }; - -#endif // _AUTODOUBLESPINBOX_H_ diff --git a/companion/src/shared/autohexspinbox.h b/companion/src/shared/autohexspinbox.h index 7db76d2a3..d7df46bd6 100644 --- a/companion/src/shared/autohexspinbox.h +++ b/companion/src/shared/autohexspinbox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _AUTOHEXSPINBOX_H_ -#define _AUTOHEXSPINBOX_H_ +#pragma once #include "hexspinbox.h" #include "modeledit/modeledit.h" @@ -68,5 +67,3 @@ class AutoHexSpinBox: public HexSpinBox ModelPanel * panel; bool lock; }; - -#endif // _AUTOHEXSPINBOX_H_ diff --git a/companion/src/shared/autolineedit.h b/companion/src/shared/autolineedit.h index e8567c803..0f888e6df 100644 --- a/companion/src/shared/autolineedit.h +++ b/companion/src/shared/autolineedit.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _AUTOLINEEDIT_H_ -#define _AUTOLINEEDIT_H_ +#pragma once #include #include @@ -99,5 +98,3 @@ class AutoLineEdit: public QLineEdit GenericPanel * panel; bool lock; }; - -#endif // _AUTOLINEEDIT_H_ diff --git a/companion/src/shared/autoprecisioncombobox.h b/companion/src/shared/autoprecisioncombobox.h index 4e143f286..e93059199 100644 --- a/companion/src/shared/autoprecisioncombobox.h +++ b/companion/src/shared/autoprecisioncombobox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _AUTOPRECISIONCOMBOBOX_H_ -#define _AUTOPRECISIONCOMBOBOX_H_ +#pragma once #include "genericpanel.h" @@ -253,5 +252,3 @@ class AutoPrecisionComboBox: public QComboBox QString m_suffix = ""; bool m_lock = false; }; - -#endif // _AUTOPRECISIONCOMBOBOX_H_ diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index 5351355b6..bbb9b7254 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _GENERICPANEL_H_ -#define _GENERICPANEL_H_ +#pragma once #include @@ -65,5 +64,3 @@ class GenericPanel : public QWidget void setFocusFilter(QWidget * w); void disableMouseScrolling(); }; - -#endif // _GENERICPANEL_H_ diff --git a/companion/src/shared/hexspinbox.h b/companion/src/shared/hexspinbox.h index fc404d98a..204cc8a55 100644 --- a/companion/src/shared/hexspinbox.h +++ b/companion/src/shared/hexspinbox.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _HEXSPINBOX_H_ -#define _HEXSPINBOX_H_ +#pragma once #include @@ -40,5 +39,3 @@ class HexSpinBox : public QSpinBox private: QRegExpValidator *validator; }; - -#endif // _HEXSPINBOX_H_ diff --git a/companion/src/shared/verticalscrollarea.h b/companion/src/shared/verticalscrollarea.h index 36f564dd7..d409a18ca 100644 --- a/companion/src/shared/verticalscrollarea.h +++ b/companion/src/shared/verticalscrollarea.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _VERTICALSCROLLAREA_H_ -#define _VERTICALSCROLLAREA_H_ +#pragma once #include @@ -36,6 +35,3 @@ class VerticalScrollArea : public QScrollArea private: GenericPanel * panel; }; - -#endif // _VERTICALSCROLLAREA_H_ - From 138b4fd49e56128a0fa29b9bbe02782391613a69 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 31 Jan 2021 21:12:03 +0100 Subject: [PATCH 134/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index a47100b3b..06466819f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2120,3 +2120,5 @@ Lukas Kana Peter Newman Kevin Cave Arold Reinders +Jason Plant +John Beech From c084a4a3c19d9b60277cfff763f29d407c245fa7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 31 Jan 2021 21:12:53 +0100 Subject: [PATCH 135/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 06466819f..0165efcf4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2122,3 +2122,4 @@ Kevin Cave Arold Reinders Jason Plant John Beech +Ian Parris From e0a5f268291572581612cb42955f96175cedffc3 Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 1 Feb 2021 08:04:02 +1100 Subject: [PATCH 136/300] Stop compile warnings --- companion/src/firmwares/boards.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index cfeabc918..a201d50ed 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -91,11 +91,9 @@ uint32_t Boards::getFourCC(Type board) return 0x4178746F; case BOARD_RADIOMASTER_T8: return 0x4378746F; - case BOARD_UNKNOWN: - break; + default: + return 0; } - - return 0; } int Boards::getEEpromSize(Board::Type board) @@ -130,9 +128,9 @@ int Boards::getEEpromSize(Board::Type board) case BOARD_JUMPER_T18: case BOARD_RADIOMASTER_TX16S: return 0; + default: + return 0; } - - return 0; } int Boards::getFlashSize(Type board) From 2aeb5f7ae6c5a0b24dc944b94b42f3ced6b03fa5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 1 Feb 2021 08:49:21 +0100 Subject: [PATCH 137/300] Ensure module are on after model change --- radio/src/pulses/pulses.cpp | 8 ++++++++ radio/src/pulses/pulses.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index ed56f8b61..7f0650afa 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -484,6 +484,10 @@ bool setupPulsesInternalModule() { uint8_t protocol = getRequiredProtocol(INTERNAL_MODULE); + if(protocol != PROTOCOL_CHANNELS_NONE) { + INTERNAL_MODULE_ON(); + } + heartbeat |= (HEART_TIMER_PULSES << INTERNAL_MODULE); if (moduleState[INTERNAL_MODULE].protocol != protocol) { @@ -502,6 +506,10 @@ bool setupPulsesExternalModule() { uint8_t protocol = getRequiredProtocol(EXTERNAL_MODULE); + if(protocol != PROTOCOL_CHANNELS_NONE) { + EXTERNAL_MODULE_ON(); + } + heartbeat |= (HEART_TIMER_PULSES << EXTERNAL_MODULE); if (moduleState[EXTERNAL_MODULE].protocol != protocol) { diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 84ecee015..6f37f2a60 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -353,7 +353,9 @@ inline void startPulses() setupPulsesInternalModule(); #endif +#if defined(HARDWARE_EXTERNAL_MODULE) setupPulsesExternalModule(); +#endif #if defined(HARDWARE_EXTRA_MODULE) extramodulePpmStart(); From 6c819382b4432a2d2c88dc1c420b62c9df0f370c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 2 Feb 2021 14:10:34 +0100 Subject: [PATCH 138/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0165efcf4..8d9a23ddb 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2123,3 +2123,4 @@ Arold Reinders Jason Plant John Beech Ian Parris +Danny Music From 1067d642f39780bbf7ebe645a092df7d026c7a3c Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 2 Feb 2021 16:19:17 +0100 Subject: [PATCH 139/300] Cosmetics --- radio/src/gui/128x64/model_setup.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index b2c39b63b..46b92919b 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -236,7 +236,7 @@ inline uint8_t MODULE_SUBTYPE_ROWS(int moduleIdx) } #define POT_WARN_ROWS ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0) -#define TIMER_ROWS(x) 2, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t) 1 : (uint8_t)0 +#define TIMER_ROWS(x) 2, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t)1 : (uint8_t)0 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2) #if defined(PCBSKY9X) @@ -316,7 +316,7 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 6 * FW, y, TIMER_COUNTDOWN_START(timerIdx), (menuHorizontalPosition == 1 ? attr : 0) | LEFT); lcdDrawChar(lcdLastRightPos, y, 's'); } - if (attr && s_editMode>0) { + if (attr && s_editMode > 0) { switch (menuHorizontalPosition) { case 0: CHECK_INCDEC_MODELVAR(event, timer.countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1); @@ -509,7 +509,7 @@ void menuModelSetup(event_t event) } uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS); + LcdFlags blink = ((s_editMode > 0) ? BLINK|INVERS : INVERS); LcdFlags attr = (sub == k ? blink : 0); switch (k) { @@ -540,7 +540,7 @@ void menuModelSetup(event_t event) timerMode += TMRMODE_COUNT-1; timer->mode = timerMode; #if defined(AUTOSWITCH) - if (s_editMode>0) { + if (s_editMode > 0) { int8_t val = timer->mode - (TMRMODE_COUNT-1); int8_t switchVal = checkIncDecMovedSwitch(val); if (val != switchVal) { From 0970b44e8a83d1aac7198a43ae0cbd5ec3f49af5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 2 Feb 2021 16:21:14 +0100 Subject: [PATCH 140/300] Cosmetics --- radio/src/gui/128x64/model_setup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 46b92919b..6208ad218 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -311,7 +311,7 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) { TimerData & timer = g_model.timers[timerIdx]; lcdDrawTextAlignedLeft(y, STR_BEEPCOUNTDOWN); - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VBEEPCOUNTDOWN, timer.countdownBeep, (menuHorizontalPosition==0 ? attr : 0)); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VBEEPCOUNTDOWN, timer.countdownBeep, (menuHorizontalPosition == 0 ? attr : 0)); if (timer.countdownBeep != COUNTDOWN_SILENT) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 6 * FW, y, TIMER_COUNTDOWN_START(timerIdx), (menuHorizontalPosition == 1 ? attr : 0) | LEFT); lcdDrawChar(lcdLastRightPos, y, 's'); From a11198bc7282114b7a1be8ebe365f67d8f593145 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 3 Feb 2021 08:26:09 +0100 Subject: [PATCH 141/300] Fix compilation --- radio/src/gui/128x64/model_setup.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 6208ad218..33a3d1c36 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -428,9 +428,7 @@ void menuModelSetup(event_t event) MENU_TAB({ HEADER_LINE_COLUMNS 0, - TIMER_ROWS, - TIMER_ROWS, - TIMER_ROWS, + TIMERS_ROWS, 0, // Extended limits 1, // Extended trims 0, // Show trims From a2b591face8c3a5077e71116fafb8236d421196b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 3 Feb 2021 08:31:43 +0100 Subject: [PATCH 142/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8d9a23ddb..5231f9615 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2124,3 +2124,4 @@ Jason Plant John Beech Ian Parris Danny Music +Eric Bissonnette From f1e957e466eb5c771f6f3fd6aa3d8c43098cfda0 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 3 Feb 2021 10:42:04 +0100 Subject: [PATCH 143/300] Improve --- radio/src/pulses/pulses.cpp | 20 ++++++++++++-------- radio/src/pulses/pulses.h | 4 ++++ radio/src/storage/storage_common.cpp | 8 ++++---- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 7f0650afa..94fb99d52 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -480,14 +480,16 @@ bool setupPulsesInternalModule(uint8_t protocol) } } +void stopPulsesInternalModule() +{ + intmoduleStop(); + moduleState[INTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; +} + bool setupPulsesInternalModule() { uint8_t protocol = getRequiredProtocol(INTERNAL_MODULE); - if(protocol != PROTOCOL_CHANNELS_NONE) { - INTERNAL_MODULE_ON(); - } - heartbeat |= (HEART_TIMER_PULSES << INTERNAL_MODULE); if (moduleState[INTERNAL_MODULE].protocol != protocol) { @@ -502,14 +504,16 @@ bool setupPulsesInternalModule() } #endif +void stopPulsesExternalModule() +{ + extmoduleStop(); + moduleState[EXTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; +} + bool setupPulsesExternalModule() { uint8_t protocol = getRequiredProtocol(EXTERNAL_MODULE); - if(protocol != PROTOCOL_CHANNELS_NONE) { - EXTERNAL_MODULE_ON(); - } - heartbeat |= (HEART_TIMER_PULSES << EXTERNAL_MODULE); if (moduleState[EXTERNAL_MODULE].protocol != protocol) { diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 6f37f2a60..c08f8bb88 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -315,8 +315,12 @@ extern TrainerPulsesData trainerPulsesData; #if defined(HARDWARE_INTERNAL_MODULE) bool setupPulsesInternalModule(); +void stopPulsesInternalModule(); #endif +#if defined(HARDWARE_EXTERNAL_MODULE) bool setupPulsesExternalModule(); +void stopPulsesExternalModule(); +#endif void setupPulsesDSM2(); void setupPulsesCrossfire(); void setupPulsesGhost(); diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 40bac6a67..857cdd1a6 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -53,11 +53,11 @@ void preModelLoad() } pauseMixerCalculations(); -#if defined(INTMODULE_PWR_GPIO) - INTERNAL_MODULE_OFF(); +#if defined(HARDWARE_INTERNAL_MODULE) + stopPulsesInternalModule(); #endif -#if defined(EXTMODULE_PWR_GPIO) - EXTERNAL_MODULE_OFF(); +#if defined(HARDWARE_EXTERNAL_MODULE) + stopPulsesExternalModule(); #endif stopTrainer(); From f7d17b715c2a4fb2c5eb1e1863b6aba7a1898192 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 3 Feb 2021 12:00:48 +0100 Subject: [PATCH 144/300] Please Travis --- radio/src/storage/storage_common.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 857cdd1a6..6edf79834 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -53,11 +53,13 @@ void preModelLoad() } pauseMixerCalculations(); +#if !defined(SIMU) #if defined(HARDWARE_INTERNAL_MODULE) stopPulsesInternalModule(); #endif #if defined(HARDWARE_EXTERNAL_MODULE) stopPulsesExternalModule(); +#endif #endif stopTrainer(); From 00cf3f87580ff2eb643c90cfc74550a8117b9654 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 3 Feb 2021 22:06:22 +0100 Subject: [PATCH 145/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5231f9615..384f48bbd 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2125,3 +2125,4 @@ John Beech Ian Parris Danny Music Eric Bissonnette +Riso LTD From 963d7455b721ab90047919ec0f193a8aa415ccc6 Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 5 Feb 2021 16:50:02 +1100 Subject: [PATCH 146/300] Fix source filters for inputs and mixes --- companion/src/modeledit/expodialog.cpp | 2 +- companion/src/modeledit/mixerdialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 808cff7ba..abede1d73 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -92,7 +92,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G if (firmware->getCapability(VirtualInputs)) { ui->inputName->setMaxLength(firmware->getCapability(InputsLength)); id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), - (RawSource::InputSourceGroups & ~ RawSource::NoneGroup & ~RawSource::InputsGroup)), + (RawSource::InputSourceGroups & ~RawSource::NoneGroup & ~RawSource::InputsGroup) | RawSource::TelemGroup), "RawSource"); ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(ed->srcRaw.toValue())); diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 67b01f8f6..645a25274 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -49,7 +49,7 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, this->setWindowTitle(tr("DEST -> %1").arg(RawSource(SOURCE_TYPE_CH, md->destCh - 1).toString(&model, &generalSettings))); id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), - ((RawSource::InputSourceGroups | RawSource::ScriptsGroup) & ~ RawSource::NoneGroup)), + (RawSource::InputSourceGroups & ~RawSource::NoneGroup) | RawSource::ScriptsGroup), "RawSource"); ui->sourceCB->setModel(dialogFilteredItemModels->getItemModel(id)); ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); From b78b45723444d931315323835348ce1b7fe15fa9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 5 Feb 2021 13:17:54 +0100 Subject: [PATCH 147/300] Please Travis --- radio/src/pulses/pulses.cpp | 4 +++- radio/src/storage/storage_common.cpp | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 94fb99d52..f6651ce4f 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -483,7 +483,9 @@ bool setupPulsesInternalModule(uint8_t protocol) void stopPulsesInternalModule() { intmoduleStop(); - moduleState[INTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; + if (pulsesStarted()) { + moduleState[INTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; + } } bool setupPulsesInternalModule() diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 6edf79834..857cdd1a6 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -53,13 +53,11 @@ void preModelLoad() } pauseMixerCalculations(); -#if !defined(SIMU) #if defined(HARDWARE_INTERNAL_MODULE) stopPulsesInternalModule(); #endif #if defined(HARDWARE_EXTERNAL_MODULE) stopPulsesExternalModule(); -#endif #endif stopTrainer(); From b9b5ee8a9d70f703e81ae7a632163117332bc884 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 6 Feb 2021 14:58:34 +0100 Subject: [PATCH 148/300] Add switch I X9D+2019 --- radio/src/lua/CMakeLists.txt | 1 + radio/src/lua/api_general.cpp | 2 ++ radio/src/targets/taranis/CMakeLists.txt | 1 + 3 files changed, 4 insertions(+) diff --git a/radio/src/lua/CMakeLists.txt b/radio/src/lua/CMakeLists.txt index 138c8b60a..589b5d1c1 100644 --- a/radio/src/lua/CMakeLists.txt +++ b/radio/src/lua/CMakeLists.txt @@ -24,6 +24,7 @@ if(PYTHONINTERP_FOUND) add_lua_export_target(xlite ${LUA_INCLUDES} -DPCBTARANIS -DPCBXLITE) add_lua_export_target(xlites ${LUA_INCLUDES} -DPCBTARANIS -DPCBXLITES -DPCBXLITE -DGYRO) add_lua_export_target(x9d ${LUA_INCLUDES} -DPCBTARANIS -DPCBX9D -DPCBX9) + add_lua_export_target(x9d+2019 ${LUA_INCLUDES} -DPCBTARANIS -DPCBX9D -DPCBX9 -DRADIO_X9DP2019) add_lua_export_target(x9e ${LUA_INCLUDES} -DPCBTARANIS -DPCBX9E -DPCBX9) add_lua_export_target(x10 ${LUA_INCLUDES} -DPCBHORUS -DPCBX10) add_lua_export_target(x12s ${LUA_INCLUDES} -DPCBHORUS -DPCBX12S) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 18b8c0fb8..b205b1c70 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -54,6 +54,8 @@ #include "lua/lua_exports_xlites.inc" #elif defined(PCBXLITE) #include "lua/lua_exports_xlite.inc" +#elif defined(RADIO_X9DP2019) + #include "lua/lua_exports_x9d+2019.inc" #elif defined(PCBTARANIS) #include "lua/lua_exports_x9d.inc" #endif diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index 54779f137..f399ae550 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -63,6 +63,7 @@ elseif(PCB STREQUAL X9D+) option(INTERNAL_MODULE_PXX1 "Support for PXX1 internal module" OFF) option(INTERNAL_MODULE_PXX2 "Support for PXX2 internal module" ON) set(FLAVOUR x9d+2019) + set(LUA_EXPORT lua_export_x9d+2019) set(CPU_TYPE STM32F4) set(CPU_TYPE_FULL STM32F407xE) # for size report set(LINKER_SCRIPT targets/taranis/stm32f4_flash.ld) From 02e91afff2f96ae171d3c9857488b8f1e0dcb3b7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 8 Feb 2021 16:07:49 +0100 Subject: [PATCH 149/300] Fix model menu 9x --- radio/src/gui/128x64/model_setup.cpp | 33 ++++++++-------------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 33a3d1c36..10baf8724 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -129,9 +129,6 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM, -#if defined(PCBSKY9X) && defined(REVX) - ITEM_MODEL_SETUP_EXTERNAL_MODULE_OUTPUT_TYPE, -#endif #if defined(AFHDS3) ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_RX_FREQ, ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_ACTUAL_POWER, @@ -151,6 +148,9 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2, ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3, +#if defined(PCBSKY9X) && defined(REVX) + ITEM_MODEL_SETUP_EXTERNAL_MODULE_OUTPUT_TYPE, +#endif #if defined(PCBSKY9X) ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL, ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS, @@ -443,30 +443,15 @@ void menuModelSetup(event_t event) 0, // Throttle warning NUM_SWITCHES-1, // Switch warning - NUM_STICKS+NUM_POTS+NUM_SLIDERS-1, // Center beeps + NUM_STICKS + NUM_POTS + NUM_SLIDERS - 1, // Center beeps 0, // Global functions - LABEL(ExternalModule), - MODULE_TYPE_ROWS(EXTERNAL_MODULE), - MULTIMODULE_TYPE_ROWS(EXTERNAL_MODULE) - MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE) - MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE) - - MODULE_CHANNELS_ROWS(EXTERNAL_MODULE), - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum - 0, // Output type (OpenDrain / PushPull) - MODULE_POWER_ROW(EXTERNAL_MODULE), - 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 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3 - + EXTERNAL_MODULE_ROWS +#if defined(PCBSKY9X) && defined(REVX) + 0, // Output type (OpenDrain / PushPull) +#endif EXTRA_MODULE_ROWS + TRAINER_ROWS }); #endif From c88999827f47850a2d9d93b0b74c10def213d4b6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 8 Feb 2021 21:33:55 +0100 Subject: [PATCH 150/300] Compilation fix --- radio/src/gui/128x64/model_setup.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 10baf8724..0b9cf9868 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -239,6 +239,12 @@ inline uint8_t MODULE_SUBTYPE_ROWS(int moduleIdx) #define TIMER_ROWS(x) 2, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t)1 : (uint8_t)0 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2) +#if defined(PCBSKY9X) && defined(REVX) + #define EXTERNAL_MODULE_OUTPUT_ROW 0, // Output type (OpenDrain / PushPull) +#else + #define EXTERNAL_MODULE_OUTPUT_ROW +#endif + #if defined(PCBSKY9X) #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2, #else @@ -447,9 +453,9 @@ void menuModelSetup(event_t event) 0, // Global functions EXTERNAL_MODULE_ROWS -#if defined(PCBSKY9X) && defined(REVX) - 0, // Output type (OpenDrain / PushPull) -#endif + + EXTERNAL_MODULE_OUTPUT_ROW + EXTRA_MODULE_ROWS TRAINER_ROWS From 1f038dc5ce958b5ae31e9ccc31eaf84ceebd1f8a Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 9 Feb 2021 13:17:11 +0100 Subject: [PATCH 151/300] Output type only with 9xrpro and ppm and refactor --- radio/src/gui/128x64/model_setup.cpp | 52 ++++++++-------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 0b9cf9868..136ab6801 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -211,8 +211,8 @@ enum MenuModelSetupItems { #endif #if defined(PCBSKY9X) && defined(REVX) - #define OUTPUT_TYPE_ROW (isModulePPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW), -#elif defined(PCBSKY9X) + #define OUTPUT_TYPE_ROW (isModulePPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW), // Output type (OpenDrain / PushPull) +#else #define OUTPUT_TYPE_ROW #endif @@ -239,12 +239,6 @@ inline uint8_t MODULE_SUBTYPE_ROWS(int moduleIdx) #define TIMER_ROWS(x) 2, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t)1 : (uint8_t)0 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2) -#if defined(PCBSKY9X) && defined(REVX) - #define EXTERNAL_MODULE_OUTPUT_ROW 0, // Output type (OpenDrain / PushPull) -#else - #define EXTERNAL_MODULE_OUTPUT_ROW -#endif - #if defined(PCBSKY9X) #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2, #else @@ -393,12 +387,22 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) #define EXTERNAL_MODULE_ROWS #endif +#if defined(PCBTARANIS) + #define WARN_ROWS \ + SW_WARN_ROWS, /* Switch warning */ \ + POT_WARN_ROWS, /* Pot warning */ +#else + #define WARN_ROWS \ + NUM_SWITCHES - 1, /* Switch warning */ +#endif + void menuModelSetup(event_t event) { int8_t old_editMode = s_editMode; #if defined(PCBTARANIS) int8_t old_posHorz = menuHorizontalPosition; +#endif MENU_TAB({ HEADER_LINE_COLUMNS @@ -416,8 +420,7 @@ void menuModelSetup(event_t event) LABEL(PreflightCheck), 0, // Checklist 0, // Throttle warning - SW_WARN_ROWS, // Switch warning - POT_WARN_ROWS, // Pot warning + WARN_ROWS NUM_STICKS + NUM_POTS + NUM_SLIDERS - 1, // Center beeps 0, // Global functions @@ -428,39 +431,12 @@ void menuModelSetup(event_t event) EXTERNAL_MODULE_ROWS - TRAINER_ROWS - }); -#else - MENU_TAB({ - HEADER_LINE_COLUMNS - 0, - TIMERS_ROWS, - 0, // Extended limits - 1, // Extended trims - 0, // Show trims - 0, // Trims step - 0, // Throttle reverse - 0, // Throttle trace source - 0, // Throttle trim - 0, // Throttle trim switch - - LABEL(PreflightCheck), - 0, // Checklist - 0, // Throttle warning - NUM_SWITCHES-1, // Switch warning - - NUM_STICKS + NUM_POTS + NUM_SLIDERS - 1, // Center beeps - 0, // Global functions - - EXTERNAL_MODULE_ROWS - - EXTERNAL_MODULE_OUTPUT_ROW + OUTPUT_TYPE_ROW EXTRA_MODULE_ROWS TRAINER_ROWS }); -#endif MENU_CHECK(menuTabModel, MENU_MODEL_SETUP, HEADER_LINE + ITEM_MODEL_SETUP_LINES_COUNT); title(STR_MENUSETUP); From da3f3f60322793f21dfc3c22fb27d449ae376224 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 9 Feb 2021 17:27:12 +0100 Subject: [PATCH 152/300] Update CREDITS.txt --- CREDITS.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 384f48bbd..e77c0266f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2126,3 +2126,13 @@ Ian Parris Danny Music Eric Bissonnette Riso LTD +William Booth +Ferenc Kunkli +Offer Shmuely +Troy Atneosen +Long Technologies +John Jaugilas +Jan-Hinrich Klee +Hans-Reinhard Mette +Alejandro Casals Oliver +David Homewood From e5d0b9d854d4a2ce5b5c611a8214653b48817b9b Mon Sep 17 00:00:00 2001 From: Kilrah Date: Wed, 10 Feb 2021 14:25:32 +0100 Subject: [PATCH 153/300] Add missing externalaccess option for T18 --- radio/util/fwoptions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/util/fwoptions.py b/radio/util/fwoptions.py index 4ce915426..2349bdd18 100755 --- a/radio/util/fwoptions.py +++ b/radio/util/fwoptions.py @@ -256,6 +256,7 @@ options_jumper_t18 = { "nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"), "flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None), "bluetooth": ("BLUETOOTH", "YES", "NO"), + "externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"), } options_radiomaster_tx12 = { From 5280afecf6f248dfb08a6c41be568ec259b5695f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 11 Feb 2021 08:43:49 +0100 Subject: [PATCH 154/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index e77c0266f..19cc3b980 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2136,3 +2136,4 @@ Jan-Hinrich Klee Hans-Reinhard Mette Alejandro Casals Oliver David Homewood +John Dunning From bdf9e630caac5c3f276b4134f97e1b0cea64f231 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 11 Feb 2021 17:27:53 +0100 Subject: [PATCH 155/300] Improve --- radio/src/pulses/pulses.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index f6651ce4f..32ae59342 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -482,8 +482,8 @@ bool setupPulsesInternalModule(uint8_t protocol) void stopPulsesInternalModule() { - intmoduleStop(); - if (pulsesStarted()) { + if (moduleState[INTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED) { + intmoduleStop(); moduleState[INTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; } } @@ -508,8 +508,10 @@ bool setupPulsesInternalModule() void stopPulsesExternalModule() { - extmoduleStop(); - moduleState[EXTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; + if (moduleState[EXTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED)) { + extmoduleStop(); + moduleState[EXTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; + } } bool setupPulsesExternalModule() From e4b64b1d0e28879ae61f94d187ba1f5954668dab Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 12 Feb 2021 07:03:14 +0100 Subject: [PATCH 156/300] Fix compilation --- radio/src/pulses/pulses.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 32ae59342..c5cb0abbf 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -508,7 +508,7 @@ bool setupPulsesInternalModule() void stopPulsesExternalModule() { - if (moduleState[EXTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED)) { + if (moduleState[EXTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED) { extmoduleStop(); moduleState[EXTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; } From 4b3b81ae3f999c2b1253c63a523e5ea7597febfd Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 12 Feb 2021 11:44:41 +0100 Subject: [PATCH 157/300] Cosmetics --- radio/src/translations/it.h.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 9a0785a1e..796827755 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -462,7 +462,7 @@ #define TR_TTRIM TR("Trim Mot.", INDENT "Trim Motore") #define TR_TTRIM_SW TR("T-Trim-Sw", INDENT "Trim switch") #define TR_BEEPCTR TR("Beep al c.", "Beep al centro") -#define TR_USE_GLOBAL_FUNCS "Usa Funz. Globali" +#define TR_USE_GLOBAL_FUNCS TR("Funz. Glob.", "Usa Funz. Globali") #if defined(PCBSKY9X) && defined(REVX) #define TR_OUTPUT_TYPE INDENT "Uscita" #endif @@ -883,7 +883,7 @@ #define TR_MORE_OPTIONS_AVAILABLE "Altre opzioni assenti" #define TR_NO_MODULE_INFORMATION "Nessuna info del modulo" #define TR_EXTERNALRF "Modulo esterno" -#define TR_FAILSAFE "Modo failsafe" +#define TR_FAILSAFE TR("Modo Fsafe", "Modo failsafe") #define TR_FAILSAFESET TR(INDENT "FAILSAFE", INDENT "IMPOSTAZIONI FAILSAFE") #define TR_REG_ID "Reg. ID" #define TR_OWNER_ID "Owner ID" From d1f500fae74e826bc113bea2dc89c2aee15b41b5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 12 Feb 2021 11:58:56 +0100 Subject: [PATCH 158/300] Fix some italian translation on bw --- radio/src/translations/it.h.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 796827755..493ff118e 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -871,28 +871,28 @@ #define TR_FLASH_EXTERNAL_MODULE TR("Prog. modulo est.", "Programma modulo esterno") #define TR_FLASH_INTERNAL_MULTI TR("Prog. MULTI int.", "Programma MULTI interno") #define TR_FLASH_EXTERNAL_MULTI TR("Prog. MULTI est.", "Programma MULTI esterno") -#define TR_FIRMWARE_UPDATE_ERROR TR(INDENT "Errore agg. FW",INDENT "Errore aggiornamento firmware") +#define TR_FIRMWARE_UPDATE_ERROR TR(INDENT "Errore agg. FW",INDENT "Errore aggiornamento firmware") #define TR_FIRMWARE_UPDATE_SUCCESS "Aggiornamento" BREAKSPACE "riuscito" -#define TR_WRITING "Scrittura..." -#define TR_CONFIRM_FORMAT "Confermi formattazione?" -#define TR_INTERNALRF "Modulo interno" +#define TR_WRITING "Scrittura..." +#define TR_CONFIRM_FORMAT "Confermi formattazione?" +#define TR_INTERNALRF "Modulo interno" #define TR_INTERNAL_MODULE TR("Modulo int.", "Modulo interno") #define TR_EXTERNAL_MODULE TR("Modulo est.", "Modulo esterno") #define TR_OPENTX_UPGRADE_REQUIRED "OpenTX richiede aggiornamento" #define TR_TELEMETRY_DISABLED TR("Telem. off", "Telem. disabilitata") #define TR_MORE_OPTIONS_AVAILABLE "Altre opzioni assenti" #define TR_NO_MODULE_INFORMATION "Nessuna info del modulo" -#define TR_EXTERNALRF "Modulo esterno" -#define TR_FAILSAFE TR("Modo Fsafe", "Modo failsafe") -#define TR_FAILSAFESET TR(INDENT "FAILSAFE", INDENT "IMPOSTAZIONI FAILSAFE") +#define TR_EXTERNALRF "Modulo esterno" +#define TR_FAILSAFE TR(INDENT "Failsafe", "Modo failsafe") +#define TR_FAILSAFESET TR(INDENT "FAILSAFE", INDENT "IMPOSTAZIONI FAILSAFE") #define TR_REG_ID "Reg. ID" #define TR_OWNER_ID "Owner ID" #define TR_PINMAPSET "PINMAP" -#define TR_HOLD "Hold" +#define TR_HOLD "Hold" #define TR_HOLD_UPPERCASE "HOLD" -#define TR_NONE "None" +#define TR_NONE "None" #define TR_NONE_UPPERCASE "NONE" -#define TR_MENUSENSOR "SENSORE" +#define TR_MENUSENSOR "SENSORE" #define TR_POWERMETER_PEAK "Picco" #define TR_POWERMETER_POWER "Potenza" #define TR_POWERMETER_ATTN "Att." @@ -934,7 +934,7 @@ #define TR_SPEAKER INDENT "Speaker" #define TR_BUZZER INDENT "Buzzer" #define TR_BYTES "Bytes" -#define TR_MODULE_BIND "[Bind]" +#define TR_MODULE_BIND TR("[Bnd]","[Bind]") #define TR_POWERMETER_ATTN_NEEDED "Attenuatore necessario" #define TR_PXX2_SELECT_RX "Seleziona RX..." #define TR_PXX2_DEFAULT "" @@ -946,8 +946,8 @@ #define TR_RECEIVER_RESET "Resetto RX?" #define TR_SHARE "Condividere" #define TR_BIND "Bind" -#define TR_REGISTER TR("Reg", "Registrare") -#define TR_MODULE_RANGE "[Range]" +#define TR_REGISTER TR("Reg", "Registrare") +#define TR_MODULE_RANGE TR("[Rng]","[Range]") #define TR_RECEIVER_OPTIONS TR("OPZIONI RX", "OPZIONI RICEVENTE") #define TR_DEL_BUTTON BUTTON(TR("Canc.", "Cancella")) #define TR_RESET_BTN "[Reset]" @@ -970,7 +970,7 @@ #define TR_TOPLCDTIMER "Timer LCD Sup." #define TR_UNIT "Unita" #define TR_TELEMETRY_NEWSENSOR INDENT "Aggiungi nuovo sensore" -#define TR_CHANNELRANGE INDENT "Numero Canali" +#define TR_CHANNELRANGE TR(INDENT "Num Canali", INDENT "Numero Canali") #define TR_AFHDS3_RX_FREQ TR("Freq. RX", "Frequenza RX") #define TR_AFHDS3_ONE_TO_ONE_TELEMETRY TR("Unicast/Tel.", "Unicast/Telemetria") #define TR_AFHDS3_ONE_TO_MANY "Multicast" From 6bd6d214f66bc28d29a83f5a54520735373131e6 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 12 Feb 2021 12:56:16 +0100 Subject: [PATCH 159/300] Fix some italian translation on bw --- radio/src/translations/it.h.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 493ff118e..3534ac06b 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -883,7 +883,7 @@ #define TR_MORE_OPTIONS_AVAILABLE "Altre opzioni assenti" #define TR_NO_MODULE_INFORMATION "Nessuna info del modulo" #define TR_EXTERNALRF "Modulo esterno" -#define TR_FAILSAFE TR(INDENT "Failsafe", "Modo failsafe") +#define TR_FAILSAFE TR(INDENT "Failsafe", INDENT "Modo failsafe") #define TR_FAILSAFESET TR(INDENT "FAILSAFE", INDENT "IMPOSTAZIONI FAILSAFE") #define TR_REG_ID "Reg. ID" #define TR_OWNER_ID "Owner ID" From fa2dbcf72a214bbb7e5396c224d9e98c343cc961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Charpentier?= Date: Fri, 12 Feb 2021 13:03:58 +0100 Subject: [PATCH 160/300] Lapin fou/fr translation (#8280) * Updated French translation. --- companion/src/translations/companion_fr.ts | 821 +++++++-------------- 1 file changed, 286 insertions(+), 535 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 89a6734e2..1d1b1b331 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -743,58 +743,58 @@ Manche Droit: Profondeur, Direction Boards - + Left Horizontal Gauche Horizontal - + Left Vertical Gauche Vertical - + Right Vertical Droit Vertical - + Right Horizontal Droit Horizontal - + Aux. 1 - + Aux. 2 - - + + Unknown Inconnu - + Rud Dir - + Ele Prf - + Thr Gaz - + Ail @@ -8399,11 +8399,6 @@ Mixage actif par défaut si non-renseigné. Warn(%1) Alerte(%1) - - - Offset - Décalage - Flight modes @@ -8557,8 +8552,8 @@ Mixage actif par défaut si non-renseigné. - - + + Source @@ -8598,252 +8593,89 @@ Mixage actif par défaut si non-renseigné. FrSky D (câble) - + Alti Alt - + Alti+ Alt+ - + VSpeed Vitesse verticale - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells Velm - - Calculated - Calculé - - - - Add - Addition - - - - Average - Moyenne - - - - + Min - - + Max - - Multiply - Multiplication - - - - Totalise - - - - - Cell - Element LiPo - - - - Consumption - Consommation - - - - Distance - - - - - Lowest - Min - - - - Cell %1 - Elém. %1 - - - - Highest - Max - - - - Delta - - - - - Auto Offset - Offset Auto - - - - 0. - - - - - 0.0 - - - - - 0.00 - - - - - Id - - - - - - Sensor - Capteur - - - - Sources - - - - - GPS - - - - - Blades - Pales - - - - F - - - - - Inst - - - - - Alt - Altitude - - - - Unit - Unité - - - - Prec - - - - - Ratio - - - - - Multi - - - - - Filter - Filtre - - - - Persist - - - - - Positive - - - - - Log - - - - + Numbers Chiffres - + Bars Barres - + Script - + Filename Nom de fichier - + Error: Unable to open or read file! Erreur: Impossible d'ouvrir ou de lire le fichier! @@ -8861,9 +8693,8 @@ Mixage actif par défaut si non-renseigné. - - - + + None Aucun @@ -8939,7 +8770,6 @@ Mixage actif par défaut si non-renseigné. - Custom Prédéfini @@ -9353,42 +9183,42 @@ Mixage actif par défaut si non-renseigné. ModulePanel - + Value Valeur - + Hold Maintien - + No Pulse Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - + Bind on channel Bind avec VOIE @@ -10660,226 +10490,327 @@ E SensorData - + Raw (-) Chiffres bruts (-) - - + + V - + A - + mA - + kts - + m/s - + km/h - + mph - + m - + f - + °C - + °F - + % - + mAh - + W - + mW - + dB - + rpms - + g - + ° - + Rad - + hours heures - + minutes - + seconds secondes - + ml/minute - + Hertz - + mS - + uS - + TELE - - Custom - Prédéfini + + Formula + Formule - - Calculated - Calculé - - - - Add - Addition - - - - Average - Moyenne - - - - Minimum + + Id - - Maximum + + Instance + + + + + + Sensor + Capteur + + + + Sources - Multiply - Multiplication + GPS + - - Totalize - Totalisation + + Alt + Altitude - - Cell - Element LiPo - - - - Consumption - Consommation + + Unit + Unité - Distance + Precision + Précision + + + + Ratio + + + + + Offset + Décalage + + + + Auto Offset + Blades + Pales + + + + Multiplier + Multiplicateur + + + + Filter + Filtre + + + + Persist + Persistant + + + + Positive + + + + + Log + + + + + Custom + Prédéfini + + + + Calculated + Calculé + + + + Add + Addition + + + + Average + Moyenne + + + + Minimum + + + + + Maximum + + + + + Multiply + Multiplication + + + + Totalize + Totalisation + + + + Cell + Element LiPo + + + + Consumption + Consommation + + + + Distance + + + + Lowest Min - + Cell %1 Elément %1 - + Highest Max - + Delta + + + Y + O + + + + N + + Setup @@ -11063,92 +10994,92 @@ Si cette option est cochée, la voie des gaz est inversée: le ralenti est &apo SetupPanel - + Popup menu available Menu contextuel disponible - + Timer %1 Chrono %1 - + THR GAZ - + Profile Settings Paramètres du profil - + SD structure path not specified or invalid Chemin de la structure SD non spécifié ou invalide - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Timer. Are you sure? Effacer le Chrono. Êtes-vous sûr? - + Clear all Timers. Are you sure? Effacer tous les Chronos. Êtes-vous sûr? - + Cut Timer. Are you sure? Couper le Chrono. Êtes-vous sûr? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? @@ -12625,52 +12556,52 @@ Trop d'erreurs, abandon. TelemetryPanel - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Telemetry screen %1 Ecran de télémesure %1 - + Source - + Low Alarm Alarme basse - + Critical Alarm Alarme critique - + Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (non supporté) - + Delete Sensor. Are you sure? Supprimer le Capteur. Êtes-vous sûr? @@ -12688,279 +12619,99 @@ Trop d'erreurs, abandon. - - Custom - Prédéfini - - - - Calculated - Calculé - - - + Id - + Instance - + Rx - - Add - Addition - - - - Average - Moyenne - - - - Min - - - - - Max - - - - - Multiply - Multiplication - - - - Totalize - Totalisation - - - - Cell - Element LiPo - - - - Consumption - Consommation - - - - Dist - Distance - - - + Cells Sensor : Capteur d'éléments: - - - - - - - - + + + + + + + + --- - + GPS Sensor : Capteur GPS: - + Alt. Sensor : Capteur d'altitude: - + Sensor : Capteur: - - V - - - - - A - - - - - mA - - - - - kt - - - - - m/s - - - - - ft/s - - - - - km/h - - - - - mph - - - - - m - - - - - ft - - - - - °C - - - - - °F - - - - - % - - - - - mAh - - - - - W - - - - - mW - - - - - dBm - - - - - g - - - - - ° - - - - - Rad - - - - - mL - - - - - US fl.Oz. - - - - - Raw (-) - Chiffres bruts (-) - - - - RPM - Tours/minute - - - - ml/min - - - - + Precision Précision - + Ratio - + Blades Pales - + Offset Décalage - + Multiplier Multiplicateur - + Auto Offset Offset auto - + Filter Filtre - + Persistent Persistant - + Positive - + Logs @@ -12968,72 +12719,72 @@ Trop d'erreurs, abandon. TelemetrySensorPanel - + TELE%1 - + Popup menu available Menu contextuel disponible - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Cut Telemetry Sensor. Are you sure? Couper le capteur de télémétrie. Êtes-vous sûr? - + Clear Telemetry Sensor. Are you sure? Effacer le capteur de télémétrie. Êtes-vous sûr? - + Clear all Telemetry Sensors. Are you sure? Effacez tous les capteurs de télémétrie. Êtes-vous sûr? @@ -13403,12 +13154,12 @@ hh:mm:ss Timer - + Countdown Compte à rebours - + Minute Call Annonces minutes From 8751ae31755360668308e5def25d7c7a14b7cffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=8F=20=E3=81=BE=E3=82=8A=E3=81=82?= =?UTF-8?q?=E3=81=A6=E3=82=8C=E3=81=98=E3=81=82?= <36994795+ToshihiroMakuuchi@users.noreply.github.com> Date: Sat, 13 Feb 2021 22:20:45 +0900 Subject: [PATCH 161/300] Update Japanese translation. --- companion/src/translations/companion_ja.ts | 2044 +++++++++----------- 1 file changed, 894 insertions(+), 1150 deletions(-) diff --git a/companion/src/translations/companion_ja.ts b/companion/src/translations/companion_ja.ts index 2d7d02b4d..d423959d4 100644 --- a/companion/src/translations/companion_ja.ts +++ b/companion/src/translations/companion_ja.ts @@ -730,58 +730,58 @@ Mode 4: Boards - + Left Horizontal 左・横 - + Left Vertical 左・縦 - + Right Vertical 右・縦 - + Right Horizontal 右・横 - + Aux. 1 AUX. 1 - + Aux. 2 AUX. 2 - - + + Unknown 不明 - + Rud ラダー - + Ele エレベーター - + Thr スロットル - + Ail エルロン @@ -828,44 +828,44 @@ Mode 4: - Channels + ChannelsPanel - + Name 名称 - + Subtrim サブトリム - + Min 最小 - + Max 最大 - + Direction 方向 - + Curve カーブ - + PPM Center PPM センター - + Linear Subtrim 線形サブトリム @@ -890,67 +890,67 @@ Mode 4: リバース - + Delete Channel. Are you sure? チャンネルを削除します。よろしいですか? - + Cut Channel. Are you sure? チャンネルを切り取ります。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Channel. Are you sure? チャンネルを消去します。よろしいですか? - + Clear all Channels. Are you sure? チャンネルをすべて消去します。よろしいですか? @@ -1090,37 +1090,37 @@ Mode 4: 送信機と機体モデルの設定 - + Select or create a file for exported Settings: エクスポートした設定ファイルを選択または作成します: - + Press the 'Retry' button to choose another file. 別のファイルを選択するには『再試行』ボタンを押してください。 - + Simulator for this firmware is not yet available このファームウェアのシミュレータはまだ利用できません - + Uknown error during Simulator startup. シミュレータ起動中に不明なエラーが発生しました。 - + Simulator Error シミュレータ エラー - + Data Load Error データロード エラー - + Error occurred while starting simulator. シミュレータ起動中にエラーが発生しました。 @@ -1376,51 +1376,28 @@ If you have a settings backup file, you may import that instead. - CurveGroup + CurveReference - + Diff 差分 - + Expo エクスポ - + Func 機能 - + Curve カーブ - - CurveReference - - - Diff(%1) - 差分(%1) - - - - Expo(%1) - エクスポ(%1) - - - - Function(%1) - 機能(%1) - - - - Curve(%1) - カーブ(%1) - - Curves @@ -1509,113 +1486,116 @@ If you have a settings backup file, you may import that instead. Point size 点のサイズ + + + CurvesPanel - + Curve %1 カーブ %1 - + Popup menu available 利用可能なポップアップメニュー - + %1 points %1 点 - + Linear 線形 - + Single Expo シングルエクスポ - + Symmetrical f(x)=-f(-x) 対称 f(x)=-f(-x) - + Symmetrical f(x)=f(-x) 対称 f(x)=f(-x) - + Editing curve %1 カーブ %1 編集 - + Not enough free points in EEPROM to store the curve. EEPROMにカーブを保存する十分な空き容量がありません。 - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Curve. Are you sure? カーブを消去します。よろしいですか? - + Clear all Curves. Are you sure? カーブをすべて消去します。よろしいですか? - + Cut Curve. Are you sure? カーブを切り取ります。よろしいですか? - + Delete Curve. Are you sure? カーブを削除します。よろしいですか? @@ -1689,8 +1669,13 @@ If you have a settings backup file, you may import that instead. - Set Timer %1 - 設定: タイマー %1 + Set %1 + 設定 %1 + + + + Set Failsafe + フェイルセーフ設定 @@ -1750,75 +1735,50 @@ If you have a settings backup file, you may import that instead. Adjust %1 - アジャスト %1 - - - - SetFailsafe Int. Module - 設定: フェイルセーフ 内部モジュール + 調整 %1 - SetFailsafe Ext. Module - 設定: フェイルセーフ 外部モジュール - - - RangeCheck Int. Module レンジチェック 内部モジュール - + RangeCheck Ext. Module レンジチェック 外部モジュール - + Bind Int. Module バインド 内部モジュール - + Bind Ext. Module バインド 外部モジュール - - Timer1 - タイマー1 - - - - Timer2 - タイマー2 - - - - Timer3 - タイマー3 - - - + Flight フライト - + Telemetry テレメトリー - + Rotary Encoder エンコーダ再生 - + REa エンコーダ再生a - + REb エンコーダ再生b @@ -1855,7 +1815,7 @@ If you have a settings backup file, you may import that instead. 無効 - + CFN CFN @@ -1863,139 +1823,139 @@ If you have a settings backup file, you may import that instead. CustomFunctionsPanel - + Switch スイッチ - + Action 実行 - + Parameters パラメータ - + Enable 有効 - + Popup menu available 利用可能なポップアップメニュー - + SF%1 SF%1 - + GF%1 GF%1 - + ON ON - + Error occurred while trying to play sound, possibly the file is already opened. (Err: %1 [%2]) サウンド再生中にエラーが発生しました。おそらくファイルは既に開かれています (Err: %1 [%2]) - + Unable to find or open sound file: %1 音源ファイルが見つからない、または開けません。 %1 - + Delete Function. Are you sure? ファンクションを削除します。よろしいですか? - + Cut Special Function. Are you sure? ファンクションを切り取ります。よろしいですか? - + Clear Function. Are you sure? ファンクションを消去します。よろしいですか? - + Clear all Functions. Are you sure? ファンクションをすべて消去します。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Value 値 - + Source 選択元 - + GVAR GVAR - + Increment 増加 @@ -2163,48 +2123,48 @@ If you have a settings backup file, you may import that instead. フィールド %1 変換エラー - + Switch スイッチ - + Switch スイッチ - + cannot be exported on this board! このボードにエクスポートできません! - + Source 選択元 - + Source %1 cannot be exported on this board! このボードでは選択元 %1 をエクスポートできません! - + OpenTX only accepts %1 points in all curves OpenTXはすべてのカーブで %1 点のみを許容します - + OpenTx only accepts %1 points in all curves OpenTxはすべてのカーブで %1 点のみを許容します - - + + OpenTX on this board doesn't accept this function このボード上のOpenTXはこの機能を受け付けません - + OpenTX doesn't accept this radio protocol OpenTXはこの送信機プロトコルを受け付けません @@ -2219,18 +2179,18 @@ If you have a settings backup file, you may import that instead. <html><head/><body><p>Enable or disable the filter. If the button won't stay enabled, it is likely there is a syntax error in the Regular Expression entered.</p></body></html> - <html><head/><body><p>フィルタを有効または無効にします。ボタンが有効にならない場合は、入力した正規表現に構文エラーがある可能性があります。</p></body></html> + <html><head/><body><p>フィルターを有効または無効にします。ボタンが有効にならない場合は、入力した正規表現に構文エラーがある可能性があります。</p></body></html> Filter: - フィルタ: + フィルター: <html><head/><body><p>Enter filter text here. Click the help/info button for details about using the filter. </p><p> To <b>remove a remembered entry</b> from the filter list, first choose it, and then press <code>Shift-Delete</code> (or <code>Shift-Backspace</code>) key combination.</p></body></html> - <html><head/><body><p>ここにフィルタテキストを入力してください。フィルタの使用方法の詳細は、help / info ボタンをクリックしてください。</p><p> + <html><head/><body><p>ここにフィルタテキストを入力してください。フィルターの使用方法の詳細は、help / info ボタンをクリックしてください。</p><p> フィルタリストから<b>保存したエントリを削除</b>するには、選択してから<code>Shift+Delete</code> (または<code>Shift+Backspace</code>) キーを押してください。</p></body></html> @@ -2246,12 +2206,12 @@ To <b>remove a remembered entry</b> from the filter list, first cho Filter &Help - フィルタ &ヘルプ + フィルター &ヘルプ Show information about using the filter. - フィルタの使用に関する情報を表示します。 + フィルターの使用に関する情報を表示します。 @@ -2276,22 +2236,22 @@ To <b>remove a remembered entry</b> from the filter list, first cho Enable &Filter - 有効 &フィルタ + 有効 &フィルター Turn the filter on/off. - フィルタをON / OFFします。 + フィルターをON / OFFします。 <html><head><style>kbd {background-color: palette(alternate-base); font-size: large; white-space: nowrap;}</style></head><body><p>The filter supports two syntax types: basic matching with common wildcards as well as full Perl-style (<code>pcre</code>) Regular Expressions.</p><p>By default a filter will only show lines which match (<b>inclusive</b>). To make an <b>exclusive</b> filter which removes matching lines, prefix the filter expression with a <kbd>!</kbd> (exclamation mark).</p><p>To use <b>Regular Expressions</b> (RegEx), prefix the filter text with a <kbd>/</kbd> (slash) or <kbd>^</kbd> (up caret). <ul><li>Put the <kbd>/</kbd> or <kbd>^</kbd> after the exclusive <kbd>!</kbd> indicator if you're using one.</li><li>By default the match is case-sensitive. To make it insensitive, add the typical <kbd>/i</kbd> (slash i) operator at the end of your RegEx.</li><li>If you use a caret (^) to denote a RegEx, it will become part of the Reg. Ex. (that is, matches from start of line).</li><li>If the RegEx is invalid, the filter edit field should show a red border and you will not be able to enable the filter.</li><li>A useful resource for testing REs (with a full reference) can be found at <a href="http://www.regexr.com/">http://www.regexr.com/</a></li></ul></p><p>To use <b>basic matching</b> just type any text.<ul><li>Wildcards: <kbd>*</kbd> (asterisk) matches zero or more of any character(s), and <kbd>?</kbd> (question mark) matches any single character.</li><li>The match is always case-insensitive.</li><li>The match always starts from the beginning of a log line. To ignore characters at the start, use a leading <kbd>*</kbd> wildcard.</li><li>A trailing <kbd>*</kbd> is always implied (that is, matches anything to the end of the log line). To avoid this, use a RegEx.</li><li>You can match literal wildcard characters by prefixing them with a <kbd>\</kbd> (backslash) character (eg. "foo\*bar" matches "foo*bar").</li></ul></p><p>After <b>editing text</b>, press ENTER or TAB key (or click anywhere outside the box) to update the filter.</p><p>To <b>remove an entry</b> from the filter selector list, first choose it, and while in the line editor press <kbd>Shift-Delete</kbd> (or <kbd>Shift-Backspace</kbd>) key combination. The default filters cannot be removed. Up to 50 filters are stored.</p></body></html> - <html><head><style>kbd {background-color: palette(alternate-base); font-size: large; white-space: nowrap;}</style></head><body><p>フィルタは2つのシンタックスタイプをサポートしています: 一般的なワイルドカードを用いた完全一致、そして完全なPerlスタイル (<code>pcre</code>) 正規表現です。初期値ではフィルタは一致する (<b>包括的</b>) な行のみを表示します。一致する行を削除する<b>排他的</b>フィルタを作成するには、フィルタ式の先頭に<kbd>!</kbd>(Exclamationマーク)を付けます。</p><p>To use <b>Regular Expressions</b> (RegEx), prefix the filter text with a <kbd>/</kbd> (slash) or <kbd>^</kbd> (up caret). <b>正規表現</b> (RegEx)を使用するには、フィルタテキストの先頭に<kbd>/</kbd> (スラッシュ) または<kbd>^</kbd>(アップキャレット)を付けます。<ul><li>既に <kbd>/</kbd> または <kbd>^</kbd> を使用している場合は、 <kbd>!</kbd> の後ろに置きます。</li><li>初期値では大文字と小文字が区別されます。それを鈍感にするには、正規表現の最後に典型的な <kbd>/i</kbd> (スラッシュ+i)演算子を追加します。</li> <li> 正規表現を表すためにキャレット『^』を使用すると、それは正規表現の一部になります。例(つまり、行頭から一致します。)</li> <li> 正規表現が無効な場合、フィルタ編集フィールドには赤い枠が表示されフィルタを有効にすることはできません。</li> <li> 正規表現のテストに役立つリソース(参考文献)は、<a href="http://www.regexr.com/"> http://www.regexr.com/にあります。</a></li> </ul></p><p><b>標準一致 basic matching</b> を使用するには、テキストを入力するだけです。<ul><li>ワイルドカード: <kbd>*</kbd> (アスタリスク)は0個以上の任意の文字に一致し、 <kbd>?</kbd> (クエスチョンマーク) は任意の1文字に一致します。</li><li>この一致では常に大文字と小文字が区別されません。</li><li>一致は常にログの先頭行から始まります。 先頭の文字を無視するには、先頭に<kbd>*</kbd>ワイルドカードを使用します。</li><li>末尾の <kbd>*</kbd> は常に暗黙的に指定されます。(つまり、ログ末尾行までが対象) これを回避するには、正規表現を使用します。</li><li>リテラルワイルドカード文字の前に <kbd>\</kbd> (バックスラッシュ) 文字を付けることで一致させることができます。(例: "foo\*bar"は"foo*bar"と一致)</li></ul></p><p><b>テキスト編集</b>後、EnterまたはTabキーを押して (またはボックスの外側をクリックし) フィルタを更新します。</p><p>フィルタセレクタリストから<b>エントリー削除</b>をするには、まずエントリーを選択しエディタで<kbd>Shift+Delete</kbd> (または<kbd>Shift+Backspace</kbd>) を押します。デフォルトのフィルタは削除できません。また最大50個のフィルタが保存されています。</p></body></html> + <html><head><style>kbd {background-color: palette(alternate-base); font-size: large; white-space: nowrap;}</style></head><body><p>フィルターは2つのシンタックスタイプをサポートしています: 一般的なワイルドカードを用いた完全一致、そして完全なPerlスタイル (<code>pcre</code>) 正規表現です。初期値ではフィルターは一致する (<b>包括的</b>) な行のみを表示します。一致する行を削除する<b>排他的</b>フィルターを作成するには、フィルター式の先頭に<kbd>!</kbd>(Exclamationマーク)を付けます。</p><p>To use <b>Regular Expressions</b> (RegEx), prefix the filter text with a <kbd>/</kbd> (slash) or <kbd>^</kbd> (up caret). <b>正規表現</b> (RegEx)を使用するには、フィルターテキストの先頭に<kbd>/</kbd> (スラッシュ) または<kbd>^</kbd>(アップキャレット)を付けます。<ul><li>既に <kbd>/</kbd> または <kbd>^</kbd> を使用している場合は、 <kbd>!</kbd> の後ろに置きます。</li><li>初期値では大文字と小文字が区別されます。それを鈍感にするには、正規表現の最後に典型的な <kbd>/i</kbd> (スラッシュ+i)演算子を追加します。</li> <li> 正規表現を表すためにキャレット『^』を使用すると、それは正規表現の一部になります。例(つまり、行頭から一致します。)</li> <li> 正規表現が無効な場合、フィルター編集フィールドには赤い枠が表示されフィルターを有効にすることはできません。</li> <li> 正規表現のテストに役立つリソース(参考文献)は、<a href="http://www.regexr.com/"> http://www.regexr.com/にあります。</a></li> </ul></p><p><b>標準一致 basic matching</b> を使用するには、テキストを入力するだけです。<ul><li>ワイルドカード: <kbd>*</kbd> (アスタリスク)は0個以上の任意の文字に一致し、 <kbd>?</kbd> (クエスチョンマーク) は任意の1文字に一致します。</li><li>この一致では常に大文字と小文字が区別されません。</li><li>一致は常にログの先頭行から始まります。 先頭の文字を無視するには、先頭に<kbd>*</kbd>ワイルドカードを使用します。</li><li>末尾の <kbd>*</kbd> は常に暗黙的に指定されます。(つまり、ログ末尾行までが対象) これを回避するには、正規表現を使用します。</li><li>リテラルワイルドカード文字の前に <kbd>\</kbd> (バックスラッシュ) 文字を付けることで一致させることができます。(例: "foo\*bar"は"foo*bar"と一致)</li></ul></p><p><b>テキスト編集</b>後、EnterまたはTabキーを押して (またはボックスの外側をクリックし) フィルターを更新します。</p><p>フィルターセレクタリストから<b>エントリー削除</b>をするには、まずエントリーを選択しエディタで<kbd>Shift+Delete</kbd> (または<kbd>Shift+Backspace</kbd>) を押します。デフォルトのフィルターは削除できません。また最大50個のフィルターが保存されています。</p></body></html> Debug Console Filter Help - デバッグコンソールフィルタ ヘルプ + デバッグコンソールフィルター ヘルプ @@ -2536,27 +2496,27 @@ If blank then the input is considered to be "ON" all the time.ミキサー選択元 - + Edit %1 編集 %1 - + Popup menu available 利用可能なポップアップメニュー - + Clear All すべて消去 - + Set All すべて設定 - + Invert All すべてリバース @@ -2925,255 +2885,293 @@ Blank means include all. ?, *, and [...] wildcards accepted. Firmware - + Channel values displayed in us 表示されるチャンネル値 - + No OverrideCH functions available 使用可能な上書きチャンネル機能はありません - + Possibility to enable FAI MODE (no telemetry) at field フィールドでFAIモード (テレメトリーなし) を有効にする可能性 - + FAI MODE (no telemetry) always enabled FAIモード (テレメトリーなし) を常に有効 - + Removes D8 FrSky protocol support which is not legal for use in the EU on radios sold after Jan 1st, 2015 2015/01/01以降に販売された送信機に対し、EUでの使用に適さないD8 FrSkyプロトコルサポートを削除 - + Enable non certified firmwares 認定されていないファームウェアを有効にする - - + + + + + Disable HELI menu and cyclic mix support HELIメニューとサイクリックミックスサポートを無効にする - - + + + + + Disable Global variables グローバル変数を無効にする - - + + + + + Enable Lua custom scripts screen Luaカスタムスクリプト画面を有効にする - + Use alternative SQT5 font SQT5 代替フォントを使用する - + Pots use in menus navigation メニューナビゲーションでダイヤルを使用 - + FrSky Taranis X9D+ FrSky Taranis X9D+ - - + + Disable RAS (SWR) RAS (SWR)を無効にする - + FrSky Taranis X9D+ 2019 - + FrSky Taranis X9D FrSky Taranis X9D - + Haptic module installed タッチパネル対応モジュールをインストール - + FrSky Taranis X9E FrSky Taranis X9E - + Confirmation before radio shutdown 送信機シャットダウン前の確認 - + Horus gimbals installed (Hall sensors) Horusジンバル (ホールセンサー) をインストール - + FrSky Taranis X9-Lite FrSky Taranis X9-Lite - + FrSky Taranis X9-Lite S - + FrSky Taranis X7 / X7S FrSky Taranis X7 / X7S - + FrSky Taranis X-Lite S/PRO FrSky Taranis X-Lite S/PRO - + FrSky Taranis X-Lite FrSky Taranis X-Lite - + FrSky Horus X10 / X10S FrSky Horus X10 / X10S - + + + + + + Support hardware mod: R9M ACCESS + ハードウェアモジュールサポート: R9M ACCESS + + + FrSky Horus X12S FrSky Horus X12S - + Use ONLY with first DEV pcb version 初期DEV pcbバージョンでのみ使用 - - + + Support for MULTI internal module MULTI内部モジュールのサポート - - - + + Jumper T-Lite + Jumper T-Lite + + + + + Support for bluetooth module Bluetoothモジュールのサポート - + + Radiomaster TX12 + Radiomaster TX12 + + + + Radiomaster T8 + Radiomaster T8 + + + + Allow bind using bind key + バインドキーを使用したバインドを許可 + + + Radiomaster TX16S / SE / Hall / Masterfire Radiomaster TX16S / SE / Hall / Masterfire - + Jumper T18 Jumper T18 - + Turnigy 9XR-PRO Turnigy 9XR-PRO - + Enable HELI menu and cyclic mix support HELIメニューとサイクリックミックスのサポートを有効にする - + Enable AFHDS3 support AFHDS3サポート 有効 - + Global variables グローバル変数 - + In model setup menus automatically set source by moving the control モデル設定メニューでは、コントローラーを動かし自動的に選択元を設定します - + In model setup menus automatically set switch by moving the control モデル設定メニューでは、コントローラーを動かし自動的にスイッチを設定します - + No graphical check boxes and sliders グラフィカルなチェックボックスとスライダーはありません - + Battery graph バッテリーグラフ - + Don't use bold font for highlighting active items 有効なアイテムを強調表示するために太字フォントを使用しない - + FrSky Taranis X7 / X7S Access FrSky Taranis X7 / X7S ACCESS - - + + Support for ACCESS internal module replacement ACCESS内部モジュール変更のサポート - + Enable resetting values by pressing up and down at the same time 上下に同時に押し、値のリセットを有効にする - + 9X with AR9X board 9X AR9Xボード - + FrSky Horus X10 Express / X10S Express FrSky Horus X10 Express / X10S Express - + Jumper T12 / T12 Pro Jumper T12 / T12 Pro - + Jumper T16 / T16+ / T16 Pro Jumper T16 / T16+ / T16 Pro - + Support internal GPS 内蔵GPSサポート - + 9X with Sky9x board 9X Sky9Xボード @@ -3670,88 +3668,88 @@ You are currently using: FlightModePanel - - + + Popup menu available 利用可能なポップアップメニュー - + Trim disabled トリム 無効 - + Own Trim 独自トリム - + Use Trim from Flight mode %1 フライトモードからトリムを使用してください %1 - + Use Trim from Flight mode %1 + Own Trim as an offset フライトモードからトリムを使用してください %1+オフセット独自トリム - + Rotary Encoder %1 ロータリーエンコーダ %1 - + Name 名称 - + Value source 元値 - + Value 値 - + Unit ユニット - + Prec - + Min 最小 - + Max 最大 - + Popup enabled ポップアップ有効 - + GVAR%1 - + 0._ - + 0.0 0.0 @@ -3767,49 +3765,49 @@ You are currently using: - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 @@ -3819,63 +3817,63 @@ You are currently using: フライトモードを消去します。よろしいですか? - + Clear all Flight Modes. Are you sure? フライトモードをすべて消去します。よろしいですか? - + Cut Flight Mode. Are you sure? フライトモードを切り取ります。よろしいですか? - + Delete Flight Mode. Are you sure? フライトモードを削除します。よろしいですか? - + Clear Global Variable across all Flight Modes. Are you sure? すべてのフライトモードのグローバル変数を消去します。よろしいですか? - + Clear Global Variable. Are you sure? グローバル変数を消去します。よろしいですか? - + Cut Global Variable across all Flight Modes. Are you sure? すべてのフライトモードのグローバル変数を切り取ります。よろしいですか? - + Cut Global Variable. Are you sure? グローバル変数を切り取ります。よろしいですか? - + Delete Global Variable. Are you sure? グローバル変数を削除します。よろしいですか? - + Paste to selected Global Variable across all Flight Modes. Are you sure? すべてのフライトモードで選択したグローバル変数を貼り付けます。よろしいですか? - + Clear all Global Variables for all Flight Modes. Are you sure? すべてのフライトモードのすべてのグローバル変数を消去します。よろしいですか? - + Clear all Global Variables for this Flight Mode. Are you sure? このフライトモードのすべてのグローバル変数を消去します。よろしいですか? - + Clear 消去 @@ -3883,17 +3881,17 @@ You are currently using: FlightModesPanel - + Flight Mode %1 フライトモード %1 - + (%1) (%1) - + (default) (初期値) @@ -4133,12 +4131,12 @@ p, li { white-space: pre-wrap; } - + Own value 独自トリム - + Flight mode %1 value フライトモード %1 値 @@ -4168,52 +4166,52 @@ These will be relevant for all models in the same EEPROM. これらは同じEEPROM内のすべてのモデルに関連します。 - + Setup セットアップ - + Global Functions グローバルファンクション - + Trainer トレーナー - + Hardware ハードウェア - + Calibration 校正 - + Wrong data in profile, radio calibration was not retrieved プロファイルデータが正しくなく、送信機校正情報が取得できませんでした - + Wrong data in profile, Switch/pot config not retrieved プロファイルデータが正しくなく、スイッチ/ダイヤル構成が取得できませんでした - + Wrong data in profile, hw related parameters were not retrieved プロファイルデータが正しくなく、HW関連パラメーターが取得できませんでした - + Do you want to store calibration in %1 profile<br>overwriting existing calibration? 既存の校正情報を上書きして %1 プロファイルに校正情報を保存しますか? - + Calibration and HW parameters saved. 校正情報とHWパラメータが保存されました。 @@ -4221,7 +4219,7 @@ These will be relevant for all models in the same EEPROM. GeneralSettings - + Radio Settings 送信機設定 @@ -4306,7 +4304,7 @@ These will be relevant for all models in the same EEPROM. Adjust RTC - 時計調整 + 内蔵時計調整 @@ -4823,7 +4821,7 @@ Acceptable values are 5v..10v Low EEPROM Warning - EEPROM残量警告 + EEPROM 残量警告 @@ -5262,252 +5260,257 @@ Are you sure ? フォーム - + SQ SQ - + SR SR - + LS2 LS2 - + SP SP - + SO SO - + S4 S4 - + RS RS - + SB SB - + PPM 2 PPM 2 - + Antenna アンテナ - + S5 S5 - + OFF OFF - + S-Port Mirror S-Port ミラー - + Telemetry テレメトリー - + SBUS Trainer SBUS トレーナー - + Debug デバッグ - + RTC Batt Check - RTC Battチェック + 内蔵電池チェック - + + S.Port Power + S.Port 出力 + + + Rud ラダー - + PPM 3 PPM 3 - + S1 S1 - + S2 S2 - + S3 S3 - + PPM 1 PPM 1 - + Serial Port シリアルポート - + v v - + PPM Multiplier PPM 乗算 - + Current Offset 現在のオフセット - + PPM 4 PPM 4 - + SA SA - + Ele エレベーター - + Ail エルロン - + Thr スロットル - + SC SC - + LS LS - + SD SD - + Battery Offset バッテリーオフセット - + SE SE - + SF SF - + SG SG - + SH SH - + SI SI - + SJ SJ - + SK SK - + SL SL - + SM SM - + SN SN - + RS2 RS2 - + Bluetooth Bluetooth - + ADC Filter - ADCフィルタ + ADCフィルター - + Device Name: デバイス名: @@ -5516,8 +5519,8 @@ Are you sure ? HardwarePanel - - + + None なし @@ -5537,67 +5540,67 @@ Are you sure ? 3 ポジション - + Pot with detent ダイヤル (ノッチあり) - + Multipos switch マルチPosスイッチ - + Pot without detent ダイヤル (ノッチなし) - + Slider with detent スライダー (ノッチあり) - + OFF OFF - + Enabled 有効 - + Telemetry テレメトリー - + Trainer トレーナー - + Internal 内部 - + Ask 確認 - + Per model Per モデル - + Internal + External 内部 + 外部 - + External 外部 @@ -5723,159 +5726,159 @@ Are you sure ? InputsPanel - - - + + + Move Up 上へ移動 - - + + Ctrl+Up - - - + + + Move Down 下へ移動 - - + + Ctrl+Down - + Clear All Inputs すべての入力項目を消去 - + Not enough available Inputs! 入力項目が不足しています! - + Delete selected Input lines. Are you sure? 選択したInput行を削除します。よろしいですか? - + Cut selected Input lines. Are you sure? 選択したInput行を切り取ります。よろしいですか? - + Lines 直線 - + &Add &追加 - + Ctrl+A - + &Edit &編集 - + Enter Enter - + &Delete &削除 - - + + Delete 削除 - + &Copy &コピー - + Ctrl+C - + &Cut &切り取り - + Ctrl+X - + &Paste &貼り付け - + Ctrl+V - + Du&plicate &複製 - + Ctrl+U - + Input 入力 - + Insert 挿入 - + Clear 消去 - + Clear All すべて消去 - + Clear all Input lines. Are you sure? すべてのInput行を消去します。よろしいですか? - + Clear all lines for the selected Inputs. Are you sure? 選択したすべてのInput行を消去します。よろしいですか? - + Delete all lines for the selected Inputs. Are you sure? 選択したすべてのInput行を削除します。よろしいですか? @@ -5901,7 +5904,7 @@ Are you sure ? LimitsGroup - + GV @@ -6032,112 +6035,112 @@ Are you sure ? LogicalSwitchesPanel - + Function 機能 - + V1 - + V2 - + AND Switch & スイッチ - + Duration 期間 - + Delay 遅延 - + Popup menu available 利用可能なポップアップメニュー - + (instant) (瞬間) - + (infinite) (無制限) - + Delete Logical Switch. Are you sure? 論理スイッチを削除します。よろしいですか? - + Cut Logical Switch. Are you sure? 論理スイッチを切り取ります。よろしいですか? - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Logical Switch. Are you sure? 論理スイッチを消去します。よろしいですか? - + Clear all Logical Switches. Are you sure? 論理スイッチをすべて消去します。よろしいですか? @@ -7872,27 +7875,27 @@ If blank then the mix is considered to be "ON" all the time. 空白の場合、ミックスは常に『オン』と見なします。 - + DEST -> %1 - + Click to access popup menu クリックしてポップアップメニューにアクセス - + Clear All すべて消去 - + Set All すべて設定 - + Invert All すべてリバース @@ -7923,136 +7926,136 @@ If blank then the mix is considered to be "ON" all the time. MixesPanel - - + + Move Up 上へ移動 - - + + Ctrl+Up - - + + Move Down 下へ移動 - - + + Ctrl+Down - + Clear Mixes ミキサーを消去 - + Not enough available mixers! ミキサー項目が足りません! - + Delete selected Mix lines. Are you sure? 選択したMix行を削除します。よろしいですか? - + Cut selected Mix lines. Are you sure? 選択したMix行を切り取ります。よろしいですか? - + &Add &追加 - + Ctrl+A - + &Edit &編集 - + Enter Enter - + &Toggle highlight &ハイライト切替 - + Ctrl+T - + &Delete &削除 - + Delete 削除 - + &Copy &コピー - + Ctrl+C - + C&ut &切り取り - + Ctrl+X - + &Paste &貼り付け - + Ctrl+V - + Du&plicate &複製 - + Ctrl+U - + Clear Mixes? ミキサーを消去しますか? - + Really clear all the mixes? すべてのミキサーを本当に消去しますか? @@ -8069,6 +8072,11 @@ If blank then the mix is considered to be "ON" all the time. Throttle Source スロットル値 + + + THR + THR + ModelEdit @@ -8083,52 +8091,52 @@ If blank then the mix is considered to be "ON" all the time. シミュレート - + Setup セットアップ - + Heli ヘリ - + Flight Modes フライトモード - + Inputs 入力 - + Mixes ミキサー - + Outputs 出力 - + Curves カーブ - + Logical Switches 論理スイッチ - + Special Functions スペシャルファンクション - + Telemetry テレメトリー @@ -8183,8 +8191,8 @@ If blank then the mix is considered to be "ON" all the time. - - + + OFF OFF @@ -8225,7 +8233,7 @@ If blank then the mix is considered to be "ON" all the time. - + Mode モード @@ -8259,14 +8267,13 @@ If blank then the mix is considered to be "ON" all the time. - - + Delay 遅延 - + Receiver 受信機 @@ -8358,11 +8365,10 @@ If blank then the mix is considered to be "ON" all the time. - - - - - + + + + None なし @@ -8403,528 +8409,353 @@ If blank then the mix is considered to be "ON" all the time. FM%1+%2 - - - - Weight - ウェイト - - - - - Switch - スイッチ - - + NoTrim トリムなし + Offset(%1) オフセット(%1) - + MULT! 複数! - + No DR/Expo DR/Expoなし - - - Offset - オフセット + + + Weight(%1) + ウェイト(%1) - - Slow - ゆっくり + + + Switch(%1) + スイッチ(%1) - - Warn - 警告 + + Delay(u%1:d%2) + 遅延(u%1:d%2) - + + Slow(u%1:d%2) + 速度低下(u%1:d%2) + + + + Warn(%1) + 警告(%1) + + + Disabled in all flight modes すべてのフライトモードを無効 - + Flight modes フライトモード - + Flight mode フライトモード - + All すべて - + Edge Edge:端 - + instant 簡易 - + Sticky Sticky:追尾 - + Timer タイマー - + missing 間違い - + Duration 期間 - - - + + Custom カスタム - + Standard 標準 - + Extended Limits 拡張制限 - + Display Checklist ディスプレイ チェックリスト - + Global Functions グローバルファンクション - + Manual 手動 - + Auto 自動 - + Failsafe Mode フェイルセーフモード - - + + Hold ホールド - + No Pulse パルスなし - + Not set セットなし - + No pulses パルスなし - + Silent 消音 - + Beeps ビープ - + Voice 音声 - + Haptic タッチパネル - + Flight フライト - + Manual reset 手動リセット - + Step ステップ - + Display ディスプレイ - + Extended 拡張 - + Never なし - + On Change 変更 - + Always 常に - - - + + + Source 選択元 - + Trim idle only トリムアイドルのみ - + Warning 警告 - + Reversed リバース - + Tmr Tmr - + FrSky S.PORT - + FrSky D - + FrSky D (cable) - + Alti - + Alti+ - + VSpeed - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells セル - - Calculated - 計算値 - - - - Add - 追加 - - - - Average - 平均 - - - - + Min 最小 - - + Max 最大 - - Multiply - 乗算 - - - - Totalise - 合計 - - - - Cell - セル - - - - Consumption - 消費 - - - - Distance - 距離 - - - - Lowest - より低く - - - - Cell %1 - セル %1 - - - - Highest - より高く - - - - Delta - 差分 - - - - Formula - 定式 - - - - - Id - - - - - Instance - インスタンス - - - - - - - Sensor - センサー - - - - - Sources - 選択元 - - - - - GPS - GPS - - - - Alt. - Alt. - - - - - Blades - ブレード - - - - Multi. - マルチ. - - - - F - - - - - Inst - - - - - Alt - - - - - Unit - - - - - Prec - 精度 - - - - Ratio - レシオ - - - - Multi - マルチ - - - - A/Offset - A/オフセット - - - - Filter - フィルタ - - - - Persist - 持続 - - - - Positive - ノーマル - - - - Log - ログ - - - + Numbers ナンバー - + Bars バー - + Script スクリプト - + Filename ファイル名 - + Error: Unable to open or read file! エラー: ファイルを開けないか、読み取れません! @@ -9019,7 +8850,7 @@ If blank then the mix is considered to be "ON" all the time. Low Power - 低パワー + 低出力 @@ -9320,12 +9151,12 @@ If blank then the mix is considered to be "ON" all the time. 2 W - + Positive ノーマル - + Negative リバース @@ -9333,42 +9164,42 @@ If blank then the mix is considered to be "ON" all the time. ModulePanel - + Value 値 - + Hold ホールド - + No Pulse パルスなし - + Ask 確認 - + Internal 内部 - + Internal + External 内部 + 外部 - + External 外部 - + Bind on channel チャンネルへバインド @@ -9844,22 +9675,22 @@ If blank then the mix is considered to be "ON" all the time. OpenTxEepromInterface - + Unknown error 不明のエラー - + ... plus %1 errors ...追加 %1 エラー - + Cannot write radio settings 送信機設定を書き込めません - + Cannot write model %1 機体モデル %1 を書き込めません @@ -10007,12 +9838,12 @@ If blank then the mix is considered to be "ON" all the time. QObject - + WARNING 警告 - + <p>Importing JumperTX data into OpenTX 2.3 is <b>not supported and dangerous.</b></p> <p>It is unfortunately not possible for us to differentiate JumperTX data from legitimate FrSky X10 data, but <b>You should only continue here if the file you opened comes from a real FrSky X10.</b></p> <p>Do you really want to continue?</p> <p>JumperTXデータをOpenTX 2.3にインポートすることは<b>サポート対象外となり危険な行為です。</b></p> <p>残念ながら、JumperTXデータを正当なFrSky X10データと区別することができませんが、<b>開いたファイルが実際のFrSky X10で生成されたものである場合にのみ、続行する事ができます。</b></p> <p>本当に続行しますか?</p> @@ -10367,17 +10198,6 @@ X REb エンコーダ再生b - - - - ??? - - - - - ---- - - I @@ -10406,7 +10226,7 @@ X - + SRC @@ -10414,22 +10234,22 @@ X RawSwitch - + ↑ - + ↓ - + - - + ! @@ -10654,146 +10474,327 @@ X SensorData - - + + Formula + 定式 + + + + Id + ID + + + + Instance + インスタンス + + + + + Sensor + センサー + + + + Sources + 選択元 + + + + GPS + GPS + + + + Alt + 高度 + + + + Unit + ユニット + + + + Precision + 精度 + + + + Ratio + レシオ + + + + Offset + オフセット + + + + Auto Offset + 自動オフセット + + + + Blades + ブレード + + + + Multiplier + 乗算 + + + + Filter + フィルター + + + + Persist + 持続 + + + + Positive + ノーマル + + + + Log + ログ + + + + Custom + カスタム + + + + Calculated + 計算値 + + + + Add + 追加 + + + + Average + 平均 + + + + Minimum + 最低値 + + + + Maximum + 最大値 + + + + Multiply + 乗算 + + + + Totalize + 合計 + + + + Cell + セル + + + + Consumption + 消費 + + + + Distance + 距離 + + + + Lowest + より低く + + + + Cell %1 + セル %1 + + + + Highest + より高く + + + + Delta + 差分 + + + + Raw (-) + Raw (-) + + + + V V - + A - + mA - + kts - + m/s - + km/h - + mph - + m - + f - + °C - + °F - + % - + mAh - + W - + mW - + dB - + rpms - + g - + ° - + Rad - + hours - + minutes - + seconds - + ml/minute - + Hertz - + mS - + uS - + TELE + + + Y + Y + + + + N + N + Setup @@ -10975,92 +10976,92 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi SetupPanel - + Popup menu available 利用可能なポップアップメニュー - + Timer %1 タイマー %1 - + THR - + Profile Settings プロファイル設定 - + SD structure path not specified or invalid SDカード保存先パスが指定されていないか無効です - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Clear Timer. Are you sure? タイマーを消去します。よろしいですか? - + Clear all Timers. Are you sure? タイマーをすべて消去します。よろしいですか? - + Cut Timer. Are you sure? タイマーを切り取ります。よろしいですか? - + Delete Timer. Are you sure? タイマーを削除します。よろしいですか? @@ -11786,62 +11787,62 @@ The default is configured in the chosen Radio Profile. 送信機シミュレータ (%1) - + Could not determine startup data source. 起動データ元を特定できませんでした。 - + Could not load data, possibly wrong format. データをロードできません、フォーマットが間違っている可能性があります。 - + Data Load Error データロード エラー - + Invalid startup data provided. Plese specify a proper file/path. 無効なスタートアップデータが提供されました。正しいファイル/パスを指定してください。 - + Simulator Startup Error シミュレータ スタートアップ エラー - + Error saving data: could open file for writing: '%1' データ保存エラー: 書き込み用にファイルを開くことができました: 「%1」 - + Error saving data: could not get data from simulator interface. データ保存エラー: シミュレータのインターフェイスからデータを取得できませんでした。 - + An unexpected error occurred while attempting to save radio data to file '%1'. 送信機データをファイル「%1」に保存しようとしたときに予期せぬエラーが発生しました。 - + Data Save Error データ保存 エラー - + Cannot open joystick, joystick disabled スティックが接続不可、もしくはスティックが無効です - + Radio firmware error: %1 送信機 ファームウェア エラー: %1 - + - Flight Mode %1 (#%2) - フライトモード %1 (#%2) @@ -12514,22 +12515,22 @@ Too many errors, giving up. 最大 - + None なし - + Numbers ナンバー - + Bars バー - + Script スクリプト @@ -12537,109 +12538,52 @@ Too many errors, giving up. TelemetryPanel - + Telemetry screen %1 テレメトリースクリーン %1 - + FrSky S.PORT - + FrSky D - + FrSky D (cable) - + Source 信号元 - + Low Alarm 低アラーム - + Critical Alarm クリティカルアラーム - + Winged Shadow How High Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (サポートなし) - - Alti - - - - - Alti+ - - - - - VSpeed - - - - - - - A1 - - - - - - - A2 - - - - - - A3 - - - - - - A4 - - - - - - FAS - - - - - Cells - セル - - - - --- - --- - - - + Delete Sensor. Are you sure? テレメトリーセンサーを削除します。よろしいですか? @@ -12657,279 +12601,99 @@ Too many errors, giving up. TELE## - - Custom - カスタム - - - - Calculated - 計算値 - - - + Id ID - + Instance インスタンス - + Rx 受信 - - Add - 追加 - - - - Average - 平均 - - - - Min - 最小 - - - - Max - 最大 - - - - Multiply - 乗算 - - - - Totalize - 合計 - - - - Cell - セル - - - - Consumption - 消費 - - - - Dist - 距離 - - - + Cells Sensor : セル センサー : - - - - - - - - + + + + + + + + --- --- - + GPS Sensor : GPS センサー : - + Alt. Sensor : 高度 センサー : - + Sensor : センサー : - - Raw (-) - Raw (-) - - - - V - V - - - - A - - - - - mA - - - - - kt - - - - - m/s - - - - - ft/s - - - - - km/h - - - - - mph - - - - - m - 分 - - - - ft - - - - - °C - - - - - °F - - - - - % - - - - - mAh - - - - - W - - - - - mW - - - - - dBm - - - - - RPM - - - - - g - - - - - ° - - - - - Rad - - - - - mL - - - - - US fl.Oz. - - - - - ml/min - - - - + Precision 精度 - + Ratio レシオ - + Blades ブレード - + Offset オフセット - + Multiplier 乗算 - + Auto Offset 自動オフセット - + Filter - フィルタ + フィルター - + Persistent 持続 - + Positive ノーマル - + Logs ログ @@ -12937,92 +12701,72 @@ Too many errors, giving up. TelemetrySensorPanel - + TELE%1 - + Popup menu available 利用可能なポップアップメニュー - - Lowest - より低く - - - - Cell %1 - セル %1 - - - - Highest - より高く - - - - Delta - 差分 - - - + Copy コピー - + Cut 切り取り - + Paste 貼り付け - + Clear 消去 - + Insert 挿入 - + Delete 削除 - + Move Up 上へ移動 - + Move Down 下へ移動 - + Clear All すべて消去 - + Cut Telemetry Sensor. Are you sure? テレメトリーセンサーを切り取ります。よろしいですか? - + Clear Telemetry Sensor. Are you sure? テレメトリーセンサーを消去します。よろしいですか? - + Clear all Telemetry Sensors. Are you sure? テレメトリーセンサーのすべてを消去します。よろしいですか? @@ -13391,12 +13135,12 @@ hh:mm:ss Timer - + Countdown カウントダウン - + Minute Call 音声時報 @@ -13857,7 +13601,7 @@ CTRL+スクロールまたはPAGE UP/DOWNキーを押すと、大きなステ Has your helicopter got an adjustable gyro for the tail? - このヘリコプターはテールのアジャストジャイロを実装しましたか? + このヘリコプターはテールのジャイロ調整を実装しましたか? From 92b25e71fc1a63c81e5bacf1377a1726fc070a8b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 13 Feb 2021 22:30:45 +0100 Subject: [PATCH 162/300] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 19cc3b980..07df1ae15 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2137,3 +2137,6 @@ Hans-Reinhard Mette Alejandro Casals Oliver David Homewood John Dunning +Konrad Helbach +Bernard Delpy +Raymond Ellsworth From e94f6c0ccdc1ecd800f2ef27a28120878bc9ec41 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 14 Feb 2021 10:40:34 +0100 Subject: [PATCH 163/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 07df1ae15..669935391 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2140,3 +2140,4 @@ John Dunning Konrad Helbach Bernard Delpy Raymond Ellsworth +Bogdan Musial From 7eaede341aaf051ee66e71b2f0d7b5f3a91e4171 Mon Sep 17 00:00:00 2001 From: Risto Date: Sun, 14 Feb 2021 14:33:49 +0100 Subject: [PATCH 164/300] RTOS_MS_PER_TICK calculation simplification CFG_CPU_FREQ cancels itself out in the original equation: ((CFG_CPU_FREQ / CFG_SYSTICK_FREQ) / (CFG_CPU_FREQ / 1000)) In addition, comment is now fixed - 1ms is used for STM32, 2ms was used for other chips: https://github.com/opentx/opentx/blob/e94f6c0ccdc1ecd800f2ef27a28120878bc9ec41/radio/src/thirdparty/CoOS/OsConfig.h#L88-L92 --- radio/src/rtos.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/rtos.h b/radio/src/rtos.h index 6e42ebf30..98d0c0415 100644 --- a/radio/src/rtos.h +++ b/radio/src/rtos.h @@ -155,7 +155,7 @@ template } #endif - #define RTOS_MS_PER_TICK ((CFG_CPU_FREQ / CFG_SYSTICK_FREQ) / (CFG_CPU_FREQ / 1000)) // RTOS timer tick length in ms (currently 2) + #define RTOS_MS_PER_TICK (1000 / CFG_SYSTICK_FREQ) // RTOS timer tick length in ms (currently 1 for STM32, 2 for others) typedef OS_TID RTOS_TASK_HANDLE; typedef OS_MutexID RTOS_MUTEX_HANDLE; From 2d1276f08119f97f238d9b29e92f104fb221e570 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 15 Feb 2021 09:15:58 +0100 Subject: [PATCH 165/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 669935391..d83f82e79 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2141,3 +2141,5 @@ Konrad Helbach Bernard Delpy Raymond Ellsworth Bogdan Musial +Гасак Сергей +Philip Garcia From 24fdbcbbab0246213abd4c41e99f783d08cffee5 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 15 Feb 2021 13:10:29 +0100 Subject: [PATCH 166/300] Send 0 with getRSSI when no telem --- radio/src/lua/api_general.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index b205b1c70..60ebe39fe 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1411,7 +1411,10 @@ Get RSSI value as well as low and critical RSSI alarm levels (in dB) */ static int luaGetRSSI(lua_State * L) { - lua_pushunsigned(L, min((uint8_t)99, TELEMETRY_RSSI())); + if (TELEMETRY_STREAMING()) + lua_pushunsigned(L, min((uint8_t)99, TELEMETRY_RSSI())); + else + lua_pushunsigned(L, 0); lua_pushunsigned(L, g_model.rssiAlarms.getWarningRssi()); lua_pushunsigned(L, g_model.rssiAlarms.getCriticalRssi()); return 3; From d4aec69b3f1d7073a17e5d421781e6a7b4bd1ea7 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 15 Feb 2021 13:22:43 +0100 Subject: [PATCH 167/300] Allow a wider range for battery settings in companion to care for 1S --- companion/src/generaledit/generalsetup.ui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/companion/src/generaledit/generalsetup.ui b/companion/src/generaledit/generalsetup.ui index 7353b9c4b..422eddc7d 100644 --- a/companion/src/generaledit/generalsetup.ui +++ b/companion/src/generaledit/generalsetup.ui @@ -1527,7 +1527,7 @@ Mode 4: Battery warning voltage. This is the threashhold where the battery warning sounds. -Acceptable values are 5v..10v +Acceptable values are 3v..12v @@ -1539,7 +1539,7 @@ Acceptable values are 5v..10v 1 - 4.000000000000000 + 3.000000000000000 12.000000000000000 @@ -1679,7 +1679,7 @@ Acceptable values are 5v..10v 1 - 4.000000000000000 + 3.000000000000000 16.000000000000000 @@ -1708,7 +1708,7 @@ Acceptable values are 5v..10v 1 - 4.000000000000000 + 3.000000000000000 16.000000000000000 From 96eb5d2bb0aa190f64da943ddc7c2a3e2ae5a5d2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 15 Feb 2021 14:01:26 +0100 Subject: [PATCH 168/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d83f82e79..38c1f732d 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2143,3 +2143,4 @@ Raymond Ellsworth Bogdan Musial Гасак Сергей Philip Garcia +Helder Simoes From af8dff9da296e0c87cdd1ac8d215c1f9fc512752 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 15 Feb 2021 14:37:07 +0100 Subject: [PATCH 169/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 38c1f732d..8553a80fc 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2144,3 +2144,4 @@ Bogdan Musial Гасак Сергей Philip Garcia Helder Simoes +Thomas Blanchin From 9e254e7dd621ba69dbb2cff63923c7aeb3396eb5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 16 Feb 2021 10:43:15 +0100 Subject: [PATCH 170/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8553a80fc..ffa4dca00 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2145,3 +2145,4 @@ Bogdan Musial Philip Garcia Helder Simoes Thomas Blanchin +Jean-Luc Mesnier From eaa972cd2fbe2db5b975661227ba92022a752449 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 17 Feb 2021 11:48:08 +0100 Subject: [PATCH 171/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ffa4dca00..ff67af694 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2146,3 +2146,4 @@ Philip Garcia Helder Simoes Thomas Blanchin Jean-Luc Mesnier +Michael Bajer From 621fa17b85b920a4af46e3f57200962fa05106df Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Feb 2021 11:04:05 +0100 Subject: [PATCH 172/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ff67af694..38080254c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2147,3 +2147,4 @@ Helder Simoes Thomas Blanchin Jean-Luc Mesnier Michael Bajer +David Homewood From 85780d43026220c9d0676432f1c64b6f84c63ed9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Feb 2021 11:15:39 +0100 Subject: [PATCH 173/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 38080254c..38b255ccf 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2148,3 +2148,4 @@ Thomas Blanchin Jean-Luc Mesnier Michael Bajer David Homewood +Marc Sanders From ecfa393a9b8f673b8aa378c401a72d5f040f5fab Mon Sep 17 00:00:00 2001 From: Risto Date: Wed, 17 Feb 2021 17:29:42 +0100 Subject: [PATCH 174/300] Uniform CLI stackinfo output in bytes. Main task size currently in double words, the CoOS RTOS task stack sizes in bytes. Available stack size output was in double words, now everything uniform in bytes also added "bytes" label to CLI output to make this clear. --- radio/src/cli.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index cb72b8076..8d64e7972 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -699,11 +699,11 @@ int cliTrace(const char ** argv) int cliStackInfo(const char ** argv) { - serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize()); - serialPrint("[MENUS] %d available / %d", menusStack.available(), menusStack.size()); - serialPrint("[MIXER] %d available / %d", mixerStack.available(), mixerStack.size()); - serialPrint("[AUDIO] %d available / %d", audioStack.available(), audioStack.size()); - serialPrint("[CLI] %d available / %d", cliStack.available(), cliStack.size()); + serialPrint("[MAIN] %d available / %d bytes", stackAvailable()*4, stackSize()*4); + serialPrint("[MENUS] %d available / %d bytes", menusStack.available()*4, menusStack.size()); + serialPrint("[MIXER] %d available / %d bytes", mixerStack.available()*4, mixerStack.size()); + serialPrint("[AUDIO] %d available / %d bytes", audioStack.available()*4, audioStack.size()); + serialPrint("[CLI] %d available / %d bytes", cliStack.available()*4, cliStack.size()); return 0; } From cae6d56b74818ede0529d3ffec42136f289b117d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Feb 2021 08:49:25 +0100 Subject: [PATCH 175/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 38b255ccf..4b5871c38 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2149,3 +2149,4 @@ Jean-Luc Mesnier Michael Bajer David Homewood Marc Sanders +Буров Александр From 2a719199e3446c99c960b99fa7a744279125470d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Feb 2021 16:50:53 +0100 Subject: [PATCH 176/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 4b5871c38..e6623b971 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2150,3 +2150,4 @@ Michael Bajer David Homewood Marc Sanders Буров Александр +Rudy T'Jolleyn From d1a60cca8de80a8ecf5fc8a177f19d15cc37e816 Mon Sep 17 00:00:00 2001 From: 3djc Date: Fri, 19 Feb 2021 16:53:01 +0100 Subject: [PATCH 177/300] Multi : support SX1276 power indication and protect against unknown options coming from multi --- radio/src/gui/128x64/model_setup.cpp | 4 +++- radio/src/gui/212x64/model_setup.cpp | 3 +++ radio/src/gui/480x272/model_setup.cpp | 3 +++ radio/src/gui/gui_common.cpp | 5 +++++ radio/src/pulses/modules_helpers.h | 2 ++ radio/src/translations/untranslated.h | 2 +- 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 136ab6801..28ca2c0b9 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1544,7 +1544,9 @@ void menuModelSetup(event_t event) const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); if (status.isValid()) { - MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); + if (status.optionDisp >= getMaxMultiOptions()) { + status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) + } lcdDrawText(INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]); if (attr && status.optionDisp == 2) { lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 5a2d05446..390d55700 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1299,6 +1299,9 @@ void menuModelSetup(event_t event) MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(); if (status.isValid()) { + if (status.optionDisp >= getMaxMultiOptions()) { + status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) + } lcdDrawText(INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]); if (attr && status.optionDisp == 2) { lcdDrawText(MODEL_SETUP_3RD_COLUMN+22, y, "RSSI(", LEFT); diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index cd9974ac5..724f12db4 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1681,6 +1681,9 @@ bool menuModelSetup(event_t event) MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); if (status.isValid()) { + if (status.optionDisp >= getMaxMultiOptions()) { + status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) + } lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]); if (attr && status.optionDisp == 2) { lcdDrawNumber(LCD_W - 10, y, TELEMETRY_RSSI(), RIGHT, 0, "RSSI(", ")"); diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 368aac163..0cc14467a 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -997,6 +997,11 @@ const char* mm_options_strings::options[] = { STR_MULTI_RFPOWER }; +const uint8_t getMaxMultiOptions() +{ + return DIM(mm_options_strings::options); +} + const mm_protocol_definition multi_protocols[] = { // Protocol as defined in pulses\modules_constants.h, number of sub_protocols - 1, Failsafe supported, Disable channel mapping supported, Subtype string, Option type {MODULE_SUBTYPE_MULTI_FLYSKY, 4, false, true, STR_SUBTYPE_FLYSKY, nullptr}, diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 0272a3825..b01db972d 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -42,6 +42,8 @@ struct mm_options_strings { static const char* options[]; }; +const uint8_t getMaxMultiOptions(); + struct mm_protocol_definition { uint8_t protocol; uint8_t maxSubtype; diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 74735bc7f..2b1c05407 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -134,7 +134,7 @@ #define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""Futaba\0""J6 Pro\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FlSky2A""Q2x2\0 ""Walkera""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esky150""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMini""Traxxas""NCC1701""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpine""Potensi""ZSX\0 ""Height\0""Scanner""FrSkyRX""FS2A_RX""HoTT\0 ""FX816\0 ""BayanRX""Pelikan""Tiger\0 ""XK\0 ""XN297DU""FrSkyX2""FrSkyR9""Propel\0""FrSkyL\0""Skyartc""ESky-v2""DSM RX\0""JJRC345""Q90C\0 ""Kyosho\0""RadLink""ExpLRS\0""Realacc""OMP\0 ""M-Link\0""Wfly 2\0""E016Hv2" #define LEN_MULTI_POWER "\005" -#define TR_MULTI_POWER "10mW\0""25mW\0""50mW\0""100mW""200mW""300mW""500mW""1W\0 ""2W\0 " +#define TR_MULTI_POWER "1.6mW""2.0mW""2.5mW""3.2mW""4.0mW""5.0mW""6.3mW""7.9mW""10mW\0""13mW\0""16mW\0""20mW\0""25mW\0""32mW\0""40mW\0""50mW\0" #define LEN_AFHDS3_PROTOCOLS "\x008" #define TR_AFHDS3_PROTOCOLS "PWM/IBUS""PWM/SBUS""PPM/IBUS""PPM/SBUS" From 8e13639f5de489e7ab86fb37f14c3aa31a0127f2 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sat, 20 Feb 2021 07:39:24 +1100 Subject: [PATCH 178/300] Mirror rssi alarms in companion --- companion/src/modeledit/telemetry.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 8cda220a9..ec4491734 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -813,7 +813,9 @@ void TelemetryPanel::setup() ui->ignoreSensorIds->setField(model->frsky.ignoreSensorIds, this); ui->disableTelemetryAlarms->setField(model->rssiAlarms.disabled); + ui->rssiAlarmWarningSB->setRange(45 - 30, 45 + 30); ui->rssiAlarmWarningSB->setValue(model->rssiAlarms.warning); + ui->rssiAlarmCriticalSB->setRange(42 - 30, 42 + 30); ui->rssiAlarmCriticalSB->setValue(model->rssiAlarms.critical); ui->rssiSourceLabel->show(); From 10dadf1f785b5863255ad0f3128d5cbb334bf3d7 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 21 Feb 2021 12:25:47 +0100 Subject: [PATCH 179/300] Multi : improve options support part 1 --- radio/src/gui/128x64/model_setup.cpp | 46 ++++++++++----------------- radio/src/gui/212x64/model_setup.cpp | 46 ++++++++++----------------- radio/src/gui/480x272/model_setup.cpp | 40 +++++++++-------------- radio/src/gui/gui_common.cpp | 19 +++++++++++ radio/src/gui/gui_common.h | 1 + radio/src/translations.cpp | 1 + radio/src/translations.h | 1 + radio/src/translations/untranslated.h | 3 ++ 8 files changed, 73 insertions(+), 84 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 28ca2c0b9..ca60a17af 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1538,43 +1538,29 @@ void menuModelSetup(event_t event) { #if defined(MULTIMODULE) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { + const char * title = getMultiOptionTitle(moduleIdx); + + lcdDrawText(INDENT_WIDTH, y, title); + if (title == STR_MULTI_RFTUNE) { + lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); + lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT); + lcdDrawText(lcdLastRightPos, y, ")", LEFT); + } + int optionValue = g_model.moduleData[moduleIdx].multi.optionValue; - - MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); - - if (status.isValid()) { - if (status.optionDisp >= getMaxMultiOptions()) { - status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) - } - lcdDrawText(INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]); - if (attr && status.optionDisp == 2) { - lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); - lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT); - lcdDrawText(lcdLastRightPos, y, ")", LEFT); - } - } - else { - const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto); - if (pdef->optionsstr) { - lcdDrawText(INDENT_WIDTH, y, pdef->optionsstr); - if (attr && pdef->optionsstr == STR_MULTI_RFTUNE) { - lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); - lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT); - lcdDrawText(lcdLastRightPos, y, ")", LEFT); - } - } - } - int8_t min, max; getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); - } - else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { + if (title == STR_MULTI_RFPOWER) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); } + else if (title == STR_MULTI_TELEMETRY) { + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_TELEMETRY_MODE, optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 390d55700..6c698f53e 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1294,41 +1294,29 @@ void menuModelSetup(event_t event) #if defined(MULTIMODULE) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { + const char * title = getMultiOptionTitle(moduleIdx); + + lcdDrawText(INDENT_WIDTH, y, title); + if (title == STR_MULTI_RFTUNE) { + lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); + lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT); + lcdDrawText(lcdLastRightPos, y, ")", LEFT); + } + int optionValue = g_model.moduleData[moduleIdx].multi.optionValue; - - MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); - const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(); - if (status.isValid()) { - if (status.optionDisp >= getMaxMultiOptions()) { - status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) - } - lcdDrawText(INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]); - if (attr && status.optionDisp == 2) { - lcdDrawText(MODEL_SETUP_3RD_COLUMN+22, y, "RSSI(", LEFT); - lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT); - lcdDrawText(lcdLastRightPos, y, ")", LEFT); - } - } - else { - const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto); - if (pdef->optionsstr) - lcdDrawText(INDENT_WIDTH, y, pdef->optionsstr); - if (pdef->optionsstr == STR_MULTI_RFTUNE) { - lcdDrawText(MODEL_SETUP_3RD_COLUMN+22, y, "RSSI(", LEFT); - lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT); - lcdDrawText(lcdLastRightPos, y, ")", LEFT); - } - } - + const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); int8_t min, max; getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); - } - else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { + if (title == STR_MULTI_RFPOWER) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); } + else if (title == STR_MULTI_TELEMETRY) { + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_TELEMETRY_MODE, optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 724f12db4..55566524b 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1676,38 +1676,27 @@ bool menuModelSetup(event_t event) { #if defined(MULTIMODULE) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { + const char * title = getMultiOptionTitle(moduleIdx); + + lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, title); + if (title == STR_MULTI_RFTUNE) { + lcdDrawNumber(LCD_W - 10, y, TELEMETRY_RSSI(), RIGHT, 0, "RSSI(", ")"); + } + int optionValue = g_model.moduleData[moduleIdx].multi.optionValue; - - MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); - if (status.isValid()) { - if (status.optionDisp >= getMaxMultiOptions()) { - status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) - } - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]); - if (attr && status.optionDisp == 2) { - lcdDrawNumber(LCD_W - 10, y, TELEMETRY_RSSI(), RIGHT, 0, "RSSI(", ")"); - } - } - else { - const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto); - if (pdef->optionsstr) { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, pdef->optionsstr); - if (attr && pdef->optionsstr == STR_MULTI_RFTUNE) { - lcdDrawNumber(LCD_W - 10, y, TELEMETRY_RSSI(), RIGHT, 0, "RSSI(", ")"); - } - } - } - int8_t min, max; getMultiOptionValues(multi_proto, min, max); - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); - } - else if (multi_proto == MODULE_SUBTYPE_MULTI_FRSKY_R9) { + if (title == STR_MULTI_RFPOWER) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); } + else if (title == STR_MULTI_TELEMETRY) { + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_TELEMETRY_MODE, optionValue, LEFT | attr); + } + else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); + } else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, LEFT | attr, event); @@ -1718,6 +1707,7 @@ bool menuModelSetup(event_t event) else lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); } + if (attr) { CHECK_INCDEC_MODELVAR(event, optionValue, min, max); if (checkIncDec_Ret) { diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 0cc14467a..b493247d5 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -1096,3 +1096,22 @@ void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, Lcd else lcdDrawMMM(x, y, flags); } + +#if defined(MULTIMODULE) +const char* getMultiOptionTitle(uint8_t moduleIdx) +{ + MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); + + if (status.isValid()) { + if (status.optionDisp >= getMaxMultiOptions()) { + status.optionDisp = 1; // Unknown options are defaulted to type 1 (basic option) + } + return mm_options_strings::options[status.optionDisp]; + } + else { + const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); + const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto); + return pdef->optionsstr; + } +} +#endif diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 8f30a624e..784509378 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -318,6 +318,7 @@ inline uint8_t MODULE_OPTION_ROW(uint8_t moduleIdx) { } void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags); +const char* getMultiOptionTitle(uint8_t moduleIdx); const char * writeScreenshot(); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 455d6490d..94b53d85f 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -98,6 +98,7 @@ ISTR(INPUTNAMES); #if defined(MULTIMODULE) ISTR(MULTI_PROTOCOLS); ISTR(MULTI_POWER); +ISTR(MULTI_TELEMETRY_MODE); #endif #if defined(BLUETOOTH) diff --git a/radio/src/translations.h b/radio/src/translations.h index a9131d8a5..3e9ce3216 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -161,6 +161,7 @@ extern const char STR_R9M_PXX2_RF_PROTOCOLS[]; extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; extern const char STR_MULTI_POWER[]; +extern const char STR_MULTI_TELEMETRY_MODE[]; extern const char STR_SPORT_MODES[]; #if defined(AFHDS3) diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index 2b1c05407..da0a481c8 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -136,6 +136,9 @@ #define LEN_MULTI_POWER "\005" #define TR_MULTI_POWER "1.6mW""2.0mW""2.5mW""3.2mW""4.0mW""5.0mW""6.3mW""7.9mW""10mW\0""13mW\0""16mW\0""20mW\0""25mW\0""32mW\0""40mW\0""50mW\0" +#define LEN_MULTI_TELEMETRY_MODE "\007" +#define TR_MULTI_TELEMETRY_MODE "Off\0 ""On\0 ""Off+Aux""On+Aux\0" + #define LEN_AFHDS3_PROTOCOLS "\x008" #define TR_AFHDS3_PROTOCOLS "PWM/IBUS""PWM/SBUS""PPM/IBUS""PPM/SBUS" From 08a5015ea0c11f015ba3e9c626fead42e738c7ad Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 22 Feb 2021 08:07:38 +1100 Subject: [PATCH 180/300] Stop extended limits affecting output fields referencing gvars --- companion/src/modeledit/channels.cpp | 6 +++--- companion/src/modeledit/channels.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index 27374a8c4..f0353ee45 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -57,7 +57,7 @@ LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row spinbox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); QHBoxLayout *horizontalLayout = new QHBoxLayout(); - QCheckBox *gv = new QCheckBox(tr("GV")); + gv = new QCheckBox(tr("GV")); gv->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); horizontalLayout->addWidget(gv); QComboBox *cb = new QComboBox(); @@ -84,14 +84,14 @@ void LimitsGroup::updateMinMax(int max) if (spinbox->maximum() == 0) { spinbox->setMinimum(-max * displayStep); gvarGroup->setMinimum(-max); - if (value < -max) { + if (!gv->isChecked() && value < -max) { value = -max; } } if (spinbox->minimum() == 0) { spinbox->setMaximum(max * displayStep); gvarGroup->setMaximum(max); - if (value > max) { + if (!gv->isChecked() && value > max) { value = max; } } diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index 2a65f497a..70da877e7 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -50,6 +50,7 @@ class LimitsGroup GVarGroup *gvarGroup; int &value; double displayStep; + QCheckBox *gv; }; class ChannelsPanel : public ModelPanel From b978f6042335a02e30fba98b2920461d99e25c5f Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 22 Feb 2021 09:00:13 +0100 Subject: [PATCH 181/300] Fix Companion len script filename --- companion/src/modeledit/telemetry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 8cda220a9..336403bcf 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -111,7 +111,7 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, lock = false; if (IS_TARANIS(firmware->getBoard())) { - QSet scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/TELEMETRY", QStringList() << "*.lua", 8); + QSet scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/TELEMETRY", QStringList() << "*.lua", 6); Helpers::populateFileComboBox(ui->scriptName, scriptsSet, screen.body.script.filename); connect(ui->scriptName, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptNameEdited())); connect(ui->scriptName, SIGNAL(editTextChanged ( const QString)), this, SLOT(scriptNameEdited())); From 5c5683894e8a591c52eefca2db42089f26d0dbb1 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 22 Feb 2021 11:45:28 +0100 Subject: [PATCH 182/300] Multi : improve options support part 2 --- radio/src/gui/128x64/model_setup.cpp | 7 +++++++ radio/src/gui/212x64/model_setup.cpp | 5 +++++ radio/src/gui/480x272/model_setup.cpp | 7 +++++++ radio/src/gui/gui_common.cpp | 3 ++- radio/src/pulses/modules_helpers.h | 4 ---- radio/src/translations.cpp | 2 ++ radio/src/translations.h | 2 ++ radio/src/translations/cz.h.txt | 1 + radio/src/translations/de.h.txt | 1 + radio/src/translations/en.h.txt | 1 + radio/src/translations/es.h.txt | 1 + radio/src/translations/fi.h.txt | 1 + radio/src/translations/fr.h.txt | 1 + radio/src/translations/it.h.txt | 1 + radio/src/translations/nl.h.txt | 1 + radio/src/translations/pl.h.txt | 1 + radio/src/translations/pt.h.txt | 1 + radio/src/translations/se.h.txt | 1 + radio/src/translations/untranslated.h | 3 +++ 19 files changed, 39 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index ca60a17af..77ce40c42 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1554,6 +1554,8 @@ void menuModelSetup(event_t event) if (title == STR_MULTI_RFPOWER) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); + min = 0; + max = 15; } else if (title == STR_MULTI_TELEMETRY) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_TELEMETRY_MODE, optionValue, LEFT | attr); @@ -1561,6 +1563,11 @@ void menuModelSetup(event_t event) else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); } + else if (title == STR_MULTI_WBUS) { + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_WBUS_MODE, optionValue, LEFT | attr); + min = 0; + max = 1; + } else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { optionValue = optionValue & 0x01; editCheckBox(optionValue, MODEL_SETUP_2ND_COLUMN, y, "", LEFT | attr, event); diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 6c698f53e..47796ac79 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -1314,6 +1314,11 @@ void menuModelSetup(event_t event) else if (title == STR_MULTI_TELEMETRY) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_TELEMETRY_MODE, optionValue, LEFT | attr); } + else if (title == STR_MULTI_WBUS) { + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_WBUS_MODE, optionValue, LEFT | attr); + min = 0; + max = 1; + } else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); } diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index 55566524b..895b406da 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -1690,10 +1690,17 @@ bool menuModelSetup(event_t event) if (title == STR_MULTI_RFPOWER) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_POWER, optionValue, LEFT | attr); + min = 0; + max = 15; } else if (title == STR_MULTI_TELEMETRY) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_TELEMETRY_MODE, optionValue, LEFT | attr); } + else if (title == STR_MULTI_WBUS) { + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_WBUS_MODE, optionValue, LEFT | attr); + min = 0; + max = 1; + } else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, 50 + 5 * optionValue, LEFT | attr); } diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index b493247d5..98048c906 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -994,7 +994,8 @@ const char* mm_options_strings::options[] = { STR_MULTI_SERVOFREQ, STR_MULTI_MAX_THROW, STR_MULTI_RFCHAN, - STR_MULTI_RFPOWER + STR_MULTI_RFPOWER, + STR_MULTI_WBUS }; const uint8_t getMaxMultiOptions() diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index b01db972d..b2bd77de4 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -673,10 +673,6 @@ inline void getMultiOptionValues(int8_t multi_proto, int8_t & min, int8_t & max) min = -1; max = 84; break; - case MODULE_SUBTYPE_MULTI_FRSKY_R9: - min = 0; // 10mW - max = 5; // 300mW - break; default: min = -128; max = 127; diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 94b53d85f..c54248064 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -99,6 +99,7 @@ ISTR(INPUTNAMES); ISTR(MULTI_PROTOCOLS); ISTR(MULTI_POWER); ISTR(MULTI_TELEMETRY_MODE); +ISTR(MULTI_WBUS_MODE); #endif #if defined(BLUETOOTH) @@ -708,6 +709,7 @@ const char STR_BIND_OK[] = TR_BIND_OK; const char STR_MULTI_CUSTOM[] = TR_MULTI_CUSTOM; const char STR_MULTI_RFTUNE[] = TR_MULTI_RFTUNE; const char STR_MULTI_RFPOWER[] = TR_MULTI_RFPOWER; +const char STR_MULTI_WBUS[] = TR_MULTI_WBUS; const char STR_MULTI_TELEMETRY[] = TR_MULTI_TELEMETRY; const char STR_MULTI_VIDFREQ[] = TR_MULTI_VIDFREQ; const char STR_MULTI_OPTION[] = TR_MULTI_OPTION; diff --git a/radio/src/translations.h b/radio/src/translations.h index 3e9ce3216..90112a9f3 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -162,6 +162,7 @@ extern const char STR_DSM_PROTOCOLS[]; extern const char STR_MULTI_PROTOCOLS[]; extern const char STR_MULTI_POWER[]; extern const char STR_MULTI_TELEMETRY_MODE[]; +extern const char STR_MULTI_WBUS_MODE[]; extern const char STR_SPORT_MODES[]; #if defined(AFHDS3) @@ -404,6 +405,7 @@ extern const char STR_MULTI_OPTION[]; extern const char STR_MULTI_VIDFREQ[]; extern const char STR_MULTI_RFTUNE[]; extern const char STR_MULTI_RFPOWER[]; +extern const char STR_MULTI_WBUS[]; extern const char STR_MULTI_TELEMETRY[]; extern const char STR_MULTI_AUTOBIND[]; extern const char STR_DISABLE_CH_MAP[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 59eae1a46..26c96a182 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -639,6 +639,7 @@ #define TR_RECEIVER "Přijímač" #define TR_MULTI_RFTUNE TR(INDENT "Ladění frek", INDENT "Jemné ladění frek. RF") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetrie" #define TR_MULTI_VIDFREQ TR(INDENT "Freq. videa", INDENT "Frekvence videa") #define TR_RFPOWER "Výkon RF" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index fb36f0021..1e8d7ce96 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -641,6 +641,7 @@ #define TR_RECEIVER "Empfänger" #define TR_MULTI_RFTUNE TR(INDENT "RF Freq.", INDENT "RF Freq. Feintuning") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR(INDENT "Vid. Freq.", INDENT "Video Frequenz") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index f66e99a33..3ec8c1e31 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -640,6 +640,7 @@ #define TR_RECEIVER "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 0e5fa3dc7..31a0b8afc 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -642,6 +642,7 @@ #define TR_RECEIVER "Receptor" #define TR_MULTI_RFTUNE TR("Sint.freq.", "RF Freq. sint.fina") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetría" #define TR_MULTI_VIDFREQ TR("Freq.vídeo", "Frecuencia vídeo") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 0bd9c954f..1158806f0 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -658,6 +658,7 @@ #define TR_RECEIVER "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index fb818f7e8..5e59ba68d 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -661,6 +661,7 @@ #define TR_RECEIVER "Récept." #define TR_MULTI_RFTUNE TR(INDENT "Ajust.fréq", INDENT "Ajustement fréq.") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Télémétrie" #define TR_MULTI_VIDFREQ TR(INDENT "Fréq. vidéo", INDENT "Fréquence vidéo") #define TR_RFPOWER TR("Puiss. RF", "Puissance RF") diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 9a0785a1e..2e08ac45d 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -660,6 +660,7 @@ #define TR_RECEIVER "Ricevente" #define TR_MULTI_RFTUNE TR("Tune RF", "Tune fine Freq. RF") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetria" #define TR_MULTI_VIDFREQ TR("Freq. video", "Frequenza video") #define TR_RFPOWER "Potenza RF" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 13f567589..f9a2be448 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -646,6 +646,7 @@ #define TR_RECEIVER "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index bf59714cf..200072288 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -659,6 +659,7 @@ #define TR_RECEIVER INDENT "Receiver" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 8c794822e..0d1d597ac 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -661,6 +661,7 @@ #define TR_SYNCMENU "Sync [MENU]" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 54c081eb3..c3d3267d3 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -659,6 +659,7 @@ #define TR_SYNCMENU "Synk [MENU]" #define TR_MULTI_RFTUNE TR("Freq tune", "RF Freq. fine tune") #define TR_MULTI_RFPOWER "RF power" +#define TR_MULTI_WBUS "Output" #define TR_MULTI_TELEMETRY "Telemetry" #define TR_MULTI_VIDFREQ TR("Vid. freq.", "Video frequency") #define TR_RFPOWER "RF Power" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index da0a481c8..a758b1a31 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -136,6 +136,9 @@ #define LEN_MULTI_POWER "\005" #define TR_MULTI_POWER "1.6mW""2.0mW""2.5mW""3.2mW""4.0mW""5.0mW""6.3mW""7.9mW""10mW\0""13mW\0""16mW\0""20mW\0""25mW\0""32mW\0""40mW\0""50mW\0" +#define LEN_MULTI_WBUS_MODE "\004" +#define TR_MULTI_WBUS_MODE "WBUS""PPM\0" + #define LEN_MULTI_TELEMETRY_MODE "\007" #define TR_MULTI_TELEMETRY_MODE "Off\0 ""On\0 ""Off+Aux""On+Aux\0" From af7cf2321e8f8a906694e6620727c9ad2a6c62ce Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Feb 2021 15:29:33 +0100 Subject: [PATCH 183/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index e6623b971..8492196a9 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2151,3 +2151,5 @@ David Homewood Marc Sanders Буров Александр Rudy T'Jolleyn +Jay Watkins +Blaz Vodopivec From 0851e1123371e3d1469739cf44739e20f6e73a0e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Feb 2021 15:30:53 +0100 Subject: [PATCH 184/300] Update CREDITS.txt --- CREDITS.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8492196a9..6d3f4f6bc 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2153,3 +2153,7 @@ Marc Sanders Rudy T'Jolleyn Jay Watkins Blaz Vodopivec +David De Salvo +Willem Verschoren +Dennis Kathrens +Alan Aucote From fb00e928ea2f3e8e10581db04b1f28deb202b063 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Feb 2021 19:52:01 +0100 Subject: [PATCH 185/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 6d3f4f6bc..dfc22342c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2157,3 +2157,5 @@ David De Salvo Willem Verschoren Dennis Kathrens Alan Aucote +Richard Jerome Danstrom +David Garcia-Mendia From 45cd6432c7e8cec4c335fc2a6cd77750c53e457a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Feb 2021 22:22:27 +0100 Subject: [PATCH 186/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index dfc22342c..41c1461d4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2159,3 +2159,4 @@ Dennis Kathrens Alan Aucote Richard Jerome Danstrom David Garcia-Mendia +Mark Jones From 27d3bf8a3cd52a97e02053ca87398862ee972b10 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Feb 2021 18:41:21 +0100 Subject: [PATCH 187/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 41c1461d4..0e7467e02 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2160,3 +2160,4 @@ Alan Aucote Richard Jerome Danstrom David Garcia-Mendia Mark Jones +John Zseleczky From aa06a9d7d1d3543a9c67d299ab9ee1a37330a154 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 25 Feb 2021 07:40:50 +1100 Subject: [PATCH 188/300] Extended limits do not apply to offset --- companion/src/modeledit/channels.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index f0353ee45..b896c44d7 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -241,12 +241,11 @@ void ChannelsPanel::nameEdited() void ChannelsPanel::refreshExtendedLimits() { - int channelMax = model->getChannelsMax(); + int channelMax = model->getChannelsMax() * 10; for (int i = 0 ; i < CPN_MAX_CHNOUT; i++) { - chnOffset[i]->updateMinMax(10 * channelMax); - chnMin[i]->updateMinMax(10 * channelMax); - chnMax[i]->updateMinMax(10 * channelMax); + chnMin[i]->updateMinMax(channelMax); + chnMax[i]->updateMinMax(channelMax); } emit modified(); } From c2c9879de6e503df7a9162c9bab8747cf0edc0e2 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 25 Feb 2021 08:03:56 +1100 Subject: [PATCH 189/300] Allow decimals for channel min and max --- companion/src/modeledit/channels.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index b896c44d7..22ca3f3be 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -25,35 +25,26 @@ LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, int min, int max, int deflt, ModelPanel * panel): firmware(firmware), spinbox(new QDoubleSpinBox()), - value(value), - displayStep(0.1) + value(value) { Board::Type board = firmware->getBoard(); bool allowGVars = IS_HORUS_OR_TARANIS(board); - int internalStep = 1; spinbox->setProperty("index", row); spinbox->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); spinbox->setAccelerated(true); + spinbox->setDecimals(1); if (firmware->getCapability(PPMUnitMicroseconds)) { displayStep = 0.512; - spinbox->setDecimals(1); spinbox->setSuffix("us"); } else { - spinbox->setDecimals(0); + displayStep = 0.1; spinbox->setSuffix("%"); } - if (deflt == 0 /*it's the offset*/) { - spinbox->setDecimals(1); - } - else { - internalStep *= 10; - } - - spinbox->setSingleStep(displayStep * internalStep); + spinbox->setSingleStep(displayStep); spinbox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); QHBoxLayout *horizontalLayout = new QHBoxLayout(); From 71d3f1924918c18bb9803bcd03d6e982243f2ef4 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 25 Feb 2021 16:17:51 +0100 Subject: [PATCH 190/300] Cosmetcis --- radio/src/gui/gui_common.cpp | 2 +- radio/src/gui/gui_common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 98048c906..1bd84b21e 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -1099,7 +1099,7 @@ void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, Lcd } #if defined(MULTIMODULE) -const char* getMultiOptionTitle(uint8_t moduleIdx) +const char * getMultiOptionTitle(uint8_t moduleIdx) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 784509378..e55c16581 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -318,7 +318,7 @@ inline uint8_t MODULE_OPTION_ROW(uint8_t moduleIdx) { } void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags); -const char* getMultiOptionTitle(uint8_t moduleIdx); +const char * getMultiOptionTitle(uint8_t moduleIdx); const char * writeScreenshot(); From 1218e20104e1049f54706d425cb03aeaf9623a92 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 26 Feb 2021 10:46:26 +0100 Subject: [PATCH 191/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 0e7467e02..fd76180b4 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2161,3 +2161,4 @@ Richard Jerome Danstrom David Garcia-Mendia Mark Jones John Zseleczky +Xiao Sun From ad0134d3ce21522b65ce3454462a4735ff3ae129 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sat, 27 Feb 2021 08:08:20 +1100 Subject: [PATCH 192/300] Add gvref data model to value/gv fields in inputs, outputs and mixes --- companion/src/helpers.cpp | 50 ++++++++++--------------- companion/src/helpers.h | 11 +++--- companion/src/modeledit/channels.cpp | 27 +++++++------ companion/src/modeledit/channels.h | 15 ++++---- companion/src/modeledit/expodialog.cpp | 7 +++- companion/src/modeledit/mixerdialog.cpp | 8 +++- 6 files changed, 57 insertions(+), 61 deletions(-) diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index 020f1b45b..c1cd7de79 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -32,6 +32,7 @@ #include "simulatorinterface.h" #include "simulatormainwindow.h" #include "storage/sdcard.h" +#include "filtereditemmodels.h" #include #include @@ -117,7 +118,8 @@ void CompanionIcon::addImage(const QString & baseimage, Mode mode, State state) * GVarGroup */ -GVarGroup::GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBox * weightCB, int & weight, const ModelData & model, const int deflt, const int mini, const int maxi, const double step, bool allowGvars): +GVarGroup::GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBox * weightCB, int & weight, const ModelData & model, + const int deflt, const int mini, const int maxi, const double step, FilteredItemModel * gvarModel): QObject(), weightGV(weightGV), weightSB(weightSB), @@ -131,8 +133,8 @@ GVarGroup::GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBo step(step), lock(true) { - if (allowGvars && getCurrentFirmware()->getCapability(Gvars)) { - Helpers::populateGVCB(*weightCB, weight, model); + if (gvarModel && gvarModel->rowCount() > 0) { + weightCB->setModel(gvarModel); connect(weightGV, SIGNAL(stateChanged(int)), this, SLOT(gvarCBChanged(int))); connect(weightCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged())); } @@ -152,12 +154,19 @@ GVarGroup::GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBo void GVarGroup::gvarCBChanged(int state) { - weightCB->setVisible(state); - if (weightSB) + if (!lock) { + weightCB->setVisible(state); + if (weightGV->isChecked()) { + // set CB to +GV1 + int cnt = getCurrentFirmware()->getCapability(Gvars); + if (weightCB->count() > cnt) + weightCB->setCurrentIndex(cnt); + else + weightCB->setCurrentIndex(0); + } weightSB->setVisible(!state); - else - weightSB->setVisible(!state); - valuesChanged(); + valuesChanged(); + } } void GVarGroup::valuesChanged() @@ -168,7 +177,7 @@ void GVarGroup::valuesChanged() else if (sb) weight = sb->value(); else - weight = round(dsb->value()/step); + weight = round(dsb->value() / step); emit valueChanged(); } @@ -180,7 +189,7 @@ void GVarGroup::setWeight(int val) int tval; - if (val>maxi || val maxi || val < mini) { tval = deflt; weightGV->setChecked(true); weightSB->hide(); @@ -214,27 +223,6 @@ void GVarGroup::setWeight(int val) * Helpers namespace functions */ -void Helpers::populateGVCB(QComboBox & b, int value, const ModelData & model) -{ - int count = getCurrentFirmware()->getCapability(Gvars); - - b.clear(); - - for (int i=-count; i<=-1; i++) { - int16_t gval = (int16_t)(-10000+i); - b.addItem("-" + RawSource(SOURCE_TYPE_GVAR, abs(i)-1).toString(&model), gval); - } - - for (int i=1; i<=count; i++) { - int16_t gval = (int16_t)(10000+i); - b.addItem(RawSource(SOURCE_TYPE_GVAR, i-1).toString(&model), gval); - } - - b.setCurrentIndex(b.findData(value)); - if (b.currentIndex() == -1) - b.setCurrentIndex(count); -} - // TODO: Move lookup to GVarData class (w/out combobox) void Helpers::populateGvarUseCB(QComboBox * b, unsigned int phase) { diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 107ca2aa0..8bb1cc6b8 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _HELPERS_H_ -#define _HELPERS_H_ +#pragma once #include "eeprominterface.h" #include @@ -60,12 +59,15 @@ class CompanionIcon: public QIcon { void addImage(const QString &baseimage, Mode mode = Normal, State state = Off); }; +class FilteredItemModel; + class GVarGroup: public QObject { Q_OBJECT public: - GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBox * weightCB, int & weight, const ModelData & model, const int deflt, const int mini, const int maxi, const double step=1.0, bool allowGVars=true); + GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBox * weightCB, int & weight, const ModelData & model, + const int deflt, const int mini, const int maxi, const double step = 1.0, FilteredItemModel * gvarModel = nullptr); void setWeight(int val); @@ -101,7 +103,6 @@ class GVarGroup: public QObject { namespace Helpers { void populateGvarUseCB(QComboBox *b, unsigned int phase); - void populateGVCB(QComboBox & b, int value, const ModelData & model); void populateFileComboBox(QComboBox * b, const QSet & set, const QString & current); void getFileComboBoxValue(QComboBox * b, char * dest, int length); @@ -234,5 +235,3 @@ private: }; extern Stopwatch gStopwatch; - -#endif // _HELPERS_H_ diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index 22ca3f3be..5ec77ce55 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -22,14 +22,12 @@ #include "helpers.h" #include "filtereditemmodels.h" -LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, int min, int max, int deflt, ModelPanel * panel): +LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, + int min, int max, int deflt, FilteredItemModel * gvarModel, ModelPanel * panel): firmware(firmware), spinbox(new QDoubleSpinBox()), value(value) { - Board::Type board = firmware->getBoard(); - bool allowGVars = IS_HORUS_OR_TARANIS(board); - spinbox->setProperty("index", row); spinbox->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); spinbox->setAccelerated(true); @@ -56,7 +54,7 @@ LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row horizontalLayout->addWidget(cb); horizontalLayout->addWidget(spinbox); tableLayout->addLayout(row, col, horizontalLayout); - gvarGroup = new GVarGroup(gv, spinbox, cb, value, model, deflt, min, max, displayStep, allowGVars); + gvarGroup = new GVarGroup(gv, spinbox, cb, value, model, deflt, min, max, displayStep, gvarModel); QObject::connect(gvarGroup, &GVarGroup::valueChanged, panel, &ModelPanel::modified); } @@ -87,6 +85,7 @@ void LimitsGroup::updateMinMax(int max) } } } + ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels) @@ -94,8 +93,12 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting chnCapability = firmware->getCapability(Outputs); int channelNameMaxLen = firmware->getCapability(ChannelsName); - curveFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_Curve)); - connectItemModelEvents(curveFilteredModel); + dialogFilteredItemModels = new FilteredItemModelFactory(); + + int crvid = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_Curve)), "Curve"); + connectItemModelEvents(dialogFilteredItemModels->getItemModel(crvid)); + + int gvid = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef)), "GVarRef"); QStringList headerLabels; headerLabels << "#"; @@ -137,13 +140,13 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting } // Channel offset - chnOffset[i] = new LimitsGroup(firmware, tableLayout, i, col++, model.limitData[i].offset, model, -1000, 1000, 0, this); + chnOffset[i] = new LimitsGroup(firmware, tableLayout, i, col++, model.limitData[i].offset, model, -1000, 1000, 0, dialogFilteredItemModels->getItemModel(gvid), this); // Channel min - chnMin[i] = new LimitsGroup(firmware, tableLayout, i, col++, model.limitData[i].min, model, -model.getChannelsMax() * 10, 0, -1000, this); + chnMin[i] = new LimitsGroup(firmware, tableLayout, i, col++, model.limitData[i].min, model, -model.getChannelsMax() * 10, 0, -1000, dialogFilteredItemModels->getItemModel(gvid), this); // Channel max - chnMax[i] = new LimitsGroup(firmware, tableLayout, i, col++, model.limitData[i].max, model, 0, model.getChannelsMax() * 10, 1000, this); + chnMax[i] = new LimitsGroup(firmware, tableLayout, i, col++, model.limitData[i].max, model, 0, model.getChannelsMax() * 10, 1000, dialogFilteredItemModels->getItemModel(gvid), this); // Channel inversion invCB[i] = new QComboBox(this); @@ -156,7 +159,7 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { curveCB[i] = new QComboBox(this); curveCB[i]->setProperty("index", i); - curveCB[i]->setModel(curveFilteredModel); + curveCB[i]->setModel(dialogFilteredItemModels->getItemModel(crvid)); connect(curveCB[i], SIGNAL(currentIndexChanged(int)), this, SLOT(curveEdited())); tableLayout->addWidget(i, col++, curveCB[i]); } @@ -204,7 +207,7 @@ ChannelsPanel::~ChannelsPanel() delete centerSB[i]; delete symlimitsChk[i]; } - delete curveFilteredModel; + delete dialogFilteredItemModels; } void ChannelsPanel::symlimitsEdited() diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index 70da877e7..574e316d8 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _CHANNELS_H_ -#define _CHANNELS_H_ +#pragma once #include "helpers.h" #include "modeledit.h" @@ -27,7 +26,7 @@ #include class CompoundItemModelFactory; -class FilteredItemModel; +class FilteredItemModelFactory; constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel"; @@ -38,7 +37,8 @@ class LimitsGroup Q_DECLARE_TR_FUNCTIONS(LimitsGroup) public: - LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, int min, int max, int deflt, ModelPanel * panel=NULL); + LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, + int min, int max, int deflt, FilteredItemModel * gvarModel, ModelPanel * panel = nullptr); ~LimitsGroup(); void setValue(int val); @@ -58,7 +58,8 @@ class ChannelsPanel : public ModelPanel Q_OBJECT public: - ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); + ChannelsPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + CompoundItemModelFactory * sharedItemModels); virtual ~ChannelsPanel(); public slots: @@ -102,9 +103,7 @@ class ChannelsPanel : public ModelPanel int selectedIndex; int chnCapability; CompoundItemModelFactory *sharedItemModels; - FilteredItemModel *curveFilteredModel; void updateItemModels(); void connectItemModelEvents(const FilteredItemModel * itemModel); + FilteredItemModelFactory *dialogFilteredItemModels; }; - -#endif // _CHANNELS_H_ diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index abede1d73..be549144c 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -50,8 +50,11 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G setWindowTitle(tr("Edit %1").arg(RawSource(srcType, ed->chn).toString(&model, &generalSettings))); QRegExp rx(CHAR_FOR_NAMES_REGEX); - gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100); - gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef)), "GVarRef"); + gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100, 1.0, + dialogFilteredItemModels->getItemModel(id)); + gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100, 1.0, + dialogFilteredItemModels->getItemModel(id)); curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter); diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 645a25274..6cc39ffd1 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -55,9 +55,13 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); int limit = firmware->getCapability(OffsetWeight); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef)), "GVarRef"); + + gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit, 1.0, + dialogFilteredItemModels->getItemModel(id)); + gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit, 1.0, + dialogFilteredItemModels->getItemModel(id)); - gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit); - gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit); curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter); curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, From 9aaa329e4e54f650548920ea36aec1e9a0dfc4d1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 28 Feb 2021 17:32:10 +0100 Subject: [PATCH 193/300] Update CREDITS.txt --- CREDITS.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index fd76180b4..1fa8b356a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2162,3 +2162,7 @@ David Garcia-Mendia Mark Jones John Zseleczky Xiao Sun +Glenn Harvey +Albrecht Friebel +Gunter Klemke +Travis McCarthy From d213034bfd56be0e2c6c6be3e0db958f176fe836 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 28 Feb 2021 05:13:34 +1100 Subject: [PATCH 194/300] Add item data models for action and reset param --- .../src/firmwares/customfunctiondata.cpp | 88 +++++++++++++-- companion/src/firmwares/customfunctiondata.h | 16 ++- companion/src/modeledit/customfunctions.cpp | 102 ++++++++---------- companion/src/modeledit/customfunctions.h | 15 +-- 4 files changed, 143 insertions(+), 78 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index d4942c60c..56891dc46 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -22,6 +22,7 @@ #include "eeprominterface.h" #include "radiodata.h" #include "radiodataconversionstate.h" +#include "compounditemmodels.h" void CustomFunctionData::clear() { @@ -200,7 +201,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const else if (func >= FuncAdjustGV1 && func < FuncCount) { switch (adjustMode) { case FUNC_ADJUST_GVAR_CONSTANT: - return tr("Value ") + QString("%1").arg(param); + return tr("Value") + QString(" %1").arg(param); case FUNC_ADJUST_GVAR_SOURCE: case FUNC_ADJUST_GVAR_GVAR: return RawSource(param).toString(); @@ -209,7 +210,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const QString unit; val = param * model->gvarData[func - FuncAdjustGV1].multiplierGet(); unit = model->gvarData[func - FuncAdjustGV1].unitToString(); - return QString("Increment: %1%2").arg(val).arg(unit); + return tr("Increment") + QString(": %1%2").arg(val).arg(unit); } } return ""; @@ -217,15 +218,20 @@ QString CustomFunctionData::paramToString(const ModelData * model) const QString CustomFunctionData::repeatToString() const { - if (repeatParam == -1) { - return tr("played once, not during startup"); + return repeatToString(repeatParam); +} + +// static +QString CustomFunctionData::repeatToString(int value) const +{ + if (value == -1) { + return tr("Played once, not during startup"); } - else if (repeatParam == 0) { - return ""; + else if (value == 0) { + return tr("No repeat"); } else { - unsigned int step = 1; - return tr("repeat(%1s)").arg(step * repeatParam); + return tr("Repeat(%1s)").arg(value); } } @@ -312,3 +318,69 @@ void CustomFunctionData::convert(RadioDataConversionState & cstate) param = RawSource(param).convert(cstate.withComponentField("PARAM")).toValue(); } } + +// static +QString CustomFunctionData::harpicToString(int value) +{ + return QString("%1").arg(value); +} + +// static +QStringList CustomFunctionData::playsoundStringList() +{ + return QStringList("Beep 1" "Beep 2" "Beep 3" "Warn 1" "Warn 2" "Cheep" "Ratata" "Tick" + "Siren" "Ring" "SciFi" "Robot" "Chirp" "Tada" "Crickt" "AlmClk"); +} + +// static +QString CustomFunctionData::playsoundToString(int value) +{ + QStringList strl = playsoundStringList(); + if (value < strl.count()) + return strl.at(value); + else + return CPN_STR_UNKNOWN_ITEM; +} + +// static +AbstractStaticItemModel * CustomFunctionData::repeatItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("customfunctiondata.repeat"); + + for (int i = -1; i <= 60; i++) { + mdl->appendToItemList(repeatToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * CustomFunctionData::playSoundsItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("customfunctiondata.playsounds"); + + for (int i = 0; i <= 60; i++) { + mdl->appendToItemList(playsoundsToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * CustomFunctionData::harpicItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("customfunctiondata.harpic"); + + for (int i = 0; i <= 3; i++) { + mdl->appendToItemList(harpicToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 9e597441f..63c68e2d0 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef CUSTOMFUNCTIONDATA_H -#define CUSTOMFUNCTIONDATA_H +#pragma once #include "boards.h" #include "constants.h" @@ -32,6 +31,7 @@ class Firmware; class ModelData; class GeneralSettings; class RadioDataConversionState; +class AbstractStaticItemModel; enum AssignFunc { FuncOverrideCH1 = 0, @@ -109,6 +109,8 @@ class CustomFunctionData { QString paramToString(const ModelData * model) const; QString repeatToString() const; QString enabledToString() const; + QString playsoundsToString() const; + QString harpicToString() const; static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); static void populatePlaySoundParams(QStringList & qs); @@ -120,7 +122,11 @@ class CustomFunctionData { void convert(RadioDataConversionState & cstate); + static QString repeatToString(int value); + static QStringList playsoundStringList(); + static QString playsoundsToString(int value); + static QString harpicToString(int value); + static AbstractStaticItemModel * repeatItemModel(); + static AbstractStaticItemModel * playsoundsItemModel(); + static AbstractStaticItemModel * harpicItemModel(); }; - - -#endif // CUSTOMFUNCTIONDATA_H diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 5c15bd538..66f317ab2 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -30,7 +30,7 @@ RepeatComboBox::RepeatComboBox(QWidget *parent, int & repeatParam): repeatParam(repeatParam) { unsigned int step = 1; - int value = repeatParam/step; + int value = repeatParam / step; setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); @@ -59,7 +59,7 @@ void RepeatComboBox::onIndexChanged(int index) void RepeatComboBox::update() { unsigned int step = 1; - int value = repeatParam/step; + int value = repeatParam / step; if (step == 1) { value++; } @@ -77,18 +77,35 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, lock = true; fswCapability = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions); - rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), - model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext); - connectItemModelEvents(rawSwitchFilteredModel); + dlgFilterFactory = new FilteredItemModelFactory(); - rawSourceAllModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)); - connectItemModelEvents(rawSourceAllModel); + funcActionsId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CustomFuncAction), + model ? CustomFunctionData::AllFunctionContexts : CustomFunctionData::GlobalFunctionsContext), + "Function Actions"); + connectItemModelEvents(dlgFilterFactory->getItemModel(funcActionsId)); - rawSourceInputsModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::InputSourceGroups); - connectItemModelEvents(rawSourceInputsModel); + funcResetParamId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CustomFuncResetParam)), + "Reset Params"); + connectItemModelEvents(dlgFilterFactory->getItemModel(funcResetParamId)); - rawSourceGVarsModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::GVarsGroup); - connectItemModelEvents(rawSourceGVarsModel); + rawSwitchId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext), + "RawSwitch"); + connectItemModelEvents(dlgFilterFactory->getItemModel(rawSwitchId)); + + rawSourceAllId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)), + "RawSource All"); + connectItemModelEvents(dlgFilterFactory->getItemModel(rawSourceAllId)); + + rawSourceInputsId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + RawSource::InputSourceGroups), + "RawSource Inputs"); + connectItemModelEvents(dlgFilterFactory->getItemModel(rawSourceInputsId)); + + rawSourceGVarsId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + RawSource::GVarsGroup), + "RawSource GVars"); + connectItemModelEvents(dlgFilterFactory->getItemModel(rawSourceGVarsId)); if (!firmware->getCapability(VoicesAsNumbers)) { tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength)); @@ -135,32 +152,24 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint))); tableLayout->addWidget(i, 0, label); - // s1.report("label"); // The switch fswtchSwtch[i] = new QComboBox(this); - fswtchSwtch[i]->setModel(rawSwitchFilteredModel); + fswtchSwtch[i]->setModel(dlgFilterFactory->getItemModel(rawSwitchId)); fswtchSwtch[i]->setCurrentIndex(fswtchSwtch[i]->findData(functions[i].swtch.toValue())); fswtchSwtch[i]->setProperty("index", i); fswtchSwtch[i]->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); fswtchSwtch[i]->setMaxVisibleItems(10); connect(fswtchSwtch[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited())); tableLayout->addWidget(i, 1, fswtchSwtch[i]); - // s1.report("switch"); // The function fswtchFunc[i] = new QComboBox(this); - if (!i) { - populateFuncCB(fswtchFunc[i], functions[i].func); - } - else { - fswtchFunc[i]->setModel(fswtchFunc[0]->model()); - fswtchFunc[i]->setCurrentIndex(fswtchFunc[i]->findData(functions[i].func)); - } + fswtchFunc[i]->setModel(dlgFilterFactory->getItemModel(funcActionsId)); + fswtchFunc[i]->setCurrentIndex(fswtchFunc[i]->findData(functions[i].func)); fswtchFunc[i]->setProperty("index", i); connect(fswtchFunc[i], SIGNAL(currentIndexChanged(int)), this, SLOT(functionEdited())); tableLayout->addWidget(i, 2, fswtchFunc[i]); - // s1.report("func"); // The parameters QHBoxLayout * paramLayout = new QHBoxLayout(); @@ -250,10 +259,7 @@ CustomFunctionsPanel::~CustomFunctionsPanel() { if (mediaPlayer) stopSound(mediaPlayerCurrent); - delete rawSwitchFilteredModel; - delete rawSourceAllModel; - delete rawSourceInputsModel; - delete rawSourceGVarsModel; + delete dlgFilterFactory; } void CustomFunctionsPanel::onMediaPlayerStateChanged(QMediaPlayer::State state) @@ -653,33 +659,6 @@ void CustomFunctionsPanel::onCustomContextMenuRequested(QPoint pos) contextMenu.exec(globalPos); } -void CustomFunctionsPanel::populateFuncCB(QComboBox *b, unsigned int value) -{ - b->clear(); - for (unsigned int i = 0; i < FuncCount; i++) { - if (((i >= FuncOverrideCH1 && i <= FuncOverrideCH32) && (!model || !firmware->getCapability(SafetyChannelCustomFunction))) || - ((i == FuncVolume || i == FuncBackgroundMusic || i == FuncBackgroundMusicPause) && !firmware->getCapability(HasVolume)) || - ((i == FuncPlayScript && !IS_HORUS_OR_TARANIS(firmware->getBoard()))) || - ((i == FuncPlayHaptic) && !firmware->getCapability(Haptic)) || - ((i == FuncPlayBoth) && !firmware->getCapability(HasBeeper)) || - ((i == FuncLogs) && !firmware->getCapability(HasSDLogs)) || - ((i == FuncSetTimer3) && firmware->getCapability(Timers) < 3) || - ((i == FuncScreenshot) && !IS_HORUS_OR_TARANIS(firmware->getBoard())) || - ((i >= FuncRangeCheckInternalModule && i <= FuncBindExternalModule) && (!model || !firmware->getCapability(DangerousFunctions))) || - ((i >= FuncAdjustGV1 && i <= FuncAdjustGVLast) && (!model || !firmware->getCapability(Gvars))) - ) { - // skipped - continue; - } - else { - b->addItem(CustomFunctionData(AssignFunc(i)).funcToString(model), i); - if (i == value) { - b->setCurrentIndex(b->count() - 1); - } - } - } -} - void CustomFunctionsPanel::populateGVmodeCB(QComboBox *b, unsigned int value) { b->clear(); @@ -705,25 +684,30 @@ void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsi b->setCurrentIndex(value); } else if (function == FuncReset) { - CustomFunctionData::populateResetParams(model, b, value); + b->setModel(dlgFilterFactory->getItemModel(funcResetParamId)); + b->setCurrentIndex(b->findData(value)); } else if (function == FuncVolume || function == FuncBacklight) { - b->setModel(rawSourceInputsModel); + b->setModel(dlgFilterFactory->getItemModel(rawSourceInputsId)); b->setCurrentIndex(b->findData(value)); } else if (function == FuncPlayValue) { - b->setModel(rawSourceAllModel); + b->setModel(dlgFilterFactory->getItemModel(rawSourceAllId)); b->setCurrentIndex(b->findData(value)); } else if (function >= FuncAdjustGV1 && function <= FuncAdjustGVLast) { switch (adjustmode) { case 1: - b->setModel(rawSourceInputsModel); + b->setModel(dlgFilterFactory->getItemModel(rawSourceInputsId)); b->setCurrentIndex(b->findData(value)); + if (b->currentIndex() < 0) + b->setCurrentIndex(0); break; case 2: - b->setModel(rawSourceGVarsModel); + b->setModel(dlgFilterFactory->getItemModel(rawSourceGVarsId)); b->setCurrentIndex(b->findData(value)); + if (b->currentIndex() < 0) + b->setCurrentIndex(0); break; } } @@ -828,7 +812,7 @@ bool CustomFunctionsPanel::moveUpAllowed() const return selectedIndex > 0; } -void CustomFunctionsPanel::connectItemModelEvents(const FilteredItemModel * itemModel) +void CustomFunctionsPanel::connectItemModelEvents(FilteredItemModel * itemModel) { connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &CustomFunctionsPanel::onItemModelAboutToBeUpdated); connect(itemModel, &FilteredItemModel::updateComplete, this, &CustomFunctionsPanel::onItemModelUpdateComplete); diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index e41baf672..7a1fd9c39 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -27,6 +27,7 @@ #include class CompoundItemModelFactory; +class FilteredItemModelFactory; class FilteredItemModel; class TimerEdit; @@ -87,7 +88,6 @@ class CustomFunctionsPanel : public GenericPanel void onItemModelUpdateComplete(); private: - void populateFuncCB(QComboBox *b, unsigned int value); void populateGVmodeCB(QComboBox *b, unsigned int value); void populateFuncParamCB(QComboBox *b, uint function, unsigned int value, unsigned int adjustmode=0); bool hasClipboardData(QByteArray * data = nullptr) const; @@ -96,12 +96,15 @@ class CustomFunctionsPanel : public GenericPanel bool moveUpAllowed() const; void swapData(int idx1, int idx2); void resetCBsAndRefresh(int idx); - void connectItemModelEvents(const FilteredItemModel * itemModel); + void connectItemModelEvents(FilteredItemModel * itemModel); - FilteredItemModel * rawSwitchFilteredModel; - FilteredItemModel * rawSourceAllModel; - FilteredItemModel * rawSourceInputsModel; - FilteredItemModel * rawSourceGVarsModel; + FilteredItemModelFactory * dlgFilterFactory; + int funcActionsId; + int funcResetParamId; + int rawSwitchId; + int rawSourceAllId; + int rawSourceInputsId; + int rawSourceGVarsId; QSet tracksSet; QSet scriptsSet; From e6e41f18d7fe8a6dd52ade378749b80278b0c1e0 Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 1 Mar 2021 10:00:21 +1100 Subject: [PATCH 195/300] Add static models --- .../src/firmwares/customfunctiondata.cpp | 75 +++++++++---------- companion/src/firmwares/customfunctiondata.h | 14 ++-- companion/src/modeledit/customfunctions.cpp | 58 +++++++------- companion/src/modeledit/customfunctions.h | 5 +- 4 files changed, 75 insertions(+), 77 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 56891dc46..428e4eead 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -24,6 +24,16 @@ #include "radiodataconversionstate.h" #include "compounditemmodels.h" +void CustomFunctionData::convert(RadioDataConversionState & cstate) +{ + cstate.setComponent(tr("CFN"), 8); + cstate.setSubComp(nameToString(cstate.subCompIdx, (cstate.toModel() ? false : true))); + swtch.convert(cstate); + if (func == FuncVolume || func == FuncBacklight || func == FuncPlayValue || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast && adjustMode == 1)) { + param = RawSource(param).convert(cstate.withComponentField("PARAM")).toValue(); + } +} + void CustomFunctionData::clear() { memset(reinterpret_cast(this), 0, sizeof(CustomFunctionData)); @@ -95,9 +105,9 @@ QString CustomFunctionData::funcToString(const ModelData * model) const else if (func == FuncSetFailsafe) return tr("Set Failsafe"); else if (func == FuncRangeCheckInternalModule) - return tr("RangeCheck Int. Module"); + return tr("Range Check Int. Module"); else if (func == FuncRangeCheckExternalModule) - return tr("RangeCheck Ext. Module"); + return tr("Range Check Ext. Module"); else if (func == FuncBindInternalModule) return tr("Bind Int. Module"); else if (func == FuncBindExternalModule) @@ -143,17 +153,6 @@ void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox b->setCurrentIndex(b->findData(value)); } -void CustomFunctionData::populatePlaySoundParams(QStringList & qs) -{ - qs <<"Beep 1" << "Beep 2" << "Beep 3" << "Warn1" << "Warn2" << "Cheep" << "Ratata" << "Tick" << "Siren" << "Ring" ; - qs << "SciFi" << "Robot" << "Chirp" << "Tada" << "Crickt" << "AlmClk" ; -} - -void CustomFunctionData::populateHapticParams(QStringList & qs) -{ - qs << "0" << "1" << "2" << "3"; -} - QString CustomFunctionData::paramToString(const ModelData * model) const { QStringList qs; @@ -164,18 +163,10 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return QString("%1").arg(param / 10.0) + tr("s"); } else if (func == FuncPlaySound) { - CustomFunctionData::populatePlaySoundParams(qs); - if (param >= 0 && param < (int)qs.count()) - return qs.at(param); - else - return tr("Inconsistent parameter"); + return playSoundToString(param); } else if (func == FuncPlayHaptic) { - CustomFunctionData::populateHapticParams(qs); - if (param >= 0 && param < (int)qs.count()) - return qs.at(param); - else - return tr("Inconsistent parameter"); + return harpicToString(param); } else if (func == FuncReset) { QComboBox cb; @@ -184,7 +175,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const if (pos >= 0) return cb.itemText(pos); else - return tr("Inconsistent parameter"); + return QString(CPN_STR_UNKNOWN_ITEM); } else if (func == FuncVolume || func == FuncPlayValue || func == FuncBacklight) { RawSource item(param); @@ -222,7 +213,7 @@ QString CustomFunctionData::repeatToString() const } // static -QString CustomFunctionData::repeatToString(int value) const +QString CustomFunctionData::repeatToString(int value) { if (value == -1) { return tr("Played once, not during startup"); @@ -309,14 +300,9 @@ bool CustomFunctionData::isResetParamAvailable(const ModelData * model, int inde return false; } -void CustomFunctionData::convert(RadioDataConversionState & cstate) +QString CustomFunctionData::harpicToString() const { - cstate.setComponent(tr("CFN"), 8); - cstate.setSubComp(nameToString(cstate.subCompIdx, (cstate.toModel() ? false : true))); - swtch.convert(cstate); - if (func == FuncVolume || func == FuncBacklight || func == FuncPlayValue || (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast && adjustMode == 1)) { - param = RawSource(param).convert(cstate.withComponentField("PARAM")).toValue(); - } + return harpicToString(param); } // static @@ -326,20 +312,27 @@ QString CustomFunctionData::harpicToString(int value) } // static -QStringList CustomFunctionData::playsoundStringList() +QStringList CustomFunctionData::playSoundStringList() { - return QStringList("Beep 1" "Beep 2" "Beep 3" "Warn 1" "Warn 2" "Cheep" "Ratata" "Tick" - "Siren" "Ring" "SciFi" "Robot" "Chirp" "Tada" "Crickt" "AlmClk"); + QStringList strl; + strl << tr("Beep 1") << tr("Beep 2") << tr("Beep 3") << tr("Warn 1") << tr("Warn 2") << tr("Cheep") << tr("Ratata") << tr("Tick") + << tr("Siren") << tr("Ring") << tr("Sci Fi") << tr("Robot") << tr("Chirp") << tr("Tada") << tr("Cricket") << tr("Alarm Clock"); + return strl; +} + +QString CustomFunctionData::playSoundToString() const +{ + return playSoundToString(param); } // static -QString CustomFunctionData::playsoundToString(int value) +QString CustomFunctionData::playSoundToString(int value) { - QStringList strl = playsoundStringList(); + QStringList strl = playSoundStringList(); if (value < strl.count()) return strl.at(value); else - return CPN_STR_UNKNOWN_ITEM; + return QString(CPN_STR_UNKNOWN_ITEM); } // static @@ -357,13 +350,13 @@ AbstractStaticItemModel * CustomFunctionData::repeatItemModel() } // static -AbstractStaticItemModel * CustomFunctionData::playSoundsItemModel() +AbstractStaticItemModel * CustomFunctionData::playSoundItemModel() { AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); mdl->setName("customfunctiondata.playsounds"); - for (int i = 0; i <= 60; i++) { - mdl->appendToItemList(playsoundsToString(i), i); + for (int i = 0; i < playSoundStringList().count(); i++) { + mdl->appendToItemList(playSoundToString(i), i); } mdl->loadItemList(); diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 63c68e2d0..2301e6567 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -102,6 +102,8 @@ class CustomFunctionData { unsigned int adjustMode; int repeatParam; + void convert(RadioDataConversionState & cstate); + void clear(); bool isEmpty() const; QString nameToString(int index, bool globalContext = false) const; @@ -109,24 +111,20 @@ class CustomFunctionData { QString paramToString(const ModelData * model) const; QString repeatToString() const; QString enabledToString() const; - QString playsoundsToString() const; + QString playSoundToString() const; QString harpicToString() const; static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); - static void populatePlaySoundParams(QStringList & qs); - static void populateHapticParams(QStringList & qs); static bool isFuncAvailable(int index); static int funcContext(int index); static int resetParamCount(const ModelData * model); static bool isResetParamAvailable(const ModelData * model, int index); - void convert(RadioDataConversionState & cstate); - static QString repeatToString(int value); - static QStringList playsoundStringList(); - static QString playsoundsToString(int value); + static QStringList playSoundStringList(); + static QString playSoundToString(int value); static QString harpicToString(int value); static AbstractStaticItemModel * repeatItemModel(); - static AbstractStaticItemModel * playsoundsItemModel(); + static AbstractStaticItemModel * playSoundItemModel(); static AbstractStaticItemModel * harpicItemModel(); }; diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 66f317ab2..f2f2e07f1 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -19,6 +19,7 @@ */ #include "customfunctions.h" +#include "compounditemmodels.h" #include "filtereditemmodels.h" #include "helpers.h" #include "appdata.h" @@ -77,35 +78,39 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, lock = true; fswCapability = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions); - dlgFilterFactory = new FilteredItemModelFactory(); + tabModelFactory = new CompoundItemModelFactory(&generalSettings, model); + playSoundId = tabModelFactory->registerItemModel(CustomFunctionData::playSoundItemModel()); + harpicId = tabModelFactory->registerItemModel(CustomFunctionData::harpicItemModel()); - funcActionsId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CustomFuncAction), + tabFilterFactory = new FilteredItemModelFactory(); + + funcActionsId = tabFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CustomFuncAction), model ? CustomFunctionData::AllFunctionContexts : CustomFunctionData::GlobalFunctionsContext), "Function Actions"); - connectItemModelEvents(dlgFilterFactory->getItemModel(funcActionsId)); + connectItemModelEvents(tabFilterFactory->getItemModel(funcActionsId)); - funcResetParamId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CustomFuncResetParam)), + funcResetParamId = tabFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_CustomFuncResetParam)), "Reset Params"); - connectItemModelEvents(dlgFilterFactory->getItemModel(funcResetParamId)); + connectItemModelEvents(tabFilterFactory->getItemModel(funcResetParamId)); - rawSwitchId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + rawSwitchId = tabFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), model ? RawSwitch::SpecialFunctionsContext : RawSwitch::GlobalFunctionsContext), "RawSwitch"); - connectItemModelEvents(dlgFilterFactory->getItemModel(rawSwitchId)); + connectItemModelEvents(tabFilterFactory->getItemModel(rawSwitchId)); - rawSourceAllId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)), + rawSourceAllId = tabFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)), "RawSource All"); - connectItemModelEvents(dlgFilterFactory->getItemModel(rawSourceAllId)); + connectItemModelEvents(tabFilterFactory->getItemModel(rawSourceAllId)); - rawSourceInputsId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + rawSourceInputsId = tabFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::InputSourceGroups), "RawSource Inputs"); - connectItemModelEvents(dlgFilterFactory->getItemModel(rawSourceInputsId)); + connectItemModelEvents(tabFilterFactory->getItemModel(rawSourceInputsId)); - rawSourceGVarsId = dlgFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), + rawSourceGVarsId = tabFilterFactory->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource), RawSource::GVarsGroup), "RawSource GVars"); - connectItemModelEvents(dlgFilterFactory->getItemModel(rawSourceGVarsId)); + connectItemModelEvents(tabFilterFactory->getItemModel(rawSourceGVarsId)); if (!firmware->getCapability(VoicesAsNumbers)) { tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength)); @@ -155,7 +160,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, // The switch fswtchSwtch[i] = new QComboBox(this); - fswtchSwtch[i]->setModel(dlgFilterFactory->getItemModel(rawSwitchId)); + fswtchSwtch[i]->setModel(tabFilterFactory->getItemModel(rawSwitchId)); fswtchSwtch[i]->setCurrentIndex(fswtchSwtch[i]->findData(functions[i].swtch.toValue())); fswtchSwtch[i]->setProperty("index", i); fswtchSwtch[i]->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); @@ -165,7 +170,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, // The function fswtchFunc[i] = new QComboBox(this); - fswtchFunc[i]->setModel(dlgFilterFactory->getItemModel(funcActionsId)); + fswtchFunc[i]->setModel(tabFilterFactory->getItemModel(funcActionsId)); fswtchFunc[i]->setCurrentIndex(fswtchFunc[i]->findData(functions[i].func)); fswtchFunc[i]->setProperty("index", i); connect(fswtchFunc[i], SIGNAL(currentIndexChanged(int)), this, SLOT(functionEdited())); @@ -259,7 +264,8 @@ CustomFunctionsPanel::~CustomFunctionsPanel() { if (mediaPlayer) stopSound(mediaPlayerCurrent); - delete dlgFilterFactory; + delete tabModelFactory; + delete tabFilterFactory; } void CustomFunctionsPanel::onMediaPlayerStateChanged(QMediaPlayer::State state) @@ -674,37 +680,35 @@ void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsi QStringList qs; b->setModel(new QStandardItemModel(b)); // clear combo box but not any shared item model if (function == FuncPlaySound) { - CustomFunctionData::populatePlaySoundParams(qs); - b->addItems(qs); - b->setCurrentIndex(value); + b->setModel(tabModelFactory->getItemModel(playSoundId)); + b->setCurrentIndex(b->findData(value)); } else if (function == FuncPlayHaptic) { - CustomFunctionData::populateHapticParams(qs); - b->addItems(qs); - b->setCurrentIndex(value); + b->setModel(tabModelFactory->getItemModel(harpicId)); + b->setCurrentIndex(b->findData(value)); } else if (function == FuncReset) { - b->setModel(dlgFilterFactory->getItemModel(funcResetParamId)); + b->setModel(tabFilterFactory->getItemModel(funcResetParamId)); b->setCurrentIndex(b->findData(value)); } else if (function == FuncVolume || function == FuncBacklight) { - b->setModel(dlgFilterFactory->getItemModel(rawSourceInputsId)); + b->setModel(tabFilterFactory->getItemModel(rawSourceInputsId)); b->setCurrentIndex(b->findData(value)); } else if (function == FuncPlayValue) { - b->setModel(dlgFilterFactory->getItemModel(rawSourceAllId)); + b->setModel(tabFilterFactory->getItemModel(rawSourceAllId)); b->setCurrentIndex(b->findData(value)); } else if (function >= FuncAdjustGV1 && function <= FuncAdjustGVLast) { switch (adjustmode) { case 1: - b->setModel(dlgFilterFactory->getItemModel(rawSourceInputsId)); + b->setModel(tabFilterFactory->getItemModel(rawSourceInputsId)); b->setCurrentIndex(b->findData(value)); if (b->currentIndex() < 0) b->setCurrentIndex(0); break; case 2: - b->setModel(dlgFilterFactory->getItemModel(rawSourceGVarsId)); + b->setModel(tabFilterFactory->getItemModel(rawSourceGVarsId)); b->setCurrentIndex(b->findData(value)); if (b->currentIndex() < 0) b->setCurrentIndex(0); diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index 7a1fd9c39..e74ce0d66 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -98,13 +98,16 @@ class CustomFunctionsPanel : public GenericPanel void resetCBsAndRefresh(int idx); void connectItemModelEvents(FilteredItemModel * itemModel); - FilteredItemModelFactory * dlgFilterFactory; + CompoundItemModelFactory * tabModelFactory; + FilteredItemModelFactory * tabFilterFactory; int funcActionsId; int funcResetParamId; int rawSwitchId; int rawSourceAllId; int rawSourceInputsId; int rawSourceGVarsId; + int playSoundId; + int harpicId; QSet tracksSet; QSet scriptsSet; From cb4ee1b30dd76d165f6f8ae9b34452216903dc60 Mon Sep 17 00:00:00 2001 From: elecpower Date: Tue, 2 Mar 2021 22:23:31 +1100 Subject: [PATCH 196/300] Revert RepeatComboBox to standard ComboBox and add item model --- .../src/firmwares/customfunctiondata.cpp | 2 +- companion/src/modeledit/customfunctions.cpp | 62 +++---------------- companion/src/modeledit/customfunctions.h | 32 ++-------- 3 files changed, 16 insertions(+), 80 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 428e4eead..84e49ceb3 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -222,7 +222,7 @@ QString CustomFunctionData::repeatToString(int value) return tr("No repeat"); } else { - return tr("Repeat(%1s)").arg(value); + return tr("Repeat %1s").arg(value); } } diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index f2f2e07f1..6be75f28f 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -19,54 +19,11 @@ */ #include "customfunctions.h" -#include "compounditemmodels.h" -#include "filtereditemmodels.h" #include "helpers.h" #include "appdata.h" #include -RepeatComboBox::RepeatComboBox(QWidget *parent, int & repeatParam): - QComboBox(parent), - repeatParam(repeatParam) -{ - unsigned int step = 1; - int value = repeatParam / step; - - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - - if (step == 1) { - addItem(tr("Played once, not during startup"), -1); - value++; - } - - addItem(tr("No repeat"), 0); - - for (unsigned int i = step; i <= 60; i += step) { - addItem(tr("%1s").arg(i), i); - } - - setCurrentIndex(value); - - connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onIndexChanged(int))); -} - -void RepeatComboBox::onIndexChanged(int index) -{ - repeatParam = itemData(index).toInt(); - emit modified(); -} - -void RepeatComboBox::update() -{ - unsigned int step = 1; - int value = repeatParam / step; - if (step == 1) { - value++; - } - setCurrentIndex(value); -} - CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): GenericPanel(parent, model, generalSettings, firmware), @@ -81,6 +38,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, tabModelFactory = new CompoundItemModelFactory(&generalSettings, model); playSoundId = tabModelFactory->registerItemModel(CustomFunctionData::playSoundItemModel()); harpicId = tabModelFactory->registerItemModel(CustomFunctionData::harpicItemModel()); + repeatId = tabModelFactory->registerItemModel(CustomFunctionData::repeatItemModel()); tabFilterFactory = new FilteredItemModelFactory(); @@ -239,9 +197,12 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, QHBoxLayout * repeatLayout = new QHBoxLayout(); tableLayout->addLayout(i, 4, repeatLayout); - fswtchRepeat[i] = new RepeatComboBox(this, functions[i].repeatParam); + fswtchRepeat[i] = new QComboBox(this); + fswtchRepeat[i]->setProperty("index", i); + fswtchRepeat[i]->setModel(tabModelFactory->getItemModel(repeatId)); + fswtchRepeat[i]->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); repeatLayout->addWidget(fswtchRepeat[i], i + 1); - connect(fswtchRepeat[i], SIGNAL(modified()), this, SLOT(onRepeatModified())); + connect(fswtchRepeat[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited())); fswtchEnable[i] = new QCheckBox(this); fswtchEnable[i]->setProperty("index", i); @@ -375,11 +336,6 @@ void CustomFunctionsPanel::functionEdited() } } -void CustomFunctionsPanel::onRepeatModified() -{ - emit modified(); -} - void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) { CustomFunctionData & cfn = functions[i]; @@ -480,8 +436,10 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) } else if (func == FuncPlaySound || func == FuncPlayHaptic || func == FuncPlayValue || func == FuncPlayPrompt || func == FuncPlayBoth || func == FuncBackgroundMusic) { if (func != FuncBackgroundMusic) { + if (modified) + cfn.repeatParam = fswtchRepeat[i]->currentData().toInt(); widgetsMask |= CUSTOM_FUNCTION_REPEAT; - fswtchRepeat[i]->update(); + fswtchRepeat[i]->setCurrentIndex(fswtchRepeat[i]->findData(cfn.repeatParam)); } if (func == FuncPlayValue) { if (modified) @@ -505,7 +463,7 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) if (modified) { if (fswtchParamGV[i]->isChecked()) { fswtchParam[i]->setMinimum(1); - cfn.param = std::min(fswtchParam[i]->value(),5.0)+(fswtchParamGV[i]->isChecked() ? 250 : 0); + cfn.param = std::min(fswtchParam[i]->value(), 5.0) + (fswtchParamGV[i]->isChecked() ? 250 : 0); } else { cfn.param = fswtchParam[i]->value(); diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index e74ce0d66..1085e78b1 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -18,39 +18,19 @@ * GNU General Public License for more details. */ -#ifndef _CUSTOMFUNCTIONS_H_ -#define _CUSTOMFUNCTIONS_H_ +#pragma once #include "modeledit.h" #include "eeprominterface.h" +#include "compounditemmodels.h" +#include "filtereditemmodels.h" #include -class CompoundItemModelFactory; -class FilteredItemModelFactory; -class FilteredItemModel; class TimerEdit; constexpr char MIMETYPE_CUSTOM_FUNCTION[] = "application/x-companion-custom-function"; -class RepeatComboBox: public QComboBox -{ - Q_OBJECT - - public: - RepeatComboBox(QWidget * parent, int & repeatParam); - void update(); - - signals: - void modified(); - - private slots: - void onIndexChanged(int); - - protected: - int & repeatParam; -}; - class CustomFunctionsPanel : public GenericPanel { Q_OBJECT @@ -69,7 +49,6 @@ class CustomFunctionsPanel : public GenericPanel void functionEdited(); void onCustomContextMenuRequested(QPoint pos); void refreshCustomFunction(int index, bool modified=false); - void onRepeatModified(); bool playSound(int index); void stopSound(int index); void toggleSound(bool play); @@ -108,6 +87,7 @@ class CustomFunctionsPanel : public GenericPanel int rawSourceGVarsId; int playSoundId; int harpicId; + int repeatId; QSet tracksSet; QSet scriptsSet; @@ -121,7 +101,7 @@ class CustomFunctionsPanel : public GenericPanel QComboBox * fswtchParamT[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchParamArmT[CPN_MAX_SPECIAL_FUNCTIONS]; QCheckBox * fswtchEnable[CPN_MAX_SPECIAL_FUNCTIONS]; - RepeatComboBox * fswtchRepeat[CPN_MAX_SPECIAL_FUNCTIONS]; + QComboBox * fswtchRepeat[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchGVmode[CPN_MAX_SPECIAL_FUNCTIONS]; QSlider * fswtchBLcolor[CPN_MAX_SPECIAL_FUNCTIONS]; QMediaPlayer * mediaPlayer; @@ -130,5 +110,3 @@ class CustomFunctionsPanel : public GenericPanel int fswCapability; int modelsUpdateCnt; }; - -#endif // _CUSTOMFUNCTIONS_H_ From f0d4c88f03551282a41df432467d478b3bbf57e6 Mon Sep 17 00:00:00 2001 From: elecpower Date: Tue, 2 Mar 2021 23:17:21 +1100 Subject: [PATCH 197/300] Gvar adjust mode to item model --- .../src/firmwares/customfunctiondata.cpp | 45 ++++++++++++++++--- companion/src/firmwares/customfunctiondata.h | 8 +++- companion/src/modeledit/customfunctions.cpp | 17 ++----- companion/src/modeledit/customfunctions.h | 2 +- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 84e49ceb3..2cd9d27a2 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -314,10 +314,8 @@ QString CustomFunctionData::harpicToString(int value) // static QStringList CustomFunctionData::playSoundStringList() { - QStringList strl; - strl << tr("Beep 1") << tr("Beep 2") << tr("Beep 3") << tr("Warn 1") << tr("Warn 2") << tr("Cheep") << tr("Ratata") << tr("Tick") - << tr("Siren") << tr("Ring") << tr("Sci Fi") << tr("Robot") << tr("Chirp") << tr("Tada") << tr("Cricket") << tr("Alarm Clock"); - return strl; + return QStringList({ tr("Beep 1"), tr("Beep 2"), tr("Beep 3"), tr("Warn 1"), tr("Warn 2"), tr("Cheep"), tr("Ratata"), tr("Tick"), + tr("Siren"), tr("Ring"), tr("Sci Fi"), tr("Robot"), tr("Chirp"), tr("Tada"), tr("Cricket"), tr("Alarm Clock") }); } QString CustomFunctionData::playSoundToString() const @@ -328,13 +326,35 @@ QString CustomFunctionData::playSoundToString() const // static QString CustomFunctionData::playSoundToString(int value) { - QStringList strl = playSoundStringList(); + const QStringList strl = playSoundStringList(); if (value < strl.count()) return strl.at(value); else return QString(CPN_STR_UNKNOWN_ITEM); } +QString CustomFunctionData::gvarAdjustModeToString() const +{ + return gvarAdjustModeToString(adjustMode); +} + +// static +QString CustomFunctionData::gvarAdjustModeToString(int value) +{ + switch (value) { + case FUNC_ADJUST_GVAR_CONSTANT: + return tr("Value"); + case FUNC_ADJUST_GVAR_SOURCE: + return tr("Source"); + case FUNC_ADJUST_GVAR_GVAR: + return tr("GVAR"); + case FUNC_ADJUST_GVAR_INCDEC: + return tr("Increment"); + default: + return QString(CPN_STR_UNKNOWN_ITEM); + } +} + // static AbstractStaticItemModel * CustomFunctionData::repeatItemModel() { @@ -353,7 +373,7 @@ AbstractStaticItemModel * CustomFunctionData::repeatItemModel() AbstractStaticItemModel * CustomFunctionData::playSoundItemModel() { AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); - mdl->setName("customfunctiondata.playsounds"); + mdl->setName("customfunctiondata.playsound"); for (int i = 0; i < playSoundStringList().count(); i++) { mdl->appendToItemList(playSoundToString(i), i); @@ -377,3 +397,16 @@ AbstractStaticItemModel * CustomFunctionData::harpicItemModel() return mdl; } +// static +AbstractStaticItemModel * CustomFunctionData::gvarAdjustModeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("customfunctiondata.gvaradjustmode"); + + for (int i = 0; i < FUNC_ADJUST_GVAR_COUNT; i++) { + mdl->appendToItemList(gvarAdjustModeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 2301e6567..949ac9ee5 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -78,7 +78,8 @@ enum GVarAdjustModes FUNC_ADJUST_GVAR_CONSTANT, FUNC_ADJUST_GVAR_SOURCE, FUNC_ADJUST_GVAR_GVAR, - FUNC_ADJUST_GVAR_INCDEC + FUNC_ADJUST_GVAR_INCDEC, + FUNC_ADJUST_GVAR_COUNT }; class CustomFunctionData { @@ -113,18 +114,21 @@ class CustomFunctionData { QString enabledToString() const; QString playSoundToString() const; QString harpicToString() const; + QString gvarAdjustModeToString() const; static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); static bool isFuncAvailable(int index); static int funcContext(int index); static int resetParamCount(const ModelData * model); static bool isResetParamAvailable(const ModelData * model, int index); - static QString repeatToString(int value); static QStringList playSoundStringList(); static QString playSoundToString(int value); static QString harpicToString(int value); + static QStringList gvarAdjustModeStringList(); + static QString gvarAdjustModeToString(int value); static AbstractStaticItemModel * repeatItemModel(); static AbstractStaticItemModel * playSoundItemModel(); static AbstractStaticItemModel * harpicItemModel(); + static AbstractStaticItemModel * gvarAdjustModeItemModel(); }; diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 6be75f28f..05eefb96b 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -39,6 +39,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, playSoundId = tabModelFactory->registerItemModel(CustomFunctionData::playSoundItemModel()); harpicId = tabModelFactory->registerItemModel(CustomFunctionData::harpicItemModel()); repeatId = tabModelFactory->registerItemModel(CustomFunctionData::repeatItemModel()); + gvarAdjustModeId = tabModelFactory->registerItemModel(CustomFunctionData::gvarAdjustModeItemModel()); tabFilterFactory = new FilteredItemModelFactory(); @@ -140,7 +141,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, fswtchGVmode[i] = new QComboBox(this); fswtchGVmode[i]->setProperty("index", i); - populateGVmodeCB(fswtchGVmode[i], functions[i].adjustMode); + fswtchGVmode[i]->setModel(tabModelFactory->getItemModel(gvarAdjustModeId)); fswtchGVmode[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents); connect(fswtchGVmode[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited())); paramLayout->addWidget(fswtchGVmode[i]); @@ -382,8 +383,8 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) else if (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) { int gvidx = func - FuncAdjustGV1; if (modified) - cfn.adjustMode = fswtchGVmode[i]->currentIndex(); - fswtchGVmode[i]->setCurrentIndex(cfn.adjustMode); + cfn.adjustMode = fswtchGVmode[i]->currentData().toInt(); + fswtchGVmode[i]->setCurrentIndex(fswtchGVmode[i]->findData(cfn.adjustMode)); widgetsMask |= CUSTOM_FUNCTION_GV_MODE | CUSTOM_FUNCTION_ENABLE; if (cfn.adjustMode == FUNC_ADJUST_GVAR_CONSTANT || cfn.adjustMode == FUNC_ADJUST_GVAR_INCDEC) { if (modified) @@ -623,16 +624,6 @@ void CustomFunctionsPanel::onCustomContextMenuRequested(QPoint pos) contextMenu.exec(globalPos); } -void CustomFunctionsPanel::populateGVmodeCB(QComboBox *b, unsigned int value) -{ - b->clear(); - b->addItem(tr("Value")); - b->addItem(tr("Source")); - b->addItem(tr("GVAR")); - b->addItem(tr("Increment")); - b->setCurrentIndex(value); -} - void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsigned int value, unsigned int adjustmode) { QStringList qs; diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index 1085e78b1..6d1668114 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -67,7 +67,6 @@ class CustomFunctionsPanel : public GenericPanel void onItemModelUpdateComplete(); private: - void populateGVmodeCB(QComboBox *b, unsigned int value); void populateFuncParamCB(QComboBox *b, uint function, unsigned int value, unsigned int adjustmode=0); bool hasClipboardData(QByteArray * data = nullptr) const; bool insertAllowed() const; @@ -88,6 +87,7 @@ class CustomFunctionsPanel : public GenericPanel int playSoundId; int harpicId; int repeatId; + int gvarAdjustModeId; QSet tracksSet; QSet scriptsSet; From 263d2854a74c804fa98fb16f176fab91c28757c3 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 3 Mar 2021 07:24:43 +1100 Subject: [PATCH 198/300] Housekeeping --- companion/src/firmwares/customfunctiondata.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 2cd9d27a2..8d2d4e87c 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -178,8 +178,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return QString(CPN_STR_UNKNOWN_ITEM); } else if (func == FuncVolume || func == FuncPlayValue || func == FuncBacklight) { - RawSource item(param); - return item.toString(model); + return RawSource(param).toString(model); } else if (func == FuncPlayPrompt || func == FuncPlayBoth) { if ( getCurrentFirmware()->getCapability(VoicesAsNumbers)) { @@ -192,16 +191,14 @@ QString CustomFunctionData::paramToString(const ModelData * model) const else if (func >= FuncAdjustGV1 && func < FuncCount) { switch (adjustMode) { case FUNC_ADJUST_GVAR_CONSTANT: - return tr("Value") + QString(" %1").arg(param); + return gvarAdjustModeToString(adjustMode) + QString(" %1").arg(param); case FUNC_ADJUST_GVAR_SOURCE: case FUNC_ADJUST_GVAR_GVAR: return RawSource(param).toString(); case FUNC_ADJUST_GVAR_INCDEC: - float val; - QString unit; - val = param * model->gvarData[func - FuncAdjustGV1].multiplierGet(); - unit = model->gvarData[func - FuncAdjustGV1].unitToString(); - return tr("Increment") + QString(": %1%2").arg(val).arg(unit); + const float val = param * model->gvarData[func - FuncAdjustGV1].multiplierGet(); + const QString unit = model->gvarData[func - FuncAdjustGV1].unitToString(); + return gvarAdjustModeToString(adjustMode) + QString(": %1%2").arg(val).arg(unit); } } return ""; @@ -347,7 +344,7 @@ QString CustomFunctionData::gvarAdjustModeToString(int value) case FUNC_ADJUST_GVAR_SOURCE: return tr("Source"); case FUNC_ADJUST_GVAR_GVAR: - return tr("GVAR"); + return tr("Global Variable"); case FUNC_ADJUST_GVAR_INCDEC: return tr("Increment"); default: From 6feb5a3ca985ed35326d4d818e4c9c9d3c8e5f2e Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 3 Mar 2021 08:51:54 +1100 Subject: [PATCH 199/300] Refactor reset param functions --- .../src/datamodels/compounditemmodels.cpp | 4 +- .../src/firmwares/customfunctiondata.cpp | 94 +++++++++---------- companion/src/firmwares/customfunctiondata.h | 4 +- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index 039e90244..f47741341 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -447,7 +447,7 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin setId(IMID_CustomFuncResetParam); setUpdateMask(IMUE_TeleSensors); - for (int i = 0; i < CustomFunctionData::resetParamCount(modelData); i++) { + for (int i = 0; i < CustomFunctionData::resetParamCount(); i++) { QStandardItem * modelItem = new QStandardItem(); modelItem->setData(i, IMDR_Id); setDynamicItemData(modelItem, i); @@ -459,7 +459,7 @@ void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, con { CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); cfd.param = value; - item->setText(cfd.paramToString(modelData)); + item->setText(CustomFunctionData::resetToString(modelData)); item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IMDR_Available); } diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 8d2d4e87c..aa2f64bbd 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -117,42 +117,6 @@ QString CustomFunctionData::funcToString(const ModelData * model) const } } -void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox * b, unsigned int value = 0) -{ - int val = 0; - Firmware * firmware = Firmware::getCurrentVariant(); - - for (int i = 0; i < CPN_MAX_TIMERS; i++, val++) { - if (i < firmware->getCapability(Timers)) { - RawSource item = RawSource(SOURCE_TYPE_SPECIAL, i + SOURCE_TYPE_SPECIAL_TIMER1_IDX); - b->addItem(item.toString(model), val); - } - } - - b->addItem(tr("Flight"), val++); - b->addItem(tr("Telemetry"), val++); - - int reCount = firmware->getCapability(RotaryEncoders); - if (reCount == 1) { - b->addItem(tr("Rotary Encoder"), val++); - } - else if (reCount == 2) { - b->addItem(tr("REa"), val++); - b->addItem(tr("REb"), val++); - } - - if (model) { - for (int i = 0; i < firmware->getCapability(Sensors); ++i) { - if (model->sensorData[i].isAvailable()) { - RawSource item = RawSource(SOURCE_TYPE_TELEMETRY, 3 * i); - b->addItem(item.toString(model), val + i); - } - } - } - - b->setCurrentIndex(b->findData(value)); -} - QString CustomFunctionData::paramToString(const ModelData * model) const { QStringList qs; @@ -169,13 +133,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return harpicToString(param); } else if (func == FuncReset) { - QComboBox cb; - CustomFunctionData::populateResetParams(model, &cb); - int pos = cb.findData(param); - if (pos >= 0) - return cb.itemText(pos); - else - return QString(CPN_STR_UNKNOWN_ITEM); + return resetToString(model, param); } else if (func == FuncVolume || func == FuncPlayValue || func == FuncBacklight) { return RawSource(param).toString(model); @@ -270,12 +228,54 @@ int CustomFunctionData::funcContext(int index) return ret; } + // static -int CustomFunctionData::resetParamCount(const ModelData * model) +QString CustomFunctionData::resetToString(const ModelData * model, int value) { - QComboBox cb; - CustomFunctionData::populateResetParams(model, &cb); - return cb.count(); + Firmware * firmware = getCurrentFirmware(); + int step = CPN_MAX_TIMERS; + + if (value < step) { + if (value < firmware->getCapability(Timers)) + return RawSource(SOURCE_TYPE_SPECIAL, value + SOURCE_TYPE_SPECIAL_TIMER1_IDX).toString(model); + else + return QString(CPN_STR_UNKNOWN_ITEM); + } + + if (value < ++step) + return tr("Flight"); + + if (value < ++step) + return tr("Telemetry"); + + int reCount = firmware->getCapability(RotaryEncoders); + + if (reCount > 0 && value < step + reCount) { + if (reCount == 1 && value < step + 1) + return tr("Rotary Encoder"); + else if (reCount == 2) { + if (value < step + 1) + return tr("REa"); + else + return tr("REb"); + } + else + return QString(CPN_STR_UNKNOWN_ITEM); + } + step += reCount; + + if (value < step + firmware->getCapability(Sensors)) + return RawSource(SOURCE_TYPE_TELEMETRY, 3 * (value - step)).toString(model); + + return QString(CPN_STR_UNKNOWN_ITEM); +} + +// static +int CustomFunctionData::resetParamCount() +{ + Firmware * firmware = getCurrentFirmware(); + + return CPN_MAX_TIMERS + 2 + firmware->getCapability(RotaryEncoders) + firmware->getCapability(Sensors); } // static diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 949ac9ee5..3240256f3 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -116,10 +116,10 @@ class CustomFunctionData { QString harpicToString() const; QString gvarAdjustModeToString() const; - static void populateResetParams(const ModelData * model, QComboBox * b, unsigned int value); static bool isFuncAvailable(int index); static int funcContext(int index); - static int resetParamCount(const ModelData * model); + static QString resetToString(const ModelData * model, int value); + static int resetParamCount(); static bool isResetParamAvailable(const ModelData * model, int index); static QString repeatToString(int value); static QStringList playSoundStringList(); From c78a784236d3a5cb0b90ed0cb8ce9710bebd9028 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 3 Mar 2021 23:29:23 +1100 Subject: [PATCH 200/300] Compile and other tweaks --- companion/src/datamodels/compounditemmodels.cpp | 4 +--- companion/src/datamodels/compounditemmodels.h | 5 +---- companion/src/firmwares/customfunctiondata.cpp | 5 +++-- companion/src/modeledit/customfunctions.cpp | 13 ++++++++----- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index f47741341..24aac5aa9 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -457,9 +457,7 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - CustomFunctionData cfd = CustomFunctionData(AssignFunc::FuncReset); - cfd.param = value; - item->setText(CustomFunctionData::resetToString(modelData)); + item->setText(CustomFunctionData::resetToString(modelData, value)); item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IMDR_Available); } diff --git a/companion/src/datamodels/compounditemmodels.h b/companion/src/datamodels/compounditemmodels.h index de32fc902..c6d204cb3 100644 --- a/companion/src/datamodels/compounditemmodels.h +++ b/companion/src/datamodels/compounditemmodels.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef COMPOUNDITEMMODELS_H -#define COMPOUNDITEMMODELS_H +#pragma once #include "rawsource.h" #include "rawswitch.h" @@ -361,5 +360,3 @@ class CompoundItemModelFactory private: void setSourceId(AbstractItemModel * itemModel); }; - -#endif // COMPOUNDITEMMODELS_H diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index aa2f64bbd..b5bece769 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -262,6 +262,7 @@ QString CustomFunctionData::resetToString(const ModelData * model, int value) else return QString(CPN_STR_UNKNOWN_ITEM); } + step += reCount; if (value < step + firmware->getCapability(Sensors)) @@ -291,8 +292,8 @@ bool CustomFunctionData::isResetParamAvailable(const ModelData * model, int inde } else if (index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders)) return true; - else if (model && index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders) + firmware->getCapability(Sensors)) - return model->sensorData[index - CPN_MAX_TIMERS - firmware->getCapability(RotaryEncoders)].isAvailable(); + else if (model && index < resetParamCount()) + return model->sensorData[index - (CPN_MAX_TIMERS + 2 + firmware->getCapability(RotaryEncoders))].isAvailable(); return false; } diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 05eefb96b..7f3e8ee19 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -99,7 +99,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, playIcon.addImage("stop.png", QIcon::Normal, QIcon::On); QStringList headerLabels; - headerLabels << "#" << tr("Switch") << tr("Action") << tr("Parameters") << tr("Enable"); + headerLabels << "#" << tr("Switch") << tr("Action") << tr("Parameters") << ""; TableLayout * tableLayout = new TableLayout(this, fswCapability, headerLabels); for (int i = 0; i < fswCapability; i++) { @@ -119,19 +119,21 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, // The switch fswtchSwtch[i] = new QComboBox(this); + fswtchSwtch[i]->setProperty("index", i); fswtchSwtch[i]->setModel(tabFilterFactory->getItemModel(rawSwitchId)); fswtchSwtch[i]->setCurrentIndex(fswtchSwtch[i]->findData(functions[i].swtch.toValue())); - fswtchSwtch[i]->setProperty("index", i); fswtchSwtch[i]->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); + fswtchSwtch[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents); fswtchSwtch[i]->setMaxVisibleItems(10); connect(fswtchSwtch[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited())); tableLayout->addWidget(i, 1, fswtchSwtch[i]); // The function fswtchFunc[i] = new QComboBox(this); + fswtchFunc[i]->setProperty("index", i); fswtchFunc[i]->setModel(tabFilterFactory->getItemModel(funcActionsId)); fswtchFunc[i]->setCurrentIndex(fswtchFunc[i]->findData(functions[i].func)); - fswtchFunc[i]->setProperty("index", i); + fswtchFunc[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents); connect(fswtchFunc[i], SIGNAL(currentIndexChanged(int)), this, SLOT(functionEdited())); tableLayout->addWidget(i, 2, fswtchFunc[i]); @@ -148,8 +150,8 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, fswtchParamGV[i] = new QCheckBox(this); fswtchParamGV[i]->setProperty("index", i); - fswtchParamGV[i]->setText("GV"); - fswtchParamGV[i]->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); + fswtchParamGV[i]->setText(tr("GV")); + fswtchParamGV[i]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); connect(fswtchParamGV[i], SIGNAL(stateChanged(int)), this, SLOT(customFunctionEdited())); paramLayout->addWidget(fswtchParamGV[i]); @@ -202,6 +204,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, fswtchRepeat[i]->setProperty("index", i); fswtchRepeat[i]->setModel(tabModelFactory->getItemModel(repeatId)); fswtchRepeat[i]->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); + fswtchRepeat[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents); repeatLayout->addWidget(fswtchRepeat[i], i + 1); connect(fswtchRepeat[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited())); From 6c3f7b1ca64b324bdade41ae2ba7368bea52cba0 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 4 Mar 2021 07:59:24 +1100 Subject: [PATCH 201/300] Consistency and statics --- .../src/datamodels/compounditemmodels.cpp | 6 ++--- .../src/firmwares/customfunctiondata.cpp | 27 ++++++++++++------- companion/src/firmwares/customfunctiondata.h | 21 ++++++++------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index 24aac5aa9..83e046c13 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -420,7 +420,7 @@ CustomFuncActionItemModel::CustomFuncActionItemModel(const GeneralSettings * con void CustomFuncActionItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(CustomFunctionData(AssignFunc(value)).funcToString(modelData)); + item->setText(CustomFunctionData::funcToString((AssignFunc)value, modelData)); item->setData(CustomFunctionData::isFuncAvailable(value), IMDR_Available); } @@ -457,8 +457,8 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin void CustomFuncResetParamItemModel::setDynamicItemData(QStandardItem * item, const int value) const { - item->setText(CustomFunctionData::resetToString(modelData, value)); - item->setData(CustomFunctionData::isResetParamAvailable(modelData, value), IMDR_Available); + item->setText(CustomFunctionData::resetToString(value, modelData)); + item->setData(CustomFunctionData::isResetParamAvailable(value, modelData), IMDR_Available); } void CustomFuncResetParamItemModel::update(const int event) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index b5bece769..43fd3c149 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -47,12 +47,19 @@ bool CustomFunctionData::isEmpty() const return (swtch.type == SWITCH_TYPE_NONE); } -QString CustomFunctionData::nameToString(int index, bool globalContext) const +// static +QString CustomFunctionData::nameToString(const int index, const bool globalContext) { return RadioData::getElementName((globalContext ? tr("GF") : tr("SF")), index + 1, 0, true); } QString CustomFunctionData::funcToString(const ModelData * model) const +{ + return funcToString(func, model); +} + +// static +QString CustomFunctionData::funcToString(const AssignFunc func, const ModelData * model) { if (func >= FuncOverrideCH1 && func <= FuncOverrideCHLast) return tr("Override %1").arg(RawSource(SOURCE_TYPE_CH, func).toString(model)); @@ -133,7 +140,7 @@ QString CustomFunctionData::paramToString(const ModelData * model) const return harpicToString(param); } else if (func == FuncReset) { - return resetToString(model, param); + return resetToString(param, model); } else if (func == FuncVolume || func == FuncPlayValue || func == FuncBacklight) { return RawSource(param).toString(model); @@ -168,7 +175,7 @@ QString CustomFunctionData::repeatToString() const } // static -QString CustomFunctionData::repeatToString(int value) +QString CustomFunctionData::repeatToString(const int value) { if (value == -1) { return tr("Played once, not during startup"); @@ -198,7 +205,7 @@ QString CustomFunctionData::enabledToString() const } // static -bool CustomFunctionData::isFuncAvailable(int index) +bool CustomFunctionData::isFuncAvailable(const int index) { Firmware * fw = getCurrentFirmware(); @@ -217,7 +224,7 @@ bool CustomFunctionData::isFuncAvailable(int index) } // static -int CustomFunctionData::funcContext(int index) +int CustomFunctionData::funcContext(const int index) { int ret = AllFunctionContexts; @@ -230,7 +237,7 @@ int CustomFunctionData::funcContext(int index) } // static -QString CustomFunctionData::resetToString(const ModelData * model, int value) +QString CustomFunctionData::resetToString(const int value, const ModelData * model) { Firmware * firmware = getCurrentFirmware(); int step = CPN_MAX_TIMERS; @@ -280,7 +287,7 @@ int CustomFunctionData::resetParamCount() } // static -bool CustomFunctionData::isResetParamAvailable(const ModelData * model, int index) +bool CustomFunctionData::isResetParamAvailable(const int index, const ModelData * model) { Firmware * firmware = getCurrentFirmware(); @@ -304,7 +311,7 @@ QString CustomFunctionData::harpicToString() const } // static -QString CustomFunctionData::harpicToString(int value) +QString CustomFunctionData::harpicToString(const int value) { return QString("%1").arg(value); } @@ -322,7 +329,7 @@ QString CustomFunctionData::playSoundToString() const } // static -QString CustomFunctionData::playSoundToString(int value) +QString CustomFunctionData::playSoundToString(const int value) { const QStringList strl = playSoundStringList(); if (value < strl.count()) @@ -337,7 +344,7 @@ QString CustomFunctionData::gvarAdjustModeToString() const } // static -QString CustomFunctionData::gvarAdjustModeToString(int value) +QString CustomFunctionData::gvarAdjustModeToString(const int value) { switch (value) { case FUNC_ADJUST_GVAR_CONSTANT: diff --git a/companion/src/firmwares/customfunctiondata.h b/companion/src/firmwares/customfunctiondata.h index 3240256f3..d5a08a96f 100644 --- a/companion/src/firmwares/customfunctiondata.h +++ b/companion/src/firmwares/customfunctiondata.h @@ -107,26 +107,27 @@ class CustomFunctionData { void clear(); bool isEmpty() const; - QString nameToString(int index, bool globalContext = false) const; QString funcToString(const ModelData * model = nullptr) const; - QString paramToString(const ModelData * model) const; + QString paramToString(const ModelData * model = nullptr) const; QString repeatToString() const; QString enabledToString() const; QString playSoundToString() const; QString harpicToString() const; QString gvarAdjustModeToString() const; - static bool isFuncAvailable(int index); - static int funcContext(int index); - static QString resetToString(const ModelData * model, int value); + static QString nameToString(const int index, const bool globalContext = false); + static QString funcToString(const AssignFunc func, const ModelData * model = nullptr); + static bool isFuncAvailable(const int index); + static int funcContext(const int index); + static QString resetToString(const int value, const ModelData * model = nullptr); static int resetParamCount(); - static bool isResetParamAvailable(const ModelData * model, int index); - static QString repeatToString(int value); + static bool isResetParamAvailable(const int index, const ModelData * model = nullptr); + static QString repeatToString(const int value); static QStringList playSoundStringList(); - static QString playSoundToString(int value); - static QString harpicToString(int value); + static QString playSoundToString(const int value); + static QString harpicToString(const int value); static QStringList gvarAdjustModeStringList(); - static QString gvarAdjustModeToString(int value); + static QString gvarAdjustModeToString(const int value); static AbstractStaticItemModel * repeatItemModel(); static AbstractStaticItemModel * playSoundItemModel(); static AbstractStaticItemModel * harpicItemModel(); From 3ba689d98927182ee39e1eff5202e56fbe4b1076 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 4 Mar 2021 08:05:15 +1100 Subject: [PATCH 202/300] Remove unused slider --- companion/src/modeledit/customfunctions.cpp | 11 ----------- companion/src/modeledit/customfunctions.h | 1 - 2 files changed, 12 deletions(-) diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 7f3e8ee19..c773e90e5 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -182,15 +182,6 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, connect(fswtchParamArmT[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited())); connect(fswtchParamArmT[i], SIGNAL(editTextChanged ( const QString)), this, SLOT(customFunctionEdited())); - fswtchBLcolor[i] = new QSlider(this); - fswtchBLcolor[i]->setProperty("index", i); - fswtchBLcolor[i]->setMinimum(0); - fswtchBLcolor[i]->setMaximum(100); - fswtchBLcolor[i]->setSingleStep(1); - fswtchBLcolor[i]->setOrientation(Qt::Horizontal); - paramLayout->addWidget(fswtchBLcolor[i]); - connect(fswtchBLcolor[i], SIGNAL(sliderReleased()), this, SLOT(customFunctionEdited())); - playBT[i] = new QToolButton(this); playBT[i]->setProperty("index", i); playBT[i]->setIcon(playIcon); @@ -310,7 +301,6 @@ void CustomFunctionsPanel::toggleSound(bool play) #define CUSTOM_FUNCTION_ENABLE (1<<6) #define CUSTOM_FUNCTION_REPEAT (1<<7) #define CUSTOM_FUNCTION_PLAY (1<<8) -#define CUSTOM_FUNCTION_BL_COLOR (1<<9) #define CUSTOM_FUNCTION_SHOW_FUNC (1<<10) @@ -551,7 +541,6 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) fswtchEnable[i]->setChecked(false); fswtchRepeat[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_REPEAT); fswtchGVmode[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_GV_MODE); - fswtchBLcolor[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_BL_COLOR); playBT[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_PLAY); } diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index 6d1668114..87775246d 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -103,7 +103,6 @@ class CustomFunctionsPanel : public GenericPanel QCheckBox * fswtchEnable[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchRepeat[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchGVmode[CPN_MAX_SPECIAL_FUNCTIONS]; - QSlider * fswtchBLcolor[CPN_MAX_SPECIAL_FUNCTIONS]; QMediaPlayer * mediaPlayer; int selectedIndex; From 227b225deaf778a60ce9641edf29bf098992028b Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 4 Mar 2021 08:22:45 +1100 Subject: [PATCH 203/300] Consistency with radio --- companion/src/firmwares/customfunctiondata.cpp | 4 ++-- companion/src/modeledit/customfunctions.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index 43fd3c149..d78c1a349 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -94,7 +94,7 @@ QString CustomFunctionData::funcToString(const AssignFunc func, const ModelData else if (func == FuncPlayValue) return tr("Play Value"); else if (func == FuncPlayScript) - return tr("Play Script"); + return tr("Lua Script"); else if (func == FuncLogs) return tr("SD Logs"); else if (func == FuncVolume) @@ -354,7 +354,7 @@ QString CustomFunctionData::gvarAdjustModeToString(const int value) case FUNC_ADJUST_GVAR_GVAR: return tr("Global Variable"); case FUNC_ADJUST_GVAR_INCDEC: - return tr("Increment"); + return tr("Inc/Decrement"); default: return QString(CPN_STR_UNKNOWN_ITEM); } diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index c773e90e5..230cf5e4c 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -201,7 +201,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, fswtchEnable[i] = new QCheckBox(this); fswtchEnable[i]->setProperty("index", i); - fswtchEnable[i]->setText(tr("ON")); + fswtchEnable[i]->setText(tr("Enable")); fswtchEnable[i]->setFixedWidth(200); repeatLayout->addWidget(fswtchEnable[i], i + 1); connect(fswtchEnable[i], SIGNAL(stateChanged(int)), this, SLOT(customFunctionEdited())); From fc03ba58d08302bab895178b26d6908b9df855bb Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 4 Mar 2021 08:25:47 +1100 Subject: [PATCH 204/300] Revert enable label --- companion/src/modeledit/customfunctions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 230cf5e4c..c773e90e5 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -201,7 +201,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, fswtchEnable[i] = new QCheckBox(this); fswtchEnable[i]->setProperty("index", i); - fswtchEnable[i]->setText(tr("Enable")); + fswtchEnable[i]->setText(tr("ON")); fswtchEnable[i]->setFixedWidth(200); repeatLayout->addWidget(fswtchEnable[i], i + 1); connect(fswtchEnable[i], SIGNAL(stateChanged(int)), this, SLOT(customFunctionEdited())); From 19add4de921d6f143c07ab6657818dd0683d4673 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 4 Mar 2021 08:47:21 +1100 Subject: [PATCH 205/300] Remove some depreciated Rotary Encoder code --- .../src/datamodels/compounditemmodels.cpp | 3 --- .../src/firmwares/customfunctiondata.cpp | 23 +++---------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index 83e046c13..f72eb4f34 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -109,7 +109,6 @@ RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSett addItems(SOURCE_TYPE_LUA_OUTPUT, RawSource::ScriptsGroup, firmware->getCapability(LuaOutputsPerScript), i * 16); addItems(SOURCE_TYPE_VIRTUAL_INPUT, RawSource::InputsGroup, firmware->getCapability(VirtualInputs)); addItems(SOURCE_TYPE_STICK, RawSource::SourcesGroup, board->getCapability(Board::MaxAnalogs)); - addItems(SOURCE_TYPE_ROTARY_ENCODER, RawSource::SourcesGroup, firmware->getCapability(RotaryEncoders)); addItems(SOURCE_TYPE_TRIM, RawSource::TrimsGroup, board->getCapability(Board::NumTrims)); addItems(SOURCE_TYPE_MAX, RawSource::SourcesGroup, 1); addItems(SOURCE_TYPE_SWITCH, RawSource::SwitchesGroup, board->getCapability(Board::Switches)); @@ -170,7 +169,6 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett addItems(SWITCH_TYPE_TELEMETRY, -1); addItems(SWITCH_TYPE_FLIGHT_MODE, -firmware->getCapability(FlightModes)); addItems(SWITCH_TYPE_VIRTUAL, -firmware->getCapability(LogicalSwitches)); - addItems(SWITCH_TYPE_ROTARY_ENCODER, -firmware->getCapability(RotaryEncoders)); addItems(SWITCH_TYPE_TRIM, -board->getCapability(Board::NumTrimSwitches)); addItems(SWITCH_TYPE_MULTIPOS_POT, -(board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions))); addItems(SWITCH_TYPE_SWITCH, -board->getCapability(Board::SwitchPositions)); @@ -181,7 +179,6 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett addItems(SWITCH_TYPE_SWITCH, board->getCapability(Board::SwitchPositions)); addItems(SWITCH_TYPE_MULTIPOS_POT, board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions)); addItems(SWITCH_TYPE_TRIM, board->getCapability(Board::NumTrimSwitches)); - addItems(SWITCH_TYPE_ROTARY_ENCODER, firmware->getCapability(RotaryEncoders)); addItems(SWITCH_TYPE_VIRTUAL, firmware->getCapability(LogicalSwitches)); addItems(SWITCH_TYPE_FLIGHT_MODE, firmware->getCapability(FlightModes)); addItems(SWITCH_TYPE_TELEMETRY, 1); diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index d78c1a349..98157cae2 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -255,23 +255,6 @@ QString CustomFunctionData::resetToString(const int value, const ModelData * mod if (value < ++step) return tr("Telemetry"); - int reCount = firmware->getCapability(RotaryEncoders); - - if (reCount > 0 && value < step + reCount) { - if (reCount == 1 && value < step + 1) - return tr("Rotary Encoder"); - else if (reCount == 2) { - if (value < step + 1) - return tr("REa"); - else - return tr("REb"); - } - else - return QString(CPN_STR_UNKNOWN_ITEM); - } - - step += reCount; - if (value < step + firmware->getCapability(Sensors)) return RawSource(SOURCE_TYPE_TELEMETRY, 3 * (value - step)).toString(model); @@ -283,7 +266,7 @@ int CustomFunctionData::resetParamCount() { Firmware * firmware = getCurrentFirmware(); - return CPN_MAX_TIMERS + 2 + firmware->getCapability(RotaryEncoders) + firmware->getCapability(Sensors); + return CPN_MAX_TIMERS + 2 + firmware->getCapability(Sensors); } // static @@ -297,10 +280,10 @@ bool CustomFunctionData::isResetParamAvailable(const int index, const ModelData else return false; } - else if (index < CPN_MAX_TIMERS + firmware->getCapability(RotaryEncoders)) + else if (index < CPN_MAX_TIMERS + 2) return true; else if (model && index < resetParamCount()) - return model->sensorData[index - (CPN_MAX_TIMERS + 2 + firmware->getCapability(RotaryEncoders))].isAvailable(); + return model->sensorData[index - (CPN_MAX_TIMERS + 2)].isAvailable(); return false; } From 3062490dacb4facc9ae8b2e51a5931b35e218ad9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 5 Mar 2021 10:39:03 +0100 Subject: [PATCH 206/300] Update CREDITS.txt --- CREDITS.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 1fa8b356a..64b706c70 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2166,3 +2166,7 @@ Glenn Harvey Albrecht Friebel Gunter Klemke Travis McCarthy +Franz Zier +Ian Contessa +Sean Sadler +Richard F Janczak From f8498ddf86f37ecf4a1eb962dec0776d220984dc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 5 Mar 2021 10:39:30 +0100 Subject: [PATCH 207/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 64b706c70..8d20bdd78 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2170,3 +2170,4 @@ Franz Zier Ian Contessa Sean Sadler Richard F Janczak +Mateusz Dziadosz From 9ae4184240207b37158f5d37226b2be307e1d2c0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 5 Mar 2021 23:13:43 +0100 Subject: [PATCH 208/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8d20bdd78..5104a3af0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2171,3 +2171,4 @@ Ian Contessa Sean Sadler Richard F Janczak Mateusz Dziadosz +Thomas Merchant From 5b0de67d5d7ea5720d104cc4a56ee6548e1e5bec Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 5 Mar 2021 23:24:29 +0100 Subject: [PATCH 209/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 5104a3af0..63b59bbca 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2172,3 +2172,4 @@ Sean Sadler Richard F Janczak Mateusz Dziadosz Thomas Merchant +M L Perkins From 694355c1b4484f196bbcb80e83760eec6178e1d6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 6 Mar 2021 20:01:15 +0100 Subject: [PATCH 210/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 63b59bbca..8965152da 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2173,3 +2173,4 @@ Richard F Janczak Mateusz Dziadosz Thomas Merchant M L Perkins +Stefano Boggia From ea301e44edcb1f5a51fd59ee476f2c7e952facda Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 7 Mar 2021 10:14:12 +1100 Subject: [PATCH 211/300] Refactor io_data.h and cpp into separate xdata h and cpp files --- companion/src/firmwares/CMakeLists.txt | 7 +- companion/src/firmwares/curvedata.cpp | 48 +++++ companion/src/firmwares/curvedata.h | 56 +++++ .../{io_data.cpp => flightmodedata.cpp} | 121 +---------- companion/src/firmwares/flightmodedata.h | 59 ++++++ companion/src/firmwares/heli_data.cpp | 21 ++ companion/src/firmwares/heli_data.h | 44 ++++ companion/src/firmwares/input_data.cpp | 35 +++ companion/src/firmwares/input_data.h | 61 ++++++ companion/src/firmwares/io_data.h | 199 ------------------ companion/src/firmwares/mixdata.cpp | 35 +++ companion/src/firmwares/mixdata.h | 65 ++++++ companion/src/firmwares/modeldata.h | 12 +- companion/src/firmwares/output_data.cpp | 60 ++++++ companion/src/firmwares/output_data.h | 52 +++++ 15 files changed, 554 insertions(+), 321 deletions(-) create mode 100644 companion/src/firmwares/curvedata.cpp create mode 100644 companion/src/firmwares/curvedata.h rename companion/src/firmwares/{io_data.cpp => flightmodedata.cpp} (56%) create mode 100644 companion/src/firmwares/flightmodedata.h create mode 100644 companion/src/firmwares/heli_data.cpp create mode 100644 companion/src/firmwares/heli_data.h create mode 100644 companion/src/firmwares/input_data.cpp create mode 100644 companion/src/firmwares/input_data.h delete mode 100644 companion/src/firmwares/io_data.h create mode 100644 companion/src/firmwares/mixdata.cpp create mode 100644 companion/src/firmwares/mixdata.h create mode 100644 companion/src/firmwares/output_data.cpp create mode 100644 companion/src/firmwares/output_data.h diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index bf05bc44d..3bc2ca275 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -2,16 +2,21 @@ set(firmwares_SRCS adjustmentreference.cpp boards.cpp + curvedata.cpp curvereference.cpp customfunctiondata.cpp eeprominterface.cpp generalsettings.cpp gvardata.cpp - io_data.cpp + input_data.cpp + flightmodedata.cpp + heli_data.cpp logicalswitchdata.cpp + mixdata.cpp modeldata.cpp moduledata.cpp multiprotocols.cpp + output_data.cpp radiodata.cpp radiodataconversionstate.cpp rawsource.cpp diff --git a/companion/src/firmwares/curvedata.cpp b/companion/src/firmwares/curvedata.cpp new file mode 100644 index 000000000..c32eac4cc --- /dev/null +++ b/companion/src/firmwares/curvedata.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "curvedata.h" +#include "radiodata.h" + +CurveData::CurveData() +{ + clear(); +} + +void CurveData::clear(int count) +{ + memset(reinterpret_cast(this), 0, sizeof(CurveData)); + this->count = count; +} + +bool CurveData::isEmpty() const +{ + for (int i = 0; i < count; i++) { + if (points[i].y != 0) { + return false; + } + } + return true; +} + +QString CurveData::nameToString(const int idx) const +{ + return RadioData::getElementName(tr("CV"), idx + 1, name); +} diff --git a/companion/src/firmwares/curvedata.h b/companion/src/firmwares/curvedata.h new file mode 100644 index 000000000..cb083375e --- /dev/null +++ b/companion/src/firmwares/curvedata.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "constants.h" + +#include + +class CurvePoint { + public: + int8_t x; + int8_t y; +}; + +#define CURVEDATA_NAME_LEN 6 + +class CurveData { + Q_DECLARE_TR_FUNCTIONS(CurveData) + + public: + enum CurveType { + CURVE_TYPE_STANDARD, + CURVE_TYPE_CUSTOM, + CURVE_TYPE_LAST = CURVE_TYPE_CUSTOM + }; + + CurveData(); + + CurveType type; + bool smooth; + int count; + CurvePoint points[CPN_MAX_POINTS]; + char name[CURVEDATA_NAME_LEN + 1]; + + void clear(int count = 5); + bool isEmpty() const; + QString nameToString(const int idx) const; +}; diff --git a/companion/src/firmwares/io_data.cpp b/companion/src/firmwares/flightmodedata.cpp similarity index 56% rename from companion/src/firmwares/io_data.cpp rename to companion/src/firmwares/flightmodedata.cpp index a958750d8..c0a268e96 100644 --- a/companion/src/firmwares/io_data.cpp +++ b/companion/src/firmwares/flightmodedata.cpp @@ -18,121 +18,17 @@ * GNU General Public License for more details. */ -#include "io_data.h" - -#include "radiodata.h" // for RadioData::getElementName +#include "flightmodedata.h" +#include "radiodata.h" #include "radiodataconversionstate.h" -/* - * ExpoData - */ - -void ExpoData::convert(RadioDataConversionState & cstate) +void FlightModeData::convert(RadioDataConversionState & cstate) { - cstate.setComponent(tr("INP"), 3); - cstate.setSubComp(RawSource(SOURCE_TYPE_VIRTUAL_INPUT, chn).toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType) % tr(" (@%1)").arg(cstate.subCompIdx)); - srcRaw.convert(cstate); + cstate.setComponent("FMD", 2); + cstate.setSubComp(nameToString(cstate.subCompIdx)); swtch.convert(cstate); } -bool ExpoData::isEmpty() const -{ - return (chn == 0 && mode == INPUT_MODE_NONE); -} - -/* - * MixData - */ - -void MixData::convert(RadioDataConversionState & cstate) -{ - cstate.setComponent(tr("MIX"), 4); - cstate.setSubComp(RawSource(SOURCE_TYPE_CH, destCh-1).toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType) % tr(" (@%1)").arg(cstate.subCompIdx)); - srcRaw.convert(cstate); - swtch.convert(cstate); -} - -bool MixData::isEmpty() const -{ - return (destCh == 0); -} - -/* - * LimitData - */ - -QString LimitData::minToString() const -{ - return QString::number((qreal)min/10); -} - -QString LimitData::maxToString() const -{ - return QString::number((qreal)max/10); -} - -QString LimitData::revertToString() const -{ - return revert ? tr("INV") : tr("NOR"); -} - -QString LimitData::nameToString(int index) const -{ - return RadioData::getElementName(tr("CH"), index + 1, name); -} - -QString LimitData::offsetToString() const -{ - return QString::number((qreal)offset/10, 'f', 1); -} - -void LimitData::clear() -{ - memset(reinterpret_cast(this), 0, sizeof(LimitData)); - min = -1000; - max = +1000; -} - -bool LimitData::isEmpty() const -{ - return (min == -1000 && max == 1000 && !revert && !offset && !ppmCenter && !symetrical && name[0] == '\0' && !curve.isSet()); -} - -/* - * CurveData - */ - -CurveData::CurveData() -{ - clear(); -} - -void CurveData::clear(int count) -{ - memset(this, 0, sizeof(CurveData)); - this->count = count; -} - -bool CurveData::isEmpty() const -{ - for (int i=0; i(this), 0, sizeof(FlightModeData)); @@ -149,13 +45,6 @@ QString FlightModeData::nameToString(int phaseIdx) const return RadioData::getElementName(tr("FM"), phaseIdx, name); // names are zero-based, FM0, FM1, etc } -void FlightModeData::convert(RadioDataConversionState & cstate) -{ - cstate.setComponent("FMD", 2); - cstate.setSubComp(nameToString(cstate.subCompIdx)); - swtch.convert(cstate); -} - bool FlightModeData::isEmpty(int phaseIdx) const { if (name[0] != '\0' || swtch.isSet() || fadeIn != 0 || fadeOut != 0) diff --git a/companion/src/firmwares/flightmodedata.h b/companion/src/firmwares/flightmodedata.h new file mode 100644 index 000000000..8578d04e5 --- /dev/null +++ b/companion/src/firmwares/flightmodedata.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "constants.h" +#include "rawswitch.h" + +#include + +class RadioDataConversionState; + +#define FLIGHTMODE_NAME_LEN 10 +#define RENC_MAX_VALUE 1024 +#define RENC_MIN_VALUE -RENC_MAX_VALUE + +class FlightModeData { + Q_DECLARE_TR_FUNCTIONS(FlightModeData) + + public: + FlightModeData() { clear(0); } + + int trimMode[CPN_MAX_TRIMS]; + int trimRef[CPN_MAX_TRIMS]; + int trim[CPN_MAX_TRIMS]; + RawSwitch swtch; + char name[FLIGHTMODE_NAME_LEN + 1]; + unsigned int fadeIn; + unsigned int fadeOut; + int rotaryEncoders[CPN_MAX_ENCODERS]; + int gvars[CPN_MAX_GVARS]; + + void convert(RadioDataConversionState & cstate); + void clear(const int phaseIdx); + bool isEmpty(int phaseIdx) const; + QString nameToString(int phaseIdx) const; + bool isGVarEmpty(int phaseIdx, int gvIdx) const; + bool isREncEmpty(int phaseIdx, int reIdx) const; + int linkedFlightModeZero(int phaseIdx, int maxOwnValue) const; + int linkedGVarFlightModeZero(int phaseIdx) const; + int linkedREncFlightModeZero(int phaseIdx) const; +}; diff --git a/companion/src/firmwares/heli_data.cpp b/companion/src/firmwares/heli_data.cpp new file mode 100644 index 000000000..b1fb7b36e --- /dev/null +++ b/companion/src/firmwares/heli_data.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "heli_data.h" diff --git a/companion/src/firmwares/heli_data.h b/companion/src/firmwares/heli_data.h new file mode 100644 index 000000000..734b29928 --- /dev/null +++ b/companion/src/firmwares/heli_data.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "rawsource.h" + +#include + +class SwashRingData { + Q_DECLARE_TR_FUNCTIONS(SwashRingData) + + public: + SwashRingData() { clear(); } + + int elevatorWeight; + int aileronWeight; + int collectiveWeight; + unsigned int type; + RawSource collectiveSource; + RawSource aileronSource; + RawSource elevatorSource; + unsigned int value; + + void clear() { memset(reinterpret_cast(this), 0, sizeof(SwashRingData)); } +}; + diff --git a/companion/src/firmwares/input_data.cpp b/companion/src/firmwares/input_data.cpp new file mode 100644 index 000000000..9b9ecfe20 --- /dev/null +++ b/companion/src/firmwares/input_data.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "input_data.h" +#include "radiodataconversionstate.h" + +void ExpoData::convert(RadioDataConversionState & cstate) +{ + cstate.setComponent(tr("INP"), 3); + cstate.setSubComp(RawSource(SOURCE_TYPE_VIRTUAL_INPUT, chn).toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType) % tr(" (@%1)").arg(cstate.subCompIdx)); + srcRaw.convert(cstate); + swtch.convert(cstate); +} + +bool ExpoData::isEmpty() const +{ + return (chn == 0 && mode == INPUT_MODE_NONE); +} diff --git a/companion/src/firmwares/input_data.h b/companion/src/firmwares/input_data.h new file mode 100644 index 000000000..27b6b6282 --- /dev/null +++ b/companion/src/firmwares/input_data.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "curvereference.h" +#include "rawsource.h" +#include "rawswitch.h" + +#include + +class RadioDataConversionState; + +enum InputMode { + INPUT_MODE_NONE, + INPUT_MODE_POS, + INPUT_MODE_NEG, + INPUT_MODE_BOTH +}; + +#define EXPODATA_NAME_LEN 10 + +class ExpoData { + Q_DECLARE_TR_FUNCTIONS(ExpoData) + + public: + ExpoData() { clear(); } + + RawSource srcRaw; + unsigned int scale; + unsigned int mode; // InputMode + unsigned int chn; + RawSwitch swtch; + unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 + int weight; + int offset; + CurveReference curve; + int carryTrim; + char name[EXPODATA_NAME_LEN + 1]; + + void convert(RadioDataConversionState & cstate); + void clear() { memset(reinterpret_cast(this), 0, sizeof(ExpoData)); } + bool isEmpty() const; +}; diff --git a/companion/src/firmwares/io_data.h b/companion/src/firmwares/io_data.h deleted file mode 100644 index b70b174ea..000000000 --- a/companion/src/firmwares/io_data.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IO_DATA_H -#define IO_DATA_H - -#include "constants.h" -#include "curvereference.h" -#include "rawsource.h" -#include "rawswitch.h" - -#include - -class RadioDataConversionState; - -enum InputMode { - INPUT_MODE_NONE, - INPUT_MODE_POS, - INPUT_MODE_NEG, - INPUT_MODE_BOTH -}; - -#define EXPODATA_NAME_LEN 10 - -class ExpoData { - Q_DECLARE_TR_FUNCTIONS(ExpoData) - - public: - ExpoData() { clear(); } - RawSource srcRaw; - unsigned int scale; - unsigned int mode; // InputMode - unsigned int chn; - RawSwitch swtch; - unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 - int weight; - int offset; - CurveReference curve; - int carryTrim; - char name[EXPODATA_NAME_LEN+1]; - void clear() { memset(reinterpret_cast(this), 0, sizeof(ExpoData)); } - void convert(RadioDataConversionState & cstate); - bool isEmpty() const; -}; - -enum MltpxValue { - MLTPX_ADD=0, - MLTPX_MUL=1, - MLTPX_REP=2 -}; - -#define MIXDATA_NAME_LEN 10 - -class MixData { - Q_DECLARE_TR_FUNCTIONS(MixData) - - public: - MixData() { clear(); } - void convert(RadioDataConversionState & cstate); - - unsigned int destCh; // 1..CPN_MAX_CHNOUT - RawSource srcRaw; - int weight; - RawSwitch swtch; - CurveReference curve; //0=symmetrisch - unsigned int delayUp; - unsigned int delayDown; - unsigned int speedUp; // Servogeschwindigkeit aus Tabelle (10ms Cycle) - unsigned int speedDown; // 0 nichts - int carryTrim; - bool noExpo; - MltpxValue mltpx; // multiplex method 0=+ 1=* 2=replace - unsigned int mixWarn; // mixer warning - unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 - int sOffset; - char name[MIXDATA_NAME_LEN+1]; - - void clear() { memset(reinterpret_cast(this), 0, sizeof(MixData)); } - bool isEmpty() const; -}; - -#define LIMITDATA_NAME_LEN 6 - -class LimitData { - Q_DECLARE_TR_FUNCTIONS(LimitData) - - public: - LimitData() { clear(); } - int min; - int max; - bool revert; - int offset; - int ppmCenter; - bool symetrical; - int failsafe; - char name[LIMITDATA_NAME_LEN + 1]; - CurveReference curve; - QString minToString() const; - QString maxToString() const; - QString offsetToString() const; - QString revertToString() const; - QString nameToString(int index) const; - void clear(); - bool isEmpty() const; -}; - -class CurvePoint { - public: - int8_t x; - int8_t y; -}; - -#define CURVEDATA_NAME_LEN 6 - -class CurveData { - Q_DECLARE_TR_FUNCTIONS(CurveData) - - public: - enum CurveType { - CURVE_TYPE_STANDARD, - CURVE_TYPE_CUSTOM, - CURVE_TYPE_LAST = CURVE_TYPE_CUSTOM - }; - - CurveData(); - void clear(int count = 5); - bool isEmpty() const; - QString nameToString(const int idx) const; - - CurveType type; - bool smooth; - int count; - CurvePoint points[CPN_MAX_POINTS]; - char name[CURVEDATA_NAME_LEN+1]; -}; - -#define FLIGHTMODE_NAME_LEN 10 -#define RENC_MAX_VALUE 1024 -#define RENC_MIN_VALUE -RENC_MAX_VALUE - -class FlightModeData { - Q_DECLARE_TR_FUNCTIONS(FlightModeData) - - public: - FlightModeData() { clear(0); } - int trimMode[CPN_MAX_TRIMS]; - int trimRef[CPN_MAX_TRIMS]; - int trim[CPN_MAX_TRIMS]; - RawSwitch swtch; - char name[FLIGHTMODE_NAME_LEN+1]; - unsigned int fadeIn; - unsigned int fadeOut; - int rotaryEncoders[CPN_MAX_ENCODERS]; - int gvars[CPN_MAX_GVARS]; - void clear(const int phaseIdx); - QString nameToString(int phaseIdx) const; - void convert(RadioDataConversionState & cstate); - bool isEmpty(int phaseIdx) const; - bool isGVarEmpty(int phaseIdx, int gvIdx) const; - bool isREncEmpty(int phaseIdx, int reIdx) const; - int linkedFlightModeZero(int phaseIdx, int maxOwnValue) const; - int linkedGVarFlightModeZero(int phaseIdx) const; - int linkedREncFlightModeZero(int phaseIdx) const; -}; - -class SwashRingData { - Q_DECLARE_TR_FUNCTIONS(SwashRingData) - - public: - SwashRingData() { clear(); } - int elevatorWeight; - int aileronWeight; - int collectiveWeight; - unsigned int type; - RawSource collectiveSource; - RawSource aileronSource; - RawSource elevatorSource; - unsigned int value; - void clear() { memset(reinterpret_cast(this), 0, sizeof(SwashRingData)); } -}; - -#endif // IO_DATA_H diff --git a/companion/src/firmwares/mixdata.cpp b/companion/src/firmwares/mixdata.cpp new file mode 100644 index 000000000..ac2893a4d --- /dev/null +++ b/companion/src/firmwares/mixdata.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mixdata.h" +#include "radiodataconversionstate.h" + +void MixData::convert(RadioDataConversionState & cstate) +{ + cstate.setComponent(tr("MIX"), 4); + cstate.setSubComp(RawSource(SOURCE_TYPE_CH, destCh-1).toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType) % tr(" (@%1)").arg(cstate.subCompIdx)); + srcRaw.convert(cstate); + swtch.convert(cstate); +} + +bool MixData::isEmpty() const +{ + return (destCh == 0); +} diff --git a/companion/src/firmwares/mixdata.h b/companion/src/firmwares/mixdata.h new file mode 100644 index 000000000..dc81298f0 --- /dev/null +++ b/companion/src/firmwares/mixdata.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "curvereference.h" +#include "rawsource.h" +#include "rawswitch.h" + +#include + +class RadioDataConversionState; + +enum MltpxValue { + MLTPX_ADD, + MLTPX_MUL, + MLTPX_REP +}; + +#define MIXDATA_NAME_LEN 10 + +class MixData { + Q_DECLARE_TR_FUNCTIONS(MixData) + + public: + MixData() { clear(); } + + unsigned int destCh; // 1..CPN_MAX_CHNOUT + RawSource srcRaw; + int weight; + RawSwitch swtch; + CurveReference curve; + unsigned int delayUp; + unsigned int delayDown; + unsigned int speedUp; + unsigned int speedDown; + int carryTrim; + bool noExpo; + MltpxValue mltpx; + unsigned int mixWarn; + unsigned int flightModes; // -5=!FP4, 0=normal, 5=FP4 + int sOffset; + char name[MIXDATA_NAME_LEN + 1]; + + void convert(RadioDataConversionState & cstate); + void clear() { memset(reinterpret_cast(this), 0, sizeof(MixData)); } + bool isEmpty() const; +}; diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 847997d94..80b7d7353 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -18,15 +18,19 @@ * GNU General Public License for more details. */ -#ifndef MODELDATA_H -#define MODELDATA_H +#pragma once #include "constants.h" +#include "curvedata.h" #include "customfunctiondata.h" #include "gvardata.h" -#include "io_data.h" +#include "flightmodedata.h" +#include "heli_data.h" +#include "input_data.h" #include "logicalswitchdata.h" +#include "mixdata.h" #include "moduledata.h" +#include "output_data.h" #include "sensordata.h" #include "telem_data.h" @@ -346,5 +350,3 @@ class ModelData { void sortMixes(); void updateResetParam(CustomFunctionData * cfd); }; - -#endif // MODELDATA_H diff --git a/companion/src/firmwares/output_data.cpp b/companion/src/firmwares/output_data.cpp new file mode 100644 index 000000000..63127d242 --- /dev/null +++ b/companion/src/firmwares/output_data.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "output_data.h" +#include "radiodata.h" +#include "radiodataconversionstate.h" + +void LimitData::clear() +{ + memset(reinterpret_cast(this), 0, sizeof(LimitData)); + min = -1000; + max = +1000; +} + +bool LimitData::isEmpty() const +{ + return (min == -1000 && max == 1000 && !revert && !offset && !ppmCenter && !symetrical && name[0] == '\0' && !curve.isSet()); +} + +QString LimitData::minToString() const +{ + return QString::number((qreal)min / 10); +} + +QString LimitData::maxToString() const +{ + return QString::number((qreal)max / 10); +} + +QString LimitData::revertToString() const +{ + return revert ? tr("INV") : tr("NOR"); +} + +QString LimitData::nameToString(int index) const +{ + return RadioData::getElementName(tr("CH"), index + 1, name); +} + +QString LimitData::offsetToString() const +{ + return QString::number((qreal)offset / 10, 'f', 1); +} diff --git a/companion/src/firmwares/output_data.h b/companion/src/firmwares/output_data.h new file mode 100644 index 000000000..423f03e49 --- /dev/null +++ b/companion/src/firmwares/output_data.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "curvereference.h" + +#include + +#define LIMITDATA_NAME_LEN 6 + +class LimitData { + Q_DECLARE_TR_FUNCTIONS(LimitData) + + public: + LimitData() { clear(); } + + int min; + int max; + bool revert; + int offset; + int ppmCenter; + bool symetrical; + int failsafe; + char name[LIMITDATA_NAME_LEN + 1]; + CurveReference curve; + + void clear(); + bool isEmpty() const; + QString minToString() const; + QString maxToString() const; + QString offsetToString() const; + QString revertToString() const; + QString nameToString(int index) const; +}; From f4866c822ab5a71962edf1f33990bc2ff666f246 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 7 Mar 2021 23:15:15 +0100 Subject: [PATCH 212/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8965152da..eeb3a1ef5 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2174,3 +2174,5 @@ Mateusz Dziadosz Thomas Merchant M L Perkins Stefano Boggia +Новоселов Юрий +James Rhodes From 4660dd852f5a3556c2937c32214de7b766f12f3b Mon Sep 17 00:00:00 2001 From: ruddy douglass Date: Mon, 8 Mar 2021 16:42:34 -0500 Subject: [PATCH 213/300] Resolve issue 8347 by checking if the for nullptr. Additionally resolve a couple other similar issues for keyboard accellerators --- companion/src/modeledit/inputs.cpp | 7 ++++++- companion/src/modeledit/mixes.cpp | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index f48d7d916..d12655c08 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -351,6 +351,8 @@ void InputsPanel::expoOpen(QListWidgetItem *item) if (!item) item = ExposlistWidget->currentItem(); + if (item == nullptr) return; + int idx = item->data(Qt::UserRole).toByteArray().at(0); if (idx < 0) { int ch = -idx - 1; @@ -368,7 +370,10 @@ void InputsPanel::expoOpen(QListWidgetItem *item) void InputsPanel::expoAdd() { - int index = ExposlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0); + QListWidgetItem *item = ExposlistWidget->currentItem(); + if (item == nullptr) return; + + int index = item->data(Qt::UserRole).toByteArray().at(0); if (index < 0) { // if empty then return relevant index expoOpen(); diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index 3d95bd04d..7f3da65b8 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -354,7 +354,10 @@ void MixesPanel::mixersDuplicate() void MixesPanel::mixerOpen() { - int idx = mixersListWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0); + QListWidgetItem *item = mixersListWidget->currentItem(); + if (item == nullptr) return; + + int idx = item->data(Qt::UserRole).toByteArray().at(0); if(idx < 0) { int i = -idx; idx = getMixerIndex(i); //get mixer index to insert @@ -371,7 +374,10 @@ void MixesPanel::mixerOpen() void MixesPanel::mixerHighlight() { - int idx = mixersListWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0); + QListWidgetItem *item = mixersListWidget->currentItem(); + if (item == nullptr) return; + + int idx = item->data(Qt::UserRole).toByteArray().at(0); int dest; if (idx<0) { dest = -idx; From 9b02d354d6969e7009162bd76d70fe390c7161c8 Mon Sep 17 00:00:00 2001 From: 3djc Date: Tue, 9 Mar 2021 17:07:32 +0100 Subject: [PATCH 214/300] Tidy up --- radio/src/targets/horus/hal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 81639e73a..48bd8ff30 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -420,9 +420,9 @@ #define AUX_SERIAL_USART_IRQn USART3_IRQn #define AUX_SERIAL_DMA_Stream_RX DMA1_Stream1 #define AUX_SERIAL_DMA_Channel_RX DMA_Channel_4 +#if defined(RADIO_TX16S) #define AUX_SERIAL_PWR_GPIO GPIOA #define AUX_SERIAL_PWR_GPIO_PIN GPIO_Pin_15 // PB.00 -#if defined(RADIO_TX16S) #define TRAINER_BATTERY_COMPARTMENT // allows serial port TTL trainer #endif #else From 673c8aac1aa7871233c6201f4e2b724d2207d75d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 9 Mar 2021 20:22:48 +0100 Subject: [PATCH 215/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index eeb3a1ef5..ce86be529 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2176,3 +2176,4 @@ M L Perkins Stefano Boggia Новоселов Юрий James Rhodes +Peter Gaskill From 5b44f150cfc1bc3e227b8bd5ec2e1e63b4ee5703 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 10 Mar 2021 07:10:06 +1100 Subject: [PATCH 216/300] Limit sensors available as possible RSSI source --- .../src/datamodels/compounditemmodels.cpp | 48 ++++++++++++++++++- companion/src/datamodels/compounditemmodels.h | 24 ++++++++-- companion/src/firmwares/sensordata.cpp | 22 +++++++++ companion/src/firmwares/sensordata.h | 2 + companion/src/modeledit/modeledit.cpp | 1 + companion/src/modeledit/setup.cpp | 10 +++- companion/src/modeledit/setup.h | 2 + companion/src/modeledit/telemetry.cpp | 8 +++- 8 files changed, 109 insertions(+), 8 deletions(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index 039e90244..3432258ae 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -488,7 +488,7 @@ TelemetrySourceItemModel::TelemetrySourceItemModel(const GeneralSettings * const if (!modelData) return; - setUpdateMask(IMUE_TeleSensors); + setUpdateMask(IMUE_TeleSensors | IMUE_Modules); const int count = firmware->getCapability(Sensors); for (int i = -count; i <= count; ++i) { @@ -519,6 +519,49 @@ void TelemetrySourceItemModel::update(const int event) } } +// +// RssiSourceItemModel +// + +RssiSourceItemModel::RssiSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType) : + AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) +{ + setId(IMID_RssiSource); + + if (!modelData) + return; + + setUpdateMask(IMUE_TeleSensors | IMUE_Modules); + + for (int i = 0; i <= firmware->getCapability(Sensors); ++i) { + QStandardItem * modelItem = new QStandardItem(); + modelItem->setData(i, IMDR_Id); + modelItem->setData(i < 0 ? IMDG_Negative : i > 0 ? IMDG_Positive : IMDG_None, IMDR_Flags); + setDynamicItemData(modelItem, i); + appendRow(modelItem); + } +} + +void RssiSourceItemModel::setDynamicItemData(QStandardItem * item, const int value) const +{ + item->setText(SensorData::rssiSensorToString(modelData, value)); + item->setData(SensorData::isRssiSensorAvailable(modelData, value), IMDR_Available); +} + +void RssiSourceItemModel::update(const int event) +{ + if (doUpdate(event)) { + emit aboutToBeUpdated(); + + for (int i = 0; i < rowCount(); ++i) { + setDynamicItemData(item(i), item(i)->data(IMDR_Id).toInt()); + } + + emit updateComplete(); + } +} + // // CurveRefTypeItemModel // @@ -624,6 +667,9 @@ void CompoundItemModelFactory::addItemModel(const int id) case AbstractItemModel::IMID_TeleSource: registerItemModel(new TelemetrySourceItemModel(generalSettings, modelData, firmware, board, boardType)); break; + case AbstractItemModel::IMID_RssiSource: + registerItemModel(new RssiSourceItemModel(generalSettings, modelData, firmware, board, boardType)); + break; case AbstractItemModel::IMID_CurveRefType: registerItemModel(new CurveRefTypeItemModel(generalSettings, modelData, firmware, board, boardType)); break; diff --git a/companion/src/datamodels/compounditemmodels.h b/companion/src/datamodels/compounditemmodels.h index de32fc902..7acfcbdc9 100644 --- a/companion/src/datamodels/compounditemmodels.h +++ b/companion/src/datamodels/compounditemmodels.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef COMPOUNDITEMMODELS_H -#define COMPOUNDITEMMODELS_H +#pragma once #include "rawsource.h" #include "rawswitch.h" @@ -44,6 +43,7 @@ class AbstractItemModel: public QStandardItemModel IMID_CustomFuncAction, IMID_CustomFuncResetParam, IMID_TeleSource, + IMID_RssiSource, IMID_CurveRefType, IMID_CurveRefFunc, IMID_ReservedCount, @@ -78,8 +78,9 @@ class AbstractItemModel: public QStandardItemModel IMUE_Scripts = 1 << 7, IMUE_TeleSensors = 1 << 8, IMUE_Timers = 1 << 9, + IMUE_Modules = 1 << 10, IMUE_All = IMUE_SystemRefresh | IMUE_Channels | IMUE_Curves | IMUE_FlightModes | IMUE_GVars | IMUE_Inputs | - IMUE_LogicalSwitches | IMUE_Scripts | IMUE_TeleSensors | IMUE_Timers + IMUE_LogicalSwitches | IMUE_Scripts | IMUE_TeleSensors | IMUE_Timers | IMUE_Modules }; Q_ENUM(ItemModelUpdateEvent) @@ -305,6 +306,21 @@ class TelemetrySourceItemModel: public AbstractDynamicItemModel virtual void setDynamicItemData(QStandardItem * item, const int value) const; }; +class RssiSourceItemModel: public AbstractDynamicItemModel +{ + Q_OBJECT + public: + explicit RssiSourceItemModel(const GeneralSettings * const generalSettings, const ModelData * const modelData, + Firmware * firmware, const Boards * const board, const Board::Type boardType); + virtual ~RssiSourceItemModel() {}; + + public slots: + virtual void update(const int event = IMUE_SystemRefresh) override; + + protected: + virtual void setDynamicItemData(QStandardItem * item, const int value) const; +}; + class CurveRefTypeItemModel : public AbstractStaticItemModel { Q_OBJECT @@ -361,5 +377,3 @@ class CompoundItemModelFactory private: void setSourceId(AbstractItemModel * itemModel); }; - -#endif // COMPOUNDITEMMODELS_H diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index a87dc6d21..2c0866e66 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -427,6 +427,28 @@ bool SensorData::isSourceAvailable(const ModelData * model, const int index) return false; } +#define RSSI_ID 0xF101 + +// static +bool SensorData::isRssiSensorAvailable(const ModelData * model, const int value) +{ + if (value == 0) + return true; + else { + const SensorData &sensor = model->sensorData[abs(value) - 1]; + return (sensor.isAvailable() && sensor.id == RSSI_ID); + } +} + +// static +QString SensorData::rssiSensorToString(const ModelData * model, const int value) +{ + if (value == 0) + return tr("(default)"); + else + return sourceToString(model, value); +} + // static AbstractStaticItemModel * SensorData::typeItemModel() { diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 5e888d749..2353a0ebf 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -206,6 +206,8 @@ class SensorData { static QString unitToString(const int value); static QString precToString(const int value); static QString boolToString(const bool value) { return value ? tr("Y") : tr("N"); } + static bool isRssiSensorAvailable(const ModelData * model, const int value); + static QString rssiSensorToString(const ModelData * model, const int value); static AbstractStaticItemModel * typeItemModel(); static AbstractStaticItemModel * formulaItemModel(); diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp index 3577be7ae..146bc01f0 100644 --- a/companion/src/modeledit/modeledit.cpp +++ b/companion/src/modeledit/modeledit.cpp @@ -62,6 +62,7 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncAction); sharedItemModels->addItemModel(AbstractItemModel::IMID_CustomFuncResetParam); sharedItemModels->addItemModel(AbstractItemModel::IMID_TeleSource); + sharedItemModels->addItemModel(AbstractItemModel::IMID_RssiSource); sharedItemModels->addItemModel(AbstractItemModel::IMID_CurveRefType); sharedItemModels->addItemModel(AbstractItemModel::IMID_CurveRefFunc); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index d1756aa48..b695b03ff 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -73,7 +73,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui->persistent->addItem(tr("Not persistent"), 0); ui->persistent->addItem(tr("Persistent (flight)"), 1); ui->persistent->addItem(tr("Persistent (manual reset)"), 2); - + ui->countdownStart->setField(timer.countdownStart, this); ui->countdownStart->addItem("5s", 1); ui->countdownStart->addItem("10s", 0); @@ -701,6 +701,7 @@ void ModulePanel::onProtocolChanged(int index) module.protocol = ui->protocol->itemData(index).toInt(); module.channelsCount = module.getMaxChannelCount(); update(); + emit updateItemModels(); emit modified(); } } @@ -801,6 +802,7 @@ void ModulePanel::onMultiProtocolChanged(int index) module.subType = std::min(module.subType, maxSubTypes - 1); module.channelsCount = module.getMaxChannelCount(); update(); + emit updateItemModels(); emit modified(); lock = false; } @@ -1251,6 +1253,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge modules[i] = new ModulePanel(this, model, model.moduleData[i], generalSettings, firmware, i); ui->modulesLayout->addWidget(modules[i]); connect(modules[i], &ModulePanel::modified, this, &SetupPanel::modified); + connect(modules[i], &ModulePanel::updateItemModels, this, &SetupPanel::onModuleUpdateItemModels); connect(this, &SetupPanel::extendedLimitsToggled, modules[i], &ModulePanel::onExtendedLimitsToggled); } @@ -1798,3 +1801,8 @@ void SetupPanel::updateItemModels() { sharedItemModels->update(AbstractItemModel::IMUE_Timers); } + +void SetupPanel::onModuleUpdateItemModels() +{ + sharedItemModels->update(AbstractItemModel::IMUE_Modules); +} diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 7a5cddba9..7409d7b25 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -81,6 +81,7 @@ class ModulePanel : public ModelPanel signals: void channelsRangeChanged(); void failsafeModified(unsigned index); + void updateItemModels(); private slots: void setupFailsafes(); @@ -175,6 +176,7 @@ class SetupPanel : public ModelPanel void onTimerNameChanged(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); + void onModuleUpdateItemModels(); private: Ui::Setup *ui; diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 8cda220a9..3c0ae7bf7 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -35,6 +35,7 @@ constexpr char FIM_SENSORFORMULA[] {"Sensor.Formula"}; constexpr char FIM_SENSORCELLINDEX[] {"Sensor.CellIndex"}; constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"}; constexpr char FIM_SENSORPRECISION[] {"Sensor.Precision"}; +constexpr char FIM_RSSISOURCE[] {"Rssi Source"}; TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels): @@ -696,6 +697,11 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin FIM_TELEPOSSRC); connectItemModelEvents(id); + + id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RssiSource)), + FIM_RSSISOURCE); + connectItemModelEvents(id); + id = panelItemModels->registerItemModel(SensorData::typeItemModel()); panelFilteredItemModels->registerItemModel(new FilteredItemModel(panelItemModels->getItemModel(id)), FIM_SENSORTYPE); @@ -818,7 +824,7 @@ void TelemetryPanel::setup() ui->rssiSourceLabel->show(); ui->rssiSourceLabel->setText(tr("Source")); - ui->rssiSourceCB->setModel(panelFilteredItemModels->getItemModel(FIM_TELEPOSSRC)); + ui->rssiSourceCB->setModel(panelFilteredItemModels->getItemModel(FIM_RSSISOURCE)); ui->rssiSourceCB->setField(model->rssiSource, this); ui->rssiSourceCB->show(); From 9d383c7f08abe78fccaa9caa190b5a5b7637299b Mon Sep 17 00:00:00 2001 From: ruddy douglass Date: Tue, 9 Mar 2021 16:38:40 -0500 Subject: [PATCH 217/300] Reformat to adhere to coding standards --- companion/src/modeledit/inputs.cpp | 6 ++++-- companion/src/modeledit/mixes.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index d12655c08..8636d6335 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -351,7 +351,8 @@ void InputsPanel::expoOpen(QListWidgetItem *item) if (!item) item = ExposlistWidget->currentItem(); - if (item == nullptr) return; + if (item == nullptr) + return; int idx = item->data(Qt::UserRole).toByteArray().at(0); if (idx < 0) { @@ -371,7 +372,8 @@ void InputsPanel::expoOpen(QListWidgetItem *item) void InputsPanel::expoAdd() { QListWidgetItem *item = ExposlistWidget->currentItem(); - if (item == nullptr) return; + if (item == nullptr) + return; int index = item->data(Qt::UserRole).toByteArray().at(0); diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index 7f3da65b8..023888760 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -355,7 +355,8 @@ void MixesPanel::mixersDuplicate() void MixesPanel::mixerOpen() { QListWidgetItem *item = mixersListWidget->currentItem(); - if (item == nullptr) return; + if (item == nullptr) + return; int idx = item->data(Qt::UserRole).toByteArray().at(0); if(idx < 0) { @@ -375,7 +376,8 @@ void MixesPanel::mixerOpen() void MixesPanel::mixerHighlight() { QListWidgetItem *item = mixersListWidget->currentItem(); - if (item == nullptr) return; + if (item == nullptr) + return; int idx = item->data(Qt::UserRole).toByteArray().at(0); int dest; From 936617e24461b12cc1a0faadcf1116bb94181ccc Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 10 Mar 2021 15:07:42 +1100 Subject: [PATCH 218/300] Add new model id to toString --- companion/src/datamodels/compounditemmodels.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index 3432258ae..aa3fa1756 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -46,6 +46,8 @@ QString AbstractItemModel::idToString(const int value) return "CustomFuncResetParam"; case IMID_TeleSource: return "TeleSource"; + case IMID_RssiSource: + return "RssiSource"; case IMID_CurveRefType: return "CurveRefType"; case IMID_CurveRefFunc: From 64324e3777b55a10324055448b2259fc4ca036ab Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 10 Mar 2021 11:11:42 +0100 Subject: [PATCH 219/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ce86be529..b45c8d90c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2177,3 +2177,4 @@ Stefano Boggia Новоселов Юрий James Rhodes Peter Gaskill +Jacques Temey From 1180fcbe5fe480b524e15d4a5e80c2c6ed018018 Mon Sep 17 00:00:00 2001 From: Tony Cake Date: Wed, 10 Mar 2021 07:16:55 -0800 Subject: [PATCH 220/300] Add GHST GPS Telemetry Sensors (#8352) * Add GHST GPS Telemetry Sensors --- radio/src/telemetry/ghost.cpp | 66 ++++++++++++++++++++++++++++++++--- radio/src/telemetry/ghost.h | 2 ++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 098e9c990..1ff6acd48 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -49,6 +49,13 @@ enum GHOST_ID_PACK_VOLTS = 0x000c, // Battery Pack Voltage GHOST_ID_PACK_AMPS = 0x000d, // Battery Pack Current GHOST_ID_PACK_MAH = 0x000e, // Battery Pack mAh consumed + + GHOST_ID_GPS_LAT = 0x000f, // GPS Latitude + GHOST_ID_GPS_LONG = 0x0010, // GPS Longitude + GHOST_ID_GPS_ALT = 0x0011, // GPS Altitude + GHOST_ID_GPS_HDG = 0x0012, // GPS Heading + GHOST_ID_GPS_GSPD = 0x0013, // GPS Ground Speed + GHOST_ID_GPS_SATS = 0x0014 // GPS Satellite Count }; const GhostSensor ghostSensors[] = { @@ -70,6 +77,13 @@ const GhostSensor ghostSensors[] = { {GHOST_ID_PACK_AMPS, ZSTR_CURR, UNIT_AMPS, 2}, {GHOST_ID_PACK_MAH, ZSTR_CAPACITY, UNIT_MAH, 0}, + {GHOST_ID_GPS_LAT, ZSTR_GPS, UNIT_GPS_LATITUDE, 0}, + {GHOST_ID_GPS_LONG, ZSTR_GPS, UNIT_GPS_LONGITUDE, 0}, + {GHOST_ID_GPS_GSPD, ZSTR_GSPD, UNIT_KMH, 1}, + {GHOST_ID_GPS_HDG, ZSTR_HDG, UNIT_DEGREE, 3}, + {GHOST_ID_GPS_ALT, ZSTR_ALT, UNIT_METERS, 0}, + {GHOST_ID_GPS_SATS, ZSTR_SATELLITES, UNIT_RAW, 0}, + {0x00, NULL, UNIT_RAW, 0}, }; @@ -88,7 +102,11 @@ void processGhostTelemetryValue(uint8_t index, int32_t value) return; const GhostSensor * sensor = getGhostSensor(index); - setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, sensor->id, 0, 0, value, sensor->unit, sensor->precision); + uint16_t id = sensor->id; + if (id == GHOST_ID_GPS_LONG) { + id = GHOST_ID_GPS_LAT; + } + setTelemetryValue(PROTOCOL_TELEMETRY_GHOST, id, 0, 0, value, sensor->unit, sensor->precision); } void processGhostTelemetryValueString(const GhostSensor * sensor, const char * str) @@ -105,16 +123,19 @@ bool checkGhostTelemetryFrameCRC() return (crc == telemetryRxBuffer[len + 1]); } +// hifirst uint16_t getTelemetryValue_u16(uint8_t index) { return (telemetryRxBuffer[index] << 8) | telemetryRxBuffer[index + 1]; } +// lofirst uint16_t getTelemetryValue_u16le(uint8_t index) { return (telemetryRxBuffer[index + 1] << 8) | telemetryRxBuffer[index]; } +// hifirst uint32_t getTelemetryValue_s32(uint8_t index) { uint32_t val = 0; @@ -123,6 +144,15 @@ uint32_t getTelemetryValue_s32(uint8_t index) return val; } +// lofirst +uint32_t getTelemetryValue_s32le(uint8_t index) +{ + uint32_t val = 0; + for (int i = 0; i < 4; ++i) + val <<= 8, val |= telemetryRxBuffer[index + 3 - i]; + return val; +} + void processGhostTelemetryFrame() { if (!checkGhostTelemetryFrameCRC()) { @@ -213,15 +243,40 @@ void processGhostTelemetryFrame() case GHST_DL_PACK_STAT: { #if defined(BLUETOOTH) if (g_eeGeneral.bluetoothMode == BLUETOOTH_TELEMETRY && bluetooth.state == BLUETOOTH_STATE_CONNECTED) { - bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); - } + bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); + } #endif processGhostTelemetryValue(GHOST_ID_PACK_VOLTS, getTelemetryValue_u16le(3)); processGhostTelemetryValue(GHOST_ID_PACK_AMPS, getTelemetryValue_u16le(5)); processGhostTelemetryValue(GHOST_ID_PACK_MAH, getTelemetryValue_u16le(7) * 10); - break; } + + case GHST_DL_GPS_PRIMARY: { +#if defined(BLUETOOTH) + if (g_eeGeneral.bluetoothMode == BLUETOOTH_TELEMETRY && bluetooth.state == BLUETOOTH_STATE_CONNECTED) { + bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); + } +#endif + processGhostTelemetryValue(GHOST_ID_GPS_LAT, getTelemetryValue_s32le(3) / 10); + processGhostTelemetryValue(GHOST_ID_GPS_LONG, getTelemetryValue_s32le(7) / 10); + processGhostTelemetryValue(GHOST_ID_GPS_ALT, (int16_t) getTelemetryValue_u16le(11)); + break; + } + + case GHST_DL_GPS_SECONDARY: { +#if defined(BLUETOOTH) + if (g_eeGeneral.bluetoothMode == BLUETOOTH_TELEMETRY && bluetooth.state == BLUETOOTH_STATE_CONNECTED) { + bluetooth.write(telemetryRxBuffer, telemetryRxBufferCount); + } +#endif + processGhostTelemetryValue(GHOST_ID_GPS_HDG, getTelemetryValue_u16le(5) / 10); + + // ground speed is passed via GHST as cm/s, converted to km/h for OpenTx + processGhostTelemetryValue(GHOST_ID_GPS_GSPD, (getTelemetryValue_u16le(3) * 36 + 50) / 100); + processGhostTelemetryValue(GHOST_ID_GPS_SATS, telemetryRxBuffer[7]); + break; + } } } @@ -272,6 +327,9 @@ void ghostSetDefault(int index, uint8_t id, uint8_t subId) const GhostSensor * sensor = getGhostSensor(id); if (sensor) { TelemetryUnit unit = sensor->unit; + if (unit == UNIT_GPS_LATITUDE || unit == UNIT_GPS_LONGITUDE) { + unit = UNIT_GPS; + } uint8_t prec = min(2, sensor->precision); telemetrySensor.init(sensor->name, unit, prec); } diff --git a/radio/src/telemetry/ghost.h b/radio/src/telemetry/ghost.h index 0a3195835..9a43da9b7 100755 --- a/radio/src/telemetry/ghost.h +++ b/radio/src/telemetry/ghost.h @@ -44,6 +44,8 @@ #define GHST_DL_VTX_STAT 0x22 #define GHST_DL_PACK_STAT 0x23 #define GHST_DL_MENU_DESC 0x24 +#define GHST_DL_GPS_PRIMARY 0x25 +#define GHST_DL_GPS_SECONDARY 0x26 #define GHST_RC_CTR_VAL_12BIT 0x7C0 // 0x3e0 << 1 #define GHST_RC_CTR_VAL_8BIT 0x7C From c0ec43526b74a24a43dd525ccecc6f154c210583 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 10 Mar 2021 16:19:34 +0100 Subject: [PATCH 221/300] Cosmetics --- radio/src/telemetry/ghost.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/telemetry/ghost.cpp b/radio/src/telemetry/ghost.cpp index 1ff6acd48..ad7508084 100755 --- a/radio/src/telemetry/ghost.cpp +++ b/radio/src/telemetry/ghost.cpp @@ -43,14 +43,14 @@ enum GHOST_ID_TOTAL_LATENCY = 0x0007, // Tx-side total latency GHOST_ID_VTX_FREQ = 0x0008, // Vtx Frequency (in MHz) GHOST_ID_VTX_POWER = 0x0009, // Vtx Power (in mW) - GHOST_ID_VTX_CHAN = 0x000a, // Vtx Channel - GHOST_ID_VTX_BAND = 0x000b, // Vtx Band + GHOST_ID_VTX_CHAN = 0x000A, // Vtx Channel + GHOST_ID_VTX_BAND = 0x000B, // Vtx Band - GHOST_ID_PACK_VOLTS = 0x000c, // Battery Pack Voltage - GHOST_ID_PACK_AMPS = 0x000d, // Battery Pack Current - GHOST_ID_PACK_MAH = 0x000e, // Battery Pack mAh consumed + GHOST_ID_PACK_VOLTS = 0x000C, // Battery Pack Voltage + GHOST_ID_PACK_AMPS = 0x000D, // Battery Pack Current + GHOST_ID_PACK_MAH = 0x000E, // Battery Pack mAh consumed - GHOST_ID_GPS_LAT = 0x000f, // GPS Latitude + GHOST_ID_GPS_LAT = 0x000F, // GPS Latitude GHOST_ID_GPS_LONG = 0x0010, // GPS Longitude GHOST_ID_GPS_ALT = 0x0011, // GPS Altitude GHOST_ID_GPS_HDG = 0x0012, // GPS Heading From 7613f72ec55d2ab81e91e5b96f9c4e9ac8728755 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 10 Mar 2021 16:54:01 +0100 Subject: [PATCH 222/300] Tidy up --- radio/src/targets/horus/lcd_driver.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 751aa88cd..97ecc5a61 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -141,29 +141,19 @@ static void LCD_NRSTConfig(void) GPIO_Init(LCD_GPIO_NRST, &GPIO_InitStructure); } -// TODO delay function -static void delay3(uint32_t nCount) -{ - uint32_t index = 0; - for(index = (1000 * 100 * nCount); index != 0; --index) - { - __asm("nop\n"); - } -} - static void lcd_reset(void) { #if defined(RADIO_T18) // T18 screen has issues if NRST is ever brought low NRST_HIGH(); #else NRST_HIGH(); - delay3(1); + delay_ms(1); NRST_LOW(); // RESET(); - delay3(20); + delay_ms(20); NRST_HIGH(); - delay3(30); + delay_ms(30); #endif } From b60ba23f1f2a595c96ceefe625f5efcafa05a274 Mon Sep 17 00:00:00 2001 From: 3djc Date: Wed, 10 Mar 2021 19:13:36 +0100 Subject: [PATCH 223/300] M-Link telemetry (#8281) --- radio/src/audio.cpp | 2 +- radio/src/dataconstants.h | 5 +- radio/src/lua/api_general.cpp | 1 + radio/src/targets/common/arm/CMakeLists.txt | 2 +- radio/src/telemetry/mlink.cpp | 161 ++++++++++++++++++++ radio/src/telemetry/mlink.h | 47 ++++++ radio/src/telemetry/multi.cpp | 8 + radio/src/telemetry/telemetry.h | 1 + radio/src/telemetry/telemetry_sensors.cpp | 4 + radio/src/translations/cz.h.txt | 5 +- radio/src/translations/de.h.txt | 5 +- radio/src/translations/en.h.txt | 5 +- radio/src/translations/es.h.txt | 5 +- radio/src/translations/fi.h.txt | 5 +- radio/src/translations/fr.h.txt | 5 +- radio/src/translations/it.h.txt | 5 +- radio/src/translations/nl.h.txt | 5 +- radio/src/translations/pl.h.txt | 5 +- radio/src/translations/pt.h.txt | 5 +- radio/src/translations/se.h.txt | 5 +- radio/util/tts_en.py | 1 + radio/util/tts_fr.py | 4 + 22 files changed, 276 insertions(+), 15 deletions(-) create mode 100644 radio/src/telemetry/mlink.cpp create mode 100644 radio/src/telemetry/mlink.h diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 702ec044e..04ff8a8b2 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -161,7 +161,7 @@ const char * const unitsFilenames[] = { "hertz", "ms", "us", - "spare4", + "km", "spare5", "spare6", "spare7", diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index dae853d12..b7ab6ebea 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -278,6 +278,7 @@ enum TelemetryProtocol PROTOCOL_TELEMETRY_FLYSKY_IBUS, PROTOCOL_TELEMETRY_HITEC, PROTOCOL_TELEMETRY_HOTT, + PROTOCOL_TELEMETRY_MLINK, PROTOCOL_TELEMETRY_MULTIMODULE, PROTOCOL_TELEMETRY_AFHDS3, PROTOCOL_TELEMETRY_GHOST, @@ -318,8 +319,8 @@ enum TelemetryUnit { UNIT_HERTZ, UNIT_MS, UNIT_US, - UNIT_MAX = UNIT_US, - UNIT_SPARE4, + UNIT_KM, + UNIT_MAX = UNIT_KM, UNIT_SPARE5, UNIT_SPARE6, UNIT_SPARE7, diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 60ebe39fe..3c6f20d42 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -2041,6 +2041,7 @@ const luaR_value_entry opentxConstants[] = { {"UNIT_KMH", UNIT_KMH }, {"UNIT_MPH", UNIT_MPH }, {"UNIT_METERS", UNIT_METERS }, + {"UNIT_KM", UNIT_KM }, {"UNIT_FEET", UNIT_FEET }, {"UNIT_CELSIUS", UNIT_CELSIUS }, {"UNIT_FAHRENHEIT", UNIT_FAHRENHEIT }, diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index 16169ff24..e725bc68c 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -148,7 +148,7 @@ endif() if(MULTIMODULE) add_definitions(-DMULTIMODULE) - set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/hitec.cpp telemetry/hott.cpp telemetry/multi.cpp io/multi_firmware_update.cpp) + set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/hitec.cpp telemetry/hott.cpp telemetry/mlink.cpp telemetry/multi.cpp io/multi_firmware_update.cpp) endif() if(CROSSFIRE) diff --git a/radio/src/telemetry/mlink.cpp b/radio/src/telemetry/mlink.cpp new file mode 100644 index 000000000..a7cdccb92 --- /dev/null +++ b/radio/src/telemetry/mlink.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) OpenTX + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "opentx.h" + +struct MLinkSensor +{ + const uint16_t id; + const char * name; + const TelemetryUnit unit; + const uint8_t precision; +}; + +const MLinkSensor mlinkSensors[] = { + {MLINK_RX_VOLTAGE, ZSTR_BATT, UNIT_VOLTS, 1}, + {MLINK_VOLTAGE, ZSTR_VFAS, UNIT_VOLTS, 1}, + {MLINK_CURRENT, ZSTR_CURR, UNIT_AMPS, 1}, + {MLINK_VARIO, ZSTR_VSPD, UNIT_METERS_PER_SECOND, 1}, + {MLINK_SPEED, ZSTR_SPEED, UNIT_KMH, 1}, + {MLINK_RPM, ZSTR_RPM, UNIT_RPMS, 0}, + {MLINK_TEMP, ZSTR_TEMP1, UNIT_CELSIUS, 1}, + {MLINK_HEADING, ZSTR_HDG, UNIT_DEGREE, 1}, + {MLINK_ALT, ZSTR_ALT , UNIT_METERS, 0}, + {MLINK_FUEL, ZSTR_FUEL, UNIT_PERCENT, 0}, + {MLINK_CAPACITY, ZSTR_CAPACITY, UNIT_MAH, 0}, + {MLINK_FLOW, ZSTR_FLOW, UNIT_MILLILITERS, 0}, + {MLINK_DISTANCE, ZSTR_DIST, UNIT_KM, 1}, + {MLINK_LQI, ZSTR_RSSI, UNIT_RAW, 0}, + {MLINK_LOSS, ZSTR_LOSS, UNIT_RAW, 0}, + {MLINK_TX_RSSI, ZSTR_TX_RSSI, UNIT_RAW, 0}, + {MLINK_TX_LQI, ZSTR_TX_QUALITY, UNIT_RAW, 0}, +}; + +const MLinkSensor * getMLinkSensor(uint16_t id) +{ + for (const MLinkSensor * sensor = mlinkSensors; sensor->id; sensor++) { + if (id == sensor->id) + return sensor; + } + return nullptr; +} + +void processMLinkPacket(const uint8_t * packet) +{ + const uint8_t * data = packet + 2; + + // Multi telem + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_TX_RSSI, 0, 0, (packet[0] * 100) / 31, UNIT_RAW, 0); + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_TX_LQI, 0, 0, packet[1], UNIT_RAW, 0); + + // M-Link telem + if (data[0] == 0x13) { // Telemetry type RX-9 + for (uint8_t i = 1; i < 5; i += 3) { //2 sensors per packet + int32_t val = (int16_t )(data[i + 2] << 8 | data[i + 1]); + val = val >> 1; // remove alarm flag + uint8_t adress = (data[i] & 0xF0) >> 4; + switch (data[i] & 0x0F) { + case MLINK_VOLTAGE: + if ((data[i] & 0xF0) == 0x00){ + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_RX_VOLTAGE, 0, adress, val, UNIT_VOLTS, 1); + } + else { + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_VOLTAGE, 0, adress, val, UNIT_VOLTS, 1); + } + break; + case MLINK_CURRENT: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_CURRENT, 0, adress, val, UNIT_AMPS, 1); + break; + case MLINK_VARIO: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_VARIO, 0, adress, val, UNIT_METERS_PER_SECOND, 1); + break; + case MLINK_SPEED: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_SPEED, 0, adress, val, UNIT_KMH, 1); + break; + case MLINK_RPM: + if (val < 0) { + val = -val * 10; + } + else { + val = val * 100; + } + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_RPM, 0, adress, val, UNIT_RPMS, 0); + break; + case MLINK_TEMP: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_TEMP, 0, adress, val, UNIT_CELSIUS, 1); + break; + case MLINK_HEADING: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_HEADING, 0, adress, val, UNIT_DEGREE, 1); + break; + case MLINK_ALT: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_ALT, 0, adress, val, UNIT_METERS, 0); + break; + case MLINK_FUEL: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_FUEL, 0, adress, val, UNIT_PERCENT, 0); + break; + case MLINK_CAPACITY: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_CAPACITY, 0, adress, val, UNIT_MAH, 0); + break; + case MLINK_FLOW: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_FLOW, 0, adress, val, UNIT_MILLILITERS, 0); + break; + case MLINK_DISTANCE: + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_DISTANCE, 0, adress, val, UNIT_KM, 1); + break; + case MLINK_LQI: + uint8_t mlinkRssi = data[i + 1] >> 1; + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_LQI, 0, 0, mlinkRssi, UNIT_RAW, 0); + telemetryData.rssi.set(mlinkRssi); + if (mlinkRssi > 0) { + telemetryStreaming = TELEMETRY_TIMEOUT10ms; + } + break; + } + } + } + else if (packet[2] == 0x03) { // Telemetry type RX-5 + uint16_t mlinkRssi = (packet[4] * 100) / 35; + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_LQI, 0, 0, mlinkRssi, UNIT_RAW, 0); + telemetryData.rssi.set(mlinkRssi); + if (mlinkRssi > 0) { + telemetryStreaming = TELEMETRY_TIMEOUT10ms; + } + setTelemetryValue(PROTOCOL_TELEMETRY_MLINK, MLINK_LOSS, 0, 0, packet[7], UNIT_RAW, 0); + } +} + +void mlinkSetDefault(int index, uint16_t id, uint8_t subId, uint8_t instance) +{ + TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index]; + telemetrySensor.id = id; + telemetrySensor.subId = subId; + telemetrySensor.instance = instance; + + const MLinkSensor * sensor = getMLinkSensor(id); + if (sensor) { + TelemetryUnit unit = sensor->unit; + uint8_t prec = min(2, sensor->precision); + telemetrySensor.init(sensor->name, unit, prec); + if (unit == UNIT_RPMS) { + telemetrySensor.custom.ratio = 1; + telemetrySensor.custom.offset = 1; + } + } + else { + telemetrySensor.init(id); + } + + storageDirty(EE_MODEL); +} diff --git a/radio/src/telemetry/mlink.h b/radio/src/telemetry/mlink.h new file mode 100644 index 000000000..eb5da955e --- /dev/null +++ b/radio/src/telemetry/mlink.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +enum { + MLINK_VOLTAGE = 1, + MLINK_CURRENT = 2, + MLINK_VARIO = 3, + MLINK_SPEED = 4, + MLINK_RPM = 5, + MLINK_TEMP = 6, + MLINK_HEADING = 7, + MLINK_ALT = 8, + MLINK_FUEL = 9, + MLINK_LQI = 10, + MLINK_CAPACITY = 11, + MLINK_FLOW = 12, + MLINK_DISTANCE = 13, + MLINK_RX_VOLTAGE = 16, // out of range ID for specific RxBt treatment + MLINK_LOSS = 17, // out of range ID for handling number of loss + MLINK_TX_RSSI = 18, // out of range ID for handling Telemetry RSSi reported by multi + MLINK_TX_LQI = 19, // out of range ID for handling Telemetry LQI reported by multi +}; + +void processMLinkTelemetryData(uint8_t data, uint8_t* rxBuffer, uint8_t& rxBufferCount); +void mlinkSetDefault(int index, uint16_t id, uint8_t subId, uint8_t instance); + +// Used by multi protocol +void processMLinkPacket(const uint8_t *packet); diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index b1b86eb0a..49a5b0d58 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -42,6 +42,7 @@ enum MultiPacketTypes : uint8_t FlyskyIBusTelemetryAC, MultiRxChannels, HottTelemetry, + MLinkTelemetry }; enum MultiBufferState : uint8_t @@ -379,6 +380,13 @@ static void processMultiTelemetryPaket(const uint8_t * packet, uint8_t module) TRACE("[MP] Received HoTT telemetry len %d < 14", len); break; + case MLinkTelemetry: + if (len > 6) + processMLinkPacket(data); + else + TRACE("[MP] Received M-Link telemetry len %d < 6", len); + break; + case FrSkyHubTelemetry: if (len >= 4) frskyDProcessPacket(data); diff --git a/radio/src/telemetry/telemetry.h b/radio/src/telemetry/telemetry.h index 6599db749..d70dbc6be 100644 --- a/radio/src/telemetry/telemetry.h +++ b/radio/src/telemetry/telemetry.h @@ -33,6 +33,7 @@ #include "hitec.h" #include "hott.h" #include "multi.h" + #include "mlink.h" #endif #if defined(MULTIMODULE) || defined(AFHDS3) #include "flysky_ibus.h" diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index e88807062..b439bd678 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -559,6 +559,10 @@ int setTelemetryValue(TelemetryProtocol protocol, uint16_t id, uint8_t subId, ui case PROTOCOL_TELEMETRY_HOTT: hottSetDefault(index, id, subId, instance); break; + + case PROTOCOL_TELEMETRY_MLINK: + mlinkSetDefault(index, id, subId, instance); + break; #endif #if defined(LUA) diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 26c96a182..771093985 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -260,7 +260,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metr.\0""Imper.", "Metrické""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1381,3 +1381,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 1e8d7ce96..f0091ef99 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -263,7 +263,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\012") #define TR_VUNITSSYSTEM TR("Metrik""Imper.", "Metrisch\0 ""Imperial\0 ") #define LEN_VTELEMUNIT "\003" - #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" + #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1388,3 +1388,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 3ec8c1e31..1fcb635cf 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -263,7 +263,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1385,3 +1385,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 31a0b8afc..793be030b 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -260,7 +260,7 @@ #define LEN_VUNITSSYSTEM "\010" #define TR_VUNITSSYSTEM "Métrico\0""Imperial" #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1384,3 +1384,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 1158806f0..0fa5467ea 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -281,7 +281,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1392,3 +1392,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 5e59ba68d..9190811ed 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -283,7 +283,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\012") #define TR_VUNITSSYSTEM TR("Métr.\0""Impér.", "Métriques\0""Impériales") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1409,3 +1409,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 2d25fffec..277b21458 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -284,7 +284,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\011") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metriche\0""Imperiali") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1402,3 +1402,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index f9a2be448..3e9e04641 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -264,7 +264,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Mtrsch""Engels", "Metrisch\0 ""Engels\0 ") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1394,3 +1394,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 200072288..a77acbcd3 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -281,7 +281,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") /*8 decimal*/ #define TR_VUNITSSYSTEM TR("Metr. ""Imper.", "Metryczn""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1401,3 +1401,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 0d1d597ac..3241e9ce4 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -277,7 +277,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metric""Imper.", "Metric\0 ""Imperial") #define LEN_VTELEMUNIT "\003" - #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" + #define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1401,3 +1401,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index c3d3267d3..4cd3b0df4 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -278,7 +278,7 @@ #define LEN_VUNITSSYSTEM TR("\006", "\010") #define TR_VUNITSSYSTEM TR("Metri.""Imper.", "Metriska""Imperial") #define LEN_VTELEMUNIT "\003" -#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0" +#define TR_VTELEMUNIT "-\0 ""V\0 ""A\0 ""mA\0""kts""m/s""f/s""kmh""mph""m\0 ""ft\0""@C\0""@F\0""%\0 ""mAh""W\0 ""mW\0""dB\0""rpm""g\0 ""@\0 ""rad""ml\0""fOz""mlm""Hz\0""mS\0""uS\0""km\0" #define STR_V (STR_VTELEMUNIT+1) #define STR_A (STR_VTELEMUNIT+4) @@ -1401,3 +1401,6 @@ #define ZSTR_SERVO_VOLTAGE "SrvV" #define ZSTR_SERVO_TEMPERATURE "SrvT" #define ZSTR_SERVO_STATUS "SrvS" +#define ZSTR_LOSS "Loss" +#define ZSTR_SPEED "Spd " +#define ZSTR_FLOW "Flow" diff --git a/radio/util/tts_en.py b/radio/util/tts_en.py index 616abe516..6230aafe9 100755 --- a/radio/util/tts_en.py +++ b/radio/util/tts_en.py @@ -41,6 +41,7 @@ for i, (s, f) in enumerate([("volt", "volt0"), ("volts", "volt1"), ("hertz", "hertz0"), ("hertz", "hertz1"), ("milisecond", "ms0"), ("miliseconds", "ms1"), ("microsecond", "us0"), ("microseconds", "us1"), + ("kilometer", "km0"), ("kilometers", "km1"), ("hour", "hour0"), ("hours", "hour1"), ("minute", "minute0"), ("minutes", "minute1"), ("second", "second0"), ("seconds", "second1"), diff --git a/radio/util/tts_fr.py b/radio/util/tts_fr.py index 9dc046fa5..eba461d72 100755 --- a/radio/util/tts_fr.py +++ b/radio/util/tts_fr.py @@ -39,6 +39,10 @@ for i, (s, f) in enumerate([("volts", "volt0"), ("millilitres", "ml0"), ("onces", "founce0"), ("millilitre minute", "mlpm0"), + ("microsecond", "us0"), + ("milisecond", "ms0"), + ("hertz", "hertz0"), + ("kilometre", "km0"), ("heure", "hour0"), ("minute", "minute0"), ("seconde", "second0"), From f8bd7b7fb85f5eeab5698b7392d1d50810b87013 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 10 Mar 2021 21:27:56 +0100 Subject: [PATCH 224/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index b45c8d90c..8e8a616aa 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2178,3 +2178,4 @@ Stefano Boggia James Rhodes Peter Gaskill Jacques Temey +Anthony Challis From 1f8692aed344ad69b5f12a5516667232ec0d8de9 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 11 Mar 2021 14:41:20 +1100 Subject: [PATCH 225/300] Fix delete input --- companion/src/modeledit/inputs.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index 8636d6335..1f7fd2b0b 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -351,7 +351,7 @@ void InputsPanel::expoOpen(QListWidgetItem *item) if (!item) item = ExposlistWidget->currentItem(); - if (item == nullptr) + if (item == nullptr) return; int idx = item->data(Qt::UserRole).toByteArray().at(0); @@ -372,7 +372,7 @@ void InputsPanel::expoOpen(QListWidgetItem *item) void InputsPanel::expoAdd() { QListWidgetItem *item = ExposlistWidget->currentItem(); - if (item == nullptr) + if (item == nullptr) return; int index = item->data(Qt::UserRole).toByteArray().at(0); @@ -580,13 +580,15 @@ bool InputsPanel::cmInputMoveUpAllowed() const void InputsPanel::cmInputClear() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all lines for the selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all lines for the selected Input. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; for (int i = CPN_MAX_EXPOS - 1; i >= 0; i--) { ExpoData *ed = &model->expoData[i]; - if ((int)ed->chn == inputIdx) - model->removeInput(i); + if (!ed->isEmpty()) { + if ((int)ed->chn == inputIdx) + model->removeInput(i); + } } model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_CLEAR, inputIdx); update(); @@ -596,15 +598,17 @@ void InputsPanel::cmInputClear() void InputsPanel::cmInputDelete() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete all lines for the selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete all lines for the selected Input. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; - for (int i = 0; i < CPN_MAX_EXPOS; i++) { + for (int i = CPN_MAX_EXPOS - 1; i >= 0; i--) { ExpoData *ed = &model->expoData[i]; - if ((int)ed->chn == inputIdx) - model->removeInput(i); - else if ((int)ed->chn > inputIdx) - ed->chn--; + if (!ed->isEmpty()) { + if ((int)ed->chn == inputIdx) + model->removeInput(i); + else if ((int)ed->chn > inputIdx) + ed->chn--; + } } for (int i = inputIdx; i < inputsCount; i++) { From 92e33d02659e07ef2920b5a71eabd819f3c60657 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 11 Mar 2021 20:10:03 +0100 Subject: [PATCH 226/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8e8a616aa..fc9d7f707 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2179,3 +2179,4 @@ James Rhodes Peter Gaskill Jacques Temey Anthony Challis +Gerard Falaise From 6410dcb43c8cfced638818301fc91fc09dedd6bb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 12 Mar 2021 10:17:51 +0100 Subject: [PATCH 227/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index fc9d7f707..637915fbb 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2180,3 +2180,4 @@ Peter Gaskill Jacques Temey Anthony Challis Gerard Falaise +George McGinnis From 110e1a5fc7a3b863e0a19cd0e7066c3de58aa78f Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 12 Mar 2021 10:32:56 +1100 Subject: [PATCH 228/300] Move getElementName to helpers and move timerdata to own files --- companion/src/firmwares/CMakeLists.txt | 3 +- companion/src/firmwares/datahelpers.cpp | 31 ++++++++++ .../{helpersdata.h => datahelpers.h} | 8 ++- companion/src/firmwares/modeldata.cpp | 25 -------- companion/src/firmwares/modeldata.h | 30 +-------- companion/src/firmwares/radiodata.cpp | 17 ------ companion/src/firmwares/radiodata.h | 11 ++-- companion/src/firmwares/sensordata.cpp | 4 +- companion/src/firmwares/sensordata.h | 2 +- companion/src/firmwares/timerdata.cpp | 50 +++++++++++++++ companion/src/firmwares/timerdata.h | 61 +++++++++++++++++++ 11 files changed, 159 insertions(+), 83 deletions(-) create mode 100644 companion/src/firmwares/datahelpers.cpp rename companion/src/firmwares/{helpersdata.h => datahelpers.h} (87%) create mode 100644 companion/src/firmwares/timerdata.cpp create mode 100644 companion/src/firmwares/timerdata.h diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index 3bc2ca275..b79d8fd42 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -23,11 +23,13 @@ set(firmwares_SRCS rawswitch.cpp sensordata.cpp telem_data.cpp + timerdata.cpp afhds3.cpp ersky9x/ersky9xeeprom.cpp ersky9x/ersky9xinterface.cpp opentx/opentxeeprom.cpp opentx/opentxinterface.cpp + datahelpers.cpp ) string(REPLACE ".cpp" ".h" firmwares_HDRS "${firmwares_SRCS}") @@ -35,7 +37,6 @@ string(REPLACE ".cpp" ".h" firmwares_HDRS "${firmwares_SRCS}") list(APPEND firmwares_HDRS eepromimportexport.h moduledata.h - helpersdata.h ) set(firmwares_QT diff --git a/companion/src/firmwares/datahelpers.cpp b/companion/src/firmwares/datahelpers.cpp new file mode 100644 index 000000000..7aab674ad --- /dev/null +++ b/companion/src/firmwares/datahelpers.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "datahelpers.h" + +QString DataHelpers::getElementName(const QString & prefix, const unsigned int index, const char * name, const bool padding) +{ + QString result = prefix; + result.append(padding ? QString("%1").arg(index, 2, 10, QChar('0')) : QString("%1").arg(index)); + if (name && QString(name).trimmed().length() > 0) + result.append(":" + QString(name).trimmed()); + + return result; +} diff --git a/companion/src/firmwares/helpersdata.h b/companion/src/firmwares/datahelpers.h similarity index 87% rename from companion/src/firmwares/helpersdata.h rename to companion/src/firmwares/datahelpers.h index 29273e5f0..3291e7b47 100644 --- a/companion/src/firmwares/helpersdata.h +++ b/companion/src/firmwares/datahelpers.h @@ -24,8 +24,6 @@ class FieldRange { - Q_DECLARE_TR_FUNCTIONS(FieldRange) - public: FieldRange(): decimals(0), @@ -48,3 +46,9 @@ class FieldRange QString prefix; QString unit; }; + +namespace DataHelpers +{ + QString getElementName(const QString & prefix, const unsigned int index, const char * name = 0, const bool padding = false); + +} diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 7657e2798..3efb7ae6a 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -27,31 +27,6 @@ #include "helpers.h" #include "adjustmentreference.h" -/* - * TimerData - */ - -void TimerData::convert(RadioDataConversionState & cstate) -{ - cstate.setComponent(tr("TMR"), 1); - cstate.setSubComp(tr("Timer %1").arg(cstate.subCompIdx + 1)); - mode.convert(cstate); -} - -bool TimerData::isEmpty() -{ - return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWN_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/); -} - -QString TimerData::nameToString(int index) const -{ - return RadioData::getElementName(tr("TMR", "as in Timer"), index + 1, name); -} - -/* - * ModelData - */ - ModelData::ModelData() { clear(); diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 80b7d7353..14a9386ee 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -33,6 +33,7 @@ #include "output_data.h" #include "sensordata.h" #include "telem_data.h" +#include "timerdata.h" #include @@ -55,35 +56,6 @@ class RSSIAlarmData { } }; -#define TIMER_NAME_LEN 8 - -class TimerData { - Q_DECLARE_TR_FUNCTIONS(TimerData) - - public: - enum CountDownMode { - COUNTDOWN_SILENT, - COUNTDOWN_BEEPS, - COUNTDOWN_VOICE, - COUNTDOWN_HAPTIC - }; - TimerData() { clear(); } - RawSwitch mode; - char name[TIMER_NAME_LEN+1]; - bool minuteBeep; - unsigned int countdownBeep; - unsigned int val; - unsigned int persistent; - int countdownStart; - unsigned int direction; - int pvalue; - void clear() { memset(reinterpret_cast(this), 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } - void convert(RadioDataConversionState & cstate); - bool isEmpty(); - bool isModeOff() { return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } - QString nameToString(int index) const; -}; - #define CPN_MAX_SCRIPTS 9 #define CPN_MAX_SCRIPT_INPUTS 10 class ScriptData { diff --git a/companion/src/firmwares/radiodata.cpp b/companion/src/firmwares/radiodata.cpp index 8054e5b1a..c833281fd 100644 --- a/companion/src/firmwares/radiodata.cpp +++ b/companion/src/firmwares/radiodata.cpp @@ -22,23 +22,6 @@ #include "radiodataconversionstate.h" #include "eeprominterface.h" -// static -QString RadioData::getElementName(const QString & prefix, unsigned int index, const char * name, bool padding) -{ - QString result = prefix; - if (padding) - result += QString("%1").arg(index, 2, 10, QChar('0')); - else - result += QString("%1").arg(index); - if (name) { - QString trimmed = QString(name).trimmed(); - if (trimmed.length() > 0) { - result += ":" + QString(name).trimmed(); - } - } - return result; -} - RadioData::RadioData() { models.resize(getCurrentFirmware()->getCapability(Models)); diff --git a/companion/src/firmwares/radiodata.h b/companion/src/firmwares/radiodata.h index 29d29e4eb..d289f1846 100644 --- a/companion/src/firmwares/radiodata.h +++ b/companion/src/firmwares/radiodata.h @@ -1,9 +1,10 @@ -#ifndef _RADIODATA_H_ -#define _RADIODATA_H_ +#pragma once #include "generalsettings.h" #include "modeldata.h" +#include "datahelpers.h" // required for getElementName + #include class RadioDataConversionState; @@ -32,10 +33,10 @@ class RadioData { void fixModelFilenames(); QString getNextModelFilename(); - static QString getElementName(const QString & prefix, unsigned int index, const char * name = 0, bool padding = false); + // leave here until all calls repointed + static QString getElementName(const QString & prefix, unsigned int index, const char * name = 0, bool padding = false) + { return DataHelpers::getElementName(prefix, index, name, padding); } protected: void fixModelFilename(unsigned int index); }; - -#endif // _RADIODATA_H_ diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 2c0866e66..12dcdd848 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -19,8 +19,6 @@ */ #include "sensordata.h" - -#include "radiodata.h" #include "modeldata.h" #include "eeprominterface.h" #include "compounditemmodels.h" @@ -35,7 +33,7 @@ void SensorData::updateUnit() QString SensorData::nameToString(int index) const { - return RadioData::getElementName(tr("TELE"), index + 1, label); + return DataHelpers::getElementName(tr("TELE"), index + 1, label); } QString SensorData::getOrigin(const ModelData * model) const diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index 2353a0ebf..b93af0a84 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -20,7 +20,7 @@ #pragma once -#include "helpersdata.h" +#include "datahelpers.h" #include diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp new file mode 100644 index 000000000..986ff1aa5 --- /dev/null +++ b/companion/src/firmwares/timerdata.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "timerdata.h" +#include "radiodataconversionstate.h" + +void TimerData::convert(RadioDataConversionState & cstate) +{ + cstate.setComponent(tr("TMR"), 1); + cstate.setSubComp(tr("Timer %1").arg(cstate.subCompIdx + 1)); + mode.convert(cstate); +} + +void TimerData::clear() +{ + memset(reinterpret_cast(this), 0, sizeof(TimerData)); + mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); +} + +bool TimerData::isEmpty() +{ + return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWN_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/); +} + +bool TimerData::isModeOff() +{ + return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); +} + +QString TimerData::nameToString(int index) const +{ + return DataHelpers::getElementName(tr("TMR", "as in Timer"), index + 1, name); +} diff --git a/companion/src/firmwares/timerdata.h b/companion/src/firmwares/timerdata.h new file mode 100644 index 000000000..2d5356331 --- /dev/null +++ b/companion/src/firmwares/timerdata.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "rawswitch.h" +#include "datahelpers.h" + +#include + +class RadioDataConversionState; + +#define TIMER_NAME_LEN 8 + +class TimerData { + Q_DECLARE_TR_FUNCTIONS(TimerData) + + public: + enum CountDownMode { + COUNTDOWN_SILENT, + COUNTDOWN_BEEPS, + COUNTDOWN_VOICE, + COUNTDOWN_HAPTIC, + COUNTDOWN_COUNT + }; + + TimerData() { clear(); } + + RawSwitch mode; + char name[TIMER_NAME_LEN + 1]; + bool minuteBeep; + unsigned int countdownBeep; + unsigned int val; + unsigned int persistent; + int countdownStart; + unsigned int direction; + int pvalue; + + void convert(RadioDataConversionState & cstate); + void clear(); + bool isEmpty(); + bool isModeOff(); + QString nameToString(int index) const; +}; From 03497d8fc2ec6ef1e9067729afa3eb511219eafe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 12 Mar 2021 22:50:58 +0100 Subject: [PATCH 229/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 637915fbb..7cc83e580 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2181,3 +2181,4 @@ Jacques Temey Anthony Challis Gerard Falaise George McGinnis +Antoine Soulie From ef1d0e8a82678a46ebab1fe7e557afb15c1d01ca Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 13 Mar 2021 12:16:05 +0100 Subject: [PATCH 230/300] Please Travis ? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6ddb599b5..77cc9adfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ addons: - libmpc3 - libfox-1.6-dev - libgtest-dev + - libclang-common-6.0-dev - clang-6.0 - python3-pip From a0d96ab39fc7d4e926fc27bbaff8e7453e671e34 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 13 Mar 2021 12:18:52 +0100 Subject: [PATCH 231/300] Please Travis ? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 77cc9adfc..909cf534d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ addons: - libmpc3 - libfox-1.6-dev - libgtest-dev + - lib32stdc++6 - libclang-common-6.0-dev - clang-6.0 - python3-pip From 1a4eb0636e6652af9c9197cefed2be814907032a Mon Sep 17 00:00:00 2001 From: Risto Date: Sat, 13 Mar 2021 17:57:53 +0100 Subject: [PATCH 232/300] Minor comment fix in hal.h (#8361) Minor comment fix in hal.h for AUX_SERIAL_PWR_GPIO_PIN port and pin. --- radio/src/targets/horus/hal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 48bd8ff30..b094bb333 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -422,7 +422,7 @@ #define AUX_SERIAL_DMA_Channel_RX DMA_Channel_4 #if defined(RADIO_TX16S) #define AUX_SERIAL_PWR_GPIO GPIOA - #define AUX_SERIAL_PWR_GPIO_PIN GPIO_Pin_15 // PB.00 + #define AUX_SERIAL_PWR_GPIO_PIN GPIO_Pin_15 // PA.15 #define TRAINER_BATTERY_COMPARTMENT // allows serial port TTL trainer #endif #else From fe5294c127f14e2aa0ea9a5f800dd40c805e2da6 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sat, 13 Mar 2021 08:52:06 +1100 Subject: [PATCH 233/300] Add booltostring functions to datahelpers --- companion/src/firmwares/datahelpers.cpp | 18 ++++++++++++++++++ companion/src/firmwares/datahelpers.h | 9 +++++++++ companion/src/firmwares/sensordata.cpp | 10 +++++----- companion/src/firmwares/sensordata.h | 1 - 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/companion/src/firmwares/datahelpers.cpp b/companion/src/firmwares/datahelpers.cpp index 7aab674ad..78ac15037 100644 --- a/companion/src/firmwares/datahelpers.cpp +++ b/companion/src/firmwares/datahelpers.cpp @@ -20,6 +20,24 @@ #include "datahelpers.h" +QString DataHelpers::boolToString(const bool value, const BoolFormat format) +{ + static const char *strings[] = { + QT_TRANSLATE_NOOP("DataHelpers", "Disabled"), + QT_TRANSLATE_NOOP("DataHelpers", "Enabled"), + QT_TRANSLATE_NOOP("DataHelpers", "OFF"), + QT_TRANSLATE_NOOP("DataHelpers", "ON"), + QT_TRANSLATE_NOOP("DataHelpers", "False"), + QT_TRANSLATE_NOOP("DataHelpers", "True"), + QT_TRANSLATE_NOOP("DataHelpers", "N"), + QT_TRANSLATE_NOOP("DataHelpers", "Y"), + QT_TRANSLATE_NOOP("DataHelpers", "No"), + QT_TRANSLATE_NOOP("DataHelpers", "Yes") + }; + + return QCoreApplication::translate("DataHelpers", strings[format * 2 + value]); +} + QString DataHelpers::getElementName(const QString & prefix, const unsigned int index, const char * name, const bool padding) { QString result = prefix; diff --git a/companion/src/firmwares/datahelpers.h b/companion/src/firmwares/datahelpers.h index 3291e7b47..ed46da717 100644 --- a/companion/src/firmwares/datahelpers.h +++ b/companion/src/firmwares/datahelpers.h @@ -49,6 +49,15 @@ class FieldRange namespace DataHelpers { + enum BoolFormat { + BOOL_FMT_ENABLEDISABLE, + BOOL_FMT_ONOFF, + BOOL_FMT_TRUEFALSE, + BOOL_FMT_YN, + BOOL_FMT_YESNO + }; + + QString boolToString(const bool value, const BoolFormat format); QString getElementName(const QString & prefix, const unsigned int index, const char * name = 0, const bool padding = false); } diff --git a/companion/src/firmwares/sensordata.cpp b/companion/src/firmwares/sensordata.cpp index 12dcdd848..ae602bd0d 100644 --- a/companion/src/firmwares/sensordata.cpp +++ b/companion/src/firmwares/sensordata.cpp @@ -169,7 +169,7 @@ QString SensorData::paramsToString(const ModelData * model) const int precsn = prec == 0 ? 1 : pow(10, prec); str.append(QString(FMT_LABEL_VALUE).arg(tr("Ratio")).arg((float)ratio / 10)); str.append(QString(FMT_LABEL_VALUE).arg(tr("Offset")).arg(QString::number((float)offset / precsn, 'f', prec))); - str.append(QString(FMT_LABEL_VALUE).arg(tr("Auto Offset")).arg(boolToString(autoOffset))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Auto Offset")).arg(DataHelpers::boolToString(autoOffset, DataHelpers::BOOL_FMT_YN))); } else { str.append(QString(FMT_LABEL_VALUE).arg(tr("Blades")).arg(ratio)); // TODO refactor to dedicated RPMS field @@ -178,15 +178,15 @@ QString SensorData::paramsToString(const ModelData * model) const } if (mask & SENSOR_ISCONFIGURABLE) - str.append(QString(FMT_LABEL_VALUE).arg(tr("Filter")).arg(boolToString(filter))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Filter")).arg(DataHelpers::boolToString(filter, DataHelpers::BOOL_FMT_YN))); if (type == TELEM_TYPE_CALCULATED) - str.append(QString(FMT_LABEL_VALUE).arg(tr("Persist")).arg(boolToString(persistent))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Persist")).arg(DataHelpers::boolToString(persistent, DataHelpers::BOOL_FMT_YN))); if (mask & SENSOR_HAS_POSITIVE) - str.append(QString(FMT_LABEL_VALUE).arg(tr("Positive")).arg(boolToString(onlyPositive))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Positive")).arg(DataHelpers::boolToString(onlyPositive, DataHelpers::BOOL_FMT_YN))); - str.append(QString(FMT_LABEL_VALUE).arg(tr("Log")).arg(boolToString(logs))); + str.append(QString(FMT_LABEL_VALUE).arg(tr("Log")).arg(DataHelpers::boolToString(logs, DataHelpers::BOOL_FMT_YN))); return str; } diff --git a/companion/src/firmwares/sensordata.h b/companion/src/firmwares/sensordata.h index b93af0a84..8a032cec5 100644 --- a/companion/src/firmwares/sensordata.h +++ b/companion/src/firmwares/sensordata.h @@ -205,7 +205,6 @@ class SensorData { static QString cellIndexToString(const int value); static QString unitToString(const int value); static QString precToString(const int value); - static QString boolToString(const bool value) { return value ? tr("Y") : tr("N"); } static bool isRssiSensorAvailable(const ModelData * model, const int value); static QString rssiSensorToString(const ModelData * model, const int value); From 7940515a48c6241a04f500187d9a5889650c9966 Mon Sep 17 00:00:00 2001 From: Risto Date: Sat, 13 Mar 2021 22:43:26 +0100 Subject: [PATCH 234/300] telemetry dir pin init missed oType def Added missing OType definition to telemetry direction pin. --- radio/src/targets/horus/telemetry_driver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/targets/horus/telemetry_driver.cpp b/radio/src/targets/horus/telemetry_driver.cpp index 0288363be..165e65933 100644 --- a/radio/src/targets/horus/telemetry_driver.cpp +++ b/radio/src/targets/horus/telemetry_driver.cpp @@ -34,6 +34,7 @@ static void telemetryInitDirPin() GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Pin = TELEMETRY_DIR_GPIO_PIN; GPIO_Init(TELEMETRY_DIR_GPIO, &GPIO_InitStructure); GPIO_ResetBits(TELEMETRY_DIR_GPIO, TELEMETRY_DIR_GPIO_PIN); From b4478ea48f65d79fefe94248b44b2070a224edfc Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 14 Mar 2021 09:32:56 +1100 Subject: [PATCH 235/300] Timerdata refactoring --- companion/src/firmwares/timerdata.cpp | 106 ++++++++++++++++++++++++++ companion/src/firmwares/timerdata.h | 39 ++++++++-- 2 files changed, 139 insertions(+), 6 deletions(-) diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp index 986ff1aa5..484dfe019 100644 --- a/companion/src/firmwares/timerdata.cpp +++ b/companion/src/firmwares/timerdata.cpp @@ -48,3 +48,109 @@ QString TimerData::nameToString(int index) const { return DataHelpers::getElementName(tr("TMR", "as in Timer"), index + 1, name); } + +QString TimerData::countdownBeepToString() const +{ + return countdownBeepToString(countdownBeep); +} + +QString TimerData::countdownStartToString() const +{ + return countdownStartToString(countdownStart); +} + +QString TimerData::persistentToString() const +{ + return persistentToString(persistent); +} + +// static +QString TimerData::countdownBeepToString(const int value) +{ + switch(value) { + case COUNTDOWNBEEP_SILENT: + return tr("Silent"); + case COUNTDOWNBEEP_BEEPS: + return tr("Beeps"); + case COUNTDOWNBEEP_VOICE: + return tr("Voice"); + case COUNTDOWNBEEP_HAPTIC: + return tr("Haptic"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString TimerData::countdownStartToString(const int value) +{ + switch(value) { + case COUNTDOWNSTART_5: + return tr("5s"); + case COUNTDOWNSTART_10: + return tr("10s"); + case COUNTDOWNSTART_20: + return tr("20s"); + case COUNTDOWNSTART_30: + return tr("30s"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +QString TimerData::persistentToString(const int value) +{ + switch(value) { + case PERSISTENT_NOT: + return tr("Not persistent"); + case PERSISTENT_FLIGHT: + return tr("Persistent (flight)"); + case PERSISTENT_MANUALRESET: + return tr("Persistent (manual reset)"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +// static +AbstractStaticItemModel * TimerData::countdownBeepItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("timerdata.countdownBeep"); + + for (int i = 0; i < COUNTDOWNBEEP_COUNT; i++) { + QString str = countdownBeepToString(i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * TimerData::countdownStartItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("timerdata.countdownStart"); + + for (int i = COUNTDOWNSTART_LAST - 1; i >= COUNTDOWNSTART_FIRST; i--) { + QString str = countdownStartToString(i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * TimerData::persistentItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("timerdata.persistent"); + + for (int i = 0; i < PERSISTENT_COUNT; i++) { + QString str = persistentToString(i); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/timerdata.h b/companion/src/firmwares/timerdata.h index 2d5356331..e2949cd6b 100644 --- a/companion/src/firmwares/timerdata.h +++ b/companion/src/firmwares/timerdata.h @@ -33,12 +33,28 @@ class TimerData { Q_DECLARE_TR_FUNCTIONS(TimerData) public: - enum CountDownMode { - COUNTDOWN_SILENT, - COUNTDOWN_BEEPS, - COUNTDOWN_VOICE, - COUNTDOWN_HAPTIC, - COUNTDOWN_COUNT + enum CountDownBeepType { + COUNTDOWNBEEP_SILENT, + COUNTDOWNBEEP_BEEPS, + COUNTDOWNBEEP_VOICE, + COUNTDOWNBEEP_HAPTIC, + COUNTDOWNBEEP_COUNT + }; + + enum CountDownStart { + COUNTDOWNSTART_30 = -2, + COUNTDOWNSTART_FIRST = COUNTDOWNSTART_30, + COUNTDOWNSTART_20, + COUNTDOWNSTART_10, + COUNTDOWNSTART_5, + COUNTDOWNSTART_LAST = COUNTDOWNSTART_5 + }; + + enum PersistentType { + PERSISTENT_NOT, + PERSISTENT_FLIGHT, + PERSISTENT_MANUALRESET, + PERSISTENT_COUNT }; TimerData() { clear(); } @@ -58,4 +74,15 @@ class TimerData { bool isEmpty(); bool isModeOff(); QString nameToString(int index) const; + QString countdownBeepToString(); + QString countdownStartToString(); + QString persistentToString(); + + static QString countdownBeepToString(const int value); + static QString countdownStartToString(const int value); + static QString persistentToString(const int value); + static AbstractStaticItemModel * countdownBeepItemModel(); + static AbstractStaticItemModel * countdownStartItemModel(); + static AbstractStaticItemModel * persistentItemModel(); + }; From 010d85d146a15b0150091209323925f96ac1e568 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 14 Mar 2021 10:32:25 +0100 Subject: [PATCH 236/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7cc83e580..a421eb36f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2182,3 +2182,5 @@ Anthony Challis Gerard Falaise George McGinnis Antoine Soulie +Tjeerd Jager +Bertold Van den Bergh From b11f751127b6f23c398ea70a20d15b730e1df887 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 14 Mar 2021 10:38:42 +0100 Subject: [PATCH 237/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index a421eb36f..c3cc61a76 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2184,3 +2184,4 @@ George McGinnis Antoine Soulie Tjeerd Jager Bertold Van den Bergh +Stefanie Zerbe From c40136d08a2d29918a133b5276fa7754d92b3ca9 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 14 Mar 2021 23:35:52 +1100 Subject: [PATCH 238/300] Add data models to setup timers --- .../src/datamodels/compounditemmodels.cpp | 10 ++ companion/src/datamodels/compounditemmodels.h | 1 + companion/src/firmwares/datahelpers.cpp | 21 +++ companion/src/firmwares/datahelpers.h | 6 + companion/src/firmwares/timerdata.cpp | 32 +++-- companion/src/firmwares/timerdata.h | 17 ++- companion/src/modeledit/setup.cpp | 134 ++++++++++-------- companion/src/modeledit/setup.h | 20 ++- companion/src/modeledit/setup_timer.ui | 23 ++- companion/src/modelprinter.cpp | 40 ------ companion/src/modelprinter.h | 4 - companion/src/multimodelprinter.cpp | 8 +- 12 files changed, 179 insertions(+), 137 deletions(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index ab0ba83ba..b243bf09a 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -717,6 +717,16 @@ AbstractItemModel * CompoundItemModelFactory::getItemModel(const int id) const return nullptr; } +AbstractItemModel * CompoundItemModelFactory::getItemModel(const QString name) const +{ + foreach (AbstractItemModel * itemModel, registeredItemModels) { + if (itemModel->getName() == name) + return itemModel; + } + + return nullptr; +} + bool CompoundItemModelFactory::isItemModelRegistered(const int id) const { foreach (AbstractItemModel * itemModel, registeredItemModels) { diff --git a/companion/src/datamodels/compounditemmodels.h b/companion/src/datamodels/compounditemmodels.h index 7acfcbdc9..d5a7c5f27 100644 --- a/companion/src/datamodels/compounditemmodels.h +++ b/companion/src/datamodels/compounditemmodels.h @@ -363,6 +363,7 @@ class CompoundItemModelFactory void unregisterItemModel(const int id); bool isItemModelRegistered(const int id) const; AbstractItemModel * getItemModel(const int id) const; + AbstractItemModel * getItemModel(const QString name) const; void update(const int event = AbstractItemModel::IMUE_SystemRefresh); void dumpAllItemModelContents() const; diff --git a/companion/src/firmwares/datahelpers.cpp b/companion/src/firmwares/datahelpers.cpp index 78ac15037..8ae759a1b 100644 --- a/companion/src/firmwares/datahelpers.cpp +++ b/companion/src/firmwares/datahelpers.cpp @@ -47,3 +47,24 @@ QString DataHelpers::getElementName(const QString & prefix, const unsigned int i return result; } + +QString DataHelpers::timeToString(const int value, const unsigned int mask) +{ + bool negative = value < 0 ? true : false; + int val = abs(value); + + QString result = QString("%1").arg(negative ? "-" : ((mask & TIMESTR_MASK_PADSIGN) ? " " : "")); + + if (mask & TIMESTR_MASK_HRSMINS) { + int hours = val / 3600; + if (hours > 0 || (mask & TIMESTR_MASK_ZEROHRS)) { + val -= hours * 3600; + result.append(QString("%1:").arg(hours, 2, 10, QLatin1Char('0'))); + } + } + + int minutes = val / 60; + int seconds = val % 60; + result.append(QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0'))); + return result; +} diff --git a/companion/src/firmwares/datahelpers.h b/companion/src/firmwares/datahelpers.h index ed46da717..ccd3b0f64 100644 --- a/companion/src/firmwares/datahelpers.h +++ b/companion/src/firmwares/datahelpers.h @@ -47,6 +47,11 @@ class FieldRange QString unit; }; + +constexpr unsigned int TIMESTR_MASK_HRSMINS { 1 << 1 }; +constexpr unsigned int TIMESTR_MASK_ZEROHRS { 1 << 2 }; +constexpr unsigned int TIMESTR_MASK_PADSIGN { 1 << 3 }; + namespace DataHelpers { enum BoolFormat { @@ -59,5 +64,6 @@ namespace DataHelpers QString boolToString(const bool value, const BoolFormat format); QString getElementName(const QString & prefix, const unsigned int index, const char * name = 0, const bool padding = false); + QString timeToString(const int value, const unsigned int mask); } diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp index 484dfe019..c46351df8 100644 --- a/companion/src/firmwares/timerdata.cpp +++ b/companion/src/firmwares/timerdata.cpp @@ -20,6 +20,7 @@ #include "timerdata.h" #include "radiodataconversionstate.h" +#include "compounditemmodels.h" void TimerData::convert(RadioDataConversionState & cstate) { @@ -36,7 +37,7 @@ void TimerData::clear() bool TimerData::isEmpty() { - return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWN_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/); + return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWNBEEP_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/); } bool TimerData::isModeOff() @@ -59,9 +60,14 @@ QString TimerData::countdownStartToString() const return countdownStartToString(countdownStart); } -QString TimerData::persistentToString() const +QString TimerData::persistentToString(const bool verbose) const { - return persistentToString(persistent); + return persistentToString(persistent, verbose); +} + +QString TimerData::pvalueToString() const +{ + return pvalueToString(pvalue); } // static @@ -99,25 +105,31 @@ QString TimerData::countdownStartToString(const int value) } // static -QString TimerData::persistentToString(const int value) +QString TimerData::persistentToString(const int value, const bool verbose) { switch(value) { case PERSISTENT_NOT: - return tr("Not persistent"); + return verbose ? tr("Not persistent") : tr("NOT"); case PERSISTENT_FLIGHT: - return tr("Persistent (flight)"); + return verbose ? tr("Persistent (flight)") : tr("Flight"); case PERSISTENT_MANUALRESET: - return tr("Persistent (manual reset)"); + return verbose ? tr("Persistent (manual reset)") : tr("Manual reset"); default: return CPN_STR_UNKNOWN_ITEM; } } +// static +QString TimerData::pvalueToString(const int value) +{ + return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS); +} + // static AbstractStaticItemModel * TimerData::countdownBeepItemModel() { AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); - mdl->setName("timerdata.countdownBeep"); + mdl->setName(AIM_TIMER_COUNTDOWNBEEP); for (int i = 0; i < COUNTDOWNBEEP_COUNT; i++) { QString str = countdownBeepToString(i); @@ -131,7 +143,7 @@ AbstractStaticItemModel * TimerData::countdownBeepItemModel() AbstractStaticItemModel * TimerData::countdownStartItemModel() { AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); - mdl->setName("timerdata.countdownStart"); + mdl->setName(AIM_TIMER_COUNTDOWNSTART); for (int i = COUNTDOWNSTART_LAST - 1; i >= COUNTDOWNSTART_FIRST; i--) { QString str = countdownStartToString(i); @@ -145,7 +157,7 @@ AbstractStaticItemModel * TimerData::countdownStartItemModel() AbstractStaticItemModel * TimerData::persistentItemModel() { AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); - mdl->setName("timerdata.persistent"); + mdl->setName(AIM_TIMER_PERSISTENT); for (int i = 0; i < PERSISTENT_COUNT; i++) { QString str = persistentToString(i); diff --git a/companion/src/firmwares/timerdata.h b/companion/src/firmwares/timerdata.h index e2949cd6b..fcd1ab87d 100644 --- a/companion/src/firmwares/timerdata.h +++ b/companion/src/firmwares/timerdata.h @@ -26,8 +26,13 @@ #include class RadioDataConversionState; +class AbstractStaticItemModel; -#define TIMER_NAME_LEN 8 +constexpr char AIM_TIMER_COUNTDOWNBEEP[] {"timerdata.countdownBeep"}; +constexpr char AIM_TIMER_COUNTDOWNSTART[] {"timerdata.countdownStart"}; +constexpr char AIM_TIMER_PERSISTENT[] {"timerdata.persistent"}; + +constexpr int TIMER_NAME_LEN {8}; class TimerData { Q_DECLARE_TR_FUNCTIONS(TimerData) @@ -74,13 +79,15 @@ class TimerData { bool isEmpty(); bool isModeOff(); QString nameToString(int index) const; - QString countdownBeepToString(); - QString countdownStartToString(); - QString persistentToString(); + QString countdownBeepToString() const; + QString countdownStartToString() const; + QString persistentToString(const bool verbose = true) const; + QString pvalueToString() const; static QString countdownBeepToString(const int value); static QString countdownStartToString(const int value); - static QString persistentToString(const int value); + static QString persistentToString(const int value, const bool verbose = true); + static QString pvalueToString(const int value); static AbstractStaticItemModel * countdownBeepItemModel(); static AbstractStaticItemModel * countdownStartItemModel(); static AbstractStaticItemModel * persistentItemModel(); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index b695b03ff..d3d199cc0 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -22,7 +22,6 @@ #include "ui_setup.h" #include "ui_setup_timer.h" #include "ui_setup_module.h" -#include "filtereditemmodels.h" #include "appdata.h" #include "modelprinter.h" #include "multiprotocols.h" @@ -31,54 +30,51 @@ #include +constexpr char FIM_TIMERSWITCH[] {"Timer Switch"}; +constexpr char FIM_THRSOURCE[] {"Throttle Source"}; + TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, - QWidget * prevFocus, FilteredItemModel * rawSwitchFilteredModel): + QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels): ModelPanel(parent, model, generalSettings, firmware), timer(timer), ui(new Ui::Timer) { ui->setupUi(this); - connectItemModelEvents(rawSwitchFilteredModel); + connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); lock = true; // Name int length = firmware->getCapability(TimersName); - if (length == 0) { + if (length == 0) ui->name->hide(); - } - else { - ui->name->setMaxLength(length); - } + else + ui->name->setField(timer.name, length, this); // Mode - ui->mode->setModel(rawSwitchFilteredModel); + ui->mode->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); ui->mode->setCurrentIndex(ui->mode->findData(timer.mode.toValue())); connect(ui->mode, SIGNAL(activated(int)), this, SLOT(onModeChanged(int))); + ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP)); + ui->countdownBeep->setField(timer.countdownBeep, this); + + ui->value->setMaximumTime(firmware->getMaxTimerStart()); + + ui->minuteBeep->setField(timer.minuteBeep, this); + if (!firmware->getCapability(PermTimers)) { ui->persistent->hide(); ui->persistentValue->hide(); } + else { + ui->persistent->setModel(panelItemModels->getItemModel(AIM_TIMER_PERSISTENT)); + ui->persistent->setField(timer.persistent, this); + ui->persistentValue->setText(timer.pvalueToString()); + } - ui->countdownBeep->setField(timer.countdownBeep, this); - ui->countdownBeep->addItem(tr("Silent"), TimerData::COUNTDOWN_SILENT); - ui->countdownBeep->addItem(tr("Beeps"), TimerData::COUNTDOWN_BEEPS); - ui->countdownBeep->addItem(tr("Voice"), TimerData::COUNTDOWN_VOICE); - ui->countdownBeep->addItem(tr("Haptic"), TimerData::COUNTDOWN_HAPTIC); - - ui->value->setMaximumTime(firmware->getMaxTimerStart()); - - ui->persistent->setField(timer.persistent, this); - ui->persistent->addItem(tr("Not persistent"), 0); - ui->persistent->addItem(tr("Persistent (flight)"), 1); - ui->persistent->addItem(tr("Persistent (manual reset)"), 2); - + ui->countdownStart->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNSTART)); ui->countdownStart->setField(timer.countdownStart, this); - ui->countdownStart->addItem("5s", 1); - ui->countdownStart->addItem("10s", 0); - ui->countdownStart->addItem("20s", -1); - ui->countdownStart->addItem("30s", -2); disableMouseScrolling(); QWidget::setTabOrder(prevFocus, ui->name); @@ -102,32 +98,18 @@ void TimerPanel::update() { lock = true; - ui->name->setText(timer.name); + ui->name->updateValue(); + ui->mode->setCurrentIndex(ui->mode->findData(timer.mode.toValue())); int hour = timer.val / 3600; int min = (timer.val - (hour * 3600)) / 60; int sec = (timer.val - (hour * 3600)) % 60; - - ui->mode->setCurrentIndex(ui->mode->findData(timer.mode.toValue())); ui->value->setTime(QTime(hour, min, sec)); - if (firmware->getCapability(PermTimers)) { - int sign = 1; - int pvalue = timer.pvalue; - if (pvalue < 0) { - pvalue = -pvalue; - sign = -1; - } - int hours = pvalue / 3600; - pvalue -= hours * 3600; - int minutes = pvalue / 60; - int seconds = pvalue % 60; - ui->persistentValue->setText(QString(" %1(%2:%3:%4)").arg(sign < 0 ? "-" :" ").arg(hours, 2, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0'))); - } - ui->countdownBeep->updateValue(); - ui->minuteBeep->setChecked(timer.minuteBeep); - ui->persistent->updateValue(); + ui->minuteBeep->updateValue(); + if (firmware->getCapability(PermTimers)) + ui->persistent->updateValue(); ui->countdownStart->updateValue(); lock = false; } @@ -139,10 +121,17 @@ QWidget * TimerPanel::getLastFocus() void TimerPanel::on_countdownBeep_currentIndexChanged(int index) { - if(index == TimerData::COUNTDOWN_SILENT) - ui->countdownStart->hide(); - else - ui->countdownStart->show(); + if (!lock) { + const int start = ui->countdownStart->itemData(index).toInt(); + if (start != timer.countdownStart) { + timer.countdownStart = start; + emit modified(); + if(index == TimerData::COUNTDOWNBEEP_SILENT) + ui->countdownStart->setEnabled(false); + else + ui->countdownStart->setEnabled(true); + } + } } void TimerPanel::on_value_editingFinished() @@ -197,12 +186,27 @@ void TimerPanel::connectItemModelEvents(const FilteredItemModel * itemModel) void TimerPanel::onItemModelAboutToBeUpdated() { lock = true; + modelsUpdateCnt++; } void TimerPanel::onItemModelUpdateComplete() { - update(); - lock = false; + modelsUpdateCnt--; + if (modelsUpdateCnt < 1) { + update(); + lock = false; + } +} + +void TimerPanel::on_countdownStart_currentIndexChanged(int index) +{ + if (!lock) { + const int start = ui->countdownStart->itemData(index).toInt(); + if (start != timer.countdownStart) { + timer.countdownStart = start; + emit modified(); + } + } } /******************************************************************************/ @@ -1023,16 +1027,26 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge { ui->setupUi(this); - rawSwitchFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), RawSwitch::TimersContext); - connectItemModelEvents(rawSwitchFilteredModel); + lock = true; - thrSourceFilteredModel = new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_ThrSource)); - connectItemModelEvents(thrSourceFilteredModel); + panelFilteredModels = new FilteredItemModelFactory(); + + panelFilteredModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), + RawSwitch::TimersContext), + FIM_TIMERSWITCH); + connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); + + panelFilteredModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_ThrSource)), + FIM_THRSOURCE); + connectItemModelEvents(panelFilteredModels->getItemModel(FIM_THRSOURCE)); + + panelItemModels = new CompoundItemModelFactory(&generalSettings, &model); + panelItemModels->registerItemModel(TimerData::countdownBeepItemModel()); + panelItemModels->registerItemModel(TimerData::countdownStartItemModel()); + panelItemModels->registerItemModel(TimerData::persistentItemModel()); Board::Type board = firmware->getBoard(); - lock = true; - memset(modules, 0, sizeof(modules)); QRegExp rx(CHAR_FOR_NAMES_REGEX); @@ -1112,7 +1126,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge for (int i = 0; i < CPN_MAX_TIMERS; i++) { if (i < timersCount) { - timers[i] = new TimerPanel(this, model, model.timers[i], generalSettings, firmware, prevFocus, rawSwitchFilteredModel); + timers[i] = new TimerPanel(this, model, model.timers[i], generalSettings, firmware, prevFocus, panelFilteredModels, panelItemModels); ui->gridLayout->addWidget(timers[i], 1+i, 1); connect(timers[i], &TimerPanel::modified, this, &SetupPanel::modified); connect(timers[i], &TimerPanel::nameChanged, this, &SetupPanel::onTimerNameChanged); @@ -1279,8 +1293,8 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge SetupPanel::~SetupPanel() { delete ui; - delete rawSwitchFilteredModel; - delete thrSourceFilteredModel; + delete panelFilteredModels; + delete panelItemModels; } void SetupPanel::on_extendedLimits_toggled(bool checked) diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 7409d7b25..24b1625f7 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -18,17 +18,15 @@ * GNU General Public License for more details. */ -#ifndef _SETUP_H_ -#define _SETUP_H_ +#pragma once #include "modeledit.h" #include "eeprominterface.h" +#include "compounditemmodels.h" +#include "filtereditemmodels.h" constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer"; -class CompoundItemModelFactory; -class FilteredItemModel; - namespace Ui { class Setup; class Timer; @@ -41,7 +39,7 @@ class TimerPanel : public ModelPanel public: TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, - QWidget *prevFocus, FilteredItemModel * switchModel); + QWidget *prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels); virtual ~TimerPanel(); virtual void update(); @@ -55,6 +53,7 @@ class TimerPanel : public ModelPanel void on_name_editingFinished(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); + void on_countdownStart_currentIndexChanged(int index); signals: void nameChanged(); @@ -63,6 +62,7 @@ class TimerPanel : public ModelPanel TimerData & timer; Ui::Timer * ui; void connectItemModelEvents(const FilteredItemModel * itemModel); + int modelsUpdateCnt; }; class ModulePanel : public ModelPanel @@ -184,7 +184,7 @@ class SetupPanel : public ModelPanel QVector startupSwitchesCheckboxes; QVector potWarningCheckboxes; QVector centerBeepCheckboxes; - ModulePanel * modules[CPN_MAX_MODULES+1]; + ModulePanel * modules[CPN_MAX_MODULES + 1]; TimerPanel * timers[CPN_MAX_TIMERS]; void updateStartupSwitches(); void updatePotWarnings(); @@ -199,10 +199,8 @@ class SetupPanel : public ModelPanel bool moveTimerUpAllowed() const; void swapTimerData(int idx1, int idx2); CompoundItemModelFactory * sharedItemModels; - FilteredItemModel * rawSwitchFilteredModel; - FilteredItemModel * thrSourceFilteredModel; void updateItemModels(); void connectItemModelEvents(const FilteredItemModel * itemModel); + CompoundItemModelFactory * panelItemModels; + FilteredItemModelFactory * panelFilteredModels; }; - -#endif // _SETUP_H_ diff --git a/companion/src/modeledit/setup_timer.ui b/companion/src/modeledit/setup_timer.ui index 11f566b45..989e7074e 100644 --- a/companion/src/modeledit/setup_timer.ui +++ b/companion/src/modeledit/setup_timer.ui @@ -9,7 +9,7 @@ 0 0 - 675 + 874 33 @@ -33,7 +33,7 @@ 0 - + 80 @@ -68,11 +68,18 @@ + + + + Start + + + - + Minute Call @@ -109,6 +116,16 @@ QComboBox
autocombobox.h
+ + AutoLineEdit + QLineEdit +
autolineedit.h
+
+ + AutoCheckBox + QCheckBox +
autocheckbox.h
+
diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index 282f53550..7da624e1e 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -938,36 +938,6 @@ QString ModelPrinter::printFailsafeMode(unsigned int fsmode) } } -QString ModelPrinter::printTimerCountdownBeep(unsigned int countdownBeep) -{ - switch (countdownBeep) { - case TimerData::COUNTDOWN_SILENT: - return tr("Silent"); - case TimerData::COUNTDOWN_BEEPS: - return tr("Beeps"); - case TimerData::COUNTDOWN_VOICE: - return tr("Voice"); - case TimerData::COUNTDOWN_HAPTIC: - return tr("Haptic"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} - -QString ModelPrinter::printTimerPersistent(unsigned int persistent) -{ - switch (persistent) { - case 0: - return tr("OFF"); - case 1: - return tr("Flight"); - case 2: - return tr("Manual reset"); - default: - return CPN_STR_UNKNOWN_ITEM; - } -} - QString ModelPrinter::printSettingsTrim() { QStringList str; @@ -1042,16 +1012,6 @@ QString ModelPrinter::printTimerName(int idx) return result; } -QString ModelPrinter::printTimerTimeValue(unsigned int val) -{ - return printTimeValue(val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS); -} - -QString ModelPrinter::printTimerMinuteBeep(bool mb) -{ - return printBoolean(mb, BOOLEAN_YESNO); -} - QString ModelPrinter::printTelemetryProtocol(unsigned int val) { switch (val) { diff --git a/companion/src/modelprinter.h b/companion/src/modelprinter.h index ea2eab2d8..97d697509 100644 --- a/companion/src/modelprinter.h +++ b/companion/src/modelprinter.h @@ -104,13 +104,9 @@ class ModelPrinter: public QObject QString printFailsafe(int idx); QString printFailsafeMode(unsigned int fsmode); QString printFailsafeValue(int val); - QString printTimerCountdownBeep(unsigned int countdownBeep); - QString printTimerPersistent(unsigned int persistent); QString printPPMFrameLength(int ppmFL); QString printTimerName(int idx); QString printTimeValue(const int value, const unsigned int mask); - QString printTimerMinuteBeep(bool mb); - QString printTimerTimeValue(unsigned int val); QString printTelemetryProtocol(unsigned int val); QString printLabelValue(const QString & lbl, const QString & val, const bool sep = false); QString printLabelValues(const QString & lbl, const QStringList & vals, const bool sep = false); diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 17cb30be4..472354d84 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -341,11 +341,11 @@ QString MultiModelPrinter::printTimers() columns.appendCellStart(20, true); COMPARE(modelPrinter->printTimerName(i)); columns.appendCellEnd(true); - COMPARECELLWIDTH(modelPrinter->printTimerTimeValue(model->timers[i].val), 15); + COMPARECELLWIDTH(DataHelpers::timeToString(model->timers[i].val, TIMESTR_MASK_HRSMINS), 15); COMPARECELLWIDTH(model->timers[i].mode.toString(), 15); - COMPARECELLWIDTH(modelPrinter->printTimerCountdownBeep(model->timers[i].countdownBeep), 15); - COMPARECELLWIDTH(modelPrinter->printTimerMinuteBeep(model->timers[i].minuteBeep), 15); - COMPARECELLWIDTH(modelPrinter->printTimerPersistent(model->timers[i].persistent), 20); + COMPARECELLWIDTH(model->timers[i].countdownBeepToString(), 15); + COMPARECELLWIDTH(DataHelpers::boolToString(model->timers[i].minuteBeep, DataHelpers::BOOL_FMT_YESNO), 15); + COMPARECELLWIDTH(model->timers[i].persistentToString(false), 20); columns.appendRowEnd(); } columns.appendTableEnd(); From e0cd919c3e555ee713b1eea5bf0c39b214cece04 Mon Sep 17 00:00:00 2001 From: Risto Date: Sun, 14 Mar 2021 13:39:15 +0100 Subject: [PATCH 239/300] Minor issues of old startup_stm32f42_43xx STM32F429/439 are Cortex M4 processors. By comparing this file with the appropriate startup_stm32f429bitx.s file generated by the current STM32CubeIDE for STM32F429BI (as in TX16S, T16 & T18) , I can see that the present OpenTX version misses an initial stack pointer setting. Both issues fixed herewith. --- radio/src/targets/horus/startup_stm32f42_43xxx.s | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/radio/src/targets/horus/startup_stm32f42_43xxx.s b/radio/src/targets/horus/startup_stm32f42_43xxx.s index e87226bf2..ad8a89264 100644 --- a/radio/src/targets/horus/startup_stm32f42_43xxx.s +++ b/radio/src/targets/horus/startup_stm32f42_43xxx.s @@ -1,16 +1,16 @@ /** ****************************************************************************** - * @file startup_stm32f40_41xxx.s + * @file startup_stm32f42_43xx.s * @author MCD Application Team * @version V1.3.0 * @date 08-November-2013 - * @brief STM32F40xxx/41xxx Devices vector table for RIDE7 toolchain. + * @brief STM32F42xxx/43xxx Devices vector table for RIDE7 toolchain. * This module performs: * - Set the initial SP * - Set the initial PC == Reset_Handler, * - Set the vector table entries with the exceptions ISR address * - Configure the clock system and the external SRAM mounted on - * STM324xG-EVAL board to be used as data memory (optional, + * STM32F4xG-EVAL board to be used as data memory (optional, * to be enabled by user) * - Branches to main in the C library (which eventually * calls main()). @@ -37,7 +37,7 @@ */ .syntax unified - .cpu cortex-m3 + .cpu cortex-m4 .fpu softvfp .thumb @@ -69,9 +69,9 @@ defined in linker script */ .section .text.Reset_Handler .weak Reset_Handler .type Reset_Handler, %function -Reset_Handler: - - bl pwrResetHandler /*jump to WDT reset handler where soft power control pin is turned on as soon as possible */ +Reset_Handler: + ldr sp, =_estack /* set stack pointer */ + bl pwrResetHandler /* jump to WDT reset handler where soft power control pin is turned on as soon as possible */ /* Copy the data segment initializers from flash to SRAM */ movs r1, #0 @@ -113,7 +113,7 @@ LoopPaintMainStack: /* Call the clock system intitialization function.*/ bl SystemInit -/* Call C++ constructors for static objects */ +/* Call static constructors */ bl __libc_init_array /* Call the application's entry point.*/ bl main From 61bf14977b8161ca674feaefa9f4ff61da235ab2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 15 Mar 2021 19:37:14 +0100 Subject: [PATCH 240/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index c3cc61a76..8825be55b 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2185,3 +2185,4 @@ Antoine Soulie Tjeerd Jager Bertold Van den Bergh Stefanie Zerbe +James Mandelbaum From 3c5ee61fefc7972c5f6f3f72148577cf615090bc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 15 Mar 2021 19:38:35 +0100 Subject: [PATCH 241/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8825be55b..b4f7eb581 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2186,3 +2186,4 @@ Tjeerd Jager Bertold Van den Bergh Stefanie Zerbe James Mandelbaum +Javier J. Cordovez From 099d5f76085eab1763611b4f5c1f3330f541148e Mon Sep 17 00:00:00 2001 From: elecpower Date: Tue, 16 Mar 2021 09:50:00 +1100 Subject: [PATCH 242/300] Refactor setup and modelprinter --- companion/src/firmwares/timerdata.cpp | 31 +++++++++++++++++++---- companion/src/firmwares/timerdata.h | 3 +++ companion/src/modeledit/setup.cpp | 36 ++++++++++++++++++--------- companion/src/modeledit/setup.h | 1 + companion/src/modelprinter.cpp | 31 ----------------------- companion/src/modelprinter.h | 3 --- companion/src/multimodelprinter.cpp | 15 +++++------ 7 files changed, 62 insertions(+), 58 deletions(-) diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp index c46351df8..8e85455fa 100644 --- a/companion/src/firmwares/timerdata.cpp +++ b/companion/src/firmwares/timerdata.cpp @@ -57,7 +57,10 @@ QString TimerData::countdownBeepToString() const QString TimerData::countdownStartToString() const { - return countdownStartToString(countdownStart); + if (countdownBeep == COUNTDOWNBEEP_SILENT) + return ""; + else + return countdownStartToString(countdownStart); } QString TimerData::persistentToString(const bool verbose) const @@ -70,6 +73,18 @@ QString TimerData::pvalueToString() const return pvalueToString(pvalue); } +QString TimerData::valToString() const +{ + return valToString(val); +} + +void TimerData::setCountdownBeep(const unsigned int value) +{ + countdownBeep = value; + if (value == COUNTDOWNBEEP_SILENT) + countdownStart = 0; +} + // static QString TimerData::countdownBeepToString(const int value) { @@ -122,7 +137,13 @@ QString TimerData::persistentToString(const int value, const bool verbose) // static QString TimerData::pvalueToString(const int value) { - return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS); + return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS | TIMESTR_MASK_HRSMINS); +} + +// static +QString TimerData::valToString(const int value) +{ + return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS | TIMESTR_MASK_ZEROHRS); } // static @@ -132,7 +153,7 @@ AbstractStaticItemModel * TimerData::countdownBeepItemModel() mdl->setName(AIM_TIMER_COUNTDOWNBEEP); for (int i = 0; i < COUNTDOWNBEEP_COUNT; i++) { - QString str = countdownBeepToString(i); + mdl->appendToItemList(countdownBeepToString(i), i); } mdl->loadItemList(); @@ -146,7 +167,7 @@ AbstractStaticItemModel * TimerData::countdownStartItemModel() mdl->setName(AIM_TIMER_COUNTDOWNSTART); for (int i = COUNTDOWNSTART_LAST - 1; i >= COUNTDOWNSTART_FIRST; i--) { - QString str = countdownStartToString(i); + mdl->appendToItemList(countdownStartToString(i), i); } mdl->loadItemList(); @@ -160,7 +181,7 @@ AbstractStaticItemModel * TimerData::persistentItemModel() mdl->setName(AIM_TIMER_PERSISTENT); for (int i = 0; i < PERSISTENT_COUNT; i++) { - QString str = persistentToString(i); + mdl->appendToItemList(persistentToString(i), i); } mdl->loadItemList(); diff --git a/companion/src/firmwares/timerdata.h b/companion/src/firmwares/timerdata.h index fcd1ab87d..03694120c 100644 --- a/companion/src/firmwares/timerdata.h +++ b/companion/src/firmwares/timerdata.h @@ -83,11 +83,14 @@ class TimerData { QString countdownStartToString() const; QString persistentToString(const bool verbose = true) const; QString pvalueToString() const; + QString valToString() const; + void setCountdownBeep(const unsigned int value); static QString countdownBeepToString(const int value); static QString countdownStartToString(const int value); static QString persistentToString(const int value, const bool verbose = true); static QString pvalueToString(const int value); + static QString valToString(const int value); static AbstractStaticItemModel * countdownBeepItemModel(); static AbstractStaticItemModel * countdownStartItemModel(); static AbstractStaticItemModel * persistentItemModel(); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index d3d199cc0..55489933e 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -70,7 +70,6 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge else { ui->persistent->setModel(panelItemModels->getItemModel(AIM_TIMER_PERSISTENT)); ui->persistent->setField(timer.persistent, this); - ui->persistentValue->setText(timer.pvalueToString()); } ui->countdownStart->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNSTART)); @@ -108,9 +107,15 @@ void TimerPanel::update() ui->countdownBeep->updateValue(); ui->minuteBeep->updateValue(); - if (firmware->getCapability(PermTimers)) + if (firmware->getCapability(PermTimers)) { ui->persistent->updateValue(); + ui->persistentValue->setText(timer.pvalueToString()); + } ui->countdownStart->updateValue(); + if(timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) + ui->countdownStart->setEnabled(false); + else + ui->countdownStart->setEnabled(true); lock = false; } @@ -122,14 +127,10 @@ QWidget * TimerPanel::getLastFocus() void TimerPanel::on_countdownBeep_currentIndexChanged(int index) { if (!lock) { - const int start = ui->countdownStart->itemData(index).toInt(); - if (start != timer.countdownStart) { - timer.countdownStart = start; + const unsigned int val = ui->countdownStart->itemData(index).toUInt(); + if (val != timer.countdownBeep) { + timer.setCountdownBeep(val); emit modified(); - if(index == TimerData::COUNTDOWNBEEP_SILENT) - ui->countdownStart->setEnabled(false); - else - ui->countdownStart->setEnabled(true); } } } @@ -201,9 +202,20 @@ void TimerPanel::onItemModelUpdateComplete() void TimerPanel::on_countdownStart_currentIndexChanged(int index) { if (!lock) { - const int start = ui->countdownStart->itemData(index).toInt(); - if (start != timer.countdownStart) { - timer.countdownStart = start; + const int val = ui->countdownStart->itemData(index).toInt(); + if (val != timer.countdownStart) { + timer.countdownStart = val; + emit modified(); + } + } +} + +void TimerPanel::on_persistent_currentIndexChanged(int index) +{ + if (!lock) { + const unsigned int val = ui->persistent->itemData(index).toUInt(); + if (val != timer.persistent) { + timer.persistent = val; emit modified(); } } diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 24b1625f7..72350bf4f 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -54,6 +54,7 @@ class TimerPanel : public ModelPanel void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); void on_countdownStart_currentIndexChanged(int index); + void on_persistent_currentIndexChanged(int index); signals: void nameChanged(); diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index 7da624e1e..1d7df4f93 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -298,27 +298,6 @@ QString ModelPrinter::printCenterBeep() return (strl.isEmpty() ? tr("None") : strl.join(" ")); } -QString ModelPrinter::printTimer(int idx) -{ - return printTimer(model.timers[idx]); -} - -QString ModelPrinter::printTimer(const TimerData & timer) -{ - QStringList result; - if (firmware->getCapability(TimersName) && timer.name[0]) - result += tr("Name") + QString("(%1)").arg(timer.name); - result += printTimeValue(timer.val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS); - result += timer.mode.toString(); - if (timer.countdownBeep) - result += tr("Countdown") + QString("(%1)").arg(printTimerCountdownBeep(timer.countdownBeep)); - if (timer.minuteBeep) - result += tr("Minute call"); - if (timer.persistent) - result += tr("Persistent") + QString("(%1)").arg(printTimerPersistent(timer.persistent)); - return result.join(", "); -} - QString ModelPrinter::printTrim(int flightModeIndex, int stickIndex) { const FlightModeData & fm = model.flightModeData[flightModeIndex]; @@ -1002,16 +981,6 @@ QString ModelPrinter::printPPMFrameLength(int ppmFL) return QString::number(result); } -QString ModelPrinter::printTimerName(int idx) -{ - QString result; - result = tr("Tmr") + QString("%1").arg(idx+1); - if (firmware->getCapability(TimersName) && model.timers[idx].name[0]) - result.append(":" + QString(model.timers[idx].name)); - - return result; -} - QString ModelPrinter::printTelemetryProtocol(unsigned int val) { switch (val) { diff --git a/companion/src/modelprinter.h b/companion/src/modelprinter.h index 97d697509..8be56bd64 100644 --- a/companion/src/modelprinter.h +++ b/companion/src/modelprinter.h @@ -67,8 +67,6 @@ class ModelPrinter: public QObject QString printTrim(int flightModeIndex, int stickIndex); QString printGlobalVar(int flightModeIndex, int gvarIndex); QString printRotaryEncoder(int flightModeIndex, int reIndex); - QString printTimer(int idx); - QString printTimer(const TimerData & timer); QString printInputName(int idx); QString printInputLine(int idx); QString printInputLine(const ExpoData & ed); @@ -105,7 +103,6 @@ class ModelPrinter: public QObject QString printFailsafeMode(unsigned int fsmode); QString printFailsafeValue(int val); QString printPPMFrameLength(int ppmFL); - QString printTimerName(int idx); QString printTimeValue(const int value, const unsigned int mask); QString printTelemetryProtocol(unsigned int val); QString printLabelValue(const QString & lbl, const QString & val, const bool sep = false); diff --git a/companion/src/multimodelprinter.cpp b/companion/src/multimodelprinter.cpp index 472354d84..0ebef1884 100644 --- a/companion/src/multimodelprinter.cpp +++ b/companion/src/multimodelprinter.cpp @@ -334,18 +334,19 @@ QString MultiModelPrinter::printTimers() QString str; MultiColumns columns(modelPrinterMap.size()); columns.appendSectionTableStart(); - columns.appendRowHeader(QStringList() << tr("Timers") << tr("Time") << tr("Switch") << tr("Countdown") << tr("Min.call") << tr("Persist")); + columns.appendRowHeader(QStringList() << tr("Timers") << tr("Time") << tr("Switch") << tr("Countdown") << tr("Start") << tr("Min.call") << tr("Persist")); for (int i=0; igetCapability(Timers); i++) { columns.appendRowStart(); columns.appendCellStart(20, true); - COMPARE(modelPrinter->printTimerName(i)); + COMPARE(model->timers[i].nameToString(i)); columns.appendCellEnd(true); - COMPARECELLWIDTH(DataHelpers::timeToString(model->timers[i].val, TIMESTR_MASK_HRSMINS), 15); - COMPARECELLWIDTH(model->timers[i].mode.toString(), 15); - COMPARECELLWIDTH(model->timers[i].countdownBeepToString(), 15); - COMPARECELLWIDTH(DataHelpers::boolToString(model->timers[i].minuteBeep, DataHelpers::BOOL_FMT_YESNO), 15); - COMPARECELLWIDTH(model->timers[i].persistentToString(false), 20); + COMPARECELLWIDTH(model->timers[i].valToString(), 10); + COMPARECELLWIDTH(model->timers[i].mode.toString(), 10); + COMPARECELLWIDTH(model->timers[i].countdownBeepToString(), 10); + COMPARECELLWIDTH(model->timers[i].countdownStartToString(), 10); + COMPARECELLWIDTH(DataHelpers::boolToString(model->timers[i].minuteBeep, DataHelpers::BOOL_FMT_YESNO), 10); + COMPARECELLWIDTH(model->timers[i].persistentToString(false), 15); columns.appendRowEnd(); } columns.appendTableEnd(); From 9693d87c9fb6fd1191dacc16a8202fbd31348d37 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 16 Mar 2021 09:25:50 +0100 Subject: [PATCH 243/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index b4f7eb581..d81b22303 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2187,3 +2187,4 @@ Bertold Van den Bergh Stefanie Zerbe James Mandelbaum Javier J. Cordovez +Jess Barkley From 38f1d249c94e946aeddcd4f649c424c9546deebe Mon Sep 17 00:00:00 2001 From: elecpower Date: Tue, 16 Mar 2021 23:16:11 +1100 Subject: [PATCH 244/300] Tweaks to auto objects and fixup filesyncdialog compile --- companion/src/dialogs/filesyncdialog.cpp | 2 + companion/src/firmwares/timerdata.cpp | 2 +- companion/src/modeledit/setup.cpp | 61 +++--------------------- companion/src/modeledit/setup.h | 5 -- companion/src/shared/autocheckbox.h | 31 +++++++----- companion/src/shared/autocombobox.h | 19 +++++--- 6 files changed, 39 insertions(+), 81 deletions(-) diff --git a/companion/src/dialogs/filesyncdialog.cpp b/companion/src/dialogs/filesyncdialog.cpp index 3927a3ffd..30026c931 100644 --- a/companion/src/dialogs/filesyncdialog.cpp +++ b/companion/src/dialogs/filesyncdialog.cpp @@ -39,6 +39,8 @@ #include #include #include +#include +#include FileSyncDialog::FileSyncDialog(QWidget * parent, const SyncProcess::SyncOptions & syncOptions) : QDialog(parent), diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp index 8e85455fa..eeac00b69 100644 --- a/companion/src/firmwares/timerdata.cpp +++ b/companion/src/firmwares/timerdata.cpp @@ -137,7 +137,7 @@ QString TimerData::persistentToString(const int value, const bool verbose) // static QString TimerData::pvalueToString(const int value) { - return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS | TIMESTR_MASK_HRSMINS); + return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS | TIMESTR_MASK_ZEROHRS); } // static diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 55489933e..43c7cbaca 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -112,10 +112,14 @@ void TimerPanel::update() ui->persistentValue->setText(timer.pvalueToString()); } ui->countdownStart->updateValue(); - if(timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) + if(timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) { + ui->countdownStartLabel->setEnabled(false); ui->countdownStart->setEnabled(false); - else + } + else { + ui->countdownStartLabel->setEnabled(true); ui->countdownStart->setEnabled(true); + } lock = false; } @@ -124,17 +128,6 @@ QWidget * TimerPanel::getLastFocus() return ui->persistent; } -void TimerPanel::on_countdownBeep_currentIndexChanged(int index) -{ - if (!lock) { - const unsigned int val = ui->countdownStart->itemData(index).toUInt(); - if (val != timer.countdownBeep) { - timer.setCountdownBeep(val); - emit modified(); - } - } -} - void TimerPanel::on_value_editingFinished() { if (!lock) { @@ -158,26 +151,6 @@ void TimerPanel::onModeChanged(int index) } } -void TimerPanel::on_minuteBeep_toggled(bool checked) -{ - if (!lock) { - timer.minuteBeep = checked; - emit modified(); - } -} - -void TimerPanel::on_name_editingFinished() -{ - if (!lock) { - if (QString(timer.name) != ui->name->text()) { - int length = ui->name->maxLength(); - strncpy(timer.name, ui->name->text().toLatin1(), length); - emit nameChanged(); - emit modified(); - } - } -} - void TimerPanel::connectItemModelEvents(const FilteredItemModel * itemModel) { connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); @@ -199,28 +172,6 @@ void TimerPanel::onItemModelUpdateComplete() } } -void TimerPanel::on_countdownStart_currentIndexChanged(int index) -{ - if (!lock) { - const int val = ui->countdownStart->itemData(index).toInt(); - if (val != timer.countdownStart) { - timer.countdownStart = val; - emit modified(); - } - } -} - -void TimerPanel::on_persistent_currentIndexChanged(int index) -{ - if (!lock) { - const unsigned int val = ui->persistent->itemData(index).toUInt(); - if (val != timer.persistent) { - timer.persistent = val; - emit modified(); - } - } -} - /******************************************************************************/ #define FAILSAFE_CHANNEL_HOLD 2000 diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 72350bf4f..b4e3b19c7 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -48,13 +48,8 @@ class TimerPanel : public ModelPanel private slots: void onModeChanged(int index); void on_value_editingFinished(); - void on_minuteBeep_toggled(bool checked); - void on_countdownBeep_currentIndexChanged(int index); - void on_name_editingFinished(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); - void on_countdownStart_currentIndexChanged(int index); - void on_persistent_currentIndexChanged(int index); signals: void nameChanged(); diff --git a/companion/src/shared/autocheckbox.h b/companion/src/shared/autocheckbox.h index f5b233fed..a9e70d255 100644 --- a/companion/src/shared/autocheckbox.h +++ b/companion/src/shared/autocheckbox.h @@ -21,23 +21,23 @@ #pragma once #include -#include "modeledit/modeledit.h" +#include "genericpanel.h" class AutoCheckBox: public QCheckBox { Q_OBJECT public: - explicit AutoCheckBox(QWidget *parent = 0): + explicit AutoCheckBox(QWidget * parent = nullptr): QCheckBox(parent), - field(NULL), - panel(NULL), + field(nullptr), + panel(nullptr), lock(false) { connect(this, SIGNAL(toggled(bool)), this, SLOT(onToggled(bool))); } - void setField(bool & field, ModelPanel * panel=NULL) + void setField(bool & field, GenericPanel * panel = nullptr) { this->field = &field; this->panel = panel; @@ -51,26 +51,31 @@ class AutoCheckBox: public QCheckBox void updateValue() { + if (!field) + return; lock = true; - if (field) { - setChecked(*field); - } + setChecked(*field); lock = false; } + signals: + void currentDataChanged(bool value); + protected slots: void onToggled(bool checked) { + if (panel && panel->lock) + return; if (field && !lock) { *field = checked; - if (panel) { + emit currentDataChanged(checked); + if (panel) emit panel->modified(); - } } } protected: - bool * field; - ModelPanel * panel; - bool lock; + bool *field = nullptr; + GenericPanel *panel = nullptr; + bool lock = false; }; diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index 784abaefa..a257f35d5 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -28,8 +28,13 @@ class AutoComboBox: public QComboBox Q_OBJECT public: - explicit AutoComboBox(QWidget *parent = nullptr): - QComboBox(parent) + explicit AutoComboBox(QWidget * parent = nullptr): + QComboBox(parent), + field(nullptr), + panel(nullptr), + next(0), + lock(false), + hasModel(false) { connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); } @@ -69,14 +74,14 @@ class AutoComboBox: public QComboBox } } - void setField(unsigned int & field, GenericPanel * panel=nullptr) + void setField(unsigned int & field, GenericPanel * panel = nullptr) { this->field = (int *)&field; this->panel = panel; updateValue(); } - void setField(int & field, GenericPanel * panel=nullptr) + void setField(int & field, GenericPanel * panel = nullptr) { this->field = &field; this->panel = panel; @@ -122,16 +127,16 @@ class AutoComboBox: public QComboBox const int val = itemData(index).toInt(); if (field && !lock) { *field = val; + emit currentDataChanged(val); if (panel) emit panel->modified(); } - emit currentDataChanged(val); } } protected: - int * field = nullptr; - GenericPanel * panel = nullptr; + int *field = nullptr; + GenericPanel *panel = nullptr; int next = 0; bool lock = false; bool hasModel = false; From 42f6828bb4e52fb8770a52e1a2e0fd23ba9719f7 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 06:06:10 +1100 Subject: [PATCH 245/300] More timer tweaks --- companion/src/firmwares/timerdata.cpp | 7 +++---- companion/src/firmwares/timerdata.h | 2 +- companion/src/modeledit/setup.cpp | 29 +++++++++++++++++---------- companion/src/modeledit/setup.h | 5 +++-- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/companion/src/firmwares/timerdata.cpp b/companion/src/firmwares/timerdata.cpp index eeac00b69..9034ed7e0 100644 --- a/companion/src/firmwares/timerdata.cpp +++ b/companion/src/firmwares/timerdata.cpp @@ -78,10 +78,9 @@ QString TimerData::valToString() const return valToString(val); } -void TimerData::setCountdownBeep(const unsigned int value) +void TimerData::countdownBeepChanged() { - countdownBeep = value; - if (value == COUNTDOWNBEEP_SILENT) + if (countdownBeep == COUNTDOWNBEEP_SILENT) countdownStart = 0; } @@ -166,7 +165,7 @@ AbstractStaticItemModel * TimerData::countdownStartItemModel() AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); mdl->setName(AIM_TIMER_COUNTDOWNSTART); - for (int i = COUNTDOWNSTART_LAST - 1; i >= COUNTDOWNSTART_FIRST; i--) { + for (int i = COUNTDOWNSTART_LAST; i >= COUNTDOWNSTART_FIRST; i--) { mdl->appendToItemList(countdownStartToString(i), i); } diff --git a/companion/src/firmwares/timerdata.h b/companion/src/firmwares/timerdata.h index 03694120c..07cc1eedd 100644 --- a/companion/src/firmwares/timerdata.h +++ b/companion/src/firmwares/timerdata.h @@ -84,7 +84,7 @@ class TimerData { QString persistentToString(const bool verbose = true) const; QString pvalueToString() const; QString valToString() const; - void setCountdownBeep(const unsigned int value); + void countdownBeepChanged(); static QString countdownBeepToString(const int value); static QString countdownStartToString(const int value); diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 43c7cbaca..7d887173f 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -33,7 +33,7 @@ constexpr char FIM_TIMERSWITCH[] {"Timer Switch"}; constexpr char FIM_THRSOURCE[] {"Throttle Source"}; -TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, +TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels): ModelPanel(parent, model, generalSettings, firmware), timer(timer), @@ -58,19 +58,20 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP)); ui->countdownBeep->setField(timer.countdownBeep, this); + connect(ui->countdownBeep, SIGNAL(currentDataChanged(int)), this, SLOT(onCountdownBeepDataChanged(int))); ui->value->setMaximumTime(firmware->getMaxTimerStart()); ui->minuteBeep->setField(timer.minuteBeep, this); - if (!firmware->getCapability(PermTimers)) { - ui->persistent->hide(); - ui->persistentValue->hide(); - } - else { + if (firmware->getCapability(PermTimers)) { ui->persistent->setModel(panelItemModels->getItemModel(AIM_TIMER_PERSISTENT)); ui->persistent->setField(timer.persistent, this); } + else { + ui->persistent->hide(); + ui->persistentValue->hide(); + } ui->countdownStart->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNSTART)); ui->countdownStart->setField(timer.countdownStart, this); @@ -107,12 +108,8 @@ void TimerPanel::update() ui->countdownBeep->updateValue(); ui->minuteBeep->updateValue(); - if (firmware->getCapability(PermTimers)) { - ui->persistent->updateValue(); - ui->persistentValue->setText(timer.pvalueToString()); - } ui->countdownStart->updateValue(); - if(timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) { + if (timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) { ui->countdownStartLabel->setEnabled(false); ui->countdownStart->setEnabled(false); } @@ -120,6 +117,10 @@ void TimerPanel::update() ui->countdownStartLabel->setEnabled(true); ui->countdownStart->setEnabled(true); } + if (firmware->getCapability(PermTimers)) { + ui->persistent->updateValue(); + ui->persistentValue->setText(timer.pvalueToString()); + } lock = false; } @@ -172,6 +173,12 @@ void TimerPanel::onItemModelUpdateComplete() } } +void TimerPanel::onCountdownBeepDataChanged(int index) +{ + timer.countdownBeepChanged(); + update(); +} + /******************************************************************************/ #define FAILSAFE_CHANNEL_HOLD 2000 diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index b4e3b19c7..41c3b45c7 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -38,8 +38,8 @@ class TimerPanel : public ModelPanel Q_OBJECT public: - TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, - QWidget *prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels); + TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, GeneralSettings & generalSettings, Firmware * firmware, + QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels); virtual ~TimerPanel(); virtual void update(); @@ -50,6 +50,7 @@ class TimerPanel : public ModelPanel void on_value_editingFinished(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); + void onCountdownBeepDataChanged(int index); signals: void nameChanged(); From e801f490b349afeed3e7d49f2dcc4f3f30f24b8a Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 06:23:41 +1100 Subject: [PATCH 246/300] Check panel lock on update --- companion/src/shared/autobitsetcheckbox.h | 23 ++++++++++++++++------- companion/src/shared/genericpanel.h | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/companion/src/shared/autobitsetcheckbox.h b/companion/src/shared/autobitsetcheckbox.h index ea86db956..af831948e 100644 --- a/companion/src/shared/autobitsetcheckbox.h +++ b/companion/src/shared/autobitsetcheckbox.h @@ -34,7 +34,8 @@ class AutoBitsetCheckBox: public QCheckBox Q_PROPERTY(int toggleMask READ toggleMask WRITE setToggleMask) public: - explicit AutoBitsetCheckBox(QWidget *parent = nullptr) : AutoBitsetCheckBox(QString(), parent) {} + explicit AutoBitsetCheckBox(QWidget *parent = nullptr) : + AutoBitsetCheckBox(QString(), parent) {} explicit AutoBitsetCheckBox(const QString &text, QWidget *parent = nullptr) : QCheckBox(text, parent) { @@ -42,26 +43,32 @@ class AutoBitsetCheckBox: public QCheckBox } // int field constructors - explicit AutoBitsetCheckBox(int & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : AutoBitsetCheckBox(field, bitmask, false, text, parent) {} - explicit AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : QCheckBox(text, parent) + explicit AutoBitsetCheckBox(int & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : + AutoBitsetCheckBox(field, bitmask, false, text, parent) {} + explicit AutoBitsetCheckBox(int & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : + QCheckBox(text, parent) { setField(field, bitmask, invert); init(); } - explicit AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : QCheckBox(text, parent) + explicit AutoBitsetCheckBox(int & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : + QCheckBox(text, parent) { setField(field, bitmask, false, toggleMask); init(); } // unsigned field constructors - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : AutoBitsetCheckBox(field, bitmask, false, text, parent) {} - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : QCheckBox(text, parent) + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, const QString &text = QString(), QWidget *parent = nullptr) : + AutoBitsetCheckBox(field, bitmask, false, text, parent) {} + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, bool invert, const QString &text = QString(), QWidget *parent = nullptr) : + QCheckBox(text, parent) { setField(field, bitmask, invert); init(); } - explicit AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : QCheckBox(text, parent) + explicit AutoBitsetCheckBox(unsigned & field, int bitmask, int toggleMask, const QString &text = QString(), QWidget *parent = nullptr) : + QCheckBox(text, parent) { setField(field, bitmask, false, toggleMask); init(); @@ -104,6 +111,8 @@ class AutoBitsetCheckBox: public QCheckBox void updateValue() { + if (m_panel && m_panel->lock) + return; if (!m_field) return; const bool oldLock = setLocked(true); diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index bbb9b7254..fe749b659 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -39,6 +39,7 @@ class GenericPanel : public QWidget friend class AutoLineEdit; friend class GVarGroup; friend class AutoPrecisionComboBox; + friend class AutoBitsetCheckBox; public: GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware); From 60ac1550f40d1b9cdd5a58cc438774dc1765d706 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 09:53:32 +1100 Subject: [PATCH 247/300] Standardisation of auto widgets --- companion/src/shared/autocheckbox.h | 10 +++---- companion/src/shared/autodoublespinbox.h | 28 +++++++++++++------- companion/src/shared/autohexspinbox.h | 24 +++++++++++------ companion/src/shared/autolineedit.h | 16 +++++------ companion/src/shared/autoprecisioncombobox.h | 12 ++++----- 5 files changed, 53 insertions(+), 37 deletions(-) diff --git a/companion/src/shared/autocheckbox.h b/companion/src/shared/autocheckbox.h index a9e70d255..1c1a7207c 100644 --- a/companion/src/shared/autocheckbox.h +++ b/companion/src/shared/autocheckbox.h @@ -51,11 +51,11 @@ class AutoCheckBox: public QCheckBox void updateValue() { - if (!field) - return; - lock = true; - setChecked(*field); - lock = false; + if (field) { + lock = true; + setChecked(*field); + lock = false; + } } signals: diff --git a/companion/src/shared/autodoublespinbox.h b/companion/src/shared/autodoublespinbox.h index 6560b6ea2..a6466269e 100644 --- a/companion/src/shared/autodoublespinbox.h +++ b/companion/src/shared/autodoublespinbox.h @@ -21,7 +21,7 @@ #pragma once #include -#include "modeledit/modeledit.h" +#include "genericpanel.h" #if __GNUC__ #include #endif @@ -31,23 +31,23 @@ class AutoDoubleSpinBox: public QDoubleSpinBox Q_OBJECT public: - explicit AutoDoubleSpinBox(QWidget *parent = 0): + explicit AutoDoubleSpinBox(QWidget * parent = nullptr): QDoubleSpinBox(parent), - field(NULL), - panel(NULL), + field(nullptr), + panel(nullptr), lock(false) { connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double))); } - void setField(int & field, ModelPanel * panel=NULL) + void setField(int & field, GenericPanel * panel = nullptr) { this->field = &field; this->panel = panel; updateValue(); } - void setField(unsigned int & field, ModelPanel * panel=NULL) + void setField(unsigned int & field, GenericPanel * panel = nullptr) { this->field = (int *)&field; this->panel = panel; @@ -57,7 +57,9 @@ class AutoDoubleSpinBox: public QDoubleSpinBox void updateValue() { if (field) { - setValue(float(*field)/multiplier()); + lock = true; + setValue(float(*field) / multiplier()); + lock = false; } } @@ -80,11 +82,17 @@ class AutoDoubleSpinBox: public QDoubleSpinBox } } + signals: + void currentDataChanged(double value); + protected slots: void onValueChanged(double value) { + if (panel && panel->lock) + return; if (field && !lock) { *field = round(value * multiplier()); + emit currentDataChanged(value); if (panel) { emit panel->modified(); } @@ -92,7 +100,7 @@ class AutoDoubleSpinBox: public QDoubleSpinBox } protected: - int * field; - ModelPanel * panel; - bool lock; + int * field = nullptr; + GenericPanel * panel = nullptr; + bool lock = false; }; diff --git a/companion/src/shared/autohexspinbox.h b/companion/src/shared/autohexspinbox.h index d7df46bd6..40cc24da4 100644 --- a/companion/src/shared/autohexspinbox.h +++ b/companion/src/shared/autohexspinbox.h @@ -21,23 +21,23 @@ #pragma once #include "hexspinbox.h" -#include "modeledit/modeledit.h" +#include "genericpanel.h" class AutoHexSpinBox: public HexSpinBox { Q_OBJECT public: - explicit AutoHexSpinBox(QWidget *parent = 0): + explicit AutoHexSpinBox(QWidget * parent = nullptr): HexSpinBox(parent), - field(NULL), - panel(NULL), + field(nullptr), + panel(nullptr), lock(false) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); } - void setField(unsigned int & field, ModelPanel * panel=NULL) + void setField(unsigned int & field, GenericPanel * panel = nullptr) { this->field = &field; this->panel = panel; @@ -47,15 +47,23 @@ class AutoHexSpinBox: public HexSpinBox void updateValue() { if (field) { + lock = true; setValue(*field); + lock = false; } } + signals: + void currentDataChanged(int value); + protected slots: void onValueChanged(int value) { + if (panel && panel->lock) + return; if (field && !lock) { *field = value; + emit currentDataChanged(value); if (panel) { emit panel->modified(); } @@ -63,7 +71,7 @@ class AutoHexSpinBox: public HexSpinBox } protected: - unsigned int * field; - ModelPanel * panel; - bool lock; + unsigned int * field = nullptr; + GenericPanel * panel = nullptr; + bool lock = false; }; diff --git a/companion/src/shared/autolineedit.h b/companion/src/shared/autolineedit.h index 0f888e6df..173d16e95 100644 --- a/companion/src/shared/autolineedit.h +++ b/companion/src/shared/autolineedit.h @@ -29,11 +29,11 @@ class AutoLineEdit: public QLineEdit Q_OBJECT public: - explicit AutoLineEdit(QWidget *parent = nullptr, bool updateOnChange = false): + explicit AutoLineEdit(QWidget * parent = nullptr, bool updateOnChange = false): QLineEdit(parent), field(NULL), - strField(NULL), - panel(NULL), + strField(nullptr), + panel(nullptr), lock(false) { if (updateOnChange) @@ -86,15 +86,15 @@ class AutoLineEdit: public QLineEdit else return; + emit currentDataChanged(); + if (panel) emit panel->modified(); - - emit currentDataChanged(); } protected: char * field; - QString * strField; - GenericPanel * panel; - bool lock; + QString * strField = nullptr; + GenericPanel * panel = nullptr; + bool lock = false; }; diff --git a/companion/src/shared/autoprecisioncombobox.h b/companion/src/shared/autoprecisioncombobox.h index e93059199..72dd89406 100644 --- a/companion/src/shared/autoprecisioncombobox.h +++ b/companion/src/shared/autoprecisioncombobox.h @@ -123,7 +123,7 @@ class AutoPrecisionComboBox: public QComboBox if (*m_field != val) { *m_field = rangecheckDecimals(val); updateValue(); - emit valueChanged(); + emit currentDataChanged(value); } } @@ -134,7 +134,7 @@ class AutoPrecisionComboBox: public QComboBox if (*m_field != value) { *m_field = rangecheckDecimals(value); updateValue(); - emit valueChanged(); + emit currentDataChanged((int)value); } } @@ -160,7 +160,7 @@ class AutoPrecisionComboBox: public QComboBox } signals: - void valueChanged(); + void currentDataChanged(int index); protected slots: void init() @@ -203,13 +203,13 @@ class AutoPrecisionComboBox: public QComboBox void onCurrentIndexChanged(int index) { - if (index < 0) + if (index < 0 || (m_panel && m_panel->lock) || m_lock) return; - if (m_field && !m_lock) { + if (m_field) { *m_field = itemData(index).toUInt(); + emit currentDataChanged(index); if (m_panel) emit m_panel->modified(); - emit valueChanged(); } } From 3eed78dbd56e645a61cdfca7897d1796dd33352b Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 10:07:04 +1100 Subject: [PATCH 248/300] Introduce autotimeedit widget --- companion/src/shared/CMakeLists.txt | 1 + companion/src/shared/autotimeedit.h | 97 +++++++++++++++++++++++++++++ companion/src/shared/genericpanel.h | 1 + 3 files changed, 99 insertions(+) create mode 100644 companion/src/shared/autotimeedit.h diff --git a/companion/src/shared/CMakeLists.txt b/companion/src/shared/CMakeLists.txt index 2fbbafbf9..ffd5942bf 100644 --- a/companion/src/shared/CMakeLists.txt +++ b/companion/src/shared/CMakeLists.txt @@ -16,6 +16,7 @@ set(shared_HDRS genericpanel.h hexspinbox.h autoprecisioncombobox.h + autotimeedit.h ) qt5_wrap_cpp(shared_SRCS ${shared_HDRS}) diff --git a/companion/src/shared/autotimeedit.h b/companion/src/shared/autotimeedit.h new file mode 100644 index 000000000..ef419ac95 --- /dev/null +++ b/companion/src/shared/autotimeedit.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include +#include "genericpanel.h" + +class AutoTimeEdit: public QTimeEdit +{ + Q_OBJECT + + public: + explicit AutoTimeEdit(QWidget * parent = nullptr): + QTimeEdit(parent), + field(nullptr), + panel(nullptr), + lock(false) + { + connect(this, SIGNAL(timeChanged(QTime)), this, SLOT(onTimeChanged(QTime))); + } + + void setField(int & field, GenericPanel * panel = nullptr) + { + this->field = &field; + this->panel = panel; + updateValue(); + } + + void setMinimumTime(QTime time) + { + QTimeEdit::setMinimumTime(QTime time); + } + + void setMaximumTime(QTime time) + { + QTimeEdit::setMaximumTime(QTime time); + } + + void setEnabled(bool enabled) + { + QTimeEdit::setEnabled(enabled); + } + + void updateValue() + { + if (field) { + lock = true; + int hour = *field / 3600; + int min = (*field - (hour * 3600)) / 60; + int sec = (*field - (hour * 3600)) % 60; + setTime(QTime(hour, min, sec)); + lock = false; + } + } + + signals: + void currentDataChanged(unsigned int value); + + protected slots: + void onTimeChanged(QTime time) + { + if ((panel && panel->lock) || !field || lock) + return; + + unsigned int val = time.hour() * 3600 + time.minute() * 60 + time.second(); + if (*field != val) { + *field = val; + emit currentDataChanged(val); + if (panel) + emit panel->modified(); + } + } + + protected: + int *field = nullptr; + GenericPanel *panel = nullptr; + bool lock = false; +}; + diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index fe749b659..88c7f841b 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -40,6 +40,7 @@ class GenericPanel : public QWidget friend class GVarGroup; friend class AutoPrecisionComboBox; friend class AutoBitsetCheckBox; + friend class AutoTimeEdit; public: GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware); From 521bdde1fd01a8679de8387113ac08e699104b4d Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 20:26:11 +1100 Subject: [PATCH 249/300] Use autotimeedit in setup timers --- companion/src/modeledit/setup.cpp | 31 ++++++++++---------------- companion/src/modeledit/setup.h | 2 +- companion/src/modeledit/setup_timer.ui | 7 +++++- companion/src/shared/autotimeedit.h | 12 +++++----- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 7d887173f..cb4312965 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -48,8 +48,13 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G int length = firmware->getCapability(TimersName); if (length == 0) ui->name->hide(); - else + else { ui->name->setField(timer.name, length, this); + connect(ui->name, SIGNAL(currentDataChanged()), this, SLOT(onNameChanged())); + } + + ui->value->setField(timer.val, this); + ui->value->setMaximumTime(firmware->getMaxTimerStart()); // Mode ui->mode->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); @@ -60,8 +65,6 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G ui->countdownBeep->setField(timer.countdownBeep, this); connect(ui->countdownBeep, SIGNAL(currentDataChanged(int)), this, SLOT(onCountdownBeepDataChanged(int))); - ui->value->setMaximumTime(firmware->getMaxTimerStart()); - ui->minuteBeep->setField(timer.minuteBeep, this); if (firmware->getCapability(PermTimers)) { @@ -100,11 +103,7 @@ void TimerPanel::update() ui->name->updateValue(); ui->mode->setCurrentIndex(ui->mode->findData(timer.mode.toValue())); - - int hour = timer.val / 3600; - int min = (timer.val - (hour * 3600)) / 60; - int sec = (timer.val - (hour * 3600)) % 60; - ui->value->setTime(QTime(hour, min, sec)); + ui->value->updateValue(); ui->countdownBeep->updateValue(); ui->minuteBeep->updateValue(); @@ -129,17 +128,6 @@ QWidget * TimerPanel::getLastFocus() return ui->persistent; } -void TimerPanel::on_value_editingFinished() -{ - if (!lock) { - unsigned val = ui->value->time().hour() * 3600 + ui->value->time().minute() * 60 + ui->value->time().second(); - if (timer.val != val) { - timer.val = val; - emit modified(); - } - } -} - void TimerPanel::onModeChanged(int index) { if (!lock) { @@ -173,6 +161,11 @@ void TimerPanel::onItemModelUpdateComplete() } } +void TimerPanel::onNameChanged() +{ + emit nameChanged(); +} + void TimerPanel::onCountdownBeepDataChanged(int index) { timer.countdownBeepChanged(); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 41c3b45c7..67c97f669 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -47,7 +47,7 @@ class TimerPanel : public ModelPanel private slots: void onModeChanged(int index); - void on_value_editingFinished(); + void onNameChanged(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); void onCountdownBeepDataChanged(int index); diff --git a/companion/src/modeledit/setup_timer.ui b/companion/src/modeledit/setup_timer.ui index 989e7074e..a772c6d30 100644 --- a/companion/src/modeledit/setup_timer.ui +++ b/companion/src/modeledit/setup_timer.ui @@ -43,7 +43,7 @@
- + true @@ -126,6 +126,11 @@ QCheckBox
autocheckbox.h
+ + AutoTimeEdit + QTimeEdit +
autotimeedit.h
+
diff --git a/companion/src/shared/autotimeedit.h b/companion/src/shared/autotimeedit.h index ef419ac95..8e2dff2d8 100644 --- a/companion/src/shared/autotimeedit.h +++ b/companion/src/shared/autotimeedit.h @@ -37,21 +37,21 @@ class AutoTimeEdit: public QTimeEdit connect(this, SIGNAL(timeChanged(QTime)), this, SLOT(onTimeChanged(QTime))); } - void setField(int & field, GenericPanel * panel = nullptr) + void setField(unsigned int & field, GenericPanel * panel = nullptr) { this->field = &field; this->panel = panel; updateValue(); } - void setMinimumTime(QTime time) + void setMinimumTime(const QTime time) { - QTimeEdit::setMinimumTime(QTime time); + QTimeEdit::setMinimumTime(time); } - void setMaximumTime(QTime time) + void setMaximumTime(const QTime time) { - QTimeEdit::setMaximumTime(QTime time); + QTimeEdit::setMaximumTime(time); } void setEnabled(bool enabled) @@ -90,7 +90,7 @@ class AutoTimeEdit: public QTimeEdit } protected: - int *field = nullptr; + unsigned int *field = nullptr; GenericPanel *panel = nullptr; bool lock = false; }; From aac6c74e59f6183b467b6bf5ca944bc1764d5025 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 20:26:45 +1100 Subject: [PATCH 250/300] Fix custom function reset model refresh --- companion/src/datamodels/compounditemmodels.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index b243bf09a..2f04a34fb 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -444,7 +444,7 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { setId(IMID_CustomFuncResetParam); - setUpdateMask(IMUE_TeleSensors); + setUpdateMask(IMUE_TeleSensors | IMUE_Timers); for (int i = 0; i < CustomFunctionData::resetParamCount(); i++) { QStandardItem * modelItem = new QStandardItem(); From 950fa3d10501abce591cb9cfe1b51a9914d3a8f9 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 21:40:13 +1100 Subject: [PATCH 251/300] Add support for rawsource and rawswitch in autocombobox --- companion/src/shared/autocombobox.h | 72 ++++++++++++++++++++++++----- companion/src/shared/autolineedit.h | 4 +- companion/src/shared/autotimeedit.h | 4 +- 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/companion/src/shared/autocombobox.h b/companion/src/shared/autocombobox.h index a257f35d5..ca6054555 100644 --- a/companion/src/shared/autocombobox.h +++ b/companion/src/shared/autocombobox.h @@ -22,6 +22,8 @@ #include #include "genericpanel.h" +#include "rawsource.h" +#include "rawswitch.h" class AutoComboBox: public QComboBox { @@ -34,7 +36,9 @@ class AutoComboBox: public QComboBox panel(nullptr), next(0), lock(false), - hasModel(false) + hasModel(false), + rawSource(nullptr), + rawSwitch(nullptr) { connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); } @@ -77,6 +81,8 @@ class AutoComboBox: public QComboBox void setField(unsigned int & field, GenericPanel * panel = nullptr) { this->field = (int *)&field; + this->rawSource = nullptr; + this->rawSwitch = nullptr; this->panel = panel; updateValue(); } @@ -84,6 +90,26 @@ class AutoComboBox: public QComboBox void setField(int & field, GenericPanel * panel = nullptr) { this->field = &field; + this->rawSource = nullptr; + this->rawSwitch = nullptr; + this->panel = panel; + updateValue(); + } + + void setField(RawSource & field, GenericPanel * panel = nullptr) + { + this->rawSource = &field; + this->rawSwitch = nullptr; + this->field = nullptr; + this->panel = panel; + updateValue(); + } + + void setField(RawSwitch & field, GenericPanel * panel = nullptr) + { + this->rawSwitch = &field; + this->rawSource = nullptr; + this->field = nullptr; this->panel = panel; updateValue(); } @@ -108,10 +134,18 @@ class AutoComboBox: public QComboBox void updateValue() { - if (!field) + if (!field && !rawSource && !rawSwitch) return; + lock = true; - setCurrentIndex(findData(*field)); + + if (field) + setCurrentIndex(findData(*field)); + else if (rawSource) + setCurrentIndex(findData(rawSource->toValue())); + else if (rawSwitch) + setCurrentIndex(findData(rawSwitch->toValue())); + lock = false; } @@ -123,15 +157,29 @@ class AutoComboBox: public QComboBox { if (panel && panel->lock) return; - if (index > -1) { - const int val = itemData(index).toInt(); - if (field && !lock) { - *field = val; - emit currentDataChanged(val); - if (panel) - emit panel->modified(); - } + if (lock || index < 0) + return; + + bool ok; + const int val = itemData(index).toInt(&ok); + if (!ok) + return; + + if (field && *field != val) { + *field = val; } + else if (rawSource && rawSource->toValue() != val) { + *rawSource = RawSource(val); + } + else if (rawSwitch && rawSwitch->toValue() != val) { + *rawSwitch = RawSwitch(val); + } + else + return; + + emit currentDataChanged(val); + if (panel) + emit panel->modified(); } protected: @@ -140,4 +188,6 @@ class AutoComboBox: public QComboBox int next = 0; bool lock = false; bool hasModel = false; + RawSource *rawSource = nullptr; + RawSwitch *rawSwitch = nullptr; }; diff --git a/companion/src/shared/autolineedit.h b/companion/src/shared/autolineedit.h index 173d16e95..b2815796c 100644 --- a/companion/src/shared/autolineedit.h +++ b/companion/src/shared/autolineedit.h @@ -76,7 +76,9 @@ class AutoLineEdit: public QLineEdit protected slots: void onEdited() { - if ((panel && panel->lock) || lock) + if (panel && panel->lock) + return; + if (lock) return; if (field) diff --git a/companion/src/shared/autotimeedit.h b/companion/src/shared/autotimeedit.h index 8e2dff2d8..2ba75d70e 100644 --- a/companion/src/shared/autotimeedit.h +++ b/companion/src/shared/autotimeedit.h @@ -77,7 +77,9 @@ class AutoTimeEdit: public QTimeEdit protected slots: void onTimeChanged(QTime time) { - if ((panel && panel->lock) || !field || lock) + if (panel && panel->lock) + return; + if (!field || lock) return; unsigned int val = time.hour() * 3600 + time.minute() * 60 + time.second(); From 44cfbd43fb793efcf0d5506545c29fd2e1e8f052 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 21:48:51 +1100 Subject: [PATCH 252/300] Use enhanced autocombobox for timer mode --- companion/src/modeledit/setup.cpp | 18 ++---------------- companion/src/modeledit/setup.h | 1 - companion/src/modeledit/setup_timer.ui | 2 +- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index cb4312965..9d656348f 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -56,10 +56,8 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G ui->value->setField(timer.val, this); ui->value->setMaximumTime(firmware->getMaxTimerStart()); - // Mode ui->mode->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); - ui->mode->setCurrentIndex(ui->mode->findData(timer.mode.toValue())); - connect(ui->mode, SIGNAL(activated(int)), this, SLOT(onModeChanged(int))); + ui->mode->setField(timer.mode, this); ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP)); ui->countdownBeep->setField(timer.countdownBeep, this); @@ -102,7 +100,7 @@ void TimerPanel::update() lock = true; ui->name->updateValue(); - ui->mode->setCurrentIndex(ui->mode->findData(timer.mode.toValue())); + ui->mode->updateValue(); ui->value->updateValue(); ui->countdownBeep->updateValue(); @@ -128,18 +126,6 @@ QWidget * TimerPanel::getLastFocus() return ui->persistent; } -void TimerPanel::onModeChanged(int index) -{ - if (!lock) { - bool ok; - const RawSwitch rs(ui->mode->itemData(index).toInt(&ok)); - if (ok && timer.mode.toValue() != rs.toValue()) { - timer.mode = rs; - emit modified(); - } - } -} - void TimerPanel::connectItemModelEvents(const FilteredItemModel * itemModel) { connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TimerPanel::onItemModelAboutToBeUpdated); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 67c97f669..e62041dc6 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -46,7 +46,6 @@ class TimerPanel : public ModelPanel QWidget * getLastFocus(); private slots: - void onModeChanged(int index); void onNameChanged(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); diff --git a/companion/src/modeledit/setup_timer.ui b/companion/src/modeledit/setup_timer.ui index a772c6d30..67b929bdb 100644 --- a/companion/src/modeledit/setup_timer.ui +++ b/companion/src/modeledit/setup_timer.ui @@ -56,7 +56,7 @@
- + From 8576f25aff12900e71353767c8944095bff2aa8f Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 21:51:31 +1100 Subject: [PATCH 253/300] Rename function --- companion/src/modeledit/setup.cpp | 8 +++++--- companion/src/modeledit/setup.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 9d656348f..9bfd8fd3e 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -61,7 +61,7 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP)); ui->countdownBeep->setField(timer.countdownBeep, this); - connect(ui->countdownBeep, SIGNAL(currentDataChanged(int)), this, SLOT(onCountdownBeepDataChanged(int))); + connect(ui->countdownBeep, SIGNAL(currentDataChanged(int)), this, SLOT(onCountdownBeepChanged(int))); ui->minuteBeep->setField(timer.minuteBeep, this); @@ -102,10 +102,10 @@ void TimerPanel::update() ui->name->updateValue(); ui->mode->updateValue(); ui->value->updateValue(); - ui->countdownBeep->updateValue(); ui->minuteBeep->updateValue(); ui->countdownStart->updateValue(); + if (timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) { ui->countdownStartLabel->setEnabled(false); ui->countdownStart->setEnabled(false); @@ -114,10 +114,12 @@ void TimerPanel::update() ui->countdownStartLabel->setEnabled(true); ui->countdownStart->setEnabled(true); } + if (firmware->getCapability(PermTimers)) { ui->persistent->updateValue(); ui->persistentValue->setText(timer.pvalueToString()); } + lock = false; } @@ -152,7 +154,7 @@ void TimerPanel::onNameChanged() emit nameChanged(); } -void TimerPanel::onCountdownBeepDataChanged(int index) +void TimerPanel::onCountdownBeepChanged(int index) { timer.countdownBeepChanged(); update(); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index e62041dc6..eeb34aef6 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -49,7 +49,7 @@ class TimerPanel : public ModelPanel void onNameChanged(); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); - void onCountdownBeepDataChanged(int index); + void onCountdownBeepChanged(int index); signals: void nameChanged(); From 9caba6bfcf085fd1f6eaceb94d05a165b6669b73 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 17 Mar 2021 12:18:18 +0100 Subject: [PATCH 254/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d81b22303..897bd6700 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2188,3 +2188,4 @@ Stefanie Zerbe James Mandelbaum Javier J. Cordovez Jess Barkley +Schneider Modell From 1678753c9b19d745dc7dde7a7d500f8962fc1aa6 Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 22:39:14 +1100 Subject: [PATCH 255/300] Use data model for throttle src --- companion/src/modeledit/setup.cpp | 35 ++++--------------------------- companion/src/modeledit/setup.h | 2 -- companion/src/modeledit/setup.ui | 2 +- 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 9bfd8fd3e..db3fb8b33 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -1113,6 +1113,9 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge ui->toplcdTimer->hide(); } + ui->throttleSource->setModel(panelFilteredModels->getItemModel(FIM_THRSOURCE)); + ui->throttleSource->setField(model.thrTraceSrc, this); + if (!firmware->getCapability(HasDisplayText)) { ui->displayText->hide(); ui->editText->hide(); @@ -1279,14 +1282,6 @@ void SetupPanel::on_trimIncrement_currentIndexChanged(int index) emit modified(); } -void SetupPanel::on_throttleSource_currentIndexChanged(int index) -{ - if (!lock) { - model->thrTraceSrc = ui->throttleSource->currentData().toUInt(); - emit modified(); - } -} - void SetupPanel::on_throttleTrimSwitch_currentIndexChanged(int index) { if (!lock) { @@ -1345,28 +1340,6 @@ void SetupPanel::on_image_currentIndexChanged(int index) } } -void SetupPanel::populateThrottleSourceCB() -{ - Board::Type board = firmware->getBoard(); - lock = true; - ui->throttleSource->clear(); - ui->throttleSource->addItem(tr("THR"), 0); - - int idx = 1; - for (int i = 0; i < getBoardCapability(board, Board::Pots) + getBoardCapability(board, Board::Sliders); i++, idx++) { - if (RawSource(SOURCE_TYPE_STICK, 4 + i).isAvailable(model, &generalSettings, board)) { - ui->throttleSource->addItem(firmware->getAnalogInputName(4 + i), idx); - } - } - for (int i = 0; i < firmware->getCapability(Outputs); i++, idx++) { - ui->throttleSource->addItem(RawSource(SOURCE_TYPE_CH, i).toString(model, &generalSettings), idx); - } - - int thrTraceSrcIdx = ui->throttleSource->findData(model->thrTraceSrc); - ui->throttleSource->setCurrentIndex(thrTraceSrcIdx); - lock = false; -} - void SetupPanel::populateThrottleTrimSwitchCB() { Board::Type board = firmware->getBoard(); @@ -1394,7 +1367,7 @@ void SetupPanel::update() { ui->name->setText(model->name); ui->throttleReverse->setChecked(model->throttleReversed); - populateThrottleSourceCB(); + ui->throttleSource->updateValue(); populateThrottleTrimSwitchCB(); ui->throttleWarning->setChecked(!model->disableThrottleWarning); ui->trimIncrement->setCurrentIndex(model->trimInc+2); diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index eeb34aef6..82e69ec75 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -142,7 +142,6 @@ class SetupPanel : public ModelPanel private slots: void on_name_editingFinished(); - void on_throttleSource_currentIndexChanged(int index); void on_throttleTrimSwitch_currentIndexChanged(int index); void on_throttleTrim_toggled(bool checked); void on_extendedLimits_toggled(bool checked); @@ -185,7 +184,6 @@ class SetupPanel : public ModelPanel void updateStartupSwitches(); void updatePotWarnings(); void updateBeepCenter(); - void populateThrottleSourceCB(); void populateThrottleTrimSwitchCB(); int timersCount; int selectedTimerIndex; diff --git a/companion/src/modeledit/setup.ui b/companion/src/modeledit/setup.ui index 8b2409191..ee0ec28a3 100644 --- a/companion/src/modeledit/setup.ui +++ b/companion/src/modeledit/setup.ui @@ -604,7 +604,7 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi - + 0 From 39407ed241844c5d668b49bb638540afc42a5504 Mon Sep 17 00:00:00 2001 From: elecpower Date: Thu, 18 Mar 2021 07:00:37 +1100 Subject: [PATCH 256/300] Fix timers refresh --- companion/src/modeledit/setup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index db3fb8b33..40abc2f2f 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -1738,6 +1738,7 @@ void SetupPanel::onItemModelUpdateComplete() void SetupPanel::updateItemModels() { sharedItemModels->update(AbstractItemModel::IMUE_Timers); + emit updated(); } void SetupPanel::onModuleUpdateItemModels() From 2ce1ac73edb3323b71e0b7252a0064db738bc810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Charpentier?= Date: Thu, 18 Mar 2021 12:00:45 +0100 Subject: [PATCH 257/300] French translation update (#8372) * Update French translation. --- companion/src/translations/companion_fr.ts | 685 +++++++++++---------- 1 file changed, 365 insertions(+), 320 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 1d1b1b331..7f5c0618d 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -843,127 +843,127 @@ Manche Droit: Profondeur, Direction ChannelsPanel - + Name Nom - + Subtrim Subtrim - + Min - + Max - + Direction - + Curve Courbe - + PPM Center Neutre PPM - + Linear Subtrim Subtrim linéaire - + CH%1 VOIE%1 - + Popup menu available Menu contextuel disponible - + --- - + INV - + Delete Channel. Are you sure? Supprimer la Voie. Êtes-vous sûr? - + Cut Channel. Are you sure? Couper la Voie. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Channel. Are you sure? Effacer la Voie. Êtes-vous sûr? - + Clear all Channels. Are you sure? Effacer toutes les Voies. Êtes-vous sûr? @@ -1118,37 +1118,37 @@ Manche Droit: Profondeur, Direction <p> Le type de radio sélectionnée dans le profil n'existe pas. Utilisez plutôt le type par défaut. </p> <p><b>Veuillez mettre à jour les paramètres de votre profil!</b></p>
- + Select or create a file for exported Settings: Sélectionnez ou créez un fichier pour les paramètres exportés: - + Press the 'Retry' button to choose another file. Appuyez sur le bouton 'Réessayer' pour choisir un autre fichier. - + Simulator for this firmware is not yet available Le simulateur n'est pas encore disponible pour ce firmware - + Uknown error during Simulator startup. Erreur inconnue pendant le démarrage du simulateur. - + Simulator Error Erreur Simulateur - + Data Load Error Erreur de chargement des données - + Error occurred while starting simulator. Une erreur s'est produite lors du démarrage du simulateur. @@ -1381,7 +1381,7 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import CurveData - + CV CB @@ -1614,219 +1614,297 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import CustomFunctionData - + GF FG - + SF FS - + Override %1 Remplacer %1 - + Trainer Sticks Manches Écolage - + Trainer RUD Écolage Direction - + Trainer ELE Écolage Profondeur - + Trainer THR Écolage Gaz - + Trainer AIL Écolage Ailerons - + Trainer Channels Écolage Channels - + Instant Trim Trims instantanés - + Play Sound Jouer Son - + Haptic Vibreur - + Reset Remise à zéro - + Set %1 Activer %1 - + Vario - + Play Track Jouer fichier - + Play Both Jouer les deux - + Play Value Lire valeur - - Play Script - Exécuter script + + Lua Script + Script Lua - + + Range Check Int. Module + Test Portée Module Int. + + + + Range Check Ext. Module + Test Portée Module Ext. + + + + Beep 1 + + + + + Beep 2 + + + + + Beep 3 + + + + + Warn 1 + Alerte 1 + + + + Warn 2 + Alerte 2 + + + + Cheep + Piauler + + + + Ratata + + + + + Tick + Tic-tac + + + + Siren + Sirène + + + + Ring + Sonner + + + + Sci Fi + + + + + Robot + + + + + Chirp + Gazouiller + + + + Tada + + + + + Cricket + Criquet + + + + Alarm Clock + Réveil + + + + Value + Valeur + + + + Source + + + + + Global Variable + Variable Globale + + + + Inc/Decrement + Inc/Décrémenter + + + SD Logs Logs SD - + Volume - + Backlight Rétroéclairage - + Screenshot Capture d'écran - + Background Music Musique de fond - + Background Music Pause Pause musique de fond - + Adjust %1 Ajuster %1 - + Set Failsafe Définir Failsafe - - RangeCheck Int. Module - Test portée module interne - - - - RangeCheck Ext. Module - Test portée module externe - - - + Bind Int. Module Bind module interne - + Bind Ext. Module Bind module externe - + + Played once, not during startup + Lu une fois, mais pas à la mise en route + + + + No repeat + Lu une fois + + + + Repeat %1s + Répéter %1s + + + Flight Vol - + Telemetry Télémétrie - - Rotary Encoder - Sélecteur Rotatif - - - - REa - - - - - REb - - - - + s - - - - <font color=red><b>Inconsistent parameter</b></font> - <font color=red><b>Paramètre inconsistant</b></font> - - - - Value - Valeur - - - - played once, not during startup - !1x Lu une fois, mais pas à la mise en route - - - - repeat(%1s) - Répéter chaque %1s - - - + DISABLED DESACTIVE - + CFN FPN @@ -1834,139 +1912,119 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import CustomFunctionsPanel - + Switch Interrupteur - + Action - + Parameters Paramètres - - Enable - Actif - - - + Popup menu available Menu contextuel disponible - + SF%1 FS%1 - + GF%1 FG%1 - + + GV + VG + + + ON - + Error occurred while trying to play sound, possibly the file is already opened. (Err: %1 [%2]) Une erreur s'est produite lors de la lecture du son, le fichier est peut-être déjà ouvert. (Err: %1 [%2]) - + Unable to find or open sound file: %1 Impossible de trouver ou d'ouvrir le fichier son: %1 - + Delete Function. Are you sure? Supprimer la Fonction. Êtes-vous sûr? - + Cut Special Function. Are you sure? Couper la Fonction. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - - Value - Valeur - - - - Source - Source - - - - GVAR - VG - - - - Increment - Incrément - - - + Clear Function. Are you sure? Effacer la Fonction. Êtes-vous sûr? - + Clear all Functions. Are you sure? Effacer toutes les Fonctions. Êtes-vous sûr? @@ -2386,12 +2444,12 @@ Vérifier la sélection (M64/M128) ExpoData - + INP ENT - + (@%1) @@ -2513,22 +2571,22 @@ Si vide, la ligne est toujours active. Éditer %1 - + Popup menu available Menu contextuel disponible - + Clear All Effacer Tout - + Set All Définir Tout - + Invert All Inverser Tout @@ -3672,7 +3730,7 @@ Vous utilisez actuellement: FlightModeData - + FM PV @@ -4143,12 +4201,12 @@ p, li { white-space: pre-wrap; } VG - + Own value Valeur indépendante - + Flight mode %1 value Valeur de la phase de vol %1 @@ -4701,6 +4759,17 @@ Mode 4: A T E R A G P D + + + Battery warning voltage. +This is the threashhold where the battery warning sounds. + +Acceptable values are 3v..12v + Tension d'alerte de batterie. +Seuil auquel se déclenche l'alarme de batterie. + +Plage de valeurs: 3v...12v + Power Off Delay @@ -4871,17 +4940,6 @@ Mode 4: All Tous - - - Battery warning voltage. -This is the threashhold where the battery warning sounds. - -Acceptable values are 5v..10v - Tension d'alerte de batterie. -Seuil auquel se déclenche l'alarme de batterie. - -Plage de valeurs: 5v...10v - Standard @@ -5739,27 +5797,27 @@ Cette fonction ne peut pas être désactivée sur la radio. InputsPanel - - + + Move Up Monter - + Ctrl+Up Ctrl+Haut - - + + Move Down Descendre - + Ctrl+Down Ctrl+Bas @@ -5784,113 +5842,113 @@ Cette fonction ne peut pas être désactivée sur la radio. Couper les lignes Entrée sélectionnées. Êtes-vous sûr? - + Lines Linéaire - + &Add &Ajouter - + Ctrl+A Ctrl+A - + &Edit &Édition - + Enter Entrée - + &Delete &Supprimer - - + + Delete Supprimer - + &Copy &Copier - + Ctrl+C - + &Cut &Couper - + Ctrl+X - + &Paste &Coller - + Ctrl+V - + Du&plicate Du&pliquer - + Ctrl+U - + Input Entrée - + Insert Insérer - + Clear Effacer - + Clear All Effacer Tout - + Clear all Input lines. Are you sure? Effacer toutes les lignes Entrée. Êtes-vous sûr? - + Clear all lines for the selected Inputs. Are you sure? Effacer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? - + Delete all lines for the selected Inputs. Are you sure? Supprimer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? @@ -5898,17 +5956,17 @@ Cette fonction ne peut pas être désactivée sur la radio. LimitData - + INV - + NOR - + CH VOIE @@ -5916,7 +5974,7 @@ Cette fonction ne peut pas être désactivée sur la radio. LimitsGroup - + GV VG @@ -7677,12 +7735,12 @@ Enregistrer les changements ? MixData - + MIX - + (@%1) @@ -7884,22 +7942,22 @@ Mixage actif par défaut si non-renseigné. - + Click to access popup menu Cliquez pour accéder au menu contextuel - + Clear All Effacer Tout - + Set All Définir Tout - + Invert All Inverser Tout @@ -7946,25 +8004,25 @@ Mixage actif par défaut si non-renseigné. MixesPanel - + Move Up Monter - + Ctrl+Up Ctrl+Haut - + Move Down Descendre - + Ctrl+Down Ctrl+Bas @@ -7989,92 +8047,92 @@ Mixage actif par défaut si non-renseigné. Couper les lignes Mixeur sélectionnées. Êtes-vous sûr? - + &Add &Ajouter - + Ctrl+A - + &Edit &Édition - + Enter Entrée - + Ctrl+T - + &Toggle highlight &(Dé)sélectionner - + &Delete &Supprimer - + Delete Supprimer - + &Copy &Copier - + Ctrl+C - + Ctrl+X - + C&ut &Couper - + &Paste &Coller - + Ctrl+V - + Du&plicate &Dupliquer - + Ctrl+U - + Clear Mixes? Effacer tous les mixages ? - + Really clear all the mixes? Êtes-vous sûr de vraiment vouloir effacer tous les mixages ? @@ -8105,32 +8163,32 @@ Mixage actif par défaut si non-renseigné. Simulation - + Heli Paramètres hélico - + Inputs Entrées - + Mixes Mixages - + Logical Switches Inters logiques - + Setup Configuration - + Curves Courbes @@ -8140,22 +8198,22 @@ Mixage actif par défaut si non-renseigné. Dialogue - + Flight Modes Phases de vol - + Outputs Sorties - + Special Functions Fonctions spéciales - + Telemetry Télémesure @@ -9857,12 +9915,12 @@ Mixage actif par défaut si non-renseigné. QObject - + WARNING ATTENTION - + <p>Importing JumperTX data into OpenTX 2.3 is <b>not supported and dangerous.</b></p> <p>It is unfortunately not possible for us to differentiate JumperTX data from legitimate FrSky X10 data, but <b>You should only continue here if the file you opened comes from a real FrSky X10.</b></p> <p>Do you really want to continue?</p> <p>L'importation de données JumperTX dans OpenTX 2.3 est <b> non prise en charge et dangereuse.</b></p> <p>Il n'est malheureusement pas possible pour nous de différencier les données JumperTX des données légitimes FrSky X10, donc <b>Vous devez continuer uniquement si le fichier que vous avez ouvert provient d’une véritable radio FrSky X10.</b></p> <p>Voulez-vous vraiment continuer?</p> @@ -10434,24 +10492,6 @@ E Notes de version d'OpenTX - - RepeatComboBox - - - Played once, not during startup - 1x Lu une fois, mais pas à la mise en route - - - - No repeat - 1x Lu une fois - - - - %1s - - - RudderPage @@ -10801,6 +10841,11 @@ E Delta + + + (default) + (défaut) + Y @@ -10994,92 +11039,92 @@ Si cette option est cochée, la voie des gaz est inversée: le ralenti est &apo SetupPanel - + Popup menu available Menu contextuel disponible - + Timer %1 Chrono %1 - + THR GAZ - + Profile Settings Paramètres du profil - + SD structure path not specified or invalid Chemin de la structure SD non spécifié ou invalide - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Timer. Are you sure? Effacer le Chrono. Êtes-vous sûr? - + Clear all Timers. Are you sure? Effacer tous les Chronos. Êtes-vous sûr? - + Cut Timer. Are you sure? Couper le Chrono. Êtes-vous sûr? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? @@ -12513,22 +12558,22 @@ Trop d'erreurs, abandon. Type d'écran personnalisé - + None Aucun - + Numbers Chiffres - + Bars Barres - + Script @@ -12556,52 +12601,52 @@ Trop d'erreurs, abandon. TelemetryPanel - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Telemetry screen %1 Ecran de télémesure %1 - + Source - + Low Alarm Alarme basse - + Critical Alarm Alarme critique - + Winged Shadow How High - + Winged Shadow How High (not supported) Winged Shadow How High (non supporté) - + Delete Sensor. Are you sure? Supprimer le Capteur. Êtes-vous sûr? @@ -12719,72 +12764,72 @@ Trop d'erreurs, abandon. TelemetrySensorPanel - + TELE%1 - + Popup menu available Menu contextuel disponible - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Cut Telemetry Sensor. Are you sure? Couper le capteur de télémétrie. Êtes-vous sûr? - + Clear Telemetry Sensor. Are you sure? Effacer le capteur de télémétrie. Êtes-vous sûr? - + Clear all Telemetry Sensors. Are you sure? Effacez tous les capteurs de télémétrie. Êtes-vous sûr? From 4e62d0b1b7ebfd6dd69a525e575f033fcdafc69d Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 19 Mar 2021 06:17:35 +1100 Subject: [PATCH 258/300] Fix model upd refs custom function reset timer --- companion/src/firmwares/modeldata.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 3efb7ae6a..969485081 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1356,7 +1356,6 @@ void ModelData::sortMixes() void ModelData::updateResetParam(CustomFunctionData * cfd) { - if (cfd->func != FuncReset) return; @@ -1367,6 +1366,11 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) switch (updRefInfo.type) { + case REF_UPD_TYPE_TIMER: + if (cfd->param < 0 || cfd->param > 2) + return; + idxAdj = -2; // reverse earlier offset required for rawsource + break; case REF_UPD_TYPE_SENSOR: idxAdj = 5/*3 Timers + Flight + Telemetery*/ + firmware->getCapability(RotaryEncoders); if (cfd->param < idxAdj || cfd->param > (idxAdj + firmware->getCapability(Sensors))) @@ -1376,6 +1380,8 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) return; } + qDebug() << "index1" << updRefInfo.index1 << "shift:" << updRefInfo.shift << "cfd->param:" << cfd->param << "idxAdj:" << idxAdj; + switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: From 772564ccd3c06e6ad802ef1dba85d348365fbb0c Mon Sep 17 00:00:00 2001 From: elecpower Date: Wed, 17 Mar 2021 20:26:45 +1100 Subject: [PATCH 259/300] Fix custom function reset model refresh --- companion/src/datamodels/compounditemmodels.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/src/datamodels/compounditemmodels.cpp b/companion/src/datamodels/compounditemmodels.cpp index ab0ba83ba..b64efb7b5 100644 --- a/companion/src/datamodels/compounditemmodels.cpp +++ b/companion/src/datamodels/compounditemmodels.cpp @@ -444,7 +444,7 @@ CustomFuncResetParamItemModel::CustomFuncResetParamItemModel(const GeneralSettin AbstractDynamicItemModel(generalSettings, modelData, firmware, board, boardType) { setId(IMID_CustomFuncResetParam); - setUpdateMask(IMUE_TeleSensors); + setUpdateMask(IMUE_TeleSensors | IMUE_Timers); for (int i = 0; i < CustomFunctionData::resetParamCount(); i++) { QStandardItem * modelItem = new QStandardItem(); From 1547cad595a14fc1c80cca4cd456669c4929be3f Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 19 Mar 2021 06:17:35 +1100 Subject: [PATCH 260/300] Fix model upd refs custom function reset timer --- companion/src/firmwares/modeldata.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 7657e2798..04552a6e4 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1381,7 +1381,6 @@ void ModelData::sortMixes() void ModelData::updateResetParam(CustomFunctionData * cfd) { - if (cfd->func != FuncReset) return; @@ -1392,6 +1391,11 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) switch (updRefInfo.type) { + case REF_UPD_TYPE_TIMER: + if (cfd->param < 0 || cfd->param > 2) + return; + idxAdj = -2; // reverse earlier offset required for rawsource + break; case REF_UPD_TYPE_SENSOR: idxAdj = 5/*3 Timers + Flight + Telemetery*/ + firmware->getCapability(RotaryEncoders); if (cfd->param < idxAdj || cfd->param > (idxAdj + firmware->getCapability(Sensors))) @@ -1401,6 +1405,8 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) return; } + qDebug() << "index1" << updRefInfo.index1 << "shift:" << updRefInfo.shift << "cfd->param:" << cfd->param << "idxAdj:" << idxAdj; + switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: From 227ef21bd4164cb550318fc0d90b2fa55b7b94c3 Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 19 Mar 2021 06:31:54 +1100 Subject: [PATCH 261/300] Remove debug line --- companion/src/firmwares/modeldata.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 04552a6e4..f5061b567 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1405,8 +1405,6 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) return; } - qDebug() << "index1" << updRefInfo.index1 << "shift:" << updRefInfo.shift << "cfd->param:" << cfd->param << "idxAdj:" << idxAdj; - switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: From 1747f9aa172aef6e012169987f90d150659c797f Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 19 Mar 2021 06:34:10 +1100 Subject: [PATCH 262/300] Remove debug line --- companion/src/firmwares/modeldata.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 969485081..5863890f3 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1380,8 +1380,6 @@ void ModelData::updateResetParam(CustomFunctionData * cfd) return; } - qDebug() << "index1" << updRefInfo.index1 << "shift:" << updRefInfo.shift << "cfd->param:" << cfd->param << "idxAdj:" << idxAdj; - switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: From 96825d04a0113d7ad3add51a745b93631354caa0 Mon Sep 17 00:00:00 2001 From: elecpower Date: Fri, 19 Mar 2021 07:13:25 +1100 Subject: [PATCH 263/300] Initialise variable --- companion/src/modeledit/setup.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index 40abc2f2f..3a3a7e910 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -37,7 +37,8 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G QWidget * prevFocus, FilteredItemModelFactory * panelFilteredModels, CompoundItemModelFactory * panelItemModels): ModelPanel(parent, model, generalSettings, firmware), timer(timer), - ui(new Ui::Timer) + ui(new Ui::Timer), + modelsUpdateCnt(0) { ui->setupUi(this); connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH)); From ce0e31436c764dc802c16e9b4d29b33c7c435bac Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Mar 2021 12:18:56 +0100 Subject: [PATCH 264/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 897bd6700..418aae071 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2189,3 +2189,4 @@ James Mandelbaum Javier J. Cordovez Jess Barkley Schneider Modell +Michael Hasselgaard From f5e34c12776b19df0e0c98890ff4f252f6bb7aa6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Mar 2021 12:19:12 +0100 Subject: [PATCH 265/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 418aae071..ffe08c617 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2190,3 +2190,4 @@ Javier J. Cordovez Jess Barkley Schneider Modell Michael Hasselgaard +Thomas Young From 1624d5b69cec3601d4ff3442ba4cc4b7f071e6f4 Mon Sep 17 00:00:00 2001 From: Risto Date: Fri, 19 Mar 2021 18:55:47 +0100 Subject: [PATCH 266/300] STM32 WordLength 9b -> 8b data + parity Fixed comment. STM32 WordLength 9b is here necessary due to parity bit (8b data + 1b parity) and not due to 2 stop bits. --- radio/src/targets/common/arm/stm32/aux_serial_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9cc558b51..86082f7f8 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -124,7 +124,7 @@ void auxSerialInit(unsigned int mode, unsigned int protocol) break; case UART_MODE_SBUS_TRAINER: - auxSerialSetup(SBUS_BAUDRATE, true, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // 2 stop bits requires USART_WordLength_9b + auxSerialSetup(SBUS_BAUDRATE, true, USART_WordLength_9b, USART_Parity_Even, USART_StopBits_2); // USART_WordLength_9b due to parity bit AUX_SERIAL_POWER_ON(); break; From 6a7ffd50bb2ad4568d7c757bc53dbf0a7e2e89f7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Mar 2021 09:44:04 +0100 Subject: [PATCH 267/300] Update CREDITS.txt --- CREDITS.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index ffe08c617..7d9f9fda6 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2191,3 +2191,11 @@ Jess Barkley Schneider Modell Michael Hasselgaard Thomas Young +Непочатов Алексей +Thomas Goebel +Joseph Macias +Paw Melin-Nielsen +Liam O'Brien +Ronald B Backman +Corvus Inspection Services Inc +Stuart Posnett From 02c51a19dbe36994c8192deb86258ab51b688922 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 26 Mar 2021 18:38:56 +0100 Subject: [PATCH 268/300] Update CREDITS.txt --- CREDITS.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 7d9f9fda6..58ecae6a2 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2199,3 +2199,9 @@ Liam O'Brien Ronald B Backman Corvus Inspection Services Inc Stuart Posnett +Clark Emerick +Helmut Sippel +Jhon Lennon Silva do Nascimento +Peter McCarthy +Bob Brown +Claude Lawyer From a360963b511382f928eea76be7d7de5164b1768a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 26 Mar 2021 21:42:48 +0100 Subject: [PATCH 269/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index 58ecae6a2..bb524647a 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2205,3 +2205,4 @@ Jhon Lennon Silva do Nascimento Peter McCarthy Bob Brown Claude Lawyer +Peter Fithern From f03ebccfa87808389f376f57af45a2557d83cac1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 27 Mar 2021 00:01:45 +0100 Subject: [PATCH 270/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index bb524647a..b535b828f 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2206,3 +2206,4 @@ Peter McCarthy Bob Brown Claude Lawyer Peter Fithern +Michael Hoffman From 9fd4d2873873e3987008c8d293b3cfdc543d5220 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 28 Mar 2021 07:56:52 +1100 Subject: [PATCH 271/300] Move limitdata array manipulation from ui to model --- companion/src/firmwares/modeldata.cpp | 76 +++++++++++++++++++++++++ companion/src/firmwares/modeldata.h | 8 +++ companion/src/firmwares/output_data.cpp | 3 +- companion/src/modeledit/channels.cpp | 60 +++++++------------ companion/src/modeledit/channels.h | 1 - 5 files changed, 106 insertions(+), 42 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 7657e2798..52244b342 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1478,3 +1478,79 @@ bool ModelData::isThrTraceSrcAvailable(const GeneralSettings * generalSettings, else return true; } + +void ModelData::limitsClear(const int index) +{ + if (index < 0 || index >= CPN_MAX_CHNOUT) + return; + + if (!limitData[index].isEmpty()) { + limitData[index].clear(); + updateAllReferences(REF_UPD_TYPE_CHANNEL, REF_UPD_ACT_CLEAR, index); + } +} + +void ModelData::limitsClearAll() +{ + for (int i = 0; i < CPN_MAX_CHNOUT; i++) { + limitsClear(i); + } +} + +void ModelData::limitsDelete(const int index) +{ + if (index < 0 || index >= CPN_MAX_CHNOUT) + return; + + memmove(&limitData[index], &limitData[index + 1], (CPN_MAX_CHNOUT - (index + 1)) * sizeof(LimitData)); + limitData[CPN_MAX_CHNOUT - 1].clear(); + updateAllReferences(REF_UPD_TYPE_CHANNEL, REF_UPD_ACT_SHIFT, index, 0, -1); +} + +void ModelData::limitsGet(const int index, QByteArray & data) +{ + if (index < 0 || index >= CPN_MAX_CHNOUT) + return; + + data.append((char*)&limitData[index], sizeof(LimitData)); +} + +void ModelData::limitsInsert(const int index) +{ + if (index < 0 || index >= CPN_MAX_CHNOUT) + return; + + memmove(&limitData[index + 1], &limitData[index], (CPN_MAX_CHNOUT - (index + 1)) * sizeof(LimitData)); + limitData[index].clear(); + updateAllReferences(REF_UPD_TYPE_CHANNEL, REF_UPD_ACT_SHIFT, index, 0, 1); +} + +void ModelData::limitsMove(const int index, const int offset) +{ + if (index + offset < 0 || index + offset >= CPN_MAX_CHNOUT) + return; + + int idx1 = index; + int idx2; + const int direction = offset < 0 ? -1 : 1; + const int cnt = abs(offset); + + for (int i = 1; i <= cnt; i++) { + idx2 = idx1 + direction; + LimitData chntmp = limitData[idx2]; + LimitData *chn1 = &limitData[idx1]; + LimitData *chn2 = &limitData[idx2]; + memcpy(chn2, chn1, sizeof(LimitData)); + memcpy(chn1, &chntmp, sizeof(LimitData)); + updateAllReferences(REF_UPD_TYPE_CHANNEL, REF_UPD_ACT_SWAP, idx1, idx2); + idx1 += direction; + } +} + +void ModelData::limitsSet(const int index, const QByteArray & data) +{ + if (index < 0 || index >= CPN_MAX_CHNOUT || data.size() < (int)sizeof(LimitData)) + return; + + memcpy(&limitData[index], data.constData(), sizeof(LimitData)); +} diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 80b7d7353..4794ff232 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -294,6 +294,14 @@ class ModelData { int thrTraceSrcCount() const; bool isThrTraceSrcAvailable(const GeneralSettings * generalSettings, const int index) const; + void limitsClear(const int index); + void limitsClearAll(); + void limitsDelete(const int index); + void limitsGet(const int index, QByteArray & data); + void limitsInsert(const int index); + void limitsMove(const int index, const int offset); + void limitsSet(const int index, const QByteArray & data); + protected: void removeGlobalVar(int & var); diff --git a/companion/src/firmwares/output_data.cpp b/companion/src/firmwares/output_data.cpp index 63127d242..d5cc585ee 100644 --- a/companion/src/firmwares/output_data.cpp +++ b/companion/src/firmwares/output_data.cpp @@ -31,7 +31,8 @@ void LimitData::clear() bool LimitData::isEmpty() const { - return (min == -1000 && max == 1000 && !revert && !offset && !ppmCenter && !symetrical && name[0] == '\0' && !curve.isSet()); + LimitData tmp; + return !memcmp(this, &tmp, sizeof(LimitData)); } QString LimitData::minToString() const diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index 5ec77ce55..806ea144e 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -312,10 +312,9 @@ void ChannelsPanel::cmPaste() { QByteArray data; if (hasClipboardData(&data)) { - memcpy(&model->limitData[selectedIndex], data.constData(), sizeof(LimitData)); + model->limitsSet(selectedIndex, data); updateLine(selectedIndex); updateItemModels(); - emit modified(); } } @@ -324,22 +323,18 @@ void ChannelsPanel::cmDelete() if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Channel. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; - memmove(&model->limitData[selectedIndex], &model->limitData[selectedIndex + 1], (CPN_MAX_CHNOUT - (selectedIndex + 1)) * sizeof(LimitData)); - model->limitData[chnCapability - 1].clear(); - model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1); - + model->limitsDelete(selectedIndex); for (int i = selectedIndex; i < chnCapability; i++) { updateLine(i); } updateItemModels(); - emit modified(); } void ChannelsPanel::cmCopy() { QByteArray data; - data.append((char*)&model->limitData[selectedIndex], sizeof(LimitData)); + model->limitsGet(selectedIndex, data); QMimeData *mimeData = new QMimeData; mimeData->setData(MIMETYPE_CHANNEL, data); QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard); @@ -405,12 +400,18 @@ bool ChannelsPanel::moveUpAllowed() const void ChannelsPanel::cmMoveUp() { - swapData(selectedIndex, selectedIndex - 1); + model->limitsMove(selectedIndex, -1); + updateLine(selectedIndex - 1); + updateLine(selectedIndex); + updateItemModels(); } void ChannelsPanel::cmMoveDown() { - swapData(selectedIndex, selectedIndex + 1); + model->limitsMove(selectedIndex, 1); + updateLine(selectedIndex); + updateLine(selectedIndex + 1); + updateItemModels(); } void ChannelsPanel::cmClear(bool prompt) @@ -420,11 +421,9 @@ void ChannelsPanel::cmClear(bool prompt) return; } - model->limitData[selectedIndex].clear(); - model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); + model->limitsClear(selectedIndex); updateLine(selectedIndex); updateItemModels(); - emit modified(); } void ChannelsPanel::cmClearAll() @@ -432,39 +431,19 @@ void ChannelsPanel::cmClearAll() if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Channels. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; - for (int i = 0; i < chnCapability; i++) { - model->limitData[i].clear(); - model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_CLEAR, i); - updateLine(i); - } + model->limitsClearAll(); + update(); updateItemModels(); - emit modified(); } void ChannelsPanel::cmInsert() { - memmove(&model->limitData[selectedIndex + 1], &model->limitData[selectedIndex], (CPN_MAX_CHNOUT - (selectedIndex + 1)) * sizeof(LimitData)); - model->limitData[selectedIndex].clear(); - model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1); - update(); - updateItemModels(); - emit modified(); -} - -void ChannelsPanel::swapData(int idx1, int idx2) -{ - if ((idx1 != idx2) && (!model->limitData[idx1].isEmpty() || !model->limitData[idx2].isEmpty())) { - LimitData chntmp = model->limitData[idx2]; - LimitData *chn1 = &model->limitData[idx1]; - LimitData *chn2 = &model->limitData[idx2]; - memcpy(chn2, chn1, sizeof(LimitData)); - memcpy(chn1, &chntmp, sizeof(LimitData)); - model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_SWAP, idx1, idx2); - updateLine(idx1); - updateLine(idx2); - updateItemModels(); - emit modified(); + model->limitsInsert(selectedIndex); + for (int i = selectedIndex; i < chnCapability; i++) { + updateLine(i); } + + updateItemModels(); } void ChannelsPanel::connectItemModelEvents(const FilteredItemModel * itemModel) @@ -487,4 +466,5 @@ void ChannelsPanel::onItemModelUpdateComplete() void ChannelsPanel::updateItemModels() { sharedItemModels->update(AbstractItemModel::IMUE_Channels); + emit modified(); } diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index 574e316d8..5d7600718 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -91,7 +91,6 @@ class ChannelsPanel : public ModelPanel bool insertAllowed() const; bool moveDownAllowed() const; bool moveUpAllowed() const; - void swapData(int idx1, int idx2); QLineEdit *name[CPN_MAX_CHNOUT]; LimitsGroup *chnOffset[CPN_MAX_CHNOUT]; LimitsGroup *chnMin[CPN_MAX_CHNOUT]; From 9372ff67c375acd92c164352d49178a78b383190 Mon Sep 17 00:00:00 2001 From: elecpower Date: Sun, 28 Mar 2021 08:40:49 +1100 Subject: [PATCH 272/300] Cosmetics --- companion/src/firmwares/modeldata.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 52244b342..1e5d4d59c 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1537,11 +1537,11 @@ void ModelData::limitsMove(const int index, const int offset) for (int i = 1; i <= cnt; i++) { idx2 = idx1 + direction; - LimitData chntmp = limitData[idx2]; - LimitData *chn1 = &limitData[idx1]; - LimitData *chn2 = &limitData[idx2]; - memcpy(chn2, chn1, sizeof(LimitData)); - memcpy(chn1, &chntmp, sizeof(LimitData)); + LimitData tmp = limitData[idx2]; + LimitData *d1 = &limitData[idx1]; + LimitData *d2 = &limitData[idx2]; + memcpy(d2, d1, sizeof(LimitData)); + memcpy(d1, &tmp, sizeof(LimitData)); updateAllReferences(REF_UPD_TYPE_CHANNEL, REF_UPD_ACT_SWAP, idx1, idx2); idx1 += direction; } From dbd432b3abdb7d3667b39b8b2f472c0062916663 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 28 Mar 2021 20:17:37 +0200 Subject: [PATCH 273/300] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index b535b828f..d04377815 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2207,3 +2207,6 @@ Bob Brown Claude Lawyer Peter Fithern Michael Hoffman +Tim Smith +OpenSki Institute +Simon Melov From 2cf7e3de89af17152b50ab25df48643108e736eb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 29 Mar 2021 16:29:04 +0200 Subject: [PATCH 274/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d04377815..ecf77e2ae 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2210,3 +2210,4 @@ Michael Hoffman Tim Smith OpenSki Institute Simon Melov +Richard Podolsky From eef1d5cc9ab24dc44123cf8b7e727558ec0da2a6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 30 Mar 2021 17:47:15 +0200 Subject: [PATCH 275/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ecf77e2ae..8057e9e66 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2211,3 +2211,4 @@ Tim Smith OpenSki Institute Simon Melov Richard Podolsky +James Wilson From 9a222e7ad00831f24bbb99b89d50a0816e55f1fe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 30 Mar 2021 22:04:16 +0200 Subject: [PATCH 276/300] Update CREDITS.txt --- CREDITS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 8057e9e66..ced0fb4cf 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2212,3 +2212,5 @@ OpenSki Institute Simon Melov Richard Podolsky James Wilson +John Norrbin +James Goins From ece17bf5de628f4e2457d3e88fb59cceec3150e0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 31 Mar 2021 10:08:52 +0200 Subject: [PATCH 277/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index ced0fb4cf..9a1f15566 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2214,3 +2214,4 @@ Richard Podolsky James Wilson John Norrbin James Goins +Phillip Nguyen From 874a06eeb6f12ed6215497b09ef6ace1752127ac Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 1 Apr 2021 08:25:02 +0200 Subject: [PATCH 278/300] Add space to SD edit name --- radio/src/gui/common/stdlcd/draw_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/common/stdlcd/draw_functions.cpp b/radio/src/gui/common/stdlcd/draw_functions.cpp index 056a66b08..c7d9142ed 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.cpp +++ b/radio/src/gui/common/stdlcd/draw_functions.cpp @@ -145,7 +145,7 @@ void editName(coord_t x, coord_t y, char * name, uint8_t size, event_t event, ui if (c <= 0) v = -v; } else { - v = checkIncDec(event, abs(v), '0', 'z', 0); + v = checkIncDec(event, abs(v), ' ', 'z', 0); } } From b597634b7e0a4fceb3a9d119810e448b5cd84cb1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 1 Apr 2021 08:29:42 +0200 Subject: [PATCH 279/300] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 9a1f15566..57c215c4c 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2215,3 +2215,6 @@ James Wilson John Norrbin James Goins Phillip Nguyen +Jeffrey M Engel +Richard Marotto +Greg Bell From 49c7d5312a12ef44d9e8643bbd396f7a8f3936f5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 1 Apr 2021 09:20:40 +0200 Subject: [PATCH 280/300] TODO removed --- radio/src/targets/horus/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/horus/board.h b/radio/src/targets/horus/board.h index 8550d1517..1f81076e1 100644 --- a/radio/src/targets/horus/board.h +++ b/radio/src/targets/horus/board.h @@ -381,7 +381,7 @@ enum Analogs { SLIDER_LAST = SLIDER_FIRST + NUM_SLIDERS - 1, TX_VOLTAGE, #if defined(PCBX12S) - MOUSE1, // TODO why after voltage? + MOUSE1, // after voltage because previous ones come from SPI on X12S MOUSE2, #endif NUM_ANALOGS From a167ded9b534f373904e4424e053187b90237b8a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 3 Apr 2021 10:27:22 +0200 Subject: [PATCH 281/300] Update CREDITS.txt --- CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CREDITS.txt b/CREDITS.txt index 57c215c4c..f4eb7a375 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2218,3 +2218,6 @@ Phillip Nguyen Jeffrey M Engel Richard Marotto Greg Bell +Scott Geer +Thomas Abshier + From 78dd516069c7055bc27e5013b1565d985baea69b Mon Sep 17 00:00:00 2001 From: 3djc Date: Sun, 4 Apr 2021 19:00:01 +0200 Subject: [PATCH 282/300] Allow PCBSKY9X compilation --- radio/src/opentx.h | 1 + radio/src/pulses/pulses.cpp | 43 ++++++++++++++++++++++++++++++++++- radio/src/tasks.cpp | 28 +++++++++++++++++++++++ radio/src/telemetry/multi.cpp | 3 ++- 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 3d3cc3ae7..ddfcb9830 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -502,6 +502,7 @@ void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms); void evalMixes(uint8_t tick10ms); void doMixerCalculations(); void doMixerPeriodicUpdates(); +void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms); void checkTrims(); extern uint8_t currentBacklightBright; diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 7292bb200..810fd9378 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -241,8 +241,12 @@ void enablePulsesExternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_DSM2_LP45: case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSMX: +#if defined(PCBSKY9X) + extmoduleSerialStart(DSM2_BAUDRATE, DSM2_PERIOD * 2000, false); +#else extmoduleSerialStart(); mixerSchedulerSetPeriod(EXTERNAL_MODULE, DSM2_PERIOD); +#endif break; #endif @@ -274,15 +278,23 @@ void enablePulsesExternalModule(uint8_t protocol) #if defined(MULTIMODULE) case PROTOCOL_CHANNELS_MULTIMODULE: +#if defined(PCBSKY9X) + extmoduleSerialStart(MULTIMODULE_BAUDRATE, MULTIMODULE_PERIOD * 2000, true); +#else extmoduleSerialStart(); mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD); +#endif break; #endif #if defined(SBUS) case PROTOCOL_CHANNELS_SBUS: +#if defined(PCBSKY9X) + extmoduleSerialStart(SBUS_BAUDRATE, SBUS_PERIOD_HALF_US, false); +#else extmoduleSerialStart(); mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD); +#endif break; #endif @@ -314,12 +326,18 @@ bool setupPulsesExternalModule(uint8_t protocol) #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: extmodulePulsesData.pxx.setupFrame(EXTERNAL_MODULE); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, PXX_PULSES_PERIOD); +#endif return true; #endif #if defined(PXX1) && defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML) case PROTOCOL_CHANNELS_PXX1_SERIAL: extmodulePulsesData.pxx_uart.setupFrame(EXTERNAL_MODULE); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, EXTMODULE_PXX1_SERIAL_PERIOD); +#endif return true; #endif @@ -327,13 +345,18 @@ bool setupPulsesExternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: case PROTOCOL_CHANNELS_PXX2_LOWSPEED: extmodulePulsesData.pxx2.setupFrame(EXTERNAL_MODULE); +#if defined(PCBSKY9X) + sheduleNextMixerCalculation(EXTERNAL_MODULE, PXX2_PERIOD); +#endif return true; #endif #if defined(SBUS) case PROTOCOL_CHANNELS_SBUS: setupPulsesSbus(); - +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, SBUS_PERIOD); +#endif // SBUS_PERIOD is not a constant! It can be set from UI mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD); return true; @@ -344,6 +367,9 @@ bool setupPulsesExternalModule(uint8_t protocol) case PROTOCOL_CHANNELS_DSM2_DSM2: case PROTOCOL_CHANNELS_DSM2_DSMX: setupPulsesDSM2(); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, DSM2_PERIOD); +#endif return true; #endif @@ -356,6 +382,9 @@ bool setupPulsesExternalModule(uint8_t protocol) else mixerSchedulerSetPeriod(EXTERNAL_MODULE, CROSSFIRE_PERIOD); setupPulsesCrossfire(); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, CROSSFIRE_PERIOD); +#endif return true; } #endif @@ -369,6 +398,9 @@ bool setupPulsesExternalModule(uint8_t protocol) else mixerSchedulerSetPeriod(EXTERNAL_MODULE, GHOST_PERIOD); setupPulsesGhost(); +#if defined(PCBSKPCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, GHOST_PERIOD); +#endif return true; } #endif @@ -382,6 +414,9 @@ bool setupPulsesExternalModule(uint8_t protocol) else mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD); setupPulsesMultiExternalModule(); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, MULTIMODULE_PERIOD); +#endif return true; } #endif @@ -389,12 +424,18 @@ bool setupPulsesExternalModule(uint8_t protocol) #if defined(PPM) case PROTOCOL_CHANNELS_PPM: setupPulsesPPMExternalModule(); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, PPM_PERIOD(EXTERNAL_MODULE)); +#endif return true; #endif #if defined(AFHDS3) case PROTOCOL_CHANNELS_AFHDS3: extmodulePulsesData.afhds3.setupFrame(); +#if defined(PCBSKY9X) + scheduleNextMixerCalculation(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT); +#endif return true; #endif diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index d0aa8e3d2..97f78c972 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -118,7 +118,9 @@ TASK_FUNCTION(mixerTask) s_pulses_paused = true; mixerSchedulerInit(); +#if !defined(PCBSKY9X) mixerSchedulerStart(); +#endif while (true) { #if defined(SBUS_TRAINER) @@ -142,8 +144,10 @@ TASK_FUNCTION(mixerTask) GPIO_ResetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); #endif +#if !defined(PCBSKY9X) // re-enable trigger mixerSchedulerEnableTrigger(); +#endif #if defined(SIMU) if (pwrCheck() == e_power_off) { @@ -164,7 +168,11 @@ TASK_FUNCTION(mixerTask) doMixerCalculations(); // TODO: fix runMask +#if defined(PCBSKY9X) + sendSynchronousPulses(1 << EXTERNAL_MODULE); +#else sendSynchronousPulses((1 << INTERNAL_MODULE) | (1 << EXTERNAL_MODULE)); +#endif doMixerPeriodicUpdates(); @@ -200,12 +208,32 @@ TASK_FUNCTION(mixerTask) // TODO: // - check the cause of timeouts when switching // between protocols with multi-proto RF +#if defined(DEBUG) if (timeout) serialPrint("mix sched timeout!"); +#endif } } } +void scheduleNextMixerCalculation(uint8_t module, uint32_t period_ms) +{ + // Schedule next mixer calculation time, + + if (isModuleSynchronous(module)) { + nextMixerTime[module] += period_ms / RTOS_MS_PER_TICK; + if (nextMixerTime[module] < RTOS_GET_TIME()) { + // we are late ... let's add some small delay + nextMixerTime[module] = (uint32_t) RTOS_GET_TIME() + (period_ms / RTOS_MS_PER_TICK); + } + } + else { + // for now assume mixer calculation takes 2 ms. + nextMixerTime[module] = (uint32_t) RTOS_GET_TIME() + (period_ms / RTOS_MS_PER_TICK); + } + + DEBUG_TIMER_STOP(debugTimerMixerCalcToUsage); +} #define MENU_TASK_PERIOD_TICKS (50 / RTOS_MS_PER_TICK) // 50ms diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 1cb560b14..624effbba 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -252,8 +252,9 @@ static void processMultiSyncPacket(const uint8_t * data, uint8_t module) // inputLag -= refreshRate; status.update(refreshRate, inputLag); - +#if defined(DEBUG) serialPrint("MP ADJ: R %d, L %04d", refreshRate, inputLag); +#endif } #if defined(PCBTARANIS) || defined(PCBHORUS) From d30bcb0232573d0b56363948a5c683709acf0495 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 5 Apr 2021 10:13:59 +0200 Subject: [PATCH 283/300] Update CREDITS.txt --- CREDITS.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CREDITS.txt b/CREDITS.txt index f4eb7a375..d05842991 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2220,4 +2220,7 @@ Richard Marotto Greg Bell Scott Geer Thomas Abshier - +Ernst Camenzind +Radomir Sterba +Ignacio Barrio +Bradley Murchie From 84387e7e0f99169fdf88247b4f819954505d29e6 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 11:16:17 +0200 Subject: [PATCH 284/300] Cosmetics --- radio/src/gui/gui_common.h | 1 - .../taranis/intmodule_pulses_driver.cpp | 8 --- radio/src/tasks.cpp | 4 -- radio/src/telemetry/crossfire.cpp | 1 - radio/src/telemetry/crossfire.h | 2 +- radio/src/telemetry/multi.cpp | 3 -- radio/src/telemetry/telemetry.cpp | 4 -- tools/commit-tests.sh | 52 +++++++++---------- 8 files changed, 27 insertions(+), 48 deletions(-) diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index b7d73d075..80b675b42 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -185,7 +185,6 @@ inline uint8_t MODULE_CHANNELS_ROWS(int moduleIdx) } #if defined(MULTIMODULE) - inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) { if (!isModuleMultimodule(moduleIdx)) diff --git a/radio/src/targets/taranis/intmodule_pulses_driver.cpp b/radio/src/targets/taranis/intmodule_pulses_driver.cpp index 588e1a448..5de2daa1b 100644 --- a/radio/src/targets/taranis/intmodule_pulses_driver.cpp +++ b/radio/src/targets/taranis/intmodule_pulses_driver.cpp @@ -42,14 +42,6 @@ void intmoduleSendNextFrame() #if defined(PXX1) case PROTOCOL_CHANNELS_PXX1_PULSES: { - // uint32_t last = intmodulePulsesData.pxx.getLast(); - // if (heartbeatCapture.valid) { - // if (getTmr2MHz() - heartbeatCapture.timestamp > HEARBEAT_OFFSET) - // last -= 21; - // else - // last += 19; - // intmodulePulsesData.pxx.setLast(last); - // } if (INTMODULE_DMA_STREAM->CR & DMA_SxCR_EN) return; diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 97f78c972..e71d8829a 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -67,7 +67,6 @@ bool isForcePowerOffRequested() bool isModuleSynchronous(uint8_t moduleIdx) { switch(moduleState[moduleIdx].protocol) { - case PROTOCOL_CHANNELS_PXX2_HIGHSPEED: case PROTOCOL_CHANNELS_PXX2_LOWSPEED: case PROTOCOL_CHANNELS_CROSSFIRE: @@ -109,8 +108,6 @@ void sendSynchronousPulses(uint8_t runMask) } } -//#define DEBUG_MIXER_SCHEDULER - uint32_t nextMixerTime[NUM_MODULES]; TASK_FUNCTION(mixerTask) @@ -181,7 +178,6 @@ TASK_FUNCTION(mixerTask) RTOS_UNLOCK_MUTEX(mixerMutex); DEBUG_TIMER_STOP(debugTimerMixer); - #if defined(STM32) && !defined(SIMU) if (getSelectedUsbMode() == USB_JOYSTICK_MODE) { usbJoystickUpdate(); diff --git a/radio/src/telemetry/crossfire.cpp b/radio/src/telemetry/crossfire.cpp index 173ea38f4..64dba983d 100644 --- a/radio/src/telemetry/crossfire.cpp +++ b/radio/src/telemetry/crossfire.cpp @@ -203,7 +203,6 @@ void processCrossfireTelemetryFrame() } break; - #if defined(LUA) default: if (luaInputTelemetryFifo && luaInputTelemetryFifo->hasSpace(telemetryRxBufferCount-2) ) { diff --git a/radio/src/telemetry/crossfire.h b/radio/src/telemetry/crossfire.h index fb68b8a1a..23d39947e 100644 --- a/radio/src/telemetry/crossfire.h +++ b/radio/src/telemetry/crossfire.h @@ -104,7 +104,7 @@ const uint8_t CROSSFIRE_PERIODS[] = { }; #if SPORT_MAX_BAUDRATE < 400000 || defined(DEBUG) #define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate] -#define CROSSFIRE_PERIOD (CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate]*1000) +#define CROSSFIRE_PERIOD (CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate] * 1000) #else #define CROSSFIRE_BAUDRATE 400000 #define CROSSFIRE_PERIOD 4000 /* us; 250 Hz */ diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 624effbba..d71cdad34 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -248,9 +248,6 @@ static void processMultiSyncPacket(const uint8_t * data, uint8_t module) uint16_t refreshRate = data[0] << 8 | data[1]; int16_t inputLag = data[2] << 8 | data[3]; - // if (inputLag > refreshRate/2) - // inputLag -= refreshRate; - status.update(refreshRate, inputLag); #if defined(DEBUG) serialPrint("MP ADJ: R %d, L %04d", refreshRate, inputLag); diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index 17777dc6a..700bf535a 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -378,23 +378,19 @@ Fifo * luaInputTelemetryFifo = NULL; #endif #if defined(HARDWARE_INTERNAL_MODULE) - static ModuleSyncStatus moduleSyncStatus[NUM_MODULES]; ModuleSyncStatus &getModuleSyncStatus(uint8_t moduleIdx) { return moduleSyncStatus[moduleIdx]; } - #else - static ModuleSyncStatus moduleSyncStatus; ModuleSyncStatus &getModuleSyncStatus(uint8_t moduleIdx) { return moduleSyncStatus; } - #endif ModuleSyncStatus::ModuleSyncStatus() diff --git a/tools/commit-tests.sh b/tools/commit-tests.sh index 9ecd88769..be5940308 100755 --- a/tools/commit-tests.sh +++ b/tools/commit-tests.sh @@ -58,32 +58,32 @@ COMMON_OPTIONS+=${EXTRA_OPTIONS} mkdir build || true cd build -#if [[ " SKY9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then -# # OpenTX on Sky9x -# rm -rf ./* -# cmake "${COMMON_OPTIONS}" -DPCB=SKY9X -DHELI=YES DLUA=YES -DTELEMETRY=FRSKY -DPPM_LIMITS_SYMETRICAL=YES -DVARIO=YES -DAUTOSWITCH=YES -DAUTOSOURCE=YES -DAUDIO=YES -DGPS=YES -DPPM_CENTER_ADJUSTABLE=YES -DFLIGHT_MODES=YES -DOVERRIDE_CHANNEL_FUNCTION=YES -DFRSKY_STICKS=YES -DGVARS=YES "${SRCDIR}" -# make -j"${CORES}" ${FIRMARE_TARGET} -# make -j"${CORES}" libsimulator -# make -j"${CORES}" tests-radio -#fi -# -#if [[ " AR9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then -# # OpenTX on AR9X -# rm -rf ./* -# cmake "${COMMON_OPTIONS}" -DPCB=AR9X -DHELI=YES -DGVARS=YES "${SRCDIR}" -# make -j"${CORES}" ${FIRMARE_TARGET} -# make -j"${CORES}" libsimulator -# make -j"${CORES}" tests-radio -#fi -# -#if [[ " 9XRPRO ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then -# # OpenTX on Sky9x -# rm -rf ./* -# cmake "${COMMON_OPTIONS}" -DPCB=9XRPRO -DHELI=YES -DGVARS=YES "${SRCDIR}" -# make -j"${CORES}" ${FIRMARE_TARGET} -# make -j"${CORES}" libsimulator -# make -j"${CORES}" tests-radio -#fi +if [[ " SKY9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on Sky9x + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=SKY9X -DHELI=YES DLUA=YES -DTELEMETRY=FRSKY -DPPM_LIMITS_SYMETRICAL=YES -DVARIO=YES -DAUTOSWITCH=YES -DAUTOSOURCE=YES -DAUDIO=YES -DGPS=YES -DPPM_CENTER_ADJUSTABLE=YES -DFLIGHT_MODES=YES -DOVERRIDE_CHANNEL_FUNCTION=YES -DFRSKY_STICKS=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi + +if [[ " AR9X ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on AR9X + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=AR9X -DHELI=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi + +if [[ " 9XRPRO ARM9X ALL " =~ \ ${FLAVOR}\ ]] ; then + # OpenTX on Sky9x + rm -rf ./* + cmake "${COMMON_OPTIONS}" -DPCB=9XRPRO -DHELI=YES -DGVARS=YES "${SRCDIR}" + make -j"${CORES}" ${FIRMARE_TARGET} + make -j"${CORES}" libsimulator + make -j"${CORES}" tests-radio +fi if [[ " X9LITE ALL " =~ \ ${FLAVOR}\ ]] ; then # OpenTX on X9LITE From 84f769dbe711f771d60408c5f303608c25a5e3f8 Mon Sep 17 00:00:00 2001 From: Risto Date: Mon, 5 Apr 2021 13:06:24 +0200 Subject: [PATCH 285/300] Fix building with TRACE_SD_CARD CMake option (#8391) --- radio/src/targets/common/arm/stm32/sdio_sd.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/radio/src/targets/common/arm/stm32/sdio_sd.c b/radio/src/targets/common/arm/stm32/sdio_sd.c index f02916b44..83980ab64 100644 --- a/radio/src/targets/common/arm/stm32/sdio_sd.c +++ b/radio/src/targets/common/arm/stm32/sdio_sd.c @@ -1107,7 +1107,9 @@ OPTIMIZE("O0") SD_Error SD_WaitReadOperation(uint32_t timeout) delay_ms(1); timeout--; } +#ifndef BOOT TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_read, (TransferError << 8) + (DMAEndOfTransfer << 1) + TransferEnd); +#endif DMAEndOfTransfer = 0; @@ -1117,7 +1119,9 @@ OPTIMIZE("O0") SD_Error SD_WaitReadOperation(uint32_t timeout) delay_ms(1); timeout--; } +#ifndef BOOT TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_read, -1); +#endif if (StopCondition == 1) { errorstatus = SD_StopTransfer(); @@ -1332,8 +1336,10 @@ OPTIMIZE("O0") SD_Error SD_WaitWriteOperation(uint32_t timeout) delay_ms(1); timeout--; } +#ifndef BOOT TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_write, (TransferError << 8) + (DMAEndOfTransfer << 1) + TransferEnd); - +#endif + DMAEndOfTransfer = 0; timeout = 100; @@ -1342,7 +1348,9 @@ OPTIMIZE("O0") SD_Error SD_WaitWriteOperation(uint32_t timeout) delay_ms(1); timeout--; } +#ifndef BOOT TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_write, -1); +#endif if (StopCondition == 1) { errorstatus = SD_StopTransfer(); @@ -1474,7 +1482,9 @@ OPTIMIZE("O0") void SD_ProcessIRQ(void) SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE); +#ifndef BOOT TRACE_SD_CARD_EVENT((TransferError != SD_OK), sd_irq, TransferError); +#endif } /** From 6565cbd1df5eeb8169fd23dfa6768d998a46b640 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 13:09:21 +0200 Subject: [PATCH 286/300] Cosmetics --- radio/src/targets/common/arm/stm32/sdio_sd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/targets/common/arm/stm32/sdio_sd.c b/radio/src/targets/common/arm/stm32/sdio_sd.c index 83980ab64..f0f97b97f 100644 --- a/radio/src/targets/common/arm/stm32/sdio_sd.c +++ b/radio/src/targets/common/arm/stm32/sdio_sd.c @@ -1107,7 +1107,7 @@ OPTIMIZE("O0") SD_Error SD_WaitReadOperation(uint32_t timeout) delay_ms(1); timeout--; } -#ifndef BOOT +#if !defined(BOOT) TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_read, (TransferError << 8) + (DMAEndOfTransfer << 1) + TransferEnd); #endif @@ -1119,7 +1119,7 @@ OPTIMIZE("O0") SD_Error SD_WaitReadOperation(uint32_t timeout) delay_ms(1); timeout--; } -#ifndef BOOT +#if !defined(BOOT) TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_read, -1); #endif @@ -1336,7 +1336,7 @@ OPTIMIZE("O0") SD_Error SD_WaitWriteOperation(uint32_t timeout) delay_ms(1); timeout--; } -#ifndef BOOT +#if !defined(BOOT) TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_write, (TransferError << 8) + (DMAEndOfTransfer << 1) + TransferEnd); #endif @@ -1348,7 +1348,7 @@ OPTIMIZE("O0") SD_Error SD_WaitWriteOperation(uint32_t timeout) delay_ms(1); timeout--; } -#ifndef BOOT +#if !defined(BOOT) TRACE_SD_CARD_EVENT((timeout == 0), sd_wait_write, -1); #endif @@ -1482,7 +1482,7 @@ OPTIMIZE("O0") void SD_ProcessIRQ(void) SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE); -#ifndef BOOT +#if !defined(BOOT) TRACE_SD_CARD_EVENT((TransferError != SD_OK), sd_irq, TransferError); #endif } From cff8109d5a15f10ea7b2530d0eb3964db6b6109c Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 17:38:10 +0200 Subject: [PATCH 287/300] Cosmetics --- radio/src/mixer_scheduler.cpp | 6 +++--- radio/src/options.h | 1 - radio/src/pulses/pulses.cpp | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/mixer_scheduler.cpp b/radio/src/mixer_scheduler.cpp index 8132fe2f0..dcf95383b 100644 --- a/radio/src/mixer_scheduler.cpp +++ b/radio/src/mixer_scheduler.cpp @@ -42,7 +42,7 @@ uint16_t getMixerSchedulerPeriod() return mixerSchedules[INTERNAL_MODULE].period; } #endif -#if 1 //defined(HARDWARE_EXTERNAL_MODULE) +#if defined(HARDWARE_EXTERNAL_MODULE) if (mixerSchedules[EXTERNAL_MODULE].period) { return mixerSchedules[EXTERNAL_MODULE].period; } @@ -58,10 +58,10 @@ void mixerSchedulerInit() void mixerSchedulerSetPeriod(uint8_t moduleIdx, uint16_t periodUs) { - if ((periodUs > 0) && (periodUs < MIN_REFRESH_RATE)) { + if (periodUs > 0 && periodUs < MIN_REFRESH_RATE) { periodUs = MIN_REFRESH_RATE; } - else if ((periodUs > 0) && (periodUs > MAX_REFRESH_RATE)) { + else if (periodUs > 0 && periodUs > MAX_REFRESH_RATE) { periodUs = MAX_REFRESH_RATE; } diff --git a/radio/src/options.h b/radio/src/options.h index 8592897af..4f1a1a077 100644 --- a/radio/src/options.h +++ b/radio/src/options.h @@ -23,7 +23,6 @@ #define OPENTX_OPTIONS_H static const char * const options[] = { - "synced", #if defined(AUTOUPDATE) "autoupdate", #endif diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 810fd9378..128e11976 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -356,11 +356,12 @@ bool setupPulsesExternalModule(uint8_t protocol) setupPulsesSbus(); #if defined(PCBSKY9X) scheduleNextMixerCalculation(EXTERNAL_MODULE, SBUS_PERIOD); -#endif +#else // SBUS_PERIOD is not a constant! It can be set from UI mixerSchedulerSetPeriod(EXTERNAL_MODULE, SBUS_PERIOD); return true; #endif +#endif #if defined(DSM2) case PROTOCOL_CHANNELS_DSM2_LP45: From 92fb19d89c2775a464bf0f0a71c0fbfc519796a2 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 17:40:55 +0200 Subject: [PATCH 288/300] Cosmetics --- radio/src/pulses/pxx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/pulses/pxx.h b/radio/src/pulses/pxx.h index b9536d723..e852de180 100644 --- a/radio/src/pulses/pxx.h +++ b/radio/src/pulses/pxx.h @@ -108,7 +108,7 @@ class PwmPxxBitTransport: public PulsesBuffer { void addTail() { // rest min value is 18000 - 200 * 48 = 8400 (4.2ms) - *(ptr - 1) = 60000;//+= rest; + *(ptr - 1) = 60000; } }; From 9ba3914ec464b600ef34381e3a950e99d53e9c25 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 17:51:13 +0200 Subject: [PATCH 289/300] Cosmetics --- radio/src/targets/taranis/extmodule_driver.cpp | 8 ++++---- radio/src/tasks.cpp | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/radio/src/targets/taranis/extmodule_driver.cpp b/radio/src/targets/taranis/extmodule_driver.cpp index b856cb309..512f16c5d 100644 --- a/radio/src/targets/taranis/extmodule_driver.cpp +++ b/radio/src/targets/taranis/extmodule_driver.cpp @@ -406,10 +406,10 @@ extern "C" void EXTMODULE_TIMER_DMA_STREAM_IRQHandler() DMA_ClearITPendingBit(EXTMODULE_TIMER_DMA_STREAM, EXTMODULE_TIMER_DMA_FLAG_TC); switch (moduleState[EXTERNAL_MODULE].protocol) { - case PROTOCOL_CHANNELS_PPM: - EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt - break; + case PROTOCOL_CHANNELS_PPM: + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + break; } } diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index e71d8829a..a051cc509 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -164,7 +164,6 @@ TASK_FUNCTION(mixerTask) doMixerCalculations(); - // TODO: fix runMask #if defined(PCBSKY9X) sendSynchronousPulses(1 << EXTERNAL_MODULE); #else From 39ceda51ef76e724d5c8a3d47086d988a30601e1 Mon Sep 17 00:00:00 2001 From: Ruddy Date: Mon, 5 Apr 2021 12:02:24 -0400 Subject: [PATCH 290/300] Resolve two sets of repeated warnings when built with VS2019 (#8358) --- radio/src/gui/480x272/layouts/layout4+2.cpp | 10 +++++----- radio/src/opentx.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/gui/480x272/layouts/layout4+2.cpp b/radio/src/gui/480x272/layouts/layout4+2.cpp index c9846ec04..d41a5f902 100644 --- a/radio/src/gui/480x272/layouts/layout4+2.cpp +++ b/radio/src/gui/480x272/layouts/layout4+2.cpp @@ -20,11 +20,11 @@ #include "opentx.h" -#define HAS_TOPBAR() (persistentData->options[0].boolValue == true) -#define HAS_FM() (persistentData->options[1].boolValue == true) -#define HAS_SLIDERS() (persistentData->options[2].boolValue == true) -#define HAS_TRIMS() (persistentData->options[3].boolValue == true) -#define IS_MIRRORED() (persistentData->options[4].boolValue == true) +#define HAS_TOPBAR() (OPTION_VALUE_BOOL(persistentData->options[0].boolValue) == true) +#define HAS_FM() (OPTION_VALUE_BOOL(persistentData->options[1].boolValue) == true) +#define HAS_SLIDERS() (OPTION_VALUE_BOOL(persistentData->options[2].boolValue) == true) +#define HAS_TRIMS() (OPTION_VALUE_BOOL(persistentData->options[3].boolValue) == true) +#define IS_MIRRORED() (OPTION_VALUE_BOOL(persistentData->options[4].boolValue) == true) const uint8_t LBM_LAYOUT_4P2[] = { #include "mask_layout4+2.lbm" diff --git a/radio/src/opentx.h b/radio/src/opentx.h index ddfcb9830..2a6409376 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -320,7 +320,7 @@ void memswap(void * a, void * b, uint8_t size); #define MASK_CFN_TYPE uint64_t // current max = 64 function switches #define MASK_FUNC_TYPE uint32_t // current max = 32 functions -typedef struct { +typedef struct _CustomFunctionsContext { MASK_FUNC_TYPE activeFunctions; MASK_CFN_TYPE activeSwitches; tmr10ms_t lastFunctionTime[MAX_SPECIAL_FUNCTIONS]; From 2dbdb325755c6ed59a1cc444eb1fbbd639a9661e Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 18:01:41 +0200 Subject: [PATCH 291/300] Cosmetics --- radio/src/targets/taranis/intmodule_pulses_driver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/targets/taranis/intmodule_pulses_driver.cpp b/radio/src/targets/taranis/intmodule_pulses_driver.cpp index 5de2daa1b..9afacfda8 100644 --- a/radio/src/targets/taranis/intmodule_pulses_driver.cpp +++ b/radio/src/targets/taranis/intmodule_pulses_driver.cpp @@ -159,10 +159,10 @@ extern "C" void INTMODULE_DMA_STREAM_IRQHandler() DMA_ClearITPendingBit(INTMODULE_DMA_STREAM, INTMODULE_DMA_FLAG_TC); switch (moduleState[INTERNAL_MODULE].protocol) { - case PROTOCOL_CHANNELS_PPM: - INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag - INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt - break; + case PROTOCOL_CHANNELS_PPM: + INTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + INTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + break; } } From 62c74049d018d8a96d87578e4b4073649973e2f9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 18:03:24 +0200 Subject: [PATCH 292/300] Cosmetics --- radio/src/opentx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 2a6409376..e0e666b3c 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -320,7 +320,7 @@ void memswap(void * a, void * b, uint8_t size); #define MASK_CFN_TYPE uint64_t // current max = 64 function switches #define MASK_FUNC_TYPE uint32_t // current max = 32 functions -typedef struct _CustomFunctionsContext { +struct CustomFunctionContext { MASK_FUNC_TYPE activeFunctions; MASK_CFN_TYPE activeSwitches; tmr10ms_t lastFunctionTime[MAX_SPECIAL_FUNCTIONS]; From e0f65a0f0556bd9cdbd549fb3fcc80045adc93d6 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 18:56:00 +0200 Subject: [PATCH 293/300] Compile fix --- radio/src/opentx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index e0e666b3c..3d5a4868e 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -320,7 +320,7 @@ void memswap(void * a, void * b, uint8_t size); #define MASK_CFN_TYPE uint64_t // current max = 64 function switches #define MASK_FUNC_TYPE uint32_t // current max = 32 functions -struct CustomFunctionContext { +struct CustomFunctionsContext { MASK_FUNC_TYPE activeFunctions; MASK_CFN_TYPE activeSwitches; tmr10ms_t lastFunctionTime[MAX_SPECIAL_FUNCTIONS]; @@ -334,7 +334,7 @@ struct CustomFunctionContext { { memclear(this, sizeof(*this)); } -} CustomFunctionsContext; +}; #include "strhelpers.h" #include "gui.h" From 2c608ad719c165b9003b3626a56d1b51ab6aa218 Mon Sep 17 00:00:00 2001 From: 3djc Date: Mon, 5 Apr 2021 19:13:11 +0200 Subject: [PATCH 294/300] Compile fix --- radio/src/gui/480x272/layouts/layout4+2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/480x272/layouts/layout4+2.cpp b/radio/src/gui/480x272/layouts/layout4+2.cpp index d41a5f902..c9846ec04 100644 --- a/radio/src/gui/480x272/layouts/layout4+2.cpp +++ b/radio/src/gui/480x272/layouts/layout4+2.cpp @@ -20,11 +20,11 @@ #include "opentx.h" -#define HAS_TOPBAR() (OPTION_VALUE_BOOL(persistentData->options[0].boolValue) == true) -#define HAS_FM() (OPTION_VALUE_BOOL(persistentData->options[1].boolValue) == true) -#define HAS_SLIDERS() (OPTION_VALUE_BOOL(persistentData->options[2].boolValue) == true) -#define HAS_TRIMS() (OPTION_VALUE_BOOL(persistentData->options[3].boolValue) == true) -#define IS_MIRRORED() (OPTION_VALUE_BOOL(persistentData->options[4].boolValue) == true) +#define HAS_TOPBAR() (persistentData->options[0].boolValue == true) +#define HAS_FM() (persistentData->options[1].boolValue == true) +#define HAS_SLIDERS() (persistentData->options[2].boolValue == true) +#define HAS_TRIMS() (persistentData->options[3].boolValue == true) +#define IS_MIRRORED() (persistentData->options[4].boolValue == true) const uint8_t LBM_LAYOUT_4P2[] = { #include "mask_layout4+2.lbm" From 2537cc878acfb6c2520df9b211cf9dd6d4db3b61 Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 5 Apr 2021 22:59:32 +1000 Subject: [PATCH 295/300] Keep pre-existing inactive logical switches --- companion/src/firmwares/modeldata.cpp | 40 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 85a7f0473..432a375f7 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -653,29 +653,43 @@ int ModelData::updateReference() LogicalSwitchData *lsd = &logicalSw[i]; if (!lsd->isEmpty()) { bool clearlsd = false; + int oldval1; + int oldval2; CSFunctionFamily family = lsd->getFunctionFamily(); switch(family) { case LS_FAMILY_VOFS: - updateSourceIntRef(lsd->val1); - if (lsd->val1 == 0) - clearlsd = true; + if (lsd->val1 != 0) { + updateSourceIntRef(lsd->val1); + if (lsd->val1 == 0) + clearlsd = true; + } break; case LS_FAMILY_STICKY: case LS_FAMILY_VBOOL: - updateSwitchIntRef(lsd->val1); - updateSwitchIntRef(lsd->val2); - if (lsd->val1 == 0 && lsd->val2 == 0) + oldval1 = lsd->val1; + oldval2 = lsd->val2; + if (lsd->val1 != 0) + updateSwitchIntRef(lsd->val1); + if (lsd->val2 != 0) + updateSwitchIntRef(lsd->val2); + if (lsd->val1 == 0 && lsd->val2 == 0 && ((lsd->val1 != oldval1 && oldval2 == 0) || (lsd->val2 != oldval2 && oldval1 == 0))) clearlsd = true; break; case LS_FAMILY_EDGE: - updateSwitchIntRef(lsd->val1); - if (lsd->val1 == 0) - clearlsd = true; + if (lsd->val1 != 0) { + updateSwitchIntRef(lsd->val1); + if (lsd->val1 == 0) + clearlsd = true; + } break; case LS_FAMILY_VCOMP: - updateSourceIntRef(lsd->val1); - updateSourceIntRef(lsd->val2); - if (lsd->val1 == 0 && lsd->val2 == 0) + oldval1 = lsd->val1; + oldval2 = lsd->val2; + if (lsd->val1 != 0) + updateSourceIntRef(lsd->val1); + if (lsd->val2 != 0) + updateSourceIntRef(lsd->val2); + if (lsd->val1 == 0 && lsd->val2 == 0 && ((lsd->val1 != oldval1 && oldval2 == 0) || (lsd->val2 != oldval2 && oldval1 == 0))) clearlsd = true; break; default: @@ -685,7 +699,7 @@ int ModelData::updateReference() lsd->clear(); appendUpdateReferenceParams(REF_UPD_TYPE_LOGICAL_SWITCH, REF_UPD_ACT_CLEAR, i); } - else { + else if (lsd->andsw != 0) { updateSwitchIntRef(lsd->andsw); } } From b6ec55769c2488bc298250e72252d2b6f5a80d26 Mon Sep 17 00:00:00 2001 From: elecpower Date: Mon, 5 Apr 2021 23:28:13 +1000 Subject: [PATCH 296/300] Fix upd ref type so clears refs equal to the one selected for deletion --- companion/src/firmwares/modeldata.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index 432a375f7..c90f90bd2 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -830,6 +830,7 @@ void ModelData::updateTypeIndexRef(R & curRef, const T type, const int idxAdj, c newRef.index = abs(curRef.index); div_t idx = div(newRef.index, updRefInfo.occurences); + div_t newidx; switch (updRefInfo.action) { @@ -847,9 +848,10 @@ void ModelData::updateTypeIndexRef(R & curRef, const T type, const int idxAdj, c if (idx.quot < (updRefInfo.index1 + idxAdj)) return; - newRef.index += updRefInfo.shift; + newRef.index = ((idx.quot + updRefInfo.shift) * updRefInfo.occurences) + idx.rem; + newidx = div(newRef.index, updRefInfo.occurences); - if (idx.quot < (updRefInfo.index1 + idxAdj) || idx.quot > (updRefInfo.maxindex + idxAdj)) { + if (newidx.quot < (updRefInfo.index1 + idxAdj) || newidx.quot > (updRefInfo.maxindex + idxAdj)) { if (defClear) newRef.clear(); else { From 066b4eb7d7d46c2e4373d893cde406b12485dd00 Mon Sep 17 00:00:00 2001 From: elecpower Date: Tue, 6 Apr 2021 05:48:19 +1000 Subject: [PATCH 297/300] For inputs and mixes when updating refs set 'new' FMs to safer option of inactive --- companion/src/firmwares/modeldata.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index c90f90bd2..b24cb4975 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -1097,7 +1097,7 @@ void ModelData::updateFlightModeFlags(unsigned int & curRef) switch (updRefInfo.action) { case REF_UPD_ACT_CLEAR: - flag[updRefInfo.index1] = false; + flag[updRefInfo.index1] = true; break; case REF_UPD_ACT_SHIFT: if(updRefInfo.shift < 0) { @@ -1105,7 +1105,7 @@ void ModelData::updateFlightModeFlags(unsigned int & curRef) if (i - updRefInfo.shift <= updRefInfo.maxindex) flag[i] = flag[i - updRefInfo.shift]; else - flag[i] = false; + flag[i] = true; } } else { @@ -1113,7 +1113,7 @@ void ModelData::updateFlightModeFlags(unsigned int & curRef) if (i - updRefInfo.shift >= updRefInfo.index1) flag[i] = flag[i - updRefInfo.shift]; else - flag[i] = false; + flag[i] = true; } } break; From 43655faee250a12fbd52e3f114728263b32d0d96 Mon Sep 17 00:00:00 2001 From: elecpower Date: Tue, 6 Apr 2021 06:34:11 +1000 Subject: [PATCH 298/300] Check AND switch before clearing LS --- companion/src/firmwares/modeldata.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp index b24cb4975..679e90f5f 100644 --- a/companion/src/firmwares/modeldata.cpp +++ b/companion/src/firmwares/modeldata.cpp @@ -695,13 +695,14 @@ int ModelData::updateReference() default: break; } - if (clearlsd) { + + if (lsd->andsw != 0) + updateSwitchIntRef(lsd->andsw); + + if (clearlsd && lsd->andsw == 0) { lsd->clear(); appendUpdateReferenceParams(REF_UPD_TYPE_LOGICAL_SWITCH, REF_UPD_ACT_CLEAR, i); } - else if (lsd->andsw != 0) { - updateSwitchIntRef(lsd->andsw); - } } } //s1.report("Logical Switches"); From 518706f292ff254ee3497b324e0cf03e4f783eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Charpentier?= Date: Tue, 6 Apr 2021 15:59:04 +0200 Subject: [PATCH 299/300] Fr translation update (#8434) * Updated French translations. --- companion/src/translations/companion_fr.ts | 938 +++++++++++---------- 1 file changed, 481 insertions(+), 457 deletions(-) diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 7f5c0618d..2a6eea996 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -95,7 +95,7 @@ Edit Settings - Éditer les paramètres + Éditer les préférences @@ -488,7 +488,7 @@ Manche Droit: Profondeur, Direction Automatic Backup Folder - Dossier de sauvegardes automatiques + Dossier des sauvegardes automatiques @@ -903,67 +903,67 @@ Manche Droit: Profondeur, Direction - + Delete Channel. Are you sure? Supprimer la Voie. Êtes-vous sûr? - + Cut Channel. Are you sure? Couper la Voie. Êtes-vous sûr? - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Channel. Are you sure? Effacer la Voie. Êtes-vous sûr? - + Clear all Channels. Are you sure? Effacer toutes les Voies. Êtes-vous sûr? @@ -2238,6 +2238,59 @@ Si vous avez un fichier de sauvegarde des paramètres, vous pouvez l'import OpenTX ne supporte pas ce protocole radio + + DataHelpers + + + Disabled + Désactivé + + + + Enabled + Activé + + + + OFF + + + + + ON + + + + + False + Faux + + + + True + Vrai + + + + N + + + + + Y + O + + + + No + Non + + + + Yes + Oui + + DebugOutput @@ -2464,7 +2517,7 @@ Vérifier la sélection (M64/M128) Flight modes - Phases de vol + Phases de Vol @@ -2675,129 +2728,129 @@ Si vide, la ligne est toujours active. FileSyncDialog - + Synchronize Files Synchroniser les fichiers - + Are you sure you wish to abort the sync? Êtes-vous sûr de vouloir annuler la synchronisation? - + Source Folder: Dosssier "Local": - + Destination Folder: Dossier "Radio": - + %1%2 Both directions, to destination folder first %1%2 Bidirectionnel, en commençant par "Radio" - + %1%2 Both directions, to source folder first %1%2 Bidirectionnel, en commençant par "Local" - + %1 Only from source folder to destination folder %1 Uniquement de "Local" vers "Radio" - + %1 Only from destination folder to source folder %1 Uniquement de "Radio" vers "Local" - + How to handle overwriting files which already exist in the destination folder. Comment gérer l'écrasement des fichiers qui existent déjà dans le dossier de destination. - + Copy only if newer and different (compare contents) Copiez seulement si "plus récent et différent" (comparez le contenu) - + Copy only if newer (do not compare contents) Copier uniquement si "plus récent" (ne pas comparer le contenu) - + Copy only if different (ignore file time stamps) Copier uniquement si "différent" (ignorer l'horodatage des fichiers) - + Always copy (force overwite existing files) Toujours copier (écraser tous les fichiers existants) - + Any size Toutes tailles - + Skip files larger than this size. Enter zero for unlimited. Ignorer tous les fichiers plus grands que cette taille. Entrez 0 pour illimité. - + Minimum reporting level. Events of this type and of higher importance are shown. WARNING: High log rates may make the user interface temporarily unresponsive. Niveau de rapport au minimum. Les événements de ce type et d'une plus grande importance sont indiqués. ATTENTION: un taux de rapport élevé peut rendre l'interface utilisateur temporairement inutilisable. - + Skipped Sauté - + Created Créé - + Updated Mis à jour - + Errors Only Erreur seulement - + Test-run only Essai - + Run as normal but do not actually copy anything. Useful for verifying results before real sync. Exécutez normalement, mais ne rien copier. Utile pour vérifier les résultats avant la synchronisation réelle. - + Log Level: Niveau de rapport: - + Filters: Filtres: - + The "Include" filter will only copy files which match the pattern(s). The "Exclude" filter will skip files matching the filter pattern(s). The Include filter is evaluated first. @@ -2806,148 +2859,148 @@ Le filtre "Exclure" ignorera les fichiers correspondant à la (aux) r Le filtre "Inclure" est évalué en premier. - + One or more file pattern(s) to exclude, separated by commas. Blank means exclude none. ?, *, and [...] wildcards accepted. Une ou plusieurs règle(s) de fichier(s) à exclure, séparés par des virgules. Blanc signifie "exclure aucun".Les métacaractères ?, * et [...] sont acceptés. - + One or more file pattern(s) to include, separated by commas. Blank means include all. ?, *, and [...] wildcards accepted. Une ou plusieurs règle(s) de fichier(s) à inclure, séparés par des virgules. Blanc signifie "inclure tous".Les métacaractères ?, * et [...] sont acceptés. - + Include: Inclure: - + Exclude: Exclure: - + Case sensitive Sensible aux majuscules et minuscules - - + + Follow links Suivez les liens - + Include hidden Inclure fichier/dossier caché - + Recursive Récursif - + Skip empty Sauter vide - + Apply filters Appliquer les filtres - + Filter Options: Options des Filtres: - + Folder Options: Options des Dossiers: - - + + Options - + Show extra options Afficher les options supplémentaires - + Reset to defaults Réinitialiser les paramètres par défaut - + Close Fermer - + Sync. Direction: Direction de la synchro.: - + Existing Files: Fichiers existants: - + Max. File Size: Taille Max Fichier: - + MB Mo - + KB ko - + Abort Annuler - + Start Démarrer - + Total: <b>%1</b>; Created: <b>%2</b>; Updated: <b>%3</b>; Skipped: <b>%4</b>; Errors: <font color=%6><b>%5</b></font>; Total: <b>%1</b>; Créé(s): <b>%2</b>; Mis à jour: <b>%3</b>; Sauté(s): <b>%4</b>; Erreur(s): <font color=%6><b>%5</b></font>; - + Current: <b>%1</b> of Courant: <b>%1</b> de - + Source folder not found. Dosssier "Local" non trouvé. - + Destination folder not found. Dosssier "Radio" non trouvé. - + Source and destination folders are the same. Les dossiers "Local" et "Radio" sont les mêmes. @@ -5725,12 +5778,12 @@ Cette fonction ne peut pas être désactivée sur la radio. Long. cyc - Cyc. longitudinal + Cyclique longitudinal Lateral cyc - Cyc. latéral + Cyclique latéral @@ -5944,29 +5997,29 @@ Cette fonction ne peut pas être désactivée sur la radio. - Clear all lines for the selected Inputs. Are you sure? + Clear all lines for the selected Input. Are you sure? Effacer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? - - Delete all lines for the selected Inputs. Are you sure? + + Delete all lines for the selected Input. Are you sure? Supprimer toutes les lignes Entrée sélectionnées. Êtes-vous sûr? LimitData - + INV - + NOR - + CH VOIE @@ -7473,7 +7526,7 @@ Do you wish to continue? Edit Radio Settings - Éditer paramètres radio + Éditer Paramètres Radio @@ -7488,7 +7541,7 @@ Do you wish to continue? Simulate Radio - Simuler radio + Simuler Radio @@ -7969,7 +8022,7 @@ Mixage actif par défaut si non-renseigné. Flight modes - Phases de vol + Phases de Vol @@ -8140,17 +8193,17 @@ Mixage actif par défaut si non-renseigné. ModelData - + Model: Modèle: - + Throttle Source Source des gaz - + THR GAZ @@ -8165,7 +8218,7 @@ Mixage actif par défaut si non-renseigné. Heli - Paramètres hélico + Paramètres Hélico @@ -8180,7 +8233,7 @@ Mixage actif par défaut si non-renseigné. Logical Switches - Inters logiques + Inters Logiques @@ -8200,7 +8253,7 @@ Mixage actif par défaut si non-renseigné. Flight Modes - Phases de vol + Phases de Vol @@ -8210,7 +8263,7 @@ Mixage actif par défaut si non-renseigné. Special Functions - Fonctions spéciales + Fonctions Spéciales @@ -8312,9 +8365,8 @@ Mixage actif par défaut si non-renseigné. - - - + + OFF @@ -8325,7 +8377,7 @@ Mixage actif par défaut si non-renseigné. - + Mode @@ -8359,13 +8411,13 @@ Mixage actif par défaut si non-renseigné. - + Delay Délai - + Receiver Récepteur @@ -8426,322 +8478,282 @@ Mixage actif par défaut si non-renseigné. - - + + Weight(%1) Ratio(%1) - - + + Switch(%1) Inter(%1) - + MULT! - + Delay(u%1:d%2) Délai(h%1:b%2) - + Slow(u%1:d%2) Ralenti(h%1:b%2) - + Warn(%1) Alerte(%1) - + Flight modes - Phases de vol + Phases de Vol - + Flight mode Phase de vol - + All Tous - + Edge Flanc - + Sticky Bistable - + Timer Chrono - + missing absent - + Duration Durée - + Extended Limits Limites étendues - + Display Checklist Afficher la checklist - + Global Functions Fonctions globales - + Manual Manuel - + Auto - + Failsafe Mode Mode Failsafe - - + + Hold Maintien - + No Pulse Pas d'impulsion - + Not set Non défini - + No pulses Pas d'impulsions - - Silent - Aucun - - - - Beeps - Bips - - - - Voice - Voix - - - - Haptic - Vibreur - - - - Flight - Vol - - - - Manual reset - RAZ manuelle - - - + Step Pas - + Display Affichage - + Extended Étendu - + Never Jamais - + On Change Sur changement - + Always Toujours - - - + + + Source - + Trim idle only Trim ralenti uniquement - + Warning Avertissement - + Reversed Inversé - - Tmr - Chrono - - - + FrSky S.PORT - + FrSky D - + FrSky D (cable) FrSky D (câble) - + Alti Alt - + Alti+ Alt+ - + VSpeed Vitesse verticale - - - + + + A1 - - - + + + A2 - - + + A3 - - + + A4 - - + + FAS - + Cells Velm - + Min - + Max - + Numbers Chiffres - + Bars Barres - + Script - + Filename Nom de fichier - + Error: Unable to open or read file! Erreur: Impossible d'ouvrir ou de lire le fichier! - - - Persistent - Persistant - Off @@ -8749,59 +8761,44 @@ Mixage actif par défaut si non-renseigné. - - - - + + + + None Aucun - - - Name - Nom - - Countdown - Compte à rebours - - - - Minute call - Annonces minutes - - - - - + + FM%1 PV%1 - + FM%1%2 PV%1%2 - + FM%1+%2 PV%1+%2 - - + + NoTrim Pas de trim - + No DR/Expo Pas d'expo/DR - - + + Offset(%1) Décalage(%1) @@ -8816,23 +8813,23 @@ Mixage actif par défaut si non-renseigné. - + Disabled in all flight modes Désactivé pour toutes les phases de vol - + instant immédiat - - + + Custom Prédéfini - + Standard @@ -9241,42 +9238,42 @@ Mixage actif par défaut si non-renseigné. ModulePanel - + Value Valeur - + Hold Maintien - + No Pulse Pas d'impulsion - + Ask Demander - + Internal Interne - + Internal + External Interne + Externe - + External Externe - + Bind on channel Bind avec VOIE @@ -9284,91 +9281,91 @@ Mixage actif par défaut si non-renseigné. MultiModelPrinter - + Input Entrée - + Weight Ratio - + Long. cyc - Cyc. longitudinal + Cyclique longitudinal - + Lateral cyc - Cyc. latéral + Cyclique latéral - + Collective Source du pas collectif - + Flight modes - Phases de vol + Phases de Vol - - + + Flight mode Phase de vol - + Switch Inter - - + + GV%1 VG%1 - + RE%1 - + Channel Voie - + Direct - + Curve Courbe - + PPM - + Linear Linéaire - + Telemetry Télémétrie - - + + Min @@ -9388,23 +9385,23 @@ Mixage actif par défaut si non-renseigné. Compte à rebours - + Modules - + Outputs Sorties - + Subtrim Subtrim - - + + Max @@ -9415,8 +9412,8 @@ Mixage actif par défaut si non-renseigné. - - + + Name Nom @@ -9471,209 +9468,214 @@ Mixage actif par défaut si non-renseigné. Persistant - + + Start + Démarrer + + + Trainer port Port écolage - + Helicopter Hélicoptère - + Swash Cyclique - - + + Type - + Ring Plateau - + F.In Fondu en entrée - + F.Out Fondu en sortie - + Global vars Variables globales - + Prec - + Popup - + Global Variables Variables globales - + Inputs Entrées - + Mixers Mixages - + Curves Courbes - + L%1 - + Logical Switches - Inters logiques + Inters Logiques - + SF%1 FS%1 - + Special Functions - Fonctions spéciales + Fonctions Spéciales - + Protocol Protocole - + Low Faible - + Critical Critique - + Telemetry audio Audio télémétrie - + Altimetry Altimétrie - - + + Vario source Source vario - + Vario limits > Limites vario > - + Sink max Chute max - + Sink min Chute min - + Climb min Pompe min - + Climb max Pompe max - + Center silent Silence centrer - + Top Bar Barre de titre de l'écran d'accueil - + Volts source Source de la tension - + Altitude source Source de l'altitude - + Parameters Paramètres - + Telemetry Sensors Capteurs Télémétrie - + Telemetry Screens Écrans Télémétrie - + GF%1 FG%1 - + Global Functions Fonctions globales - + Checklist - + Unit Unité - + RSSI Alarms Alarmes RSSI @@ -9928,7 +9930,7 @@ Mixage actif par défaut si non-renseigné. RadioData - + Models Modeles @@ -10530,332 +10532,322 @@ E SensorData - + Raw (-) Chiffres bruts (-) - - + + V - + A - + mA - + kts - + m/s - + km/h - + mph - + m - + f - + °C - + °F - + % - + mAh - + W - + mW - + dB - + rpms - + g - + ° - + Rad - + hours heures - + minutes - + seconds secondes - + ml/minute - + Hertz - + mS - + uS - + TELE - + Formula Formule - + Id - + Instance - - + + Sensor Capteur - + Sources - + GPS - + Alt Altitude - + Unit Unité - + Precision Précision - + Ratio - + Offset Décalage - + Auto Offset - + Blades Pales - + Multiplier Multiplicateur - + Filter Filtre - + Persist Persistant - + Positive - + Log - + Custom Prédéfini - + Calculated Calculé - + Add Addition - + Average Moyenne - + Minimum - + Maximum - + Multiply Multiplication - + Totalize Totalisation - + Cell Element LiPo - + Consumption Consommation - + Distance - + Lowest Min - + Cell %1 Elément %1 - + Highest Max - + Delta - + (default) (défaut) - - - Y - O - - - - N - - Setup @@ -11039,92 +11031,87 @@ Si cette option est cochée, la voie des gaz est inversée: le ralenti est &apo SetupPanel - + Popup menu available Menu contextuel disponible - + Timer %1 Chrono %1 - - THR - GAZ - - - + Profile Settings Paramètres du profil - + SD structure path not specified or invalid Chemin de la structure SD non spécifié ou invalide - + Copy Copier - + Cut Couper - + Paste Coller - + Clear Effacer - + Insert Insérer - + Delete Supprimer - + Move Up Monter - + Move Down Descendre - + Clear All Effacer Tout - + Clear Timer. Are you sure? Effacer le Chrono. Êtes-vous sûr? - + Clear all Timers. Are you sure? Effacer tous les Chronos. Êtes-vous sûr? - + Cut Timer. Are you sure? Couper le Chrono. Êtes-vous sûr? - + Delete Timer. Are you sure? Supprimer le Chrono. Êtes-vous sûr? @@ -13204,7 +13191,12 @@ hh:mm:ss Compte à rebours - + + Start + Démarrer + + + Minute Call Annonces minutes @@ -13212,21 +13204,91 @@ hh:mm:ss TimerData - + TMR - + Timer %1 Chrono %1 - + TMR as in Timer + + + Silent + Aucun + + + + Beeps + Bips + + + + Voice + Voix + + + + Haptic + Vibreur + + + + 5s + + + + + 10s + + + + + 20s + + + + + 30s + + + + + Not persistent + Non persistant + + + + NOT + + + + + Persistent (flight) + Persistant (vol) + + + + Flight + Vol + + + + Persistent (manual reset) + Persistant (RAZ manuelle) + + + + Manual reset + RAZ manuelle + TimerEdit @@ -13253,44 +13315,6 @@ Utilisez CTRL + le défilement de la souris ou les touches PAGE HAUT/PAGE BAS po - - TimerPanel - - - Silent - Aucun - - - - Beeps - Bips - - - - Voice - Voix - - - - Haptic - Vibreur - - - - Not persistent - Non persistant - - - - Persistent (flight) - Persistant (vol) - - - - Persistent (manual reset) - Persistant (RAZ manuelle) - - Trainer From 1f593c4f9edbaf4f95c9f7e8c3a0375dfd34f595 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 16:15:57 +0200 Subject: [PATCH 300/300] Update CREDITS.txt --- CREDITS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.txt b/CREDITS.txt index d05842991..0daa8f9e0 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -2224,3 +2224,4 @@ Ernst Camenzind Radomir Sterba Ignacio Barrio Bradley Murchie +Emmanuel Balintec

KfYa6b~ML8Bad^ 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 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/beeper.png b/radio/sdcard/horus/SCRIPTS/WIZARD/multirotor/img/beeper.png new file mode 100755 index 0000000000000000000000000000000000000000..57c37f2fec593299a4dbb5a2a86b3fe6382cbb2c GIT binary patch literal 71079 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