Files
edgetx-gx12/companion/src/firmwares/rawsource.cpp
T

443 lines
14 KiB
C++

/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* 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 "rawsource.h"
#include "eeprominterface.h"
#include "radiodata.h"
#include "modeldata.h"
#include "radiodataconversionstate.h"
#include <float.h>
/*
* RawSourceRange
*/
float RawSourceRange::getValue(int value)
{
return float(value) * step;
}
/*
* RawSource
*/
RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags) const
{
RawSourceRange result;
Firmware * firmware = Firmware::getCurrentVariant();
switch (type) {
case SOURCE_TYPE_TELEMETRY:
{
div_t qr = div(index, 3);
const SensorData & sensor = model->sensorData[qr.quot];
if (sensor.prec == 2)
result.step = 0.01;
else if (sensor.prec == 1)
result.step = 0.1;
else
result.step = 1;
result.min = -30000 * result.step;
result.max = +30000 * result.step;
result.decimals = sensor.prec;
result.unit = sensor.unitToString();
break;
}
case SOURCE_TYPE_LUA_OUTPUT:
result.max = 30000;
result.min = -result.max;
break;
case SOURCE_TYPE_TRIM:
result.max = (model && model->extendedTrims ? firmware->getCapability(ExtendedTrimsRange) : firmware->getCapability(TrimsRange));
result.min = -result.max;
break;
case SOURCE_TYPE_GVAR: {
GVarData gv = model->gvarData[index - 1];
result.step = gv.multiplierGet();
result.decimals = gv.prec;
result.max = gv.getMaxPrec();
result.min = gv.getMinPrec();
result.unit = gv.unitToString();
break;
}
case SOURCE_TYPE_SPECIAL:
if (abs(index) == 1) { // Batt
result.step = 0.1;
result.decimals = 1;
result.max = 25.5;
result.unit = tr("V");
}
else if (abs(index) == 2) { // Time
result.step = 60;
result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution
result.unit = tr("s");
}
else if (abs(index) == 3) { // GPS
result.max = 30000;
result.min = -result.max;
}
break;
case SOURCE_TYPE_TIMER:
result.step = 1;
result.max = 9 * 60 * 60 - 1; // 8:59:59 (to match firmware)
result.min = -result.max;
result.unit = tr("s");
break;
case SOURCE_TYPE_CH:
result.max = model->getChannelsMax(false);
result.min = -result.max;
break;
default:
result.max = 100;
result.min = -result.max;
break;
}
if (flags & RANGE_ABS_FUNCTION) {
result.min = 0;
}
return result;
}
QString RawSource::toString(const ModelData * model, const GeneralSettings * const generalSettings, Board::Type board, bool prefixCustomName) const
{
if (index < 0)
return CPN_STR_SRC_INDICATOR_NEG % RawSource(type, -index).toString(model, generalSettings, board, prefixCustomName);
if (board == Board::BOARD_UNKNOWN)
board = getCurrentBoard();
static const QString trimsAir[] = {
"", tr("Trim Rud"), tr("Trim Ele"), tr("Trim Thr"), tr("Trim Ail"), tr("Trim 5"), tr("Trim 6"), tr("Trim 7"), tr("Trim 8")
};
static const QString trimsSurface[] = {
"", tr("Trim ST"), tr("Trim TH"), tr("Trim 3"), tr("Trim 4"), tr("Trim 5"), tr("Trim 6"), tr("Trim 7"), tr("Trim 8")
};
static const QString trims2[] = {
"", tr("TrmH"), tr("TrmV")
};
static const QString special[] = {
"", tr("Batt"), tr("Time"), tr("GPS"), tr("Reserved1"), tr("Reserved2"), tr("Reserved3"), tr("Reserved4")
};
static const QString rotary[] = { "", tr("REa"), tr("REb") };
QString result;
QString dfltName;
QString custName;
switch (type) {
case SOURCE_TYPE_NONE:
return QString(CPN_STR_NONE_ITEM);
case SOURCE_TYPE_VIRTUAL_INPUT:
{
const char * name = nullptr;
if (model && index <= CPN_MAX_INPUTS)
name = model->inputNames[index - 1];
return RadioData::getElementName(tr("I", "as in Input"), index, name);
}
case SOURCE_TYPE_LUA_OUTPUT:
{
div_t qr = div(index - 1, 16);
return tr("LUA%1%2").arg(qr.quot + 1).arg(QChar('a' + qr.rem));
}
case SOURCE_TYPE_INPUT:
dfltName = Boards::getInputName(index - 1, board);
if (generalSettings && index <= CPN_MAX_INPUTS)
custName = QString(generalSettings->inputConfig[index - 1].name).trimmed();
return DataHelpers::getCompositeName(dfltName, custName, prefixCustomName);
case SOURCE_TYPE_TRIM:
return (Boards::getCapability(board, Board::NumTrims) == 2 ? CHECK_IN_ARRAY(trims2, index) :
(Boards::isAir(board) ? CHECK_IN_ARRAY(trimsAir, index) :
CHECK_IN_ARRAY(trimsSurface, index)));
case SOURCE_TYPE_MIN:
return tr("MIN");
case SOURCE_TYPE_MAX:
return tr("MAX");
case SOURCE_TYPE_SWITCH:
dfltName = Boards::getSwitchInfo(index - 1, board).name.c_str();
if (Boards::isSwitchFunc(index - 1, board)) {
if (model) {
int fsindex = Boards::getSwitchTagNum(index - 1, board) - 1;
if (fsindex >= 0 && fsindex < CPN_MAX_SWITCHES_FUNCTION)
custName = QString(model->customSwitches[fsindex].name).trimmed();
}
}
else {
if (generalSettings && index <= CPN_MAX_SWITCHES) {
custName = QString(generalSettings->switchConfig[index - 1].name).trimmed();
}
}
return DataHelpers::getCompositeName(dfltName, custName, prefixCustomName);
case SOURCE_TYPE_CUSTOM_SWITCH:
return RawSwitch(SWITCH_TYPE_VIRTUAL, index).toString(); // RawSwitch uses 1 based index
case SOURCE_TYPE_CYC:
return tr("CYC%1").arg(index);
case SOURCE_TYPE_PPM:
return RadioData::getElementName(tr("TR", "as in Trainer"), index);
case SOURCE_TYPE_CH:
if (model && index <= CPN_MAX_CHNOUT)
result = QString(model->limitData[index - 1].nameToString(index - 1)).trimmed();
if (result.isEmpty())
return LimitData().nameToString(index - 1);
return result;
case SOURCE_TYPE_SPECIAL:
return CHECK_IN_ARRAY(special, abs(index));
case SOURCE_TYPE_TIMER:
if (model && index <= CPN_MAX_TIMERS)
result = QString(model->timers[index - 1].nameToString(index - 1)).trimmed();
if (result.isEmpty())
result = TimerData().nameToString(index - 1);
return result;
case SOURCE_TYPE_TELEMETRY:
{
div_t qr = div(index - 1, 3);
if (model && qr.quot < CPN_MAX_SENSORS)
result = QString(model->sensorData[qr.quot].nameToString(qr.quot)).trimmed();
if (result.isEmpty())
result = SensorData().nameToString(qr.quot);
if (qr.rem)
result += (qr.rem == 1 ? "-" : "+");
return result;
}
case SOURCE_TYPE_GVAR:
if (model && index <= CPN_MAX_GVARS)
result = QString(model->gvarData[index - 1].nameToString(index - 1)).trimmed();
if (result.isEmpty())
result = GVarData().nameToString(index - 1);
return result;
case SOURCE_TYPE_SPACEMOUSE:
return tr("sm%1").arg(QChar('A' + (index - 1)));
case SOURCE_TYPE_FUNCTIONSWITCH_GROUP:
return tr("GR%1").arg(index);
default:
return QString(CPN_STR_UNKNOWN_ITEM);
}
}
bool RawSource::isStick(Board::Type board) const
{
if (board == Board::BOARD_UNKNOWN)
board = getCurrentBoard();
if (type == SOURCE_TYPE_INPUT && index - 1 < Boards::getCapability(board, Board::Sticks)) {
return true;
}
return false;
}
bool RawSource::isAvailable(const ModelData * const model,
const GeneralSettings * const gs,
Board::Type board,
const int flags) const
{
if (type == SOURCE_TYPE_NONE && index == 0)
return true;
if (board == Board::BOARD_UNKNOWN)
board = getCurrentBoard();
Boards b(board);
Firmware *firmware = getCurrentFirmware();
if (type == SOURCE_TYPE_CH && abs(index) > CPN_MAX_CHNOUT)
return false;
if (type == SOURCE_TYPE_FUNCTIONSWITCH_GROUP && index >= b.getCapability(Board::FunctionSwitches))
return false;
if (type == SOURCE_TYPE_CUSTOM_SWITCH && abs(index) > CPN_MAX_LOGICAL_SWITCHES)
return false;
if (type == SOURCE_TYPE_LUA_OUTPUT && div(abs(index - 1), 16).quot >= CPN_MAX_SCRIPTS)
return false;
if (type == SOURCE_TYPE_INPUT && abs(index) > b.getCapability(Board::Inputs))
return false;
if (type == SOURCE_TYPE_SWITCH && abs(index) > b.getCapability(Board::Switches))
return false;
if (type == SOURCE_TYPE_SPECIAL && abs(index) >= SOURCE_TYPE_SPECIAL_FIRST_RESERVED)
return false;
if (type == SOURCE_TYPE_TIMER && abs(index) > CPN_MAX_TIMERS)
return false;
if (type == SOURCE_TYPE_TELEMETRY && div(abs(index), 3).quot > CPN_MAX_SENSORS)
return false;
if (type == SOURCE_TYPE_CYC && !firmware->getCapability(Heli))
return false;
if (type == SOURCE_TYPE_GVAR && abs(index) > firmware->getCapability(Gvars))
return false;
if (model) {
if (type == SOURCE_TYPE_TIMER && model->timers[abs(index) - 1].isModeOff())
return false;
if (type == SOURCE_TYPE_SWITCH && b.isSwitchFunc(abs(index) - 1, board) &&
!model->isFunctionSwitchSourceAllowed(b.getSwitchTagNum(abs(index) - 1, board) - 1))
return false;
if (type == SOURCE_TYPE_VIRTUAL_INPUT && !model->isInputValid(abs(index) - 1))
return false;
if (type == SOURCE_TYPE_PPM && model->trainerMode == TRAINER_MODE_OFF)
return false;
if (type == SOURCE_TYPE_CUSTOM_SWITCH && model->logicalSw[abs(index) - 1].isEmpty())
return false;
if (type == SOURCE_TYPE_TELEMETRY) {
if (!model->sensorData[div(abs(index) - 1, 3).quot].isAvailable()) {
return false;
}
}
if (type == SOURCE_TYPE_CH && !model->hasMixes(abs(index) - 1))
return false;
if (type == SOURCE_TYPE_FUNCTIONSWITCH_GROUP) {
if (!b.getCapability(Board::FunctionSwitches))
return false;
else if (model->getFuncGroupSwitchCount(abs(index), CPN_MAX_SWITCHES_FUNCTION) == 0)
return false;
}
}
else {
if (type == SOURCE_TYPE_FUNCTIONSWITCH_GROUP && b.getCapability(Board::FunctionSwitches))
return false;
}
if (gs) {
if (type == SOURCE_TYPE_INPUT) {
if (!gs->isInputAvailable(abs(index) - 1))
return false;
if (gs->inputConfig[abs(index) - 1].flexType == Board::FLEX_SWITCH)
return false;
}
if (type == SOURCE_TYPE_SWITCH && !b.isSwitchFunc(abs(index) - 1, board) && IS_HORUS_OR_TARANIS(board) &&
!gs->switchSourceAllowed(abs(index) - 1))
return false;
}
else {
if (type == SOURCE_TYPE_INPUT) {
if (!Boards::isInputAvailable(abs(index) - 1, board))
return false;
if (Boards::getInputInfo(abs(index) - 1, board).flexType == Board::FLEX_SWITCH)
return false;
}
}
if (type == SOURCE_TYPE_TRIM && abs(index) > b.getCapability(Board::NumTrims))
return false;
if (type == SOURCE_TYPE_SPACEMOUSE &&
(abs(index) > CPN_MAX_SPACEMOUSE ||
(!(gs->serialPort[GeneralSettings::SP_AUX1] == GeneralSettings::AUX_SERIAL_SPACEMOUSE ||
gs->serialPort[GeneralSettings::SP_AUX2] == GeneralSettings::AUX_SERIAL_SPACEMOUSE))))
return false;
if (type == SOURCE_TYPE_INPUT && (flags & AVAILABLE_CONTROLSRC) &&
Boards::isInputGyroAxis(abs(index) - 1, board))
return false;
return true;
}
RawSource RawSource::convert(RadioDataConversionState & cstate)
{
cstate.setItemType(tr("Source"), 1);
RadioDataConversionState::LogField oldData(index, toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType));
if (type == SOURCE_TYPE_INPUT)
index = Boards::getInputIndex(Boards::getInputTag(oldData.id, cstate.fromType), Board::LVT_TAG, cstate.toType);
else if (type == SOURCE_TYPE_SWITCH)
index = Boards::getSwitchIndex(Boards::getSwitchTag(oldData.id, cstate.fromType), Board::LVT_TAG, cstate.toType);
// final validation (we do not pass model to isAvailable() because we don't know what has or hasn't been converted)
if (index < 0 || !isAvailable(nullptr, cstate.toGS(), cstate.toType)) {
cstate.setInvalid(oldData);
clear(); // no source is safer than an invalid one
cstate.setConverted(oldData, RadioDataConversionState::LogField(index, tr("None")));
}
return *this;
}
// static
StringTagMappingTable RawSource::getSpecialTypesLookupTable()
{
StringTagMappingTable tbl;
tbl.insert(tbl.end(), {
{std::to_string(SOURCE_TYPE_SPECIAL_NONE), "NONE"},
{std::to_string(SOURCE_TYPE_SPECIAL_TX_BATT), "TX_VOLTAGE"},
{std::to_string(SOURCE_TYPE_SPECIAL_TX_TIME), "TX_TIME"},
{std::to_string(SOURCE_TYPE_SPECIAL_TX_GPS), "TX_GPS"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED1), "RESERVED1"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED2), "RESERVED2"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED3), "RESERVED3"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED4), "RESERVED4"},
});
return tbl;
}