Companion: Russian UI (#7180)
[opentx.git] / radio / src / gui / gui_common.cpp
blobd3b826b28d00dbe491ae0b0f96190099ff4c08e0
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
23 #if defined(PCBTARANIS) || defined(PCBHORUS)
24 uint8_t switchToMix(uint8_t source)
26 div_t qr = div(source-1, 3);
27 return qr.quot+MIXSRC_FIRST_SWITCH;
29 #else
30 uint8_t switchToMix(uint8_t source)
32 if (source <= 3)
33 return MIXSRC_3POS;
34 else
35 return MIXSRC_FIRST_SWITCH - 3 + source;
37 #endif
39 int circularIncDec(int current, int inc, int min, int max, IsValueAvailable isValueAvailable)
41 do {
42 current += inc;
43 if (current < min)
44 current = max;
45 else if (current > max)
46 current = min;
47 if (!isValueAvailable || isValueAvailable(current))
48 return current;
49 } while(1);
50 return 0;
53 bool isInputAvailable(int input)
55 for (int i=0; i<MAX_EXPOS; i++) {
56 ExpoData * expo = expoAddress(i);
57 if (!EXPO_VALID(expo))
58 break;
59 if (expo->chn == input)
60 return true;
62 return false;
65 bool isRssiSensorAvailable(int sensor)
67 if (sensor == 0)
68 return true;
69 else {
70 TelemetrySensor &telemSensor = g_model.telemetrySensors[abs(sensor) - 1];
71 return (telemSensor.isAvailable() && telemSensor.id == RSSI_ID);
75 bool isSensorAvailable(int sensor)
77 if (sensor == 0)
78 return true;
79 else
80 return isTelemetryFieldAvailable(abs(sensor) - 1);
83 bool isSensorUnit(int sensor, uint8_t unit)
85 if (sensor <= 0 || sensor > MAX_TELEMETRY_SENSORS ) {
86 return true;
88 else {
89 return g_model.telemetrySensors[sensor-1].unit == unit;
93 bool isCellsSensor(int sensor)
95 return isSensorUnit(sensor, UNIT_CELLS);
98 bool isGPSSensor(int sensor)
100 return isSensorUnit(sensor, UNIT_GPS);
103 bool isAltSensor(int sensor)
105 return isSensorUnit(sensor, UNIT_DIST) || isSensorUnit(sensor, UNIT_FEET);
108 bool isVoltsSensor(int sensor)
110 return isSensorUnit(sensor, UNIT_VOLTS) || isSensorUnit(sensor, UNIT_CELLS);
113 bool isCurrentSensor(int sensor)
115 return isSensorUnit(sensor, UNIT_AMPS);
118 bool isTelemetryFieldAvailable(int index)
120 TelemetrySensor & sensor = g_model.telemetrySensors[index];
121 return sensor.isAvailable();
124 bool isTelemetryFieldComparisonAvailable(int index)
126 if (!isTelemetryFieldAvailable(index))
127 return false;
129 TelemetrySensor & sensor = g_model.telemetrySensors[index];
130 if (sensor.unit >= UNIT_DATETIME)
131 return false;
132 return true;
135 bool isChannelUsed(int channel)
137 for (int i=0; i<MAX_MIXERS; ++i) {
138 MixData *md = mixAddress(i);
139 if (md->srcRaw == 0) return false;
140 if (md->destCh == channel) return true;
141 if (md->destCh > channel) return false;
143 return false;
146 int getChannelsUsed()
148 int result = 0;
149 int lastCh = -1;
150 for (int i=0; i<MAX_MIXERS; ++i) {
151 MixData *md = mixAddress(i);
152 if (md->srcRaw == 0) return result;
153 if (md->destCh != lastCh) { ++result; lastCh = md->destCh; }
155 return result;
158 bool isSourceAvailable(int source)
160 if (source < 0)
161 return false;
163 if (source >= MIXSRC_FIRST_INPUT && source <= MIXSRC_LAST_INPUT) {
164 return isInputAvailable(source - MIXSRC_FIRST_INPUT);
167 #if defined(LUA_MODEL_SCRIPTS)
168 if (source >= MIXSRC_FIRST_LUA && source <= MIXSRC_LAST_LUA) {
169 div_t qr = div(source - MIXSRC_FIRST_LUA, MAX_SCRIPT_OUTPUTS);
170 return (qr.rem<scriptInputsOutputs[qr.quot].outputsCount);
172 #elif defined(LUA_INPUTS)
173 if (source >= MIXSRC_FIRST_LUA && source <= MIXSRC_LAST_LUA)
174 return false;
175 #endif
177 if (source >= MIXSRC_FIRST_POT && source <= MIXSRC_LAST_POT) {
178 return IS_POT_SLIDER_AVAILABLE(POT1+source - MIXSRC_FIRST_POT);
181 #if defined(PCBX10)
182 if (source >= MIXSRC_MOUSE1 && source <= MIXSRC_MOUSE2)
183 return false;
184 #endif
186 if (source >= MIXSRC_FIRST_SWITCH && source <= MIXSRC_LAST_SWITCH) {
187 return SWITCH_EXISTS(source - MIXSRC_FIRST_SWITCH);
190 #if !defined(HELI)
191 if (source >= MIXSRC_CYC1 && source <= MIXSRC_CYC3)
192 return false;
193 #endif
195 if (source >= MIXSRC_FIRST_CH && source <= MIXSRC_LAST_CH) {
196 return isChannelUsed(source - MIXSRC_FIRST_CH);
199 if (source >= MIXSRC_FIRST_LOGICAL_SWITCH && source <= MIXSRC_LAST_LOGICAL_SWITCH) {
200 LogicalSwitchData * cs = lswAddress(source - MIXSRC_FIRST_LOGICAL_SWITCH);
201 return (cs->func != LS_FUNC_NONE);
204 #if !defined(GVARS)
205 if (source >= MIXSRC_GVAR1 && source <= MIXSRC_LAST_GVAR)
206 return false;
207 #endif
209 if (source >= MIXSRC_FIRST_RESERVE && source <= MIXSRC_LAST_RESERVE)
210 return false;
212 if (source >= MIXSRC_FIRST_TELEM && source <= MIXSRC_LAST_TELEM) {
213 div_t qr = div(source - MIXSRC_FIRST_TELEM, 3);
214 if (qr.rem == 0)
215 return isTelemetryFieldAvailable(qr.quot);
216 else
217 return isTelemetryFieldComparisonAvailable(qr.quot);
220 return true;
223 bool isSourceAvailableInGlobalFunctions(int source)
225 if (source >= MIXSRC_FIRST_TELEM && source <= MIXSRC_LAST_TELEM) {
226 return false;
228 return isSourceAvailable(source);
231 bool isSourceAvailableInCustomSwitches(int source)
233 bool result = isSourceAvailable(source);
235 if (result && source >= MIXSRC_FIRST_TELEM && source <= MIXSRC_LAST_TELEM) {
236 div_t qr = div(source - MIXSRC_FIRST_TELEM, 3);
237 result = isTelemetryFieldComparisonAvailable(qr.quot);
240 return result;
243 bool isSourceAvailableInInputs(int source)
245 if (source >= MIXSRC_FIRST_POT && source <= MIXSRC_LAST_POT)
246 return IS_POT_SLIDER_AVAILABLE(POT1+source - MIXSRC_FIRST_POT);
248 #if defined(PCBX10)
249 if (source >= MIXSRC_MOUSE1 && source <= MIXSRC_MOUSE2)
250 return false;
251 #endif
253 if (source >= MIXSRC_Rud && source <= MIXSRC_MAX)
254 return true;
256 if (source >= MIXSRC_FIRST_TRIM && source <= MIXSRC_LAST_TRIM)
257 return true;
259 if (source >= MIXSRC_FIRST_SWITCH && source <= MIXSRC_LAST_SWITCH)
260 return SWITCH_EXISTS(source - MIXSRC_FIRST_SWITCH);
262 if (source >= MIXSRC_FIRST_CH && source <= MIXSRC_LAST_CH)
263 return true;
265 if (source >= MIXSRC_FIRST_LOGICAL_SWITCH && source <= MIXSRC_LAST_LOGICAL_SWITCH) {
266 LogicalSwitchData * cs = lswAddress(source - MIXSRC_SW1);
267 return (cs->func != LS_FUNC_NONE);
270 if (source >= MIXSRC_FIRST_TRAINER && source <= MIXSRC_LAST_TRAINER)
271 return true;
273 if (source >= MIXSRC_FIRST_TELEM && source <= MIXSRC_LAST_TELEM) {
274 div_t qr = div(source - MIXSRC_FIRST_TELEM, 3);
275 return isTelemetryFieldAvailable(qr.quot) && isTelemetryFieldComparisonAvailable(qr.quot);
278 return false;
281 enum SwitchContext
283 LogicalSwitchesContext,
284 ModelCustomFunctionsContext,
285 GeneralCustomFunctionsContext,
286 TimersContext,
287 MixesContext
290 bool isLogicalSwitchAvailable(int index)
292 LogicalSwitchData * lsw = lswAddress(index);
293 return (lsw->func != LS_FUNC_NONE);
296 bool isSwitchAvailable(int swtch, SwitchContext context)
298 bool negative = false;
300 if (swtch < 0) {
301 if (swtch == -SWSRC_ON || swtch == -SWSRC_ONE) {
302 return false;
304 negative = true;
305 swtch = -swtch;
308 #if defined(PCBSKY9X)
309 if (swtch >= SWSRC_FIRST_SWITCH && swtch <= SWSRC_LAST_SWITCH) {
310 UNUSED(negative);
311 return true;
313 #else
314 if (swtch >= SWSRC_FIRST_SWITCH && swtch <= SWSRC_LAST_SWITCH) {
315 div_t swinfo = switchInfo(swtch);
316 if (!SWITCH_EXISTS(swinfo.quot)) {
317 return false;
319 if (!IS_CONFIG_3POS(swinfo.quot)) {
320 if (negative) {
321 return false;
323 if (swinfo.rem == 1) {
324 // mid position not available for 2POS switches
325 return false;
328 return true;
330 #endif
332 #if NUM_XPOTS > 0
333 if (swtch >= SWSRC_FIRST_MULTIPOS_SWITCH && swtch <= SWSRC_LAST_MULTIPOS_SWITCH) {
334 int index = (swtch - SWSRC_FIRST_MULTIPOS_SWITCH) / XPOTS_MULTIPOS_COUNT;
335 if (IS_POT_MULTIPOS(POT1+index)) {
336 StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1+index];
337 return (calib->count >= ((swtch - SWSRC_FIRST_MULTIPOS_SWITCH) % XPOTS_MULTIPOS_COUNT));
339 else {
340 return false;
343 #endif
345 #if defined(PCBSKY9X) && defined(REVX)
346 if (swtch == SWSRC_REa) {
347 return false;
349 #endif
351 if (swtch >= SWSRC_FIRST_LOGICAL_SWITCH && swtch <= SWSRC_LAST_LOGICAL_SWITCH) {
352 if (context == GeneralCustomFunctionsContext) {
353 return false;
355 else if (context != LogicalSwitchesContext) {
356 return isLogicalSwitchAvailable(swtch - SWSRC_FIRST_LOGICAL_SWITCH);
360 if (context != ModelCustomFunctionsContext && context != GeneralCustomFunctionsContext && (swtch == SWSRC_ON || swtch == SWSRC_ONE)) {
361 return false;
364 if (swtch >= SWSRC_FIRST_FLIGHT_MODE && swtch <= SWSRC_LAST_FLIGHT_MODE) {
365 if (context == MixesContext || context == GeneralCustomFunctionsContext) {
366 return false;
368 else {
369 swtch -= SWSRC_FIRST_FLIGHT_MODE;
370 if (swtch == 0) {
371 return true;
373 FlightModeData * fm = flightModeAddress(swtch);
374 return (fm->swtch != SWSRC_NONE);
378 if (swtch >= SWSRC_FIRST_SENSOR && swtch <= SWSRC_LAST_SENSOR) {
379 if (context == GeneralCustomFunctionsContext)
380 return false;
381 else
382 return isTelemetryFieldAvailable(swtch - SWSRC_FIRST_SENSOR);
385 return true;
388 bool isSwitchAvailableInLogicalSwitches(int swtch)
390 return isSwitchAvailable(swtch, LogicalSwitchesContext);
393 bool isSwitchAvailableInCustomFunctions(int swtch)
395 if (menuHandlers[menuLevel] == menuModelSpecialFunctions)
396 return isSwitchAvailable(swtch, ModelCustomFunctionsContext);
397 else
398 return isSwitchAvailable(swtch, GeneralCustomFunctionsContext);
401 bool isSwitchAvailableInMixes(int swtch)
403 return isSwitchAvailable(swtch, MixesContext);
406 #if defined(COLORLCD)
407 bool isSwitch2POSWarningStateAvailable(int state)
409 return (state != 2); // two pos switch - middle state not available
411 #endif // #if defined(COLORLCD)
413 bool isSwitchAvailableInTimers(int swtch)
415 if (swtch >= 0) {
416 if (swtch < TMRMODE_COUNT)
417 return true;
418 else
419 swtch -= TMRMODE_COUNT-1;
421 else {
422 if (swtch > -TMRMODE_COUNT)
423 return false;
424 else
425 swtch += TMRMODE_COUNT-1;
428 return isSwitchAvailable(swtch, TimersContext);
431 bool isThrottleSourceAvailable(int source)
433 if (source >= THROTTLE_SOURCE_FIRST_POT && source < THROTTLE_SOURCE_FIRST_POT+NUM_POTS+NUM_SLIDERS && !IS_POT_SLIDER_AVAILABLE(POT1+source-THROTTLE_SOURCE_FIRST_POT))
434 return false;
435 else
436 return true;
439 bool isLogicalSwitchFunctionAvailable(int function)
441 return function != LS_FUNC_RANGE;
444 bool isAssignableFunctionAvailable(int function)
446 #if defined(OVERRIDE_CHANNEL_FUNCTION) || defined(GVARS)
447 bool modelFunctions = (menuHandlers[menuLevel] == menuModelSpecialFunctions);
448 #endif
450 switch (function) {
451 case FUNC_OVERRIDE_CHANNEL:
452 #if defined(OVERRIDE_CHANNEL_FUNCTION)
453 return modelFunctions;
454 #else
455 return false;
456 #endif
457 case FUNC_ADJUST_GVAR:
458 #if defined(GVARS)
459 return modelFunctions;
460 #else
461 return false;
462 #endif
463 #if !defined(HAPTIC)
464 case FUNC_HAPTIC:
465 #endif
466 case FUNC_RESERVE4:
467 #if !defined(DANGEROUS_MODULE_FUNCTIONS)
468 case FUNC_RANGECHECK:
469 case FUNC_BIND:
470 #endif
471 #if !defined(LUA)
472 case FUNC_PLAY_SCRIPT:
473 #endif
474 case FUNC_RESERVE5:
475 return false;
477 default:
478 return true;
482 bool isSourceAvailableInGlobalResetSpecialFunction(int index)
484 if (index >= FUNC_RESET_PARAM_FIRST_TELEM)
485 return false;
486 else
487 return isSourceAvailableInResetSpecialFunction(index);
490 bool isSourceAvailableInResetSpecialFunction(int index)
492 if (index >= FUNC_RESET_PARAM_FIRST_TELEM) {
493 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[index-FUNC_RESET_PARAM_FIRST_TELEM];
494 return telemetrySensor.isAvailable();
496 #if TIMERS < 3
497 else if (index == FUNC_RESET_TIMER3) {
498 return false;
500 #endif
501 #if TIMERS < 2
502 else if (index == FUNC_RESET_TIMER2) {
503 return false;
505 #endif
506 else {
507 return true;
511 bool isR9MModeAvailable(int mode)
513 #if defined(MODULE_PROTOCOL_FLEX)
514 return true;
515 #else
516 return mode <= MODULE_SUBTYPE_R9M_EU;
517 #endif
520 #if defined(PXX2)
521 bool isPxx2IsrmChannelsCountAllowed(int channels)
523 if (g_model.moduleData[INTERNAL_MODULE].subType == MODULE_SUBTYPE_ISRM_PXX2_ACCST_D16 && channels > 8)
524 return false;
525 return (channels % 8 == 0);
527 #else
528 bool isPxx2IsrmChannelsCountAllowed(int channels)
530 return true;
532 #endif
534 bool isTrainerUsingModuleBay()
536 #if defined(PCBTARANIS)
537 if (TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE <= g_model.trainerData.mode && g_model.trainerData.mode <= TRAINER_MODE_MASTER_BATTERY_COMPARTMENT)
538 return true;
539 #endif
540 return false;
543 bool isModuleUsingSport(uint8_t moduleBay, uint8_t moduleType)
545 switch (moduleType) {
546 case MODULE_TYPE_NONE:
547 case MODULE_TYPE_SBUS:
548 case MODULE_TYPE_PPM:
549 case MODULE_TYPE_DSM2:
550 case MODULE_TYPE_MULTIMODULE:
551 case MODULE_TYPE_ISRM_PXX2:
552 case MODULE_TYPE_R9M_LITE_PXX2:
553 case MODULE_TYPE_R9M_LITE_PRO_PXX2:
554 return false;
556 case MODULE_TYPE_XJT_PXX1:
557 // External XJT has a physical switch to disable S.PORT
558 case MODULE_TYPE_R9M_PXX1:
559 // R9M telemetry is disabled by pulses (pxx1.cpp)
560 if (moduleBay == EXTERNAL_MODULE)
561 return false;
563 default:
564 return true;
568 #if defined(HARDWARE_INTERNAL_MODULE)
569 bool isInternalModuleAvailable(int moduleType)
571 if (moduleType == MODULE_TYPE_NONE)
572 return true;
574 #if defined(INTERNAL_MODULE_MULTI)
575 if (moduleType == MODULE_TYPE_MULTIMODULE)
576 return true;
577 else
578 return false;
579 #endif
581 if (moduleType == MODULE_TYPE_XJT_PXX1) {
582 #if defined(PXX1) && defined(INTERNAL_MODULE_PXX1)
583 return !isModuleUsingSport(EXTERNAL_MODULE, g_model.moduleData[EXTERNAL_MODULE].type);
584 #endif
587 if (moduleType == MODULE_TYPE_ISRM_PXX2) {
588 #if defined(PXX2) && defined(INTERNAL_MODULE_PXX2)
589 return true;
590 #endif
593 if (moduleType == MODULE_TYPE_PPM) {
594 #if defined(PPM) && defined(INTERNAL_MODULE_PPM)
595 return true;
596 #endif
598 return false;
600 #endif
602 bool isExternalModuleAvailable(int moduleType)
604 if (moduleType == MODULE_TYPE_R9M_LITE_PRO_PXX1)
605 return false;
607 #if !defined(HARDWARE_EXTERNAL_MODULE_SIZE_SML)
608 if (isModuleTypeR9MLite(moduleType) || moduleType == MODULE_TYPE_XJT_LITE_PXX2)
609 return false;
610 #endif
612 #if !defined(PXX1)
613 if (isModuleTypePXX1(moduleType))
614 return false;
615 #endif
617 #if !defined(XJT)
618 if (moduleType == MODULE_TYPE_XJT_PXX1)
619 return false;
620 #endif
622 #if !defined(HARDWARE_EXTERNAL_MODULE_SIZE_STD)
623 if (moduleType == MODULE_TYPE_R9M_PXX1 || moduleType == MODULE_TYPE_R9M_PXX2)
624 return false;
625 #endif
627 if (moduleType == MODULE_TYPE_ISRM_PXX2)
628 return false; // doesn't exist for now
630 #if !defined(PXX2) || !defined(EXTMODULE_USART)
631 if (moduleType == MODULE_TYPE_XJT_LITE_PXX2 || moduleType == MODULE_TYPE_R9M_PXX2 || moduleType == MODULE_TYPE_R9M_LITE_PXX2 || moduleType == MODULE_TYPE_R9M_LITE_PRO_PXX2) {
632 return false;
634 #endif
636 #if !defined(CROSSFIRE)
637 if (moduleType == MODULE_TYPE_CROSSFIRE)
638 return false;
639 #endif
641 #if !defined(DSM2)
642 if (moduleType == MODULE_TYPE_DSM2)
643 return false;
644 #endif
646 #if !defined(SBUS)
647 if (moduleType == MODULE_TYPE_SBUS)
648 return false;
649 #endif
651 #if !defined(MULTIMODULE)
652 if (moduleType == MODULE_TYPE_MULTIMODULE)
653 return false;
654 #endif
656 #if defined(HARDWARE_INTERNAL_MODULE)
657 if (isTrainerUsingModuleBay() || (isModuleUsingSport(EXTERNAL_MODULE, moduleType) && isModuleUsingSport(INTERNAL_MODULE, g_model.moduleData[INTERNAL_MODULE].type)))
658 return false;
659 #endif
661 #if !defined(PPM)
662 if (moduleType == MODULE_TYPE_PPM)
663 return false;
664 #endif
666 return true;
669 bool isRfProtocolAvailable(int protocol)
671 #if defined(CROSSFIRE)
672 if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_CROSSFIRE) {
673 return false;
675 #endif
676 #if !defined(MODULE_PROTOCOL_D8)
677 if (protocol == MODULE_SUBTYPE_PXX1_ACCST_D8) {
678 return false;
680 #endif
681 #if defined(PCBTARANIS) || defined(PCBHORUS)
682 if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1) {
683 return false;
685 if (protocol != MODULE_SUBTYPE_PXX1_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX2) {
686 return false;
688 #endif
690 return true;
693 bool isTelemetryProtocolAvailable(int protocol)
695 #if defined(PCBTARANIS)
696 if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY && g_eeGeneral.auxSerialMode != UART_MODE_TELEMETRY) {
697 return false;
699 #endif
701 if (protocol== PROTOCOL_TELEMETRY_CROSSFIRE) {
702 return false;
705 #if !defined(MULTIMODULE)
706 if (protocol == PROTOCOL_TELEMETRY_SPEKTRUM || protocol == PROTOCOL_TELEMETRY_FLYSKY_IBUS || protocol == PROTOCOL_TELEMETRY_MULTIMODULE) {
707 return false;
709 #endif
711 #if defined(PCBHORUS)
712 if (protocol == PROTOCOL_TELEMETRY_FRSKY_D_SECONDARY) {
713 return false;
715 #endif
717 return true;
720 bool isTrainerModeAvailable(int mode)
722 #if defined(PCBTARANIS)
723 if (IS_EXTERNAL_MODULE_ENABLED() && (mode == TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE || mode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE))
724 return false;
725 #endif
727 #if defined(PCBTARANIS) && !defined(TRAINER_BATTERY_COMPARTMENT)
728 if (mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT)
729 return false;
730 #elif defined(PCBTARANIS)
731 if (mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT)
732 return g_eeGeneral.auxSerialMode == UART_MODE_SBUS_TRAINER;
733 #endif
735 #if defined(PCBX9E)
736 if (mode == TRAINER_MODE_MASTER_BLUETOOTH || mode == TRAINER_MODE_SLAVE_BLUETOOTH)
737 return false;
738 #elif defined(BLUETOOTH)
739 if (g_eeGeneral.bluetoothMode != BLUETOOTH_TRAINER && (mode == TRAINER_MODE_MASTER_BLUETOOTH || mode == TRAINER_MODE_SLAVE_BLUETOOTH))
740 return false;
741 #endif
743 #if defined(PCBXLITE) && !defined(PCBXLITES)
744 if (mode == TRAINER_MODE_MASTER_TRAINER_JACK || mode == TRAINER_MODE_SLAVE)
745 return false;
746 #endif
748 return true;
751 bool modelHasNotes()
753 char filename[sizeof(MODELS_PATH)+1+sizeof(g_model.header.name)+sizeof(TEXT_EXT)] = MODELS_PATH "/";
754 char *buf = strcat_currentmodelname(&filename[sizeof(MODELS_PATH)]);
755 strcpy(buf, TEXT_EXT);
756 if (isFileAvailable(filename)) {
757 return true;
760 #if !defined(EEPROM)
761 buf = strAppendFilename(&filename[sizeof(MODELS_PATH)], g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME);
762 strcpy(buf, TEXT_EXT);
763 if (isFileAvailable(filename)) {
764 return true;
766 #endif
768 return false;
771 int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable)
773 int retval = 0;
774 for (int i = min; i <= max; i++) {
775 if (isValueAvailable(i)) {
776 retval = i;
777 break;
780 return retval;
782 #if defined(MULTIMODULE)
784 // This maps OpenTX multi type with Pascal's Multi type
785 uint8_t convertMultiProtocol(uint8_t moduleIdx, uint8_t type)
788 // 15 for Multimodule is FrskyX or D16 which we map as a subprotocol of 3 (FrSky)
789 // all protos > frskyx are therefore also off by one
790 if (type >= 15)
791 type = type + 1;
793 // 25 is again a FrSky protocol (FrskyV) so shift again
794 if (type >= 25)
795 type = type + 1;
797 if (type == MODULE_SUBTYPE_MULTI_FRSKY) {
798 int subtype = g_model.moduleData[moduleIdx].subType;
799 if (subtype == MM_RF_FRSKY_SUBTYPE_D8) {
800 //D8
801 type = 3;
803 else if (subtype == MM_RF_FRSKY_SUBTYPE_V8) {
804 //V8
805 type = 25;
807 else {
808 type = 15;
811 return type;
814 // Third row is number of subtypes -1 (max valid subtype)
815 #define NO_SUBTYPE nullptr
817 // Table is designed to be shared with companion multi.cpp
819 // The subtype and options strings are only referenced here, so
820 // define them here to avoid duplication in all language files
821 // Also since these strings are ARM only and likely stay ARM only
822 // we don't need the special eeprom/flash string handling, just define them as
823 // local strings
825 const char STR_SUBTYPE_FLYSKY[] = "\004""Std\0""V9x9""V6x6""V912""CX20";
826 const char STR_SUBTYPE_HUBSAN[] = "\004""H107""H301""H501";
827 const char STR_SUBTYPE_FRSKY[] = "\007""D16\0 ""D8\0 ""D16 8ch""V8\0 ""LBT(EU)""LBT 8ch";
828 const char STR_SUBTYPE_HISKY[] = "\005""Std\0 ""HK310";
829 const char STR_SUBTYPE_V2X2[] = "\006""Std\0 ""JXD506";
830 const char STR_SUBTYPE_DSM[] = "\006""2 22ms""2 11ms""X 22ms""X 11ms";
831 const char STR_SUBTYPE_DEVO[] = "\004""8ch\0""10ch""12ch""6ch\0""7ch\0";
832 const char STR_SUBTYPE_YD717[] = "\007""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 ";
833 const char STR_SUBTYPE_KN[] = "\006""WLtoys""FeiLun";
834 const char STR_SUBTYPE_SYMAX[] = "\003""Std""X5C";
835 const char STR_SUBTYPE_SLT[] = "\006""V1_6ch""V2_8ch""Q100\0 ""Q200\0 ""MR100\0";
836 const char STR_SUBTYPE_CX10[] = "\007""Green\0 ""Blue\0 ""DM007\0 ""-\0 ""JC3015a""JC3015b""MK33041";
837 const char STR_SUBTYPE_CG023[] = "\005""Std\0 ""YD829";
838 const char STR_SUBTYPE_BAYANG[] = "\007""Std\0 ""H8S3D\0 ""X16 AH\0""IRDrone""DHD D4";
839 const char STR_SUBTYPE_MT99[] = "\006""MT99\0 ""H7\0 ""YZ\0 ""LS\0 ""FY805";
840 const char STR_SUBTYPE_MJXQ[] = "\007""WLH08\0 ""X600\0 ""X800\0 ""H26D\0 ""E010\0 ""H26WH\0 ""Phoenix";
841 const char STR_SUBTYPE_FY326[] = "\005""Std\0 ""FY319";
842 const char STR_SUBTYPE_HONTAI[] = "\007""Std\0 ""JJRC X1""X5C1\0 ""FQ_951";
843 const char STR_SUBTYPE_AFHDS2A[] = "\010""PWM,IBUS""PPM,IBUS""PWM,SBUS""PPM,SBUS";
844 const char STR_SUBTYPE_Q2X2[] = "\004""Q222""Q242""Q282";
845 const char STR_SUBTYPE_WK2x01[] = "\006""WK2801""WK2401""W6_5_1""W6_6_1""W6_HeL""W6_HeI";
846 const char STR_SUBTYPE_Q303[] = "\006""Std\0 ""CX35\0 ""CX10D\0""CX10WD";
847 const char STR_SUBTYPE_CABELL[] = "\007""V3\0 ""V3 Telm""-\0 ""-\0 ""-\0 ""-\0 ""F-Safe\0""Unbind\0";
848 const char STR_SUBTYPE_H83D[] = "\007""Std\0 ""H20H\0 ""H20Mini""H30Mini";
849 const char STR_SUBTYPE_CORONA[] = "\005""V1\0 ""V2\0 ""FD V3";
850 const char STR_SUBTYPE_HITEC[] = "\007""Optima\0""Opt Hub""Minima\0";
851 const char STR_SUBTYPE_BUGS_MINI[] = "\006""Std\0 ""Bugs3H";
852 const char STR_SUBTYPE_TRAXXAS[] = "\004""6519";
853 const char STR_SUBTYPE_E01X[] = "\005""E012\0""E015\0""E016H";
854 const char STR_SUBTYPE_GD00X[] = "\005""GD_V1""GD_V2";
855 const char STR_SUBTYPE_REDPINE[] = "\004""Fast""Slow";
856 const char STR_SUBTYPE_POTENSIC[] = "\003""A20";
857 const char STR_SUBTYPE_ZSX[] = "\007""280JJRC";
858 const char STR_SUBTYPE_FLYZONE[] = "\005""FZ410";
859 const char STR_SUBTYPE_FX816[] = "\003""P38";
860 const char STR_SUBTYPE_ESKY150[] = "\003""4CH""7CH";
862 const char* mm_options_strings::options[] = {
863 nullptr,
864 STR_MULTI_OPTION,
865 STR_MULTI_RFTUNE,
866 STR_MULTI_VIDFREQ,
867 STR_MULTI_FIXEDID,
868 STR_MULTI_TELEMETRY,
869 STR_MULTI_SERVOFREQ,
870 STR_MULTI_MAX_THROW,
871 STR_MULTI_RFCHAN
874 const mm_protocol_definition multi_protocols[] = {
875 // Protocol as defined in pulses\modules_constants.h, number of sub_protocols - 1, Failsafe supported, Disable channel mapping supported, Subtype string, Option type
876 {MODULE_SUBTYPE_MULTI_FLYSKY, 4, false, true, STR_SUBTYPE_FLYSKY, nullptr},
877 {MODULE_SUBTYPE_MULTI_HUBSAN, 2, false, false, STR_SUBTYPE_HUBSAN, STR_MULTI_VIDFREQ},
878 {MODULE_SUBTYPE_MULTI_FRSKY, 5, false, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE},
879 {MODULE_SUBTYPE_MULTI_HISKY, 1, false, true, STR_SUBTYPE_HISKY, nullptr},
880 {MODULE_SUBTYPE_MULTI_V2X2, 1, false, false, STR_SUBTYPE_V2X2, nullptr},
881 {MODULE_SUBTYPE_MULTI_DSM2, 3, false, true, STR_SUBTYPE_DSM, STR_MULTI_MAX_THROW},
882 {MODULE_SUBTYPE_MULTI_DEVO, 4, false, true, STR_SUBTYPE_DEVO, STR_MULTI_FIXEDID},
883 {MODULE_SUBTYPE_MULTI_YD717, 4, false, false, STR_SUBTYPE_YD717, nullptr},
884 {MODULE_SUBTYPE_MULTI_KN, 1, false, false, STR_SUBTYPE_KN, nullptr},
885 {MODULE_SUBTYPE_MULTI_SYMAX, 1, false, false, STR_SUBTYPE_SYMAX, nullptr},
886 {MODULE_SUBTYPE_MULTI_SLT, 4, false, true, STR_SUBTYPE_SLT, nullptr},
887 {MODULE_SUBTYPE_MULTI_CX10, 6, false, false, STR_SUBTYPE_CX10, nullptr},
888 {MODULE_SUBTYPE_MULTI_CG023, 1, false, false, STR_SUBTYPE_CG023, nullptr},
889 {MODULE_SUBTYPE_MULTI_BAYANG, 4, false, false, STR_SUBTYPE_BAYANG, STR_MULTI_TELEMETRY},
890 {MODULE_SUBTYPE_MULTI_MT99XX, 4, false, false, STR_SUBTYPE_MT99, nullptr},
891 {MODULE_SUBTYPE_MULTI_MJXQ, 6, false, false, STR_SUBTYPE_MJXQ, STR_MULTI_RFTUNE},
892 {MODULE_SUBTYPE_MULTI_FY326, 1, false, false, STR_SUBTYPE_FY326, nullptr},
893 {MODULE_SUBTYPE_MULTI_SFHSS, 0, true, true, NO_SUBTYPE, STR_MULTI_RFTUNE},
894 {MODULE_SUBTYPE_MULTI_HONTAI, 3, false, false, STR_SUBTYPE_HONTAI, nullptr},
895 {MODULE_SUBTYPE_MULTI_OLRS, 0, false, false, NO_SUBTYPE, STR_RFPOWER},
896 {MODULE_SUBTYPE_MULTI_FS_AFHDS2A, 3, true, true, STR_SUBTYPE_AFHDS2A, STR_MULTI_SERVOFREQ},
897 {MODULE_SUBTYPE_MULTI_Q2X2, 2, false, false, STR_SUBTYPE_Q2X2, nullptr},
898 {MODULE_SUBTYPE_MULTI_WK_2X01, 5, false, true, STR_SUBTYPE_WK2x01, nullptr},
899 {MODULE_SUBTYPE_MULTI_Q303, 3, false, false, STR_SUBTYPE_Q303, nullptr},
900 {MODULE_SUBTYPE_MULTI_CABELL, 7, false, false, STR_SUBTYPE_CABELL, STR_MULTI_OPTION},
901 {MODULE_SUBTYPE_MULTI_H83D, 3, false, false, STR_SUBTYPE_H83D, nullptr},
902 {MODULE_SUBTYPE_MULTI_CORONA, 2, false, false, STR_SUBTYPE_CORONA, STR_MULTI_RFTUNE},
903 {MODULE_SUBTYPE_MULTI_HITEC, 2, false, false, STR_SUBTYPE_HITEC, STR_MULTI_RFTUNE},
904 {MODULE_SUBTYPE_MULTI_BUGS_MINI, 1, false, false, STR_SUBTYPE_BUGS_MINI, nullptr},
905 {MODULE_SUBTYPE_MULTI_TRAXXAS, 0, false, false, STR_SUBTYPE_TRAXXAS, nullptr},
906 {MODULE_SUBTYPE_MULTI_E01X, 2, false, false, STR_SUBTYPE_E01X, STR_MULTI_OPTION},
907 {MODULE_SUBTYPE_MULTI_GD00X, 1, false, false, STR_SUBTYPE_GD00X, STR_MULTI_RFTUNE},
908 {MODULE_SUBTYPE_MULTI_KF606, 0, false, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
909 {MODULE_SUBTYPE_MULTI_REDPINE, 1, false, false, STR_SUBTYPE_REDPINE, STR_MULTI_RFTUNE},
910 {MODULE_SUBTYPE_MULTI_POTENSIC, 0, false, false, STR_SUBTYPE_POTENSIC, nullptr},
911 {MODULE_SUBTYPE_MULTI_ZSX, 0, false, false, STR_SUBTYPE_ZSX, nullptr},
912 {MODULE_SUBTYPE_MULTI_FLYZONE, 0, false, false, STR_SUBTYPE_FLYZONE, nullptr},
913 {MODULE_SUBTYPE_MULTI_FRSKYX_RX, 0, false, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
914 {MODULE_SUBTYPE_MULTI_ESky, 0, false, true, NO_SUBTYPE, nullptr},
915 {MODULE_SUBTYPE_MULTI_J6PRO, 0, false, true, NO_SUBTYPE, nullptr},
916 {MODULE_SUBTYPE_MULTI_ESKY150, 1, false, false, STR_SUBTYPE_ESKY150, nullptr},
917 {MODULE_SUBTYPE_MULTI_FX816, 0, false, false, STR_SUBTYPE_FX816, nullptr},
918 {MODULE_SUBTYPE_MULTI_HOTT, 0, true, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
919 {MM_RF_CUSTOM_SELECTED, 7, true, true, NO_SUBTYPE, STR_MULTI_OPTION},
921 // Sentinel and default for protocols not listed above (MM_RF_CUSTOM is 0xff)
922 {0xfe, 0, false, true, NO_SUBTYPE, nullptr}
925 #undef NO_SUBTYPE
927 const mm_protocol_definition *getMultiProtocolDefinition (uint8_t protocol)
929 const mm_protocol_definition *pdef;
930 for (pdef = multi_protocols; pdef->protocol != 0xfe; pdef++) {
931 if (pdef->protocol == protocol)
932 return pdef;
934 // Return the empty last protocol
935 return pdef;
937 #endif
939 void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags)
941 lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, idx+1, 0);
942 if (ZEXIST(g_eeGeneral.anaNames[idx]) || (flags && s_editMode > 0))
943 editName(x, y, g_eeGeneral.anaNames[idx], LEN_ANA_NAME, event, flags);
944 else
945 lcdDrawMMM(x, y, flags);