489 lines
15 KiB
C++
489 lines
15 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 "rawsource.h"
|
|
|
|
#include "eeprominterface.h"
|
|
#include "radiodata.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];
|
|
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 (index == 0) { //Batt
|
|
result.step = 0.1;
|
|
result.decimals = 1;
|
|
result.max = 25.5;
|
|
result.unit = tr("V");
|
|
}
|
|
else if (index == 1) { //Time
|
|
result.step = 60;
|
|
result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution
|
|
result.unit = tr("s");
|
|
}
|
|
else if (index == 2) { //GPS
|
|
result.max = 30000;
|
|
result.min = -result.max;
|
|
}
|
|
else { // Timers 1 - 3
|
|
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) const
|
|
{
|
|
if (board == Board::BOARD_UNKNOWN) {
|
|
board = getCurrentBoard();
|
|
}
|
|
|
|
static const QString trims[] = {
|
|
tr("TrmR"), tr("TrmE"), tr("TrmT"), tr("TrmA"), tr("Trm5"), tr("Trm6")
|
|
};
|
|
|
|
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") };
|
|
|
|
if (index<0) {
|
|
return QString(CPN_STR_UNKNOWN_ITEM);
|
|
}
|
|
|
|
QString result;
|
|
int genAryIdx = 0;
|
|
switch (type) {
|
|
case SOURCE_TYPE_NONE:
|
|
return QString(CPN_STR_NONE_ITEM);
|
|
|
|
case SOURCE_TYPE_VIRTUAL_INPUT:
|
|
{
|
|
const char * name = nullptr;
|
|
if (model)
|
|
name = model->inputNames[index];
|
|
return RadioData::getElementName(tr("I", "as in Input"), index + 1, name);
|
|
}
|
|
|
|
case SOURCE_TYPE_LUA_OUTPUT:
|
|
return tr("LUA%1%2").arg(index / 16 + 1).arg(QChar('a' + index % 16));
|
|
|
|
case SOURCE_TYPE_STICK:
|
|
if (generalSettings) {
|
|
if (isPot(&genAryIdx))
|
|
result = QString(generalSettings->potName[genAryIdx]);
|
|
else if (isSlider(&genAryIdx))
|
|
result = QString(generalSettings->sliderName[genAryIdx]);
|
|
else if (isStick(&genAryIdx))
|
|
result = QString(generalSettings->stickName[genAryIdx]);
|
|
}
|
|
if (result.trimmed().isEmpty())
|
|
result = Boards::getAnalogInputName(board, index);
|
|
return result;
|
|
|
|
case SOURCE_TYPE_TRIM:
|
|
return (Boards::getCapability(board, Board::NumTrims) == 2 ? CHECK_IN_ARRAY(trims2, index) : CHECK_IN_ARRAY(trims, index));
|
|
|
|
case SOURCE_TYPE_ROTARY_ENCODER:
|
|
return CHECK_IN_ARRAY(rotary, index);
|
|
|
|
case SOURCE_TYPE_MIN:
|
|
return tr("MIN");
|
|
|
|
case SOURCE_TYPE_MAX:
|
|
return tr("MAX");
|
|
|
|
case SOURCE_TYPE_SWITCH:
|
|
if (generalSettings)
|
|
result = QString(generalSettings->switchName[index]).trimmed();
|
|
if (result.isEmpty())
|
|
result = Boards::getSwitchInfo(board, index).name;
|
|
return result;
|
|
|
|
case SOURCE_TYPE_FUNCTIONSWITCH:
|
|
if (model)
|
|
result = QString(model->functionSwitchNames[index]).trimmed();
|
|
if (result.isEmpty())
|
|
result = tr("SW%1").arg(index + 1);
|
|
return result;
|
|
|
|
case SOURCE_TYPE_CUSTOM_SWITCH:
|
|
return RawSwitch(SWITCH_TYPE_VIRTUAL, index + 1).toString();
|
|
|
|
case SOURCE_TYPE_CYC:
|
|
return tr("CYC%1").arg(index + 1);
|
|
|
|
case SOURCE_TYPE_PPM:
|
|
return RadioData::getElementName(tr("TR", "as in Trainer"), index + 1);
|
|
|
|
case SOURCE_TYPE_CH:
|
|
if (model)
|
|
return model->limitData[index].nameToString(index);
|
|
else
|
|
return LimitData().nameToString(index);
|
|
|
|
case SOURCE_TYPE_SPECIAL:
|
|
if (index >= SOURCE_TYPE_SPECIAL_FIRST_TIMER && index <= SOURCE_TYPE_SPECIAL_LAST_TIMER) {
|
|
if (model)
|
|
result = model->timers[index - SOURCE_TYPE_SPECIAL_FIRST_TIMER].nameToString(index - SOURCE_TYPE_SPECIAL_FIRST_TIMER);
|
|
else
|
|
result = TimerData().nameToString(index - SOURCE_TYPE_SPECIAL_FIRST_TIMER);
|
|
}
|
|
else
|
|
result = CHECK_IN_ARRAY(special, index);
|
|
|
|
return result;
|
|
|
|
case SOURCE_TYPE_TELEMETRY:
|
|
{
|
|
div_t qr = div(index, 3);
|
|
if (model)
|
|
result = model->sensorData[qr.quot].nameToString(qr.quot);
|
|
else
|
|
result = SensorData().nameToString(qr.quot);
|
|
if (qr.rem)
|
|
result += (qr.rem == 1 ? "-" : "+");
|
|
return result;
|
|
}
|
|
|
|
case SOURCE_TYPE_GVAR:
|
|
if (model)
|
|
return model->gvarData[index].nameToString(index);
|
|
else
|
|
return GVarData().nameToString(index);
|
|
|
|
case SOURCE_TYPE_SPACEMOUSE:
|
|
return tr("sm%1").arg(QChar('A' + index));
|
|
|
|
default:
|
|
return QString(CPN_STR_UNKNOWN_ITEM);
|
|
}
|
|
}
|
|
|
|
bool RawSource::isStick(int * stickIndex, Board::Type board) const
|
|
{
|
|
if (board == Board::BOARD_UNKNOWN)
|
|
board = getCurrentBoard();
|
|
|
|
if (type == SOURCE_TYPE_STICK && index < Boards::getCapability(board, Board::Sticks)) {
|
|
if (stickIndex)
|
|
*stickIndex = index;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RawSource::isPot(int * potsIndex, Board::Type board) const
|
|
{
|
|
if (board == Board::BOARD_UNKNOWN)
|
|
board = getCurrentBoard();
|
|
|
|
Boards b(board);
|
|
if (type == SOURCE_TYPE_STICK &&
|
|
index >= b.getCapability(Board::Sticks) &&
|
|
index < b.getCapability(Board::Sticks) + b.getCapability(Board::Pots)) {
|
|
if (potsIndex)
|
|
*potsIndex = index - b.getCapability(Board::Sticks);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RawSource::isSlider(int * sliderIndex, Board::Type board) const
|
|
{
|
|
if (board == Board::BOARD_UNKNOWN)
|
|
board = getCurrentBoard();
|
|
|
|
Boards b(board);
|
|
if (type == SOURCE_TYPE_STICK &&
|
|
index >= b.getCapability(Board::Sticks) + b.getCapability(Board::Pots) &&
|
|
index < b.getCapability(Board::Sticks) + b.getCapability(Board::Pots) + b.getCapability(Board::Sliders)) {
|
|
if (sliderIndex)
|
|
*sliderIndex = index - b.getCapability(Board::Sticks) - b.getCapability(Board::Pots);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RawSource::isTimeBased(Board::Type board) const
|
|
{
|
|
return (type == SOURCE_TYPE_SPECIAL && index >= SOURCE_TYPE_SPECIAL_FIRST_TIMER && index <= SOURCE_TYPE_SPECIAL_LAST_TIMER);
|
|
}
|
|
|
|
bool RawSource::isAvailable(const ModelData * const model, const GeneralSettings * const gs, Board::Type board) const
|
|
{
|
|
if (board == Board::BOARD_UNKNOWN)
|
|
board = getCurrentBoard();
|
|
|
|
Boards b(board);
|
|
|
|
if (type == SOURCE_TYPE_STICK && index >= b.getCapability(Board::MaxAnalogs))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_SWITCH && index >= b.getCapability(Board::Switches))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_FUNCTIONSWITCH)
|
|
if (!model || index >= b.getCapability(Board::FunctionSwitches))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_SPECIAL && index >= SOURCE_TYPE_SPECIAL_FIRST_RESERVED && index <= SOURCE_TYPE_SPECIAL_LAST_RESERVED)
|
|
return false;
|
|
|
|
if (model) {
|
|
if (type == SOURCE_TYPE_SPECIAL && index >= SOURCE_TYPE_SPECIAL_FIRST_TIMER && index <= SOURCE_TYPE_SPECIAL_LAST_TIMER &&
|
|
model->timers[index - SOURCE_TYPE_SPECIAL_FIRST_TIMER].isModeOff())
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_FUNCTIONSWITCH && !model->isFunctionSwitchSourceAllowed(index))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_VIRTUAL_INPUT && !model->isInputValid(index))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_PPM && model->trainerMode == TRAINER_MODE_OFF)
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_CUSTOM_SWITCH && model->logicalSw[index].isEmpty())
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_TELEMETRY) {
|
|
if (!model->sensorData[div(index, 3).quot].isAvailable()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gs) {
|
|
int gsIdx = 0;
|
|
if (type == SOURCE_TYPE_STICK && ((isPot(&gsIdx) && !gs->isPotAvailable(gsIdx)) || (isSlider(&gsIdx) && !gs->isSliderAvailable(gsIdx))))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_SWITCH && IS_HORUS_OR_TARANIS(board) && !gs->switchSourceAllowedTaranis(index))
|
|
return false;
|
|
}
|
|
|
|
if (type == SOURCE_TYPE_TRIM && index >= b.getCapability(Board::NumTrims))
|
|
return false;
|
|
|
|
if (type == SOURCE_TYPE_SPACEMOUSE &&
|
|
(index >= CPN_MAX_SPACEMOUSE ||
|
|
(!(gs->serialPort[GeneralSettings::SP_AUX1] == GeneralSettings::AUX_SERIAL_SPACEMOUSE ||
|
|
gs->serialPort[GeneralSettings::SP_AUX2] == GeneralSettings::AUX_SERIAL_SPACEMOUSE))))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
RawSource RawSource::convert(RadioDataConversionState & cstate)
|
|
{
|
|
cstate.setItemType(tr("SRC"), 1);
|
|
RadioDataConversionState::EventType evt = RadioDataConversionState::EVT_NONE;
|
|
RadioDataConversionState::LogField oldData(index, toString(cstate.fromModel(), cstate.fromGS(), cstate.fromType));
|
|
|
|
if (type == SOURCE_TYPE_STICK) {
|
|
QStringList fromStickList(getStickList(cstate.fromBoard));
|
|
QStringList toStickList(getStickList(cstate.toBoard));
|
|
if (oldData.id < fromStickList.count())
|
|
index = toStickList.indexOf(fromStickList.at(oldData.id));
|
|
else
|
|
index = -1;
|
|
// perform forced mapping
|
|
}
|
|
|
|
if (type == SOURCE_TYPE_SWITCH) {
|
|
QStringList fromSwitchList(getSwitchList(cstate.fromBoard));
|
|
QStringList toSwitchList(getSwitchList(cstate.toBoard));
|
|
// index set to -1 if no match found
|
|
if (oldData.id < fromSwitchList.count())
|
|
index = toSwitchList.indexOf(fromSwitchList.at(oldData.id));
|
|
else
|
|
index = -1;
|
|
// perform forced mapping
|
|
if (index < 0) {
|
|
if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) {
|
|
// No SE and SG on X7 board
|
|
index = toSwitchList.indexOf("SD");
|
|
if (index >= 0)
|
|
evt = RadioDataConversionState::EVT_CVRT;
|
|
}
|
|
else if (IS_FAMILY_T12(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_FAMILY_HORUS_OR_T16(cstate.fromType))) {
|
|
// No SE and SG on T12 board
|
|
index = toSwitchList.indexOf("SD");
|
|
if (index >= 0)
|
|
evt = RadioDataConversionState::EVT_CVRT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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(NULL, cstate.toGS(), cstate.toType)) {
|
|
cstate.setInvalid(oldData);
|
|
// no source is safer than an invalid one
|
|
clear();
|
|
}
|
|
else if (evt == RadioDataConversionState::EVT_CVRT) {
|
|
cstate.setConverted(oldData, RadioDataConversionState::LogField(index, toString(cstate.toModel(), cstate.toGS(), cstate.toType)));
|
|
}
|
|
else if (oldData.id != index) {
|
|
// provide info by default if anything changed
|
|
cstate.setMoved(oldData, RadioDataConversionState::LogField(index, toString(cstate.toModel(), cstate.toGS(), cstate.toType)));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
QStringList RawSource::getStickList(Boards board) const
|
|
{
|
|
QStringList ret;
|
|
|
|
for (int i = 0; i < board.getCapability(Board::MaxAnalogs); i++) {
|
|
ret.append(board.getAnalogInputName(i));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QStringList RawSource::getSwitchList(Boards board) const
|
|
{
|
|
QStringList ret;
|
|
|
|
for (int i = 0; i < board.getCapability(Board::Switches); i++) {
|
|
ret.append(board.getSwitchInfo(i).name);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// static
|
|
StringTagMappingTable RawSource::getSpecialTypesLookupTable()
|
|
{
|
|
StringTagMappingTable tbl;
|
|
|
|
tbl.insert(tbl.end(), {
|
|
{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"},
|
|
{std::to_string(SOURCE_TYPE_SPECIAL_TIMER1), "Tmr1"},
|
|
{std::to_string(SOURCE_TYPE_SPECIAL_TIMER2), "Tmr2"},
|
|
{std::to_string(SOURCE_TYPE_SPECIAL_TIMER3), "Tmr3"},
|
|
});
|
|
|
|
return tbl;
|
|
}
|
|
|
|
// static
|
|
StringTagMappingTable RawSource::getCyclicLookupTable()
|
|
{
|
|
StringTagMappingTable tbl;
|
|
|
|
tbl.insert(tbl.end(), {
|
|
{"0", "CYC1"},
|
|
{"1", "CYC2"},
|
|
{"2", "CYC3"},
|
|
});
|
|
|
|
return tbl;
|
|
}
|