/* * 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 /* * 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; }