mirror of
https://github.com/opentx/opentx.git
synced 2025-07-15 12:25:12 +03:00
434 lines
10 KiB
C++
434 lines
10 KiB
C++
/*
|
|
* 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"
|
|
|
|
#if defined(CPUARM)
|
|
|
|
int circularIncDec(int current, int inc, int min, int max, IsValueAvailable isValueAvailable)
|
|
{
|
|
do {
|
|
current += inc;
|
|
if (current < min)
|
|
current = max;
|
|
else if (current > max)
|
|
current = min;
|
|
if (!isValueAvailable || isValueAvailable(current))
|
|
return current;
|
|
} while(1);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(VIRTUALINPUTS)
|
|
bool isInputAvailable(int input)
|
|
{
|
|
for (int i=0; i<MAX_EXPOS; i++) {
|
|
ExpoData * expo = expoAddress(i);
|
|
if (!EXPO_VALID(expo))
|
|
break;
|
|
if (expo->chn == input)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool isChannelUsed(int channel)
|
|
{
|
|
for (int i=0; i<MAX_MIXERS; ++i) {
|
|
MixData *md = mixAddress(i);
|
|
if (md->srcRaw == 0) return false;
|
|
if (md->destCh == channel) return true;
|
|
if (md->destCh > channel) return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int getChannelsUsed()
|
|
{
|
|
int result = 0;
|
|
int lastCh = -1;
|
|
for (int i=0; i<MAX_MIXERS; ++i) {
|
|
MixData *md = mixAddress(i);
|
|
if (md->srcRaw == 0) return result;
|
|
if (md->destCh != lastCh) { ++result; lastCh = md->destCh; }
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool isSourceAvailable(int source)
|
|
{
|
|
#if defined(VIRTUALINPUTS)
|
|
if (source>=MIXSRC_FIRST_INPUT && source<=MIXSRC_LAST_INPUT) {
|
|
return isInputAvailable(source - MIXSRC_FIRST_INPUT);
|
|
}
|
|
#endif
|
|
|
|
#if defined(LUA_MODEL_SCRIPTS)
|
|
if (source>=MIXSRC_FIRST_LUA && source<=MIXSRC_LAST_LUA) {
|
|
div_t qr = div(source-MIXSRC_FIRST_LUA, MAX_SCRIPT_OUTPUTS);
|
|
return (qr.rem<scriptInputsOutputs[qr.quot].outputsCount);
|
|
}
|
|
#elif defined(VIRTUALINPUTS)
|
|
if (source>=MIXSRC_FIRST_LUA && source<=MIXSRC_LAST_LUA)
|
|
return false;
|
|
#endif
|
|
|
|
if (source>=MIXSRC_FIRST_POT && source<=MIXSRC_LAST_POT) {
|
|
return IS_POT_AVAILABLE(POT1+source-MIXSRC_FIRST_POT);
|
|
}
|
|
|
|
if (source>=MIXSRC_FIRST_SWITCH && source<=MIXSRC_LAST_SWITCH) {
|
|
return SWITCH_EXISTS(source-MIXSRC_FIRST_SWITCH);
|
|
}
|
|
|
|
#if !defined(HELI)
|
|
if (source>=MIXSRC_CYC1 && source<=MIXSRC_CYC3)
|
|
return false;
|
|
#endif
|
|
|
|
if (source>=MIXSRC_FIRST_CH && source<=MIXSRC_LAST_CH) {
|
|
return isChannelUsed(source-MIXSRC_FIRST_CH);
|
|
}
|
|
|
|
if (source>=MIXSRC_FIRST_LOGICAL_SWITCH && source<=MIXSRC_LAST_LOGICAL_SWITCH) {
|
|
LogicalSwitchData * cs = lswAddress(source-MIXSRC_FIRST_LOGICAL_SWITCH);
|
|
return (cs->func != LS_FUNC_NONE);
|
|
}
|
|
|
|
#if !defined(GVARS)
|
|
if (source>=MIXSRC_GVAR1 && source<=MIXSRC_LAST_GVAR)
|
|
return false;
|
|
#endif
|
|
|
|
if (source>=MIXSRC_RESERVE1 && source<=MIXSRC_RESERVE5)
|
|
return false;
|
|
|
|
if (source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
|
|
div_t qr = div(source-MIXSRC_FIRST_TELEM, 3);
|
|
if (qr.rem == 0)
|
|
return isTelemetryFieldAvailable(qr.quot);
|
|
else
|
|
return isTelemetryFieldComparisonAvailable(qr.quot);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isSourceAvailableInGlobalFunctions(int source)
|
|
{
|
|
if (source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
|
|
return false;
|
|
}
|
|
return isSourceAvailable(source);
|
|
}
|
|
|
|
bool isSourceAvailableInCustomSwitches(int source)
|
|
{
|
|
bool result = isSourceAvailable(source);
|
|
|
|
#if defined(FRSKY)
|
|
if (result && source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
|
|
div_t qr = div(source-MIXSRC_FIRST_TELEM, 3);
|
|
result = isTelemetryFieldComparisonAvailable(qr.quot);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
bool isInputSourceAvailable(int source)
|
|
{
|
|
if (source>=MIXSRC_FIRST_POT && source<=MIXSRC_LAST_POT) {
|
|
return IS_POT_AVAILABLE(POT1+source-MIXSRC_FIRST_POT);
|
|
}
|
|
|
|
if (source>=MIXSRC_Rud && source<=MIXSRC_MAX)
|
|
return true;
|
|
|
|
if (source>=MIXSRC_FIRST_TRIM && source<=MIXSRC_LAST_TRIM)
|
|
return true;
|
|
|
|
if (source>=MIXSRC_FIRST_SWITCH && source<=MIXSRC_LAST_SWITCH)
|
|
return SWITCH_EXISTS(source-MIXSRC_FIRST_SWITCH);
|
|
|
|
if (source>=MIXSRC_FIRST_CH && source<=MIXSRC_LAST_CH)
|
|
return true;
|
|
|
|
if (source>=MIXSRC_FIRST_TRAINER && source<=MIXSRC_LAST_TRAINER)
|
|
return true;
|
|
|
|
if (source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
|
|
div_t qr = div(source-MIXSRC_FIRST_TELEM, 3);
|
|
return isTelemetryFieldAvailable(qr.quot) && isTelemetryFieldComparisonAvailable(qr.quot);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum SwitchContext
|
|
{
|
|
LogicalSwitchesContext,
|
|
ModelCustomFunctionsContext,
|
|
GeneralCustomFunctionsContext,
|
|
TimersContext,
|
|
MixesContext
|
|
};
|
|
|
|
bool isLogicalSwitchAvailable(int index)
|
|
{
|
|
LogicalSwitchData * lsw = lswAddress(index);
|
|
return (lsw->func != LS_FUNC_NONE);
|
|
}
|
|
|
|
bool isSwitchAvailable(int swtch, SwitchContext context)
|
|
{
|
|
bool negative = false;
|
|
|
|
if (swtch < 0) {
|
|
negative = true;
|
|
if (swtch == -SWSRC_ON || swtch == -SWSRC_ONE) {
|
|
return false;
|
|
}
|
|
swtch = -swtch;
|
|
}
|
|
|
|
#if defined(PCBSKY9X)
|
|
if (swtch >= SWSRC_FIRST_SWITCH && swtch <= SWSRC_LAST_SWITCH) {
|
|
negative = negative;
|
|
return true;
|
|
}
|
|
#else
|
|
if (swtch >= SWSRC_FIRST_SWITCH && swtch <= SWSRC_LAST_SWITCH) {
|
|
div_t swinfo = switchInfo(swtch);
|
|
if (!SWITCH_EXISTS(swinfo.quot)) {
|
|
return false;
|
|
}
|
|
if (!IS_3POS(swinfo.quot)) {
|
|
if (negative) {
|
|
return false;
|
|
}
|
|
if (IS_3POS_MIDDLE(swinfo.rem)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#if NUM_XPOTS > 0
|
|
if (swtch >= SWSRC_FIRST_MULTIPOS_SWITCH && swtch <= SWSRC_LAST_MULTIPOS_SWITCH) {
|
|
int index = (swtch - SWSRC_FIRST_MULTIPOS_SWITCH) / XPOTS_MULTIPOS_COUNT;
|
|
if (IS_POT_MULTIPOS(POT1+index)) {
|
|
StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1+index];
|
|
return (calib->count >= ((swtch - SWSRC_FIRST_MULTIPOS_SWITCH) % XPOTS_MULTIPOS_COUNT));
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (swtch >= SWSRC_FIRST_LOGICAL_SWITCH && swtch <= SWSRC_LAST_LOGICAL_SWITCH) {
|
|
if (context == GeneralCustomFunctionsContext) {
|
|
return false;
|
|
}
|
|
else if (context != LogicalSwitchesContext) {
|
|
return isLogicalSwitchAvailable(swtch - SWSRC_FIRST_LOGICAL_SWITCH);
|
|
}
|
|
}
|
|
|
|
if (context != ModelCustomFunctionsContext && context != GeneralCustomFunctionsContext && (swtch == SWSRC_ON || swtch == SWSRC_ONE)) {
|
|
return false;
|
|
}
|
|
|
|
if (swtch >= SWSRC_FIRST_FLIGHT_MODE && swtch <= SWSRC_LAST_FLIGHT_MODE) {
|
|
if (context == MixesContext || context == GeneralCustomFunctionsContext) {
|
|
return false;
|
|
}
|
|
else {
|
|
swtch -= SWSRC_FIRST_FLIGHT_MODE;
|
|
if (swtch == 0) {
|
|
return true;
|
|
}
|
|
FlightModeData * fm = flightModeAddress(swtch);
|
|
return (fm->swtch != SWSRC_NONE);
|
|
}
|
|
}
|
|
|
|
if (swtch >= SWSRC_FIRST_SENSOR && swtch <= SWSRC_LAST_SENSOR) {
|
|
return isTelemetryFieldAvailable(swtch - SWSRC_FIRST_SENSOR);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isSwitchAvailableInLogicalSwitches(int swtch)
|
|
{
|
|
return isSwitchAvailable(swtch, LogicalSwitchesContext);
|
|
}
|
|
|
|
bool isSwitchAvailableInCustomFunctions(int swtch)
|
|
{
|
|
if (menuHandlers[menuLevel] == menuModelCustomFunctions)
|
|
return isSwitchAvailable(swtch, ModelCustomFunctionsContext);
|
|
else
|
|
return isSwitchAvailable(swtch, GeneralCustomFunctionsContext);
|
|
}
|
|
|
|
bool isSwitchAvailableInMixes(int swtch)
|
|
{
|
|
return isSwitchAvailable(swtch, MixesContext);
|
|
}
|
|
|
|
bool isSwitchAvailableInTimers(int swtch)
|
|
{
|
|
if (swtch >= 0) {
|
|
if (swtch < TMRMODE_COUNT)
|
|
return true;
|
|
else
|
|
swtch -= TMRMODE_COUNT-1;
|
|
}
|
|
else {
|
|
if (swtch > -TMRMODE_COUNT)
|
|
return false;
|
|
else
|
|
swtch += TMRMODE_COUNT-1;
|
|
}
|
|
|
|
return isSwitchAvailable(swtch, TimersContext);
|
|
}
|
|
|
|
bool isThrottleSourceAvailable(int source)
|
|
{
|
|
if (source >= THROTTLE_SOURCE_FIRST_POT && source < THROTTLE_SOURCE_FIRST_POT+NUM_POTS && !IS_POT_AVAILABLE(POT1+source-THROTTLE_SOURCE_FIRST_POT))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool isLogicalSwitchFunctionAvailable(int function)
|
|
{
|
|
return function != LS_FUNC_RANGE;
|
|
}
|
|
|
|
bool isAssignableFunctionAvailable(int function)
|
|
{
|
|
#if defined(OVERRIDE_CHANNEL_FUNCTION) || defined(GVARS)
|
|
bool modelFunctions = (menuHandlers[menuLevel] == menuModelCustomFunctions);
|
|
#endif
|
|
|
|
switch (function) {
|
|
case FUNC_OVERRIDE_CHANNEL:
|
|
#if defined(OVERRIDE_CHANNEL_FUNCTION)
|
|
return modelFunctions;
|
|
#else
|
|
return false;
|
|
#endif
|
|
case FUNC_ADJUST_GVAR:
|
|
#if defined(GVARS)
|
|
return modelFunctions;
|
|
#else
|
|
return false;
|
|
#endif
|
|
#if !defined(HAPTIC)
|
|
case FUNC_HAPTIC:
|
|
#endif
|
|
case FUNC_RESERVE4:
|
|
#if !defined(DANGEROUS_MODULE_FUNCTIONS)
|
|
case FUNC_RANGECHECK:
|
|
case FUNC_BIND:
|
|
#endif
|
|
#if !defined(LUA)
|
|
case FUNC_PLAY_SCRIPT:
|
|
#endif
|
|
case FUNC_RESERVE5:
|
|
return false;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool isSourceAvailableInGlobalResetSpecialFunction(int index)
|
|
{
|
|
if (index >= FUNC_RESET_PARAM_FIRST_TELEM)
|
|
return false;
|
|
else
|
|
return isSourceAvailableInResetSpecialFunction(index);
|
|
}
|
|
|
|
bool isSourceAvailableInResetSpecialFunction(int index)
|
|
{
|
|
if (index >= FUNC_RESET_PARAM_FIRST_TELEM) {
|
|
TelemetrySensor & telemetrySensor = g_model.telemetrySensors[index-FUNC_RESET_PARAM_FIRST_TELEM];
|
|
return telemetrySensor.isAvailable();
|
|
}
|
|
#if TIMERS < 3
|
|
else if (index == FUNC_RESET_TIMER3) {
|
|
return false;
|
|
}
|
|
#endif
|
|
#if TIMERS < 2
|
|
else if (index == FUNC_RESET_TIMER2) {
|
|
return false;
|
|
}
|
|
#endif
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool isModuleAvailable(int module)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool isRfProtocolAvailable(int protocol)
|
|
{
|
|
#if defined(MODULE_D16_EU_ONLY_SUPPORT)
|
|
if (protocol == RF_PROTO_D8) {
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool modelHasNotes()
|
|
{
|
|
char filename[sizeof(MODELS_PATH)+1+sizeof(g_model.header.name)+sizeof(TEXT_EXT)] = MODELS_PATH "/";
|
|
char *buf = strcat_currentmodelname(&filename[sizeof(MODELS_PATH)]);
|
|
strcpy(buf, TEXT_EXT);
|
|
return isFileAvailable(filename);
|
|
}
|
|
|
|
int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable)
|
|
{
|
|
int retval = 0;
|
|
for (int i = min; i <= max; i++) {
|
|
if (isValueAvailable(i)) {
|
|
retval = i;
|
|
break;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
#endif
|