/* * 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 "eeprominterface.h" #include "firmwares/er9x/er9xinterface.h" #include "firmwares/ersky9x/ersky9xinterface.h" #include "firmwares/opentx/opentxinterface.h" #include "firmwares/opentx/opentxeeprom.h" #include "appdata.h" #include "helpers.h" #include "wizarddata.h" #include "firmwareinterface.h" #include "macros.h" #include "multiprotocols.h" #include #include #include #include #include #include const uint8_t chout_ar[] = { // First number is 0..23 -> template setup, Second is relevant channel out 1,2,3,4 , 1,2,4,3 , 1,3,2,4 , 1,3,4,2 , 1,4,2,3 , 1,4,3,2, 2,1,3,4 , 2,1,4,3 , 2,3,1,4 , 2,3,4,1 , 2,4,1,3 , 2,4,3,1, 3,1,2,4 , 3,1,4,2 , 3,2,1,4 , 3,2,4,1 , 3,4,1,2 , 3,4,2,1, 4,1,2,3 , 4,1,3,2 , 4,2,1,3 , 4,2,3,1 , 4,3,1,2 , 4,3,2,1 }; static const char specialCharsTab[] = "_-.,"; void setEEPROMString(char *dst, const char *src, int size) { memcpy(dst, src, size); for (int i=size-1; i>=0; i--) { if (dst[i] == '\0') dst[i] = ' '; else break; } } void getEEPROMString(char *dst, const char *src, int size) { memcpy(dst, src, size); dst[size] = '\0'; for (int i=size-1; i>=0; i--) { if (dst[i] == ' ') dst[i] = '\0'; else break; } } int8_t char2idx(char c) { if (c==' ') return 0; if (c>='A' && c<='Z') return 1+c-'A'; if (c>='a' && c<='z') return -1-c+'a'; if (c>='0' && c<='9') return 27+c-'0'; for (int8_t i=0;;i++) { char cc = specialCharsTab[i]; if (cc==0) return 0; if (cc==c) return 37+i; } } #define ZCHAR_MAX 40 char idx2char(int8_t idx) { if (idx == 0) return ' '; if (idx < 0) { if (idx > -27) return 'a' - idx - 1; idx = -idx; } if (idx < 27) return 'A' + idx - 1; if (idx < 37) return '0' + idx - 27; if (idx <= ZCHAR_MAX) return specialCharsTab[idx-37]; return ' '; } void setEEPROMZString(char *dst, const char *src, int size) { for (int i=size-1; i>=0; i--) dst[i] = char2idx(src[i]); } void getEEPROMZString(char *dst, const char *src, int size) { for (int i=size-1; i>=0; i--) dst[i] = idx2char(src[i]); dst[size] = '\0'; for (int i=size-1; i>=0; i--) { if (dst[i] == ' ') dst[i] = '\0'; else break; } } QString getElementName(const QString & prefix, unsigned int index, const char * name = NULL, bool padding = false) { QString result = prefix; if (padding) result += QString("%1").arg(index, 2, 10, QChar('0')); else result += QString("%1").arg(index); if (name) { QString trimmed = QString(name).trimmed(); if (trimmed.length() > 0) { result += ":" + QString(name).trimmed(); } } return result; } float ValToTim(int value) { return ((value < -109 ? 129+value : (value < 7 ? (113+value)*5 : (53+value)*10))/10.0); } int TimToVal(float value) { int temp; if (value>60) { temp=136+round((value-60)); } else if (value>2) { temp=20+round((value-2.0)*2.0); } else { temp=round(value*10.0); } return (temp-129); } /* * SensorData */ void SensorData::updateUnit() { if (type == TELEM_TYPE_CALCULATED) { if (formula == TELEM_FORMULA_CONSUMPTION) unit = UNIT_MAH; } } QString SensorData::unitString() const { switch (unit) { case UNIT_VOLTS: return QObject::tr("V"); case UNIT_AMPS: return QObject::tr("A"); case UNIT_MILLIAMPS: return QObject::tr("mA"); case UNIT_KTS: return QObject::tr("kts"); case UNIT_METERS_PER_SECOND: return QObject::tr("m/s"); case UNIT_KMH: return QObject::tr("km/h"); case UNIT_MPH: return QObject::tr("mph"); case UNIT_METERS: return QObject::tr("m"); case UNIT_FEET: return QObject::tr("f"); case UNIT_CELSIUS: return QObject::trUtf8("°C"); case UNIT_FAHRENHEIT: return QObject::trUtf8("°F"); case UNIT_PERCENT: return QObject::tr("%"); case UNIT_MAH: return QObject::tr("mAh"); case UNIT_WATTS: return QObject::tr("W"); case UNIT_MILLIWATTS: return QObject::tr("mW"); case UNIT_DB: return QObject::tr("dB"); case UNIT_RPMS: return QObject::tr("rpms"); case UNIT_G: return QObject::tr("g"); case UNIT_DEGREE: return QObject::trUtf8("°"); case UNIT_RADIANS: return QObject::trUtf8("Rad"); case UNIT_HOURS: return QObject::tr("hours"); case UNIT_MINUTES: return QObject::tr("minutes"); case UNIT_SECONDS: return QObject::tr("seconds"); case UNIT_CELLS: return QObject::tr("V"); default: return ""; } } /* * 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 = QObject::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 = QObject::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 = QObject::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 = QObject::tr("ft"); } else { result.unit = QObject::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 = QObject::trUtf8("°C"); break; case TELEMETRY_SOURCE_HDG: result.step = 2; result.max = 360; result.offset = 256; result.unit = QObject::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 = QObject::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 = QObject::tr("mph"); } else { result.step *= 1.852; result.max *= 1.852; result.unit = QObject::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 = QObject::tr("mph"); } else { result.step *= 1.852; result.max *= 1.852; result.unit = QObject::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 = QObject::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 = QObject::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 = QObject::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 = QObject::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 = QObject::tr("A"); break; case TELEMETRY_SOURCE_CONSUMPTION: result.step = 100; result.max = 25500; result.unit = QObject::tr("mAh"); break; case TELEMETRY_SOURCE_POWER: case TELEMETRY_SOURCE_POWER_MAX: result.step = 5; result.max = 1275; result.unit = QObject::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 = QObject::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 = QObject::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 = QObject::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 = QObject::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[] = { QObject::tr("TrmR"), QObject::tr("TrmE"), QObject::tr("TrmT"), QObject::tr("TrmA"), QObject::tr("Trm5"), QObject::tr("Trm6") }; static const QString special[] = { QObject::tr("Batt"), QObject::tr("Time"), QObject::tr("Timer1"), QObject::tr("Timer2"), QObject::tr("Timer3"), }; static const QString telemetry[] = { QObject::tr("Batt"), QObject::tr("Time"), QObject::tr("Timer1"), QObject::tr("Timer2"), QObject::tr("Timer3"), QObject::tr("SWR"), QObject::tr("RSSI Tx"), QObject::tr("RSSI Rx"), QObject::tr("A1"), QObject::tr("A2"), QObject::tr("A3"), QObject::tr("A4"), QObject::tr("Alt"), QObject::tr("Rpm"), QObject::tr("Fuel"), QObject::tr("T1"), QObject::tr("T2"), QObject::tr("Speed"), QObject::tr("Dist"), QObject::tr("GPS Alt"), QObject::tr("Cell"), QObject::tr("Cells"), QObject::tr("Vfas"), QObject::tr("Curr"), QObject::tr("Cnsp"), QObject::tr("Powr"), QObject::tr("AccX"), QObject::tr("AccY"), QObject::tr("AccZ"), QObject::tr("Hdg "), QObject::tr("VSpd"), QObject::tr("AirSpeed"), QObject::tr("dTE"), QObject::tr("A1-"), QObject::tr("A2-"), QObject::tr("A3-"), QObject::tr("A4-"), QObject::tr("Alt-"), QObject::tr("Alt+"), QObject::tr("Rpm+"), QObject::tr("T1+"), QObject::tr("T2+"), QObject::tr("Speed+"), QObject::tr("Dist+"), QObject::tr("AirSpeed+"), QObject::tr("Cell-"), QObject::tr("Cells-"), QObject::tr("Vfas-"), QObject::tr("Curr+"), QObject::tr("Powr+"), QObject::tr("ACC"), QObject::tr("GPS Time"), }; static const QString rotary[] = { QObject::tr("REa"), QObject::tr("REb") }; if (index<0) { return QObject::tr("???"); } QString result; int genAryIdx = 0; switch (type) { case SOURCE_TYPE_NONE: return QObject::tr("----"); case SOURCE_TYPE_VIRTUAL_INPUT: { const char * name = NULL; if (model) name = model->inputNames[index]; return getElementName(QCoreApplication::translate("Input", "I"), index + 1, name); } case SOURCE_TYPE_LUA_OUTPUT: return QObject::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 CHECK_IN_ARRAY(trims, index); case SOURCE_TYPE_ROTARY_ENCODER: return CHECK_IN_ARRAY(rotary, index); case SOURCE_TYPE_MAX: return QObject::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 QObject::tr("CYC%1").arg(index+1); case SOURCE_TYPE_PPM: return getElementName(QCoreApplication::translate("Trainer", "TR"), index + 1); case SOURCE_TYPE_CH: { const char * name = NULL; if (getCurrentFirmware()->getCapability(ChannelsName) && model) name = model->limitData[index].name; return getElementName(QCoreApplication::translate("Channel", "CH"), index + 1, name); } case SOURCE_TYPE_SPECIAL: return CHECK_IN_ARRAY(special, index); case SOURCE_TYPE_TELEMETRY: if (IS_ARM(board)) { div_t qr = div(index, 3); result = getElementName(QCoreApplication::translate("Telemetry", "TELE"), qr.quot+1, model ? model->sensorData[qr.quot].label : NULL); if (qr.rem) result += (qr.rem == 1 ? "-" : "+"); return result; } else { return CHECK_IN_ARRAY(telemetry, index); } case SOURCE_TYPE_GVAR: { const char * name = NULL; if (getCurrentFirmware()->getCapability(GvarsName) && model) name = model->gvarData[index].name; return getElementName(QCoreApplication::translate("Global Variable", "GV"), index + 1, name); } default: return QObject::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) { 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_SWR && !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; } /* * RawSwitch */ QString RawSwitch::toString(Board::Type board, const GeneralSettings * const generalSettings, const ModelData * const modelData) const { if (board == Board::BOARD_UNKNOWN) { board = getCurrentBoard(); } static const QString switches9X[] = { QString("THR"), QString("RUD"), QString("ELE"), QString("ID0"), QString("ID1"), QString("ID2"), QString("AIL"), QString("GEA"), QString("TRN") }; static const QString trimsSwitches[] = { QObject::tr("RudTrim Left"), QObject::tr("RudTrim Right"), QObject::tr("EleTrim Down"), QObject::tr("EleTrim Up"), QObject::tr("ThrTrim Down"), QObject::tr("ThrTrim Up"), QObject::tr("AilTrim Left"), QObject::tr("AilTrim Right"), QObject::tr("Trim 5 Down"), QObject::tr("Trim 5 Up"), QObject::tr("Trim 6 Down"), QObject::tr("Trim 6 Up") }; static const QString rotaryEncoders[] = { QObject::tr("REa"), QObject::tr("REb") }; static const QString timerModes[] = { QObject::tr("OFF"), QObject::tr("ON"), QObject::tr("THs"), QObject::tr("TH%"), QObject::tr("THt") }; const QStringList directionIndicators = QStringList() << CPN_STR_SW_INDICATOR_UP << CPN_STR_SW_INDICATOR_NEUT << CPN_STR_SW_INDICATOR_DN; if (index < 0) { return CPN_STR_SW_INDICATOR_REV % RawSwitch(type, -index).toString(board, generalSettings, modelData); } else { QString swName; div_t qr; switch(type) { case SWITCH_TYPE_SWITCH: if (IS_HORUS_OR_TARANIS(board)) { qr = div(index-1, 3); if (generalSettings) swName = QString(generalSettings->switchName[qr.quot]); if (swName.isEmpty()) swName = getSwitchInfo(board, qr.quot).name; return swName + directionIndicators.at(qr.rem > -1 && qr.rem < directionIndicators.size() ? qr.rem : 1); } else { return CHECK_IN_ARRAY(switches9X, index - 1); } case SWITCH_TYPE_VIRTUAL: return getElementName(QCoreApplication::translate("Logic Switch", "L"), index, NULL, true); case SWITCH_TYPE_MULTIPOS_POT: if (!Boards::getCapability(board, Board::MultiposPotsPositions)) return QObject::tr("???"); qr = div(index - 1, Boards::getCapability(board, Board::MultiposPotsPositions)); if (generalSettings && qr.quot < (int)DIM(generalSettings->potConfig)) swName = QString(generalSettings->potName[qr.quot]); if (swName.isEmpty()) swName = Boards::getAnalogInputName(board, qr.quot + Boards::getCapability(board, Board::Sticks)); return swName + "_" + QString::number(qr.rem + 1); case SWITCH_TYPE_TRIM: return CHECK_IN_ARRAY(trimsSwitches, index-1); case SWITCH_TYPE_ROTARY_ENCODER: return CHECK_IN_ARRAY(rotaryEncoders, index-1); case SWITCH_TYPE_ON: return QObject::tr("ON"); case SWITCH_TYPE_OFF: return QObject::tr("OFF"); case SWITCH_TYPE_ONE: return QObject::tr("One"); case SWITCH_TYPE_FLIGHT_MODE: return getElementName(QCoreApplication::translate("Flight mode", "FM"), index - 1, modelData ? modelData->flightModeData[index-1].name : NULL); case SWITCH_TYPE_NONE: return QObject::tr("----"); case SWITCH_TYPE_TIMER_MODE: return CHECK_IN_ARRAY(timerModes, index); case SWITCH_TYPE_SENSOR: return getElementName(QCoreApplication::translate("Telemetry", "TELE"), index, modelData ? modelData->sensorData[index-1].label : NULL); case SWITCH_TYPE_TELEMETRY: return QObject::tr("Telemetry"); default: return QObject::tr("???"); } } } bool RawSwitch::isAvailable(const ModelData * const model, const GeneralSettings * const gs, Board::Type board) { if (board == Board::BOARD_UNKNOWN) board = getCurrentBoard(); Boards b(board); if (type == SWITCH_TYPE_SWITCH && abs(index) > b.getCapability(Board::SwitchPositions)) return false; if (type == SWITCH_TYPE_TRIM && abs(index) > b.getCapability(Board::NumTrimSwitches)) return false; if (gs) { if (type == SWITCH_TYPE_SWITCH && IS_HORUS_OR_TARANIS(board) && !gs->switchPositionAllowedTaranis(abs(index))) return false; if (type == SWITCH_TYPE_MULTIPOS_POT) { int pot = div(abs(index) - 1, b.getCapability(Board::MultiposPotsPositions)).quot; if (!gs->isPotAvailable(pot) || gs->potConfig[pot] != Board::POT_MULTIPOS_SWITCH) return false; } } if (model && !model->isAvailable(*this)) return false; return true; } /* * CurveReference */ QString CurveReference::toString(const ModelData * model, bool verbose) const { if (value == 0) { return "----"; } QString ret; unsigned idx = abs(value) - 1; switch(type) { case CURVE_REF_DIFF: ret = QObject::tr("Diff(%1)").arg(Helpers::getAdjustmentString(value, model)); break; case CURVE_REF_EXPO: ret = QObject::tr("Expo(%1)").arg(Helpers::getAdjustmentString(value, model)); break; case CURVE_REF_FUNC: ret = QString("x>0" "x<0" "|x|" "f>0" "f<0" "|f|").mid(3*(value-1), 3); if (verbose) ret = QObject::tr("Function(%1)").arg(ret); break; default: if (model) ret = model->curves[idx].nameToString(idx); else ret = CurveData().nameToString(idx); if (verbose) ret = QObject::tr("Curve(%1)").arg(ret); if (value < 0) ret.prepend(CPN_STR_SW_INDICATOR_REV); break; } return ret; } bool LogicalSwitchData::isEmpty() const { return (func == 0); } CSFunctionFamily LogicalSwitchData::getFunctionFamily() const { if (func == LS_FN_EDGE) return LS_FAMILY_EDGE; else if (func == LS_FN_TIMER) return LS_FAMILY_TIMER; else if (func == LS_FN_STICKY) return LS_FAMILY_STICKY; else if (func < LS_FN_AND || func > LS_FN_ELESS) return LS_FAMILY_VOFS; else if (func < LS_FN_EQUAL) return LS_FAMILY_VBOOL; else return LS_FAMILY_VCOMP; } unsigned int LogicalSwitchData::getRangeFlags() const { int f = 0; if (func == LS_FN_DPOS || func == LS_FN_DAPOS) f |= RANGE_DELTA_FUNCTION; if (func == LS_FN_DAPOS || func == LS_FN_APOS || func == LS_FN_ANEG) f |= RANGE_ABS_FUNCTION; return f; } QString LogicalSwitchData::funcToString() const { switch (func) { case LS_FN_OFF: return QObject::tr("---"); case LS_FN_VPOS: return QObject::tr("a>x"); case LS_FN_VNEG: return QObject::tr("ax"); case LS_FN_ANEG: return QObject::tr("|a|b"); case LS_FN_LESS: return QObject::tr("a=b"); case LS_FN_ELESS: return QObject::tr("a<=b"); case LS_FN_DPOS: return QObject::tr("d>=x"); case LS_FN_DAPOS: return QObject::tr("|d|>=x"); case LS_FN_VEQUAL: return QObject::tr("a=x"); case LS_FN_VALMOSTEQUAL: return QObject::tr("a~x"); case LS_FN_TIMER: return QObject::tr("Timer"); case LS_FN_STICKY: return QObject::tr("Sticky"); case LS_FN_EDGE: return QObject::tr("Edge"); default: return QObject::tr("Unknown"); } } void CustomFunctionData::clear() { memset(this, 0, sizeof(CustomFunctionData)); if (!getCurrentFirmware()->getCapability(SafetyChannelCustomFunction)) { func = FuncTrainer; } } bool CustomFunctionData::isEmpty() const { return (swtch.type == SWITCH_TYPE_NONE); } QString CustomFunctionData::toString(int index, bool globalContext) const { return getElementName((globalContext ? QObject::tr("GF") : QObject::tr("SF")), index+1, 0, true); } QString CustomFunctionData::funcToString(const ModelData * model) const { if (func >= FuncOverrideCH1 && func <= FuncOverrideCH32) return QObject::tr("Override %1").arg(RawSource(SOURCE_TYPE_CH, func).toString(model)); else if (func == FuncTrainer) return QObject::tr("Trainer"); else if (func == FuncTrainerRUD) return QObject::tr("Trainer RUD"); else if (func == FuncTrainerELE) return QObject::tr("Trainer ELE"); else if (func == FuncTrainerTHR) return QObject::tr("Trainer THR"); else if (func == FuncTrainerAIL) return QObject::tr("Trainer AIL"); else if (func == FuncInstantTrim) return QObject::tr("Instant Trim"); else if (func == FuncPlaySound) return QObject::tr("Play Sound"); else if (func == FuncPlayHaptic) return QObject::tr("Haptic"); else if (func == FuncReset) return QObject::tr("Reset"); else if (func >= FuncSetTimer1 && func <= FuncSetTimer3) return QObject::tr("Set Timer %1").arg(func-FuncSetTimer1+1); else if (func == FuncVario) return QObject::tr("Vario"); else if (func == FuncPlayPrompt) return QObject::tr("Play Track"); else if (func == FuncPlayBoth) return QObject::tr("Play Both"); else if (func == FuncPlayValue) return QObject::tr("Play Value"); else if (func == FuncPlayScript) return QObject::tr("Play Script"); else if (func == FuncLogs) return QObject::tr("SD Logs"); else if (func == FuncVolume) return QObject::tr("Volume"); else if (func == FuncBacklight) return QObject::tr("Backlight"); else if (func == FuncScreenshot) return QObject::tr("Screenshot"); else if (func == FuncBackgroundMusic) return QObject::tr("Background Music"); else if (func == FuncBackgroundMusicPause) return QObject::tr("Background Music Pause"); else if (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast) return QObject::tr("Adjust %1").arg(RawSource(SOURCE_TYPE_GVAR, func-FuncAdjustGV1).toString(model)); else if (func == FuncSetFailsafeInternalModule) return QObject::tr("SetFailsafe Int. Module"); else if (func == FuncSetFailsafeExternalModule) return QObject::tr("SetFailsafe Ext. Module"); else if (func == FuncRangeCheckInternalModule) return QObject::tr("RangeCheck Int. Module"); else if (func == FuncRangeCheckExternalModule) return QObject::tr("RangeCheck Ext. Module"); else if (func == FuncBindInternalModule) return QObject::tr("Bind Int. Module"); else if (func == FuncBindExternalModule) return QObject::tr("Bind Ext. Module"); else { return QString("???"); // Highlight unknown functions with output of question marks.(BTW should not happen that we do not know what a function is) } } void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox * b, unsigned int value = 0) { int val = 0; Firmware * firmware = Firmware::getCurrentVariant(); Board::Type board = firmware->getBoard(); b->addItem(QObject::tr("Timer1"), val++); b->addItem(QObject::tr("Timer2"), val++); if (IS_ARM(board)) { b->addItem( QObject::tr("Timer3"), val++); } b->addItem(QObject::tr("Flight"), val++); b->addItem(QObject::tr("Telemetry"), val++); int reCount = firmware->getCapability(RotaryEncoders); if (reCount == 1) { b->addItem(QObject::tr("Rotary Encoder"), val++); } else if (reCount == 2) { b->addItem(QObject::tr("REa"), val++); b->addItem(QObject::tr("REb"), val++); } if ((int)value < b->count()) { b->setCurrentIndex(value); } if (model && IS_ARM(board)) { for (int i=0; isensorData[i].isAvailable()) { RawSource item = RawSource(SOURCE_TYPE_TELEMETRY, 3*i); b->addItem(item.toString(model), val+i); if ((int)value == val+i) { b->setCurrentIndex(b->count()-1); } } } } } void CustomFunctionData::populatePlaySoundParams(QStringList & qs) { qs <<"Beep 1" << "Beep 2" << "Beep 3" << "Warn1" << "Warn2" << "Cheep" << "Ratata" << "Tick" << "Siren" << "Ring" ; qs << "SciFi" << "Robot" << "Chirp" << "Tada" << "Crickt" << "AlmClk" ; } void CustomFunctionData::populateHapticParams(QStringList & qs) { qs << "0" << "1" << "2" << "3"; } QString CustomFunctionData::paramToString(const ModelData * model) const { QStringList qs; if (func <= FuncInstantTrim) { return QString("%1").arg(param); } else if (func==FuncLogs) { return QString("%1").arg(param/10.0) + QObject::tr("s"); } else if (func==FuncPlaySound) { CustomFunctionData::populatePlaySoundParams(qs); if (param>=0 && param<(int)qs.count()) return qs.at(param); else return QObject::tr("Inconsistent parameter"); } else if (func==FuncPlayHaptic) { CustomFunctionData::populateHapticParams(qs); if (param>=0 && param<(int)qs.count()) return qs.at(param); else return QObject::tr("Inconsistent parameter"); } else if (func==FuncReset) { QComboBox cb; CustomFunctionData::populateResetParams(model, &cb); int pos = cb.findData(param); if (pos >= 0) return cb.itemText(pos); else return QObject::tr("Inconsistent parameter"); } else if ((func==FuncVolume)|| (func==FuncPlayValue)) { RawSource item(param); return item.toString(model); } else if ((func==FuncPlayPrompt) || (func==FuncPlayBoth)) { if ( getCurrentFirmware()->getCapability(VoicesAsNumbers)) { return QString("%1").arg(param); } else { return paramarm; } } else if ((func>=FuncAdjustGV1) && (func=FuncOverrideCH1 && func<=FuncOverrideCH32) || (func>=FuncAdjustGV1 && func<=FuncAdjustGVLast) || (func==FuncReset) || (func>=FuncSetTimer1 && func<=FuncSetTimer2) || (func==FuncVolume) || (func <= FuncInstantTrim)) { if (!enabled) { return QObject::tr("DISABLED"); } } return ""; } CurveData::CurveData() { clear(5); } void CurveData::clear(int count) { memset(this, 0, sizeof(CurveData)); this->count = count; } bool CurveData::isEmpty() const { for (int i=0; igetBoardCapability(getCurrentBoard(), Board::Pots)) return false; return potConfig[index] != Board::POT_NONE; } bool GeneralSettings::isSliderAvailable(int index) const { if (index<0 || index>getBoardCapability(getCurrentBoard(), Board::Sliders)) return false; return sliderConfig[index] != Board::SLIDER_NONE; } GeneralSettings::GeneralSettings() { memset(this, 0, sizeof(GeneralSettings)); contrast = 25; for (int i=0; i < CPN_MAX_ANALOGS; ++i) { calibMid[i] = 0x200; calibSpanNeg[i] = 0x180; calibSpanPos[i] = 0x180; } Firmware * firmware = Firmware::getCurrentVariant(); Board::Type board = firmware->getBoard(); // vBatWarn is voltage in 100mV, vBatMin is in 100mV but with -9V offset, vBatMax has a -12V offset vBatWarn = 90; if (IS_TARANIS_X9E(board) || IS_HORUS_X12S(board)) { // NI-MH 9.6V vBatWarn = 87; vBatMin = -5; //8,5V vBatMax = -5; //11,5V } else if (IS_HORUS_X10(board)) { // Lipo 2V vBatWarn = 66; vBatMin = -28; // 6.2V vBatMax = -38; // 8.2V } else if (IS_TARANIS(board)) { // NI-MH 7.2V, X9D, X9D+ and X7 vBatWarn = 65; vBatMin = -30; //6V vBatMax = -40; //8V } setDefaultControlTypes(board); backlightMode = 3; // keys and sticks // backlightBright = 0; // 0 = 100% if (IS_HORUS(board)) { backlightOffBright = 20; } if (IS_ARM(board)) { speakerVolume = 12; } if (IS_HORUS(board) || IS_TARANIS_X9E(board) || IS_TARANIS_X7(board)) { strcpy(bluetoothName, "Taranis"); } templateSetup = g.profile[g.sessionId()].channelOrder(); stickMode = g.profile[g.sessionId()].defaultMode(); QString t_calib = g.profile[g.sessionId()].stickPotCalib(); int potsnum = getBoardCapability(getCurrentBoard(), Board::Pots); if (!t_calib.isEmpty()) { QString t_trainercalib=g.profile[g.sessionId()].trainerCalib(); int8_t t_txVoltageCalibration=(int8_t)g.profile[g.sessionId()].txVoltageCalibration(); int8_t t_txCurrentCalibration=(int8_t)g.profile[g.sessionId()].txCurrentCalibration(); int8_t t_PPM_Multiplier=(int8_t)g.profile[g.sessionId()].ppmMultiplier(); uint8_t t_stickMode=(uint8_t)g.profile[g.sessionId()].gsStickMode(); uint8_t t_vBatWarn=(uint8_t)g.profile[g.sessionId()].vBatWarn(); QString t_DisplaySet=g.profile[g.sessionId()].display(); QString t_BeeperSet=g.profile[g.sessionId()].beeper(); QString t_HapticSet=g.profile[g.sessionId()].haptic(); QString t_SpeakerSet=g.profile[g.sessionId()].speaker(); QString t_CountrySet=g.profile[g.sessionId()].countryCode(); if ((t_calib.length()==(CPN_MAX_STICKS+potsnum)*12) && (t_trainercalib.length()==16)) { QString Byte; int16_t byte16; bool ok; for (int i=0; i<(CPN_MAX_STICKS+potsnum); i++) { Byte=t_calib.mid(i*12,4); byte16=(int16_t)Byte.toInt(&ok,16); if (ok) calibMid[i]=byte16; Byte=t_calib.mid(4+i*12,4); byte16=(int16_t)Byte.toInt(&ok,16); if (ok) calibSpanNeg[i]=byte16; Byte=t_calib.mid(8+i*12,4); byte16=(int16_t)Byte.toInt(&ok,16); if (ok) calibSpanPos[i]=byte16; } for (int i=0; i<4; i++) { Byte=t_trainercalib.mid(i*4,4); byte16=(int16_t)Byte.toInt(&ok,16); if (ok) trainer.calib[i]=byte16; } txCurrentCalibration=t_txCurrentCalibration; txVoltageCalibration=t_txVoltageCalibration; vBatWarn=t_vBatWarn; PPM_Multiplier=t_PPM_Multiplier; stickMode = t_stickMode; } if ((t_DisplaySet.length()==6) && (t_BeeperSet.length()==4) && (t_HapticSet.length()==6) && (t_SpeakerSet.length()==6)) { uint8_t byte8u; int8_t byte8; bool ok; byte8=(int8_t)t_DisplaySet.mid(0,2).toInt(&ok,16); if (ok) optrexDisplay=(byte8==1 ? true : false); byte8u=(uint8_t)t_DisplaySet.mid(2,2).toUInt(&ok,16); if (ok) contrast=byte8u; byte8u=(uint8_t)t_DisplaySet.mid(4,2).toUInt(&ok,16); if (ok) backlightBright=byte8u; byte8=(int8_t)t_BeeperSet.mid(0,2).toUInt(&ok,16); if (ok) beeperMode=(BeeperMode)byte8; byte8=(int8_t)t_BeeperSet.mid(2,2).toInt(&ok,16); if (ok) beeperLength=byte8; byte8=(int8_t)t_HapticSet.mid(0,2).toUInt(&ok,16); if (ok) hapticMode=(BeeperMode)byte8; byte8=(int8_t)t_HapticSet.mid(2,2).toInt(&ok,16); if (ok) hapticStrength=byte8; byte8=(int8_t)t_HapticSet.mid(4,2).toInt(&ok,16); if (ok) hapticLength=byte8; byte8u=(uint8_t)t_SpeakerSet.mid(0,2).toUInt(&ok,16); if (ok) speakerMode=byte8u; byte8u=(uint8_t)t_SpeakerSet.mid(2,2).toUInt(&ok,16); if (ok) speakerPitch=byte8u; byte8u=(uint8_t)t_SpeakerSet.mid(4,2).toUInt(&ok,16); if (ok) speakerVolume=byte8u; if (t_CountrySet.length()==6) { byte8u=(uint8_t)t_CountrySet.mid(0,2).toUInt(&ok,16); if (ok) countryCode=byte8u; byte8u=(uint8_t)t_CountrySet.mid(2,2).toUInt(&ok,16); if (ok) imperial=byte8u; QString chars = t_CountrySet.mid(4, 2); ttsLanguage[0] = chars[0].toLatin1(); ttsLanguage[1] = chars[1].toLatin1(); } } } strcpy(themeName, "default"); ThemeOptionData option1 = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 }; memcpy(&themeOptionValue[0], option1, sizeof(ThemeOptionData)); ThemeOptionData option2 = { 0x03, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 }; memcpy(&themeOptionValue[1], option2, sizeof(ThemeOptionData)); } void GeneralSettings::setDefaultControlTypes(Board::Type board) { for (int i=0; i= CPN_MAX_STICKS) return -1; else return chout_ar[4*templateSetup + channel] - 1; } RawSource GeneralSettings::getDefaultSource(unsigned int channel) const { int stick = getDefaultStick(channel); if (stick >= 0) return RawSource(SOURCE_TYPE_STICK, stick); else return RawSource(SOURCE_TYPE_NONE); } int GeneralSettings::getDefaultChannel(unsigned int stick) const { for (int i=0; i<4; i++){ if (getDefaultStick(i) == (int)stick) return i; } return -1; } float FrSkyChannelData::getRatio() const { if (type==0 || type==1 || type==2) return float(ratio << multiplier) / 10.0; else return ratio << multiplier; } RawSourceRange FrSkyChannelData::getRange() const { RawSourceRange result; float ratio = getRatio(); if (type==0 || type==1 || type==2) result.decimals = 2; else result.decimals = 0; result.step = ratio / 255; result.min = offset * result.step; result.max = ratio + result.min; result.unit = QObject::tr("V"); return result; } void FrSkyScreenData::clear() { memset(this, 0, sizeof(FrSkyScreenData)); if (!IS_ARM(getCurrentBoard())) { type = TELEMETRY_SCREEN_NUMBERS; } } void FrSkyData::clear() { usrProto = 0; voltsSource = 0; altitudeSource = 0; currentSource = 0; varioMin = 0; varioCenterMin = 0; // if increment in 0.2m/s = 3.0m/s max varioCenterMax = 0; varioMax = 0; mAhPersistent = 0; storedMah = 0; fasOffset = 0; for (int i=0; i<4; i++) screens[i].clear(); varioSource = 2/*VARIO*/; blades = 2; } ModelData::ModelData() { clear(); } ModelData::ModelData(const ModelData & src) { *this = src; } ModelData & ModelData::operator = (const ModelData & src) { memcpy(this, &src, sizeof(ModelData)); return *this; } ExpoData * ModelData::insertInput(const int idx) { memmove(&expoData[idx+1], &expoData[idx], (CPN_MAX_EXPOS-(idx+1))*sizeof(ExpoData)); expoData[idx].clear(); return &expoData[idx]; } bool ModelData::isInputValid(const unsigned int idx) const { for (int i=0; imode == 0) break; if (expo->chn == idx) return true; } return false; } bool ModelData::hasExpos(uint8_t inputIdx) const { for (int i=0; i ModelData::expos(int input) const { QVector result; for (int i=0; ichn==input && ed->mode!=0) { result << ed; } } return result; } QVector ModelData::mixes(int channel) const { QVector result; for (int i=0; idestCh == channel+1) { result << md; } } return result; } void ModelData::removeInput(const int idx) { unsigned int chn = expoData[idx].chn; memmove(&expoData[idx], &expoData[idx+1], (CPN_MAX_EXPOS-(idx+1))*sizeof(ExpoData)); expoData[CPN_MAX_EXPOS-1].clear(); //also remove input name if removing last line for this input bool found = false; for (int i=0; igetCapability(VirtualInputs)) { for (int i=0; ichn = i; expo->mode = INPUT_MODE_BOTH; expo->srcRaw = settings.getDefaultSource(i); expo->weight = 100; strncpy(inputNames[i], removeAccents(expo->srcRaw.toString(this)).toLatin1().constData(), sizeof(inputNames[i])-1); } } } void ModelData::setDefaultMixes(const GeneralSettings & settings) { Board::Type board = getCurrentBoard(); if (IS_ARM(board)) { setDefaultInputs(settings); } for (int i=0; idestCh = i+1; mix->weight = 100; if (IS_ARM(board)) { mix->srcRaw = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, i); } else { mix->srcRaw = RawSource(SOURCE_TYPE_STICK, i); } } } void ModelData::setDefaultValues(unsigned int id, const GeneralSettings & settings) { clear(); used = true; sprintf(name, "MODEL%02d", id+1); for (int i=0; i 1024; } int ModelData::getGVarFieldValue(int phaseIdx, int gvarIdx) { int idx = flightModeData[phaseIdx].gvars[gvarIdx]; for (int i=0; idx>GVAR_MAX_VALUE && i= phaseIdx) nextPhase += 1; phaseIdx = nextPhase; idx = flightModeData[phaseIdx].gvars[gvarIdx]; } return idx; } void ModelData::setTrimValue(int phaseIdx, int trimIdx, int value) { for (uint8_t i=0; i 500) trim = 500; break; } } } void ModelData::removeGlobalVar(int & var) { if (var >= 126 && var <= 130) var = flightModeData[0].gvars[var-126]; else if (var <= -126 && var >= -130) var = - flightModeData[0].gvars[-126-var]; } ModelData ModelData::removeGlobalVars() { ModelData result = *this; for (int i=0; i 0; } else { return true; } } float ModelData::getGVarFieldValuePrec(int phaseIdx, int gvarIdx) { return getGVarFieldValue(phaseIdx, gvarIdx) * gvarData[gvarIdx].multiplierGet(); } QList eepromInterfaces; void unregisterEEpromInterfaces() { foreach(EEPROMInterface * intf, eepromInterfaces) { // qDebug() << "UnregisterEepromInterfaces(): deleting " << QString::number( reinterpret_cast(intf), 16 ); delete intf; } OpenTxEepromCleanup(); } void ShowEepromErrors(QWidget *parent, const QString &title, const QString &mainMessage, unsigned long errorsFound) { std::bitset errors((unsigned long long)errorsFound); QStringList errorsList; errorsList << QT_TRANSLATE_NOOP("EepromInterface", "Possible causes for this:"); if (errors.test(UNSUPPORTED_NEWER_VERSION)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is from a newer version of OpenTX"); } if (errors.test(NOT_OPENTX)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from OpenTX"); } if (errors.test(NOT_TH9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from Th9X"); } if (errors.test(NOT_GRUVIN9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from Gruvin9X"); } if (errors.test(NOT_ERSKY9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from ErSky9X"); } if (errors.test(NOT_ER9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from Er9X"); } if (errors.test(WRONG_SIZE)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom size is invalid"); } if (errors.test(WRONG_FILE_SYSTEM)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom file system is invalid"); } if (errors.test(UNKNOWN_BOARD)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is from a unknown board"); } if (errors.test(WRONG_BOARD)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is from the wrong board"); } if (errors.test(BACKUP_NOT_SUPPORTED)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom backup not supported"); } if (errors.test(UNKNOWN_ERROR)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Something that couldn't be guessed, sorry"); } if (errors.test(HAS_WARNINGS)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "Warning:"); if (errors.test(WARNING_WRONG_FIRMWARE)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Your radio probably uses a wrong firmware,\n eeprom size is 4096 but only the first 2048 are used"); } } QMessageBox msgBox(parent); msgBox.setWindowTitle(title); msgBox.setIcon(QMessageBox::Critical); msgBox.setText(mainMessage); msgBox.setInformativeText(errorsList.join("\n")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } void ShowEepromWarnings(QWidget *parent, const QString &title, unsigned long errorsFound) { std::bitset errors((unsigned long long)errorsFound); QStringList warningsList; if (errors.test(WARNING_WRONG_FIRMWARE)) { warningsList << QT_TRANSLATE_NOOP("EepromInterface", "- Your radio probably uses a wrong firmware,\n eeprom size is 4096 but only the first 2048 are used"); } if (errors.test(OLD_VERSION)) { warningsList << QT_TRANSLATE_NOOP("EepromInterface", "- Your eeprom is from an old version of OpenTX, upgrading!\n To keep your original file as a backup, please choose File -> Save As specifying a different name."); } QMessageBox msgBox(parent); msgBox.setWindowTitle(title); msgBox.setIcon(QMessageBox::Warning); msgBox.setText(QT_TRANSLATE_NOOP("EepromInterface", "Warnings!")); msgBox.setInformativeText(warningsList.join("\n")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } // static QVector Firmware::registeredFirmwares; Firmware * Firmware::defaultVariant = NULL; Firmware * Firmware::currentVariant = NULL; // static Firmware * Firmware::getFirmwareForId(const QString & id) { foreach(Firmware * firmware, registeredFirmwares) { Firmware * result = firmware->getFirmwareVariant(id); if (result) { return result; } } return defaultVariant; } void Firmware::addOption(const char *option, QString tooltip, uint32_t variant) { Option options[] = { { option, tooltip, variant }, { NULL } }; addOptions(options); } unsigned int Firmware::getVariantNumber() { unsigned int result = 0; const Firmware * base = getFirmwareBase(); QStringList options = id.mid(base->getId().length()+1).split("-", QString::SkipEmptyParts); foreach(QString option, options) { foreach(QList