Files
edgetx-gx12/companion/src/firmwares/rawsource.cpp
T
3djcandAndre Bernet cd3c4ac654 Add Jumper t12 support by 3djc (#6451)
* Jumper T12 - companion part

* more work

* Add definitions

* fix libsimulator key handling

* Fix simu keys handling

* Cosmetics

* Continued

* Fix wrong regplace

* Fix wrong regplace

* Please travis

* Please travis

* Add T12 to Travis
Switch naming and display

* Cosmetics

* Work on modules

* Work on modules

* typo

* Fix navigation

* Cosmetics

* Introduce NAVIGATION_type

* Fix

* Make NAVIGATION_type more future proof

* Lots of cleanups and improvements

* Cosmetics

* Fix internal module menu

* Cosmetics

* Fix screen reversed

* Small fixes

* Fix navigation issue

* Please travis

* Fix LCD contrast

* Fix T12 switch layout

* Fix T12 keys screen and all radios keys translations

* Complete rework of SWITCHES screen for all radios

* comsetics

* VC++ compilation
2019-05-26 19:51:17 +02:00

674 lines
21 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)
{
if (IS_ARM(getCurrentBoard()))
return float(value) * step;
else
return min + float(value) * step;
}
/*
* RawSource
*/
RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags) const
{
RawSourceRange result;
Firmware * firmware = Firmware::getCurrentVariant();
int board = firmware->getBoard();
switch (type) {
case SOURCE_TYPE_TELEMETRY:
if (IS_ARM(board)) {
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.unitString();
}
else {
result.offset = -DBL_MAX;
switch (index) {
case TELEMETRY_SOURCE_TX_BATT:
result.step = 0.1;
result.decimals = 1;
result.max = 25.5;
result.unit = tr("V");
break;
case TELEMETRY_SOURCE_TX_TIME:
result.step = 60;
result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution
result.unit = tr("s");
break;
case TELEMETRY_SOURCE_TIMER1:
case TELEMETRY_SOURCE_TIMER2:
case TELEMETRY_SOURCE_TIMER3:
result.step = 5;
result.max = 255 * result.step;
result.unit = tr("s");
break;
case TELEMETRY_SOURCE_RSSI_TX:
case TELEMETRY_SOURCE_RSSI_RX:
result.max = 100;
result.offset = 128;
break;
case TELEMETRY_SOURCE_A1_MIN:
case TELEMETRY_SOURCE_A2_MIN:
case TELEMETRY_SOURCE_A3_MIN:
case TELEMETRY_SOURCE_A4_MIN:
if (model) result = model->frsky.channels[index-TELEMETRY_SOURCE_A1_MIN].getRange();
break;
case TELEMETRY_SOURCE_A1:
case TELEMETRY_SOURCE_A2:
case TELEMETRY_SOURCE_A3:
case TELEMETRY_SOURCE_A4:
if (model) result = model->frsky.channels[index-TELEMETRY_SOURCE_A1].getRange();
break;
case TELEMETRY_SOURCE_ALT:
case TELEMETRY_SOURCE_ALT_MIN:
case TELEMETRY_SOURCE_ALT_MAX:
case TELEMETRY_SOURCE_GPS_ALT:
result.step = 8;
result.min = -500;
result.max = 1540;
if (firmware->getCapability(Imperial) || settings.imperial) {
result.step = (result.step * 105) / 32;
result.min = (result.min * 105) / 32;
result.max = (result.max * 105) / 32;
result.unit = tr("ft");
}
else {
result.unit = tr("m");
}
break;
case TELEMETRY_SOURCE_T1:
case TELEMETRY_SOURCE_T1_MAX:
case TELEMETRY_SOURCE_T2:
case TELEMETRY_SOURCE_T2_MAX:
result.min = -30;
result.max = 225;
result.unit = trUtf8("°C");
break;
case TELEMETRY_SOURCE_HDG:
result.step = 2;
result.max = 360;
result.offset = 256;
result.unit = trUtf8("°");
break;
case TELEMETRY_SOURCE_RPM:
case TELEMETRY_SOURCE_RPM_MAX:
result.step = 50;
result.max = 12750;
break;
case TELEMETRY_SOURCE_FUEL:
result.max = 100;
result.unit = tr("%");
break;
case TELEMETRY_SOURCE_ASPEED:
case TELEMETRY_SOURCE_ASPEED_MAX:
result.decimals = 1;
result.step = 2.0;
result.max = (2*255);
if (firmware->getCapability(Imperial) || settings.imperial) {
result.step *= 1.150779;
result.max *= 1.150779;
result.unit = tr("mph");
}
else {
result.step *= 1.852;
result.max *= 1.852;
result.unit = tr("km/h");
}
break;
case TELEMETRY_SOURCE_SPEED:
case TELEMETRY_SOURCE_SPEED_MAX:
result.step = 2;
result.max = (2*255);
if (firmware->getCapability(Imperial) || settings.imperial) {
result.step *= 1.150779;
result.max *= 1.150779;
result.unit = tr("mph");
}
else {
result.step *= 1.852;
result.max *= 1.852;
result.unit = tr("km/h");
}
break;
case TELEMETRY_SOURCE_VERTICAL_SPEED:
result.step = 0.1;
result.min = -12.5;
result.max = 13.0;
result.decimals = 1;
result.unit = tr("m/s");
break;
case TELEMETRY_SOURCE_DTE:
result.max = 30000;
break;
case TELEMETRY_SOURCE_DIST:
case TELEMETRY_SOURCE_DIST_MAX:
result.step = 8;
result.max = 2040;
result.unit = tr("m");
break;
case TELEMETRY_SOURCE_CELL:
case TELEMETRY_SOURCE_CELL_MIN:
result.step = 0.02;
result.max = 5.1;
result.decimals = 2;
result.unit = tr("V");
break;
case TELEMETRY_SOURCE_CELLS_SUM:
case TELEMETRY_SOURCE_CELLS_MIN:
case TELEMETRY_SOURCE_VFAS:
case TELEMETRY_SOURCE_VFAS_MIN:
result.step = 0.1;
result.max = 25.5;
result.decimals = 1;
result.unit = tr("V");
break;
case TELEMETRY_SOURCE_CURRENT:
case TELEMETRY_SOURCE_CURRENT_MAX:
result.step = 0.5;
result.max = 127.5;
result.decimals = 1;
result.unit = tr("A");
break;
case TELEMETRY_SOURCE_CONSUMPTION:
result.step = 100;
result.max = 25500;
result.unit = tr("mAh");
break;
case TELEMETRY_SOURCE_POWER:
case TELEMETRY_SOURCE_POWER_MAX:
result.step = 5;
result.max = 1275;
result.unit = tr("W");
break;
case TELEMETRY_SOURCE_ACCX:
case TELEMETRY_SOURCE_ACCY:
case TELEMETRY_SOURCE_ACCZ:
result.step = 0.01;
result.decimals = 2;
result.max = 2.55;
result.min = 0;
result.unit = tr("g");
break;
default:
result.max = 125;
break;
}
if (result.offset == -DBL_MAX) {
result.offset = result.max - (127*result.step);
}
if (flags & (RANGE_DELTA_FUNCTION | RANGE_ABS_FUNCTION)) {
result.offset = 0;
result.min = result.step * -127;
result.max = result.step * 127;
}
}
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 { // 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("Timer1"), tr("Timer2"), tr("Timer3"),
};
static const QString telemetry[] = {
tr("Batt"), tr("Time"), tr("Timer1"), tr("Timer2"), tr("Timer3"),
tr("RAS"), tr("RSSI Tx"), tr("RSSI Rx"),
tr("A1"), tr("A2"), tr("A3"), tr("A4"),
tr("Alt"), tr("Rpm"), tr("Fuel"), tr("T1"), tr("T2"),
tr("Speed"), tr("Dist"), tr("GPS Alt"),
tr("Cell"), tr("Cells"), tr("Vfas"), tr("Curr"), tr("Cnsp"), tr("Powr"),
tr("AccX"), tr("AccY"), tr("AccZ"),
tr("Hdg "), tr("VSpd"), tr("AirSpeed"), tr("dTE"),
tr("A1-"), tr("A2-"), tr("A3-"), tr("A4-"),
tr("Alt-"), tr("Alt+"), tr("Rpm+"), tr("T1+"), tr("T2+"), tr("Speed+"), tr("Dist+"), tr("AirSpeed+"),
tr("Cell-"), tr("Cells-"), tr("Vfas-"), tr("Curr+"), tr("Powr+"),
tr("ACC"), tr("GPS Time"),
};
static const QString rotary[] = { tr("REa"), tr("REb") };
if (index<0) {
return tr("???");
}
QString result;
int genAryIdx = 0;
switch (type) {
case SOURCE_TYPE_NONE:
return tr("----");
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.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_MAX:
return tr("MAX");
case SOURCE_TYPE_SWITCH:
if (generalSettings)
result = QString(generalSettings->switchName[index]);
if (result.isEmpty())
result = Boards::getSwitchInfo(board, index).name;
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:
return CHECK_IN_ARRAY(special, index);
case SOURCE_TYPE_TELEMETRY:
if (IS_ARM(board)) {
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;
}
else {
return CHECK_IN_ARRAY(telemetry, index);
}
case SOURCE_TYPE_GVAR:
if (model)
return model->gvarData[index].nameToString(index);
else
return GVarData().nameToString(index);
default:
return tr("???");
}
}
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
{
if (board == Board::BOARD_UNKNOWN)
board = getCurrentBoard();
if (IS_ARM(board))
return (type == SOURCE_TYPE_SPECIAL && index > 0);
else
return (type==SOURCE_TYPE_TELEMETRY && (index==TELEMETRY_SOURCE_TX_TIME || index==TELEMETRY_SOURCE_TIMER1 || index==TELEMETRY_SOURCE_TIMER2 || index==TELEMETRY_SOURCE_TIMER3));
}
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 (model) {
if (type == SOURCE_TYPE_VIRTUAL_INPUT && !model->isInputValid(index))
return false;
if (type == SOURCE_TYPE_CUSTOM_SWITCH && model->logicalSw[index].isEmpty())
return false;
if (type == SOURCE_TYPE_TELEMETRY) {
if (IS_ARM(board) && !model->sensorData[div(index, 3).quot].isAvailable()) {
return false;
}
else if (!IS_ARM(board)) {
Firmware * fw = getCurrentFirmware();
if (type == (int)TELEMETRY_SOURCE_TX_TIME && !fw->getCapability(RtcTime))
return false;
if (type == (int)TELEMETRY_SOURCE_RAS && !fw->getCapability(SportTelemetry))
return false;
if (type == (int)TELEMETRY_SOURCE_TIMER3 && fw->getCapability(Timers) < 3)
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;
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) {
if (cstate.toBoard.getCapability(Board::Sliders)) {
if (index >= cstate.fromBoard.getCapability(Board::Sticks) + cstate.fromBoard.getCapability(Board::Pots)) {
// 1st slider alignment
index += cstate.toBoard.getCapability(Board::Pots) - cstate.fromBoard.getCapability(Board::Pots);
}
if (isSlider(0, cstate.fromType)) {
// LS and RS sliders are after 2 aux sliders on X12 and X9E
if ((IS_HORUS_X12S(cstate.toType) || IS_TARANIS_X9E(cstate.toType)) && !IS_HORUS_X12S(cstate.fromType) && !IS_TARANIS_X9E(cstate.fromType)) {
if (index >= 7) {
index += 2; // LS/RS to LS/RS
}
}
else if (!IS_TARANIS_X9E(cstate.toType) && !IS_HORUS_X12S(cstate.toType) && (IS_HORUS_X12S(cstate.fromType) || IS_TARANIS_X9E(cstate.fromType))) {
if (index >= 7 && index <= 8) {
index += 2; // aux sliders to spare analogs (which may not exist, this is validated later)
evt = RadioDataConversionState::EVT_CVRT;
}
else if (index >= 9 && index <= 10) {
index -= 2; // LS/RS to LS/RS
}
}
}
}
if (IS_TARANIS(cstate.toType) && IS_HORUS(cstate.fromType)) {
if (index == 6)
index = 5; // pot S2 to S2
else if (index == 5)
index = -1; // 6P on Horus doesn't exist on Taranis
}
else if (IS_HORUS(cstate.toType) && IS_TARANIS(cstate.fromType) && index == 5)
{
index = 6; // pot S2 to S2
}
} // SOURCE_TYPE_STICK
if (type == SOURCE_TYPE_SWITCH) {
// SWI to SWR don't exist on !X9E board
if (!IS_TARANIS_X9E(cstate.toType) && IS_TARANIS_X9E(cstate.fromType)) {
if (index >= 8) {
index = index % 8;
evt = RadioDataConversionState::EVT_CVRT;
}
}
if (IS_TARANIS_X7(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_HORUS(cstate.fromType))) {
// No SE and SG on X7 board
if (index == 4 || index == 6) {
index = 3; // SG and SE to SD
evt = RadioDataConversionState::EVT_CVRT;
}
else if (index == 5) {
index = 4; // SF to SF
}
else if (index == 7) {
index = 5; // SH to SH
}
}
else if (IS_JUMPER_T12(cstate.toType) && (IS_TARANIS_X9(cstate.fromType) || IS_HORUS(cstate.fromType))) {
// No SE and SG on T12 board
if (index == 4 || index == 6) {
index = 3; // SG and SE to SD
evt = RadioDataConversionState::EVT_CVRT;
}
else if (index == 5) {
index = 4; // SF to SF
}
else if (index == 7) {
index = 5; // SH to SH
}
}
// Compensate for SE and SG on X9/Horus board if converting from X7
else if ((IS_TARANIS_X9(cstate.toType) || IS_HORUS(cstate.toType)) && IS_TARANIS_X7(cstate.fromType)) {
if (index == 4) {
index = 5; // SF to SF
}
else if (index == 5) {
index = 7; // SH to SH
}
}
else if ((IS_TARANIS_X9(cstate.toType) || IS_HORUS(cstate.toType)) && IS_JUMPER_T12(cstate.fromType)) {
if (index == 4) {
index = 5; // SF to SF
}
else if (index == 5) {
index = 7; // SH to SH
}
}
else if ((IS_TARANIS_X9(cstate.toType) || IS_HORUS(cstate.toType)) && IS_JUMPER_T12(cstate.fromType)) {
if (index == 4) {
index = 5; // SF to SF
}
else if (index == 5) {
index = 7; // SH to SH
}
}
} // SOURCE_TYPE_SWITCH
// final validation (we do not pass model to isAvailable() because we don't know what has or hasn't been converted)
if (!isAvailable(NULL, cstate.toGS(), cstate.toType)) {
cstate.setInvalid(oldData);
index = -1; // TODO: better way to flag invalid sources?
type = MAX_SOURCE_TYPE;
}
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;
}