Improve settings migration and management (#5107)
[opentx.git] / companion / src / eeprominterface.cpp
blob43050536575879e97ebbc743ef43a817ce136b89
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "eeprominterface.h"
22 #include "firmwares/er9x/er9xinterface.h"
23 #include "firmwares/ersky9x/ersky9xinterface.h"
24 #include "firmwares/opentx/opentxinterface.h"
25 #include "firmwares/opentx/opentxeeprom.h"
26 #include "appdata.h"
27 #include "helpers.h"
28 #include "wizarddata.h"
29 #include "firmwareinterface.h"
30 #include "macros.h"
31 #include "multiprotocols.h"
32 #include <stdio.h>
33 #include <list>
34 #include <float.h>
35 #include <QtWidgets>
36 #include <stdlib.h>
37 #include <bitset>
39 const uint8_t chout_ar[] = { // First number is 0..23 -> template setup, Second is relevant channel out
40 1,2,3,4 , 1,2,4,3 , 1,3,2,4 , 1,3,4,2 , 1,4,2,3 , 1,4,3,2,
41 2,1,3,4 , 2,1,4,3 , 2,3,1,4 , 2,3,4,1 , 2,4,1,3 , 2,4,3,1,
42 3,1,2,4 , 3,1,4,2 , 3,2,1,4 , 3,2,4,1 , 3,4,1,2 , 3,4,2,1,
43 4,1,2,3 , 4,1,3,2 , 4,2,1,3 , 4,2,3,1 , 4,3,1,2 , 4,3,2,1
46 static const char specialCharsTab[] = "_-.,";
48 void setEEPROMString(char *dst, const char *src, int size)
50 memcpy(dst, src, size);
51 for (int i=size-1; i>=0; i--) {
52 if (dst[i] == '\0')
53 dst[i] = ' ';
54 else
55 break;
59 void getEEPROMString(char *dst, const char *src, int size)
61 memcpy(dst, src, size);
62 dst[size] = '\0';
63 for (int i=size-1; i>=0; i--) {
64 if (dst[i] == ' ')
65 dst[i] = '\0';
66 else
67 break;
71 int8_t char2idx(char c)
73 if (c==' ') return 0;
74 if (c>='A' && c<='Z') return 1+c-'A';
75 if (c>='a' && c<='z') return -1-c+'a';
76 if (c>='0' && c<='9') return 27+c-'0';
77 for (int8_t i=0;;i++) {
78 char cc = specialCharsTab[i];
79 if (cc==0) return 0;
80 if (cc==c) return 37+i;
84 #define ZCHAR_MAX 40
85 char idx2char(int8_t idx)
87 if (idx == 0) return ' ';
88 if (idx < 0) {
89 if (idx > -27) return 'a' - idx - 1;
90 idx = -idx;
92 if (idx < 27) return 'A' + idx - 1;
93 if (idx < 37) return '0' + idx - 27;
94 if (idx <= ZCHAR_MAX) return specialCharsTab[idx-37];
95 return ' ';
98 void setEEPROMZString(char *dst, const char *src, int size)
100 for (int i=size-1; i>=0; i--)
101 dst[i] = char2idx(src[i]);
104 void getEEPROMZString(char *dst, const char *src, int size)
106 for (int i=size-1; i>=0; i--)
107 dst[i] = idx2char(src[i]);
108 dst[size] = '\0';
109 for (int i=size-1; i>=0; i--) {
110 if (dst[i] == ' ')
111 dst[i] = '\0';
112 else
113 break;
117 QString getElementName(const QString & prefix, unsigned int index, const char * name = NULL, bool padding = false)
119 QString result = prefix;
120 if (padding)
121 result += QString("%1").arg(index, 2, 10, QChar('0'));
122 else
123 result += QString("%1").arg(index);
124 if (name) {
125 QString trimmed = QString(name).trimmed();
126 if (trimmed.length() > 0) {
127 result += ":" + QString(name).trimmed();
130 return result;
134 float ValToTim(int value)
136 return ((value < -109 ? 129+value : (value < 7 ? (113+value)*5 : (53+value)*10))/10.0);
139 int TimToVal(float value)
141 int temp;
142 if (value>60) {
143 temp=136+round((value-60));
145 else if (value>2) {
146 temp=20+round((value-2.0)*2.0);
148 else {
149 temp=round(value*10.0);
151 return (temp-129);
154 void SensorData::updateUnit()
156 if (type == TELEM_TYPE_CALCULATED) {
157 if (formula == TELEM_FORMULA_CONSUMPTION)
158 unit = UNIT_MAH;
162 QString SensorData::unitString() const
164 switch (unit) {
165 case UNIT_VOLTS:
166 return QObject::tr("V");
167 case UNIT_AMPS:
168 return QObject::tr("A");
169 case UNIT_MILLIAMPS:
170 return QObject::tr("mA");
171 case UNIT_KTS:
172 return QObject::tr("kts");
173 case UNIT_METERS_PER_SECOND:
174 return QObject::tr("m/s");
175 case UNIT_KMH:
176 return QObject::tr("km/h");
177 case UNIT_MPH:
178 return QObject::tr("mph");
179 case UNIT_METERS:
180 return QObject::tr("m");
181 case UNIT_FEET:
182 return QObject::tr("f");
183 case UNIT_CELSIUS:
184 return QObject::trUtf8("°C");
185 case UNIT_FAHRENHEIT:
186 return QObject::trUtf8("°F");
187 case UNIT_PERCENT:
188 return QObject::tr("%");
189 case UNIT_MAH:
190 return QObject::tr("mAh");
191 case UNIT_WATTS:
192 return QObject::tr("W");
193 case UNIT_MILLIWATTS:
194 return QObject::tr("mW");
195 case UNIT_DB:
196 return QObject::tr("dB");
197 case UNIT_RPMS:
198 return QObject::tr("rpms");
199 case UNIT_G:
200 return QObject::tr("g");
201 case UNIT_DEGREE:
202 return QObject::trUtf8("°");
203 case UNIT_RADIANS:
204 return QObject::trUtf8("Rad");
205 case UNIT_HOURS:
206 return QObject::tr("hours");
207 case UNIT_MINUTES:
208 return QObject::tr("minutes");
209 case UNIT_SECONDS:
210 return QObject::tr("seconds");
211 case UNIT_CELLS:
212 return QObject::tr("V");
213 default:
214 return "";
218 bool RawSource::isTimeBased() const
220 if (IS_ARM(getCurrentBoard()))
221 return (type == SOURCE_TYPE_SPECIAL && index > 0);
222 else
223 return (type==SOURCE_TYPE_TELEMETRY && (index==TELEMETRY_SOURCE_TX_TIME || index==TELEMETRY_SOURCE_TIMER1 || index==TELEMETRY_SOURCE_TIMER2 || index==TELEMETRY_SOURCE_TIMER3));
226 float RawSourceRange::getValue(int value)
228 if (IS_ARM(getCurrentBoard()))
229 return float(value) * step;
230 else
231 return min + float(value) * step;
234 RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags) const
236 RawSourceRange result;
238 Firmware * firmware = Firmware::getCurrentVariant();
239 int board = firmware->getBoard();
240 bool singleprec = (flags & RANGE_SINGLE_PRECISION);
242 if (!singleprec && !IS_ARM(board)) {
243 singleprec = true;
246 switch (type) {
247 case SOURCE_TYPE_TELEMETRY:
248 if (IS_ARM(board)) {
249 div_t qr = div(index, 3);
250 const SensorData & sensor = model->sensorData[qr.quot];
251 if (sensor.prec == 2)
252 result.step = 0.01;
253 else if (sensor.prec == 1)
254 result.step = 0.1;
255 else
256 result.step = 1;
257 result.min = -30000 * result.step;
258 result.max = +30000 * result.step;
259 result.decimals = sensor.prec;
260 result.unit = sensor.unitString();
262 else {
263 if (singleprec) {
264 result.offset = -DBL_MAX;
267 switch (index) {
268 case TELEMETRY_SOURCE_TX_BATT:
269 result.step = 0.1;
270 result.decimals = 1;
271 result.max = 25.5;
272 result.unit = QObject::tr("V");
273 break;
274 case TELEMETRY_SOURCE_TX_TIME:
275 result.step = 1;
276 result.max = 24*60 - 1;
277 break;
278 case TELEMETRY_SOURCE_TIMER1:
279 case TELEMETRY_SOURCE_TIMER2:
280 case TELEMETRY_SOURCE_TIMER3:
281 result.step = singleprec ? 5 : 1;
282 result.max = singleprec ? 255*5 : 60*60;
283 result.unit = QObject::tr("s");
284 break;
285 case TELEMETRY_SOURCE_RSSI_TX:
286 case TELEMETRY_SOURCE_RSSI_RX:
287 result.max = 100;
288 if (singleprec) result.offset = 128;
289 break;
290 case TELEMETRY_SOURCE_A1_MIN:
291 case TELEMETRY_SOURCE_A2_MIN:
292 case TELEMETRY_SOURCE_A3_MIN:
293 case TELEMETRY_SOURCE_A4_MIN:
294 if (model) result = model->frsky.channels[index-TELEMETRY_SOURCE_A1_MIN].getRange();
295 break;
296 case TELEMETRY_SOURCE_A1:
297 case TELEMETRY_SOURCE_A2:
298 case TELEMETRY_SOURCE_A3:
299 case TELEMETRY_SOURCE_A4:
300 if (model) result = model->frsky.channels[index-TELEMETRY_SOURCE_A1].getRange();
301 break;
302 case TELEMETRY_SOURCE_ALT:
303 case TELEMETRY_SOURCE_ALT_MIN:
304 case TELEMETRY_SOURCE_ALT_MAX:
305 case TELEMETRY_SOURCE_GPS_ALT:
306 result.step = singleprec ? 8 : 1;
307 result.min = -500;
308 result.max = singleprec ? 1540 : 3000;
309 if (firmware->getCapability(Imperial) || settings.imperial) {
310 result.step = (result.step * 105) / 32;
311 result.min = (result.min * 105) / 32;
312 result.max = (result.max * 105) / 32;
313 result.unit = QObject::tr("ft");
315 else {
316 result.unit = QObject::tr("m");
318 break;
319 case TELEMETRY_SOURCE_T1:
320 case TELEMETRY_SOURCE_T1_MAX:
321 case TELEMETRY_SOURCE_T2:
322 case TELEMETRY_SOURCE_T2_MAX:
323 result.min = -30;
324 result.max = 225;
325 result.unit = QObject::trUtf8("°C");
326 break;
327 case TELEMETRY_SOURCE_HDG:
328 result.step = singleprec ? 2 : 1;
329 result.max = 360;
330 if (singleprec) result.offset = 256;
331 result.unit = QObject::trUtf8("°");
332 break;
333 case TELEMETRY_SOURCE_RPM:
334 case TELEMETRY_SOURCE_RPM_MAX:
335 result.step = singleprec ? 50 : 1;
336 result.max = singleprec ? 12750 : 30000;
337 break;
338 case TELEMETRY_SOURCE_FUEL:
339 result.max = 100;
340 result.unit = QObject::tr("%");
341 break;
342 case TELEMETRY_SOURCE_ASPEED:
343 case TELEMETRY_SOURCE_ASPEED_MAX:
344 result.decimals = 1;
345 result.step = singleprec ? 2.0 : 0.1;
346 result.max = singleprec ? (2*255) : 2000;
347 if (firmware->getCapability(Imperial) || settings.imperial) {
348 result.step *= 1.150779;
349 result.max *= 1.150779;
350 result.unit = QObject::tr("mph");
352 else {
353 result.step *= 1.852;
354 result.max *= 1.852;
355 result.unit = QObject::tr("km/h");
357 break;
358 case TELEMETRY_SOURCE_SPEED:
359 case TELEMETRY_SOURCE_SPEED_MAX:
360 result.step = singleprec ? 2 : 1;
361 result.max = singleprec ? (2*255) : 2000;
362 if (firmware->getCapability(Imperial) || settings.imperial) {
363 result.step *= 1.150779;
364 result.max *= 1.150779;
365 result.unit = QObject::tr("mph");
367 else {
368 result.step *= 1.852;
369 result.max *= 1.852;
370 result.unit = QObject::tr("km/h");
372 break;
373 case TELEMETRY_SOURCE_VERTICAL_SPEED:
374 result.step = 0.1;
375 result.min = singleprec ? -12.5 : -300.0;
376 result.max = singleprec ? 13.0 : 300.0;
377 result.decimals = 1;
378 result.unit = QObject::tr("m/s");
379 break;
380 case TELEMETRY_SOURCE_DTE:
381 result.max = 30000;
382 break;
383 case TELEMETRY_SOURCE_DIST:
384 case TELEMETRY_SOURCE_DIST_MAX:
385 result.step = singleprec ? 8 : 1;
386 result.max = singleprec ? 2040 : 10000;
387 result.unit = QObject::tr("m");
388 break;
389 case TELEMETRY_SOURCE_CELL:
390 case TELEMETRY_SOURCE_CELL_MIN:
391 result.step = singleprec ? 0.02 : 0.01;
392 result.max = 5.1;
393 result.decimals = 2;
394 result.unit = QObject::tr("V");
395 break;
396 case TELEMETRY_SOURCE_CELLS_SUM:
397 case TELEMETRY_SOURCE_CELLS_MIN:
398 case TELEMETRY_SOURCE_VFAS:
399 case TELEMETRY_SOURCE_VFAS_MIN:
400 result.step = 0.1;
401 result.max = singleprec ? 25.5 : 100.0;
402 result.decimals = 1;
403 result.unit = QObject::tr("V");
404 break;
405 case TELEMETRY_SOURCE_CURRENT:
406 case TELEMETRY_SOURCE_CURRENT_MAX:
407 result.step = singleprec ? 0.5 : 0.1;
408 result.max = singleprec ? 127.5 : 200.0;
409 result.decimals = 1;
410 result.unit = QObject::tr("A");
411 break;
412 case TELEMETRY_SOURCE_CONSUMPTION:
413 result.step = singleprec ? 100 : 1;
414 result.max = singleprec ? 25500 : 30000;
415 result.unit = QObject::tr("mAh");
416 break;
417 case TELEMETRY_SOURCE_POWER:
418 case TELEMETRY_SOURCE_POWER_MAX:
419 result.step = singleprec ? 5 : 1;
420 result.max = singleprec ? 1275 : 2000;
421 result.unit = QObject::tr("W");
422 break;
423 case TELEMETRY_SOURCE_ACCX:
424 case TELEMETRY_SOURCE_ACCY:
425 case TELEMETRY_SOURCE_ACCZ:
426 result.step = 0.01;
427 result.decimals = 2;
428 result.max = singleprec ? 2.55 : 10.00;
429 result.min = singleprec ? 0 : -10.00;
430 result.unit = QObject::tr("g");
431 break;
432 default:
433 result.max = 125;
434 break;
437 if (singleprec && result.offset==-DBL_MAX) {
438 result.offset = result.max - (127*result.step);
441 if (flags & (RANGE_DELTA_FUNCTION|RANGE_DELTA_ABS_FUNCTION)) {
442 if (singleprec) {
443 result.offset = 0;
444 result.min = result.step * -127;
445 result.max = result.step * 127;
447 else {
448 result.min = -result.max;
452 break;
454 case SOURCE_TYPE_GVAR:
455 result.max = 1024;
456 result.min = -result.max;
457 break;
459 case SOURCE_TYPE_SPECIAL:
460 if (index == 0) { //Batt
461 result.step = 0.1;
462 result.decimals = 1;
463 result.max = 25.5;
464 result.unit = QObject::tr("V");
466 else if (index == 1) { //Time
467 result.step = 1;
468 result.max = 24*60 - 1;
469 result.unit = QObject::tr("h:m");
471 else { // Timers 1 - 3
472 result.step = singleprec ? 5 : 1;
473 result.max = singleprec ? 255*5 : 60*60;
474 result.unit = singleprec ? QObject::tr("m:s") : QObject::tr("h:m:s");
476 break;
478 default:
479 if (model) {
480 result.max = model->getChannelsMax(true);
481 result.min = -result.max;
483 break;
486 if (flags & RANGE_DELTA_ABS_FUNCTION) {
487 result.min = 0;
490 return result;
493 QString RawSource::toString(const ModelData * model, const GeneralSettings * const generalSettings) const
495 static const QString trims[] = {
496 QObject::tr("TrmR"), QObject::tr("TrmE"), QObject::tr("TrmT"), QObject::tr("TrmA"), QObject::tr("Trm5"), QObject::tr("Trm6")
499 static const QString special[] = {
500 QObject::tr("Batt"), QObject::tr("Time"), QObject::tr("Timer1"), QObject::tr("Timer2"), QObject::tr("Timer3"),
503 static const QString telemetry[] = {
504 QObject::tr("Batt"), QObject::tr("Time"), QObject::tr("Timer1"), QObject::tr("Timer2"), QObject::tr("Timer3"),
505 QObject::tr("SWR"), QObject::tr("RSSI Tx"), QObject::tr("RSSI Rx"),
506 QObject::tr("A1"), QObject::tr("A2"), QObject::tr("A3"), QObject::tr("A4"),
507 QObject::tr("Alt"), QObject::tr("Rpm"), QObject::tr("Fuel"), QObject::tr("T1"), QObject::tr("T2"),
508 QObject::tr("Speed"), QObject::tr("Dist"), QObject::tr("GPS Alt"),
509 QObject::tr("Cell"), QObject::tr("Cells"), QObject::tr("Vfas"), QObject::tr("Curr"), QObject::tr("Cnsp"), QObject::tr("Powr"),
510 QObject::tr("AccX"), QObject::tr("AccY"), QObject::tr("AccZ"),
511 QObject::tr("Hdg "), QObject::tr("VSpd"), QObject::tr("AirSpeed"), QObject::tr("dTE"),
512 QObject::tr("A1-"), QObject::tr("A2-"), QObject::tr("A3-"), QObject::tr("A4-"),
513 QObject::tr("Alt-"), QObject::tr("Alt+"), QObject::tr("Rpm+"), QObject::tr("T1+"), QObject::tr("T2+"), QObject::tr("Speed+"), QObject::tr("Dist+"), QObject::tr("AirSpeed+"),
514 QObject::tr("Cell-"), QObject::tr("Cells-"), QObject::tr("Vfas-"), QObject::tr("Curr+"), QObject::tr("Powr+"),
515 QObject::tr("ACC"), QObject::tr("GPS Time"),
518 static const QString rotary[] = { QObject::tr("REa"), QObject::tr("REb") };
520 if (index<0) {
521 return QObject::tr("----");
524 QString result;
525 int genAryIdx = 0;
526 switch (type) {
527 case SOURCE_TYPE_VIRTUAL_INPUT:
529 const char * name = NULL;
530 if (model)
531 name = model->inputNames[index];
532 return getElementName(QCoreApplication::translate("Input", "I"), index + 1, name);
535 case SOURCE_TYPE_LUA_OUTPUT:
536 return QObject::tr("LUA%1%2").arg(index/16+1).arg(QChar('a'+index%16));
538 case SOURCE_TYPE_STICK:
539 if (generalSettings) {
540 if (isPot(&genAryIdx))
541 result = QString(generalSettings->potName[genAryIdx]);
542 else if (isSlider(&genAryIdx))
543 result = QString(generalSettings->sliderName[genAryIdx]);
544 else if (isStick(&genAryIdx))
545 result = QString(generalSettings->stickName[genAryIdx]);
547 if (result.isEmpty())
548 result = getCurrentFirmware()->getAnalogInputName(index);;
549 return result;
551 case SOURCE_TYPE_TRIM:
552 return CHECK_IN_ARRAY(trims, index);
553 case SOURCE_TYPE_ROTARY_ENCODER:
554 return CHECK_IN_ARRAY(rotary, index);
555 case SOURCE_TYPE_MAX:
556 return QObject::tr("MAX");
558 case SOURCE_TYPE_SWITCH:
559 if (generalSettings)
560 result = QString(generalSettings->switchName[index]);
561 if (result.isEmpty())
562 result = getSwitchInfo(getCurrentBoard(), index).name;
563 return result;
565 case SOURCE_TYPE_CUSTOM_SWITCH:
566 return RawSwitch(SWITCH_TYPE_VIRTUAL, index+1).toString();
568 case SOURCE_TYPE_CYC:
569 return QObject::tr("CYC%1").arg(index+1);
571 case SOURCE_TYPE_PPM:
572 return getElementName(QCoreApplication::translate("Trainer", "TR"), index + 1);
574 case SOURCE_TYPE_CH:
576 const char * name = NULL;
577 if (getCurrentFirmware()->getCapability(ChannelsName) && model)
578 name = model->limitData[index].name;
579 return getElementName(QCoreApplication::translate("Channel", "CH"), index + 1, name);
582 case SOURCE_TYPE_SPECIAL:
583 return CHECK_IN_ARRAY(special, index);
585 case SOURCE_TYPE_TELEMETRY:
586 if (IS_ARM(getCurrentBoard())) {
587 div_t qr = div(index, 3);
588 result = getElementName(QCoreApplication::translate("Telemetry", "TELE"), qr.quot+1, model ? model->sensorData[qr.quot].label : NULL);
589 if (qr.rem)
590 result += (qr.rem == 1 ? "-" : "+");
591 return result;
593 else {
594 return CHECK_IN_ARRAY(telemetry, index);
597 case SOURCE_TYPE_GVAR:
599 const char * name = NULL;
600 if (getCurrentFirmware()->getCapability(GvarsName) && model)
601 name = model->gvars_names[index];
602 return getElementName(QCoreApplication::translate("Global Variable", "GV"), index + 1, name);
605 default:
606 return QObject::tr("----");
610 bool RawSource::isStick(int * stickIndex) const
612 if (type == SOURCE_TYPE_STICK && index < getBoardCapability(getCurrentBoard(), Board::Sticks)) {
613 if (stickIndex)
614 *stickIndex = index;
615 return true;
617 return false;
620 bool RawSource::isPot(int * potsIndex) const
622 if (type == SOURCE_TYPE_STICK &&
623 index >= getBoardCapability(getCurrentBoard(), Board::Sticks) &&
624 index < getBoardCapability(getCurrentBoard(), Board::Sticks) + getBoardCapability(getCurrentBoard(), Board::Pots)) {
625 if (potsIndex)
626 *potsIndex = index - getBoardCapability(getCurrentBoard(), Board::Sticks);
627 return true;
629 return false;
632 bool RawSource::isSlider(int * sliderIndex) const
634 if (type == SOURCE_TYPE_STICK &&
635 index >= getBoardCapability(getCurrentBoard(), Board::Sticks) + getBoardCapability(getCurrentBoard(), Board::Pots) &&
636 index < getBoardCapability(getCurrentBoard(), Board::Sticks) + getBoardCapability(getCurrentBoard(), Board::Pots) + getBoardCapability(getCurrentBoard(), Board::Sliders)) {
637 if (sliderIndex)
638 *sliderIndex = index - getBoardCapability(getCurrentBoard(), Board::Sticks) - getBoardCapability(getCurrentBoard(), Board::Pots);
639 return true;
641 return false;
645 * RawSwitch
648 QString RawSwitch::toString(Board::Type board, const GeneralSettings * const generalSettings, const ModelData * const modelData) const
650 if (board == Board::BOARD_UNKNOWN) {
651 board = getCurrentBoard();
654 static const QString switches9X[] = {
655 QString("THR"), QString("RUD"), QString("ELE"),
656 QString("ID0"), QString("ID1"), QString("ID2"),
657 QString("AIL"), QString("GEA"), QString("TRN")
660 static const QString trimsSwitches[] = {
661 QObject::tr("RudTrim Left"), QObject::tr("RudTrim Right"),
662 QObject::tr("EleTrim Down"), QObject::tr("EleTrim Up"),
663 QObject::tr("ThrTrim Down"), QObject::tr("ThrTrim Up"),
664 QObject::tr("AilTrim Left"), QObject::tr("AilTrim Right"),
665 QObject::tr("Trim 5 Down"), QObject::tr("Trim 5 Up"),
666 QObject::tr("Trim 6 Down"), QObject::tr("Trim 6 Up")
669 static const QString rotaryEncoders[] = {
670 QObject::tr("REa"), QObject::tr("REb")
673 static const QString timerModes[] = {
674 QObject::tr("OFF"), QObject::tr("ON"),
675 QObject::tr("THs"), QObject::tr("TH%"), QObject::tr("THt")
678 const QStringList directionIndicators = QStringList()
679 << CPN_STR_SW_INDICATOR_UP
680 << CPN_STR_SW_INDICATOR_NEUT
681 << CPN_STR_SW_INDICATOR_DN;
683 if (index < 0) {
684 return CPN_STR_SW_INDICATOR_REV % RawSwitch(type, -index).toString(board, generalSettings, modelData);
686 else {
687 QString swName;
688 div_t qr;
689 switch(type) {
690 case SWITCH_TYPE_SWITCH:
691 if (IS_HORUS_OR_TARANIS(board)) {
692 qr = div(index-1, 3);
693 if (generalSettings)
694 swName = QString(generalSettings->switchName[qr.quot]);
695 if (swName.isEmpty())
696 swName = getSwitchInfo(board, qr.quot).name;
697 return swName + directionIndicators.at(qr.rem > -1 && qr.rem < directionIndicators.size() ? qr.rem : 1);
699 else {
700 return CHECK_IN_ARRAY(switches9X, index - 1);
703 case SWITCH_TYPE_VIRTUAL:
704 return getElementName(QCoreApplication::translate("Logic Switch", "L"), index, NULL, true);
706 case SWITCH_TYPE_MULTIPOS_POT:
707 if (!getCurrentFirmware()->getCapability(MultiposPotsPositions))
708 return QObject::tr("???");
709 qr = div(index - 1, getCurrentFirmware()->getCapability(MultiposPotsPositions));
710 if (generalSettings && qr.quot < (int)DIM(generalSettings->potConfig))
711 swName = QString(generalSettings->potName[qr.quot]);
712 if (swName.isEmpty())
713 swName = getCurrentFirmware()->getAnalogInputName(qr.quot + getBoardCapability(board, Board::Sticks));;
714 return swName + "_" + QString::number(qr.rem + 1);
716 case SWITCH_TYPE_TRIM:
717 return CHECK_IN_ARRAY(trimsSwitches, index-1);
719 case SWITCH_TYPE_ROTARY_ENCODER:
720 return CHECK_IN_ARRAY(rotaryEncoders, index-1);
722 case SWITCH_TYPE_ON:
723 return QObject::tr("ON");
725 case SWITCH_TYPE_OFF:
726 return QObject::tr("OFF");
728 case SWITCH_TYPE_ONE:
729 return QObject::tr("One");
731 case SWITCH_TYPE_FLIGHT_MODE:
732 return getElementName(QCoreApplication::translate("Flight mode", "FM"), index - 1, modelData ? modelData->flightModeData[index-1].name : NULL);
734 case SWITCH_TYPE_NONE:
735 return QObject::tr("----");
737 case SWITCH_TYPE_TIMER_MODE:
738 return CHECK_IN_ARRAY(timerModes, index);
740 case SWITCH_TYPE_SENSOR:
741 return getElementName(QCoreApplication::translate("Telemetry", "TELE"), index, modelData ? modelData->sensorData[index-1].label : NULL);
743 case SWITCH_TYPE_TELEMETRY:
744 return QObject::tr("Telemetry");
746 default:
747 return QObject::tr("???");
754 * CurveReference
757 QString CurveReference::toString(const ModelData * model, bool verbose) const
759 if (value == 0) {
760 return "----";
763 QString ret;
764 unsigned idx = abs(value) - 1;
766 switch(type) {
767 case CURVE_REF_DIFF:
768 ret = QObject::tr("Diff(%1)").arg(Helpers::getAdjustmentString(value, model));
769 break;
771 case CURVE_REF_EXPO:
772 ret = QObject::tr("Expo(%1)").arg(Helpers::getAdjustmentString(value, model));
773 break;
775 case CURVE_REF_FUNC:
776 ret = QString("x>0" "x<0" "|x|" "f>0" "f<0" "|f|").mid(3*(value-1), 3);
777 if (verbose)
778 ret = QObject::tr("Function(%1)").arg(ret);
779 break;
781 default:
782 if (model)
783 ret = model->curves[idx].nameToString(idx);
784 else
785 ret = CurveData().nameToString(idx);
786 if (verbose)
787 ret = QObject::tr("Curve(%1)").arg(ret);
788 if (value < 0)
789 ret.prepend(CPN_STR_SW_INDICATOR_REV);
790 break;
793 return ret;
796 bool LogicalSwitchData::isEmpty() const
798 return (func == 0);
801 CSFunctionFamily LogicalSwitchData::getFunctionFamily() const
803 if (func == LS_FN_EDGE)
804 return LS_FAMILY_EDGE;
805 else if (func == LS_FN_TIMER)
806 return LS_FAMILY_TIMER;
807 else if (func == LS_FN_STICKY)
808 return LS_FAMILY_STICKY;
809 else if (func < LS_FN_AND || func > LS_FN_ELESS)
810 return LS_FAMILY_VOFS;
811 else if (func < LS_FN_EQUAL)
812 return LS_FAMILY_VBOOL;
813 else
814 return LS_FAMILY_VCOMP;
817 unsigned int LogicalSwitchData::getRangeFlags() const
819 if (func == LS_FN_DPOS)
820 return RANGE_DELTA_FUNCTION;
821 else if (func == LS_FN_DAPOS)
822 return RANGE_DELTA_ABS_FUNCTION;
823 else
824 return 0;
827 QString LogicalSwitchData::funcToString() const
829 switch (func) {
830 case LS_FN_OFF:
831 return QObject::tr("---");
832 case LS_FN_VPOS:
833 return QObject::tr("a>x");
834 case LS_FN_VNEG:
835 return QObject::tr("a<x");
836 case LS_FN_APOS:
837 return QObject::tr("|a|>x");
838 case LS_FN_ANEG:
839 return QObject::tr("|a|<x");
840 case LS_FN_AND:
841 return QObject::tr("AND");
842 case LS_FN_OR:
843 return QObject::tr("OR");
844 case LS_FN_XOR:
845 return QObject::tr("XOR");
846 case LS_FN_EQUAL:
847 return QObject::tr("a=b");
848 case LS_FN_NEQUAL:
849 return QObject::tr("a!=b");
850 case LS_FN_GREATER:
851 return QObject::tr("a>b");
852 case LS_FN_LESS:
853 return QObject::tr("a<b");
854 case LS_FN_EGREATER:
855 return QObject::tr("a>=b");
856 case LS_FN_ELESS:
857 return QObject::tr("a<=b");
858 case LS_FN_DPOS:
859 return QObject::tr("d>=x");
860 case LS_FN_DAPOS:
861 return QObject::tr("|d|>=x");
862 case LS_FN_VEQUAL:
863 return QObject::tr("a=x");
864 case LS_FN_VALMOSTEQUAL:
865 return QObject::tr("a~x");
866 case LS_FN_TIMER:
867 return QObject::tr("Timer");
868 case LS_FN_STICKY:
869 return QObject::tr("Sticky");
870 case LS_FN_EDGE:
871 return QObject::tr("Edge");
872 default:
873 return QObject::tr("Unknown");
877 void CustomFunctionData::clear()
879 memset(this, 0, sizeof(CustomFunctionData));
880 if (!getCurrentFirmware()->getCapability(SafetyChannelCustomFunction)) {
881 func = FuncTrainer;
885 bool CustomFunctionData::isEmpty() const
887 return (swtch.type == SWITCH_TYPE_NONE);
890 QString CustomFunctionData::funcToString(const ModelData * model) const
892 if (func >= FuncOverrideCH1 && func <= FuncOverrideCH32)
893 return QObject::tr("Override %1").arg(RawSource(SOURCE_TYPE_CH, func).toString(model));
894 else if (func == FuncTrainer)
895 return QObject::tr("Trainer");
896 else if (func == FuncTrainerRUD)
897 return QObject::tr("Trainer RUD");
898 else if (func == FuncTrainerELE)
899 return QObject::tr("Trainer ELE");
900 else if (func == FuncTrainerTHR)
901 return QObject::tr("Trainer THR");
902 else if (func == FuncTrainerAIL)
903 return QObject::tr("Trainer AIL");
904 else if (func == FuncInstantTrim)
905 return QObject::tr("Instant Trim");
906 else if (func == FuncPlaySound)
907 return QObject::tr("Play Sound");
908 else if (func == FuncPlayHaptic)
909 return QObject::tr("Haptic");
910 else if (func == FuncReset)
911 return QObject::tr("Reset");
912 else if (func >= FuncSetTimer1 && func <= FuncSetTimer3)
913 return QObject::tr("Set Timer %1").arg(func-FuncSetTimer1+1);
914 else if (func == FuncVario)
915 return QObject::tr("Vario");
916 else if (func == FuncPlayPrompt)
917 return QObject::tr("Play Track");
918 else if (func == FuncPlayBoth)
919 return QObject::tr("Play Both");
920 else if (func == FuncPlayValue)
921 return QObject::tr("Play Value");
922 else if (func == FuncPlayScript)
923 return QObject::tr("Play Script");
924 else if (func == FuncLogs)
925 return QObject::tr("SD Logs");
926 else if (func == FuncVolume)
927 return QObject::tr("Volume");
928 else if (func == FuncBacklight)
929 return QObject::tr("Backlight");
930 else if (func == FuncScreenshot)
931 return QObject::tr("Screenshot");
932 else if (func == FuncBackgroundMusic)
933 return QObject::tr("Background Music");
934 else if (func == FuncBackgroundMusicPause)
935 return QObject::tr("Background Music Pause");
936 else if (func >= FuncAdjustGV1 && func <= FuncAdjustGVLast)
937 return QObject::tr("Adjust %1").arg(RawSource(SOURCE_TYPE_GVAR, func-FuncAdjustGV1).toString(model));
938 else if (func == FuncSetFailsafeInternalModule)
939 return QObject::tr("SetFailsafe Int. Module");
940 else if (func == FuncSetFailsafeExternalModule)
941 return QObject::tr("SetFailsafe Ext. Module");
942 else if (func == FuncRangeCheckInternalModule)
943 return QObject::tr("RangeCheck Int. Module");
944 else if (func == FuncRangeCheckExternalModule)
945 return QObject::tr("RangeCheck Ext. Module");
946 else if (func == FuncBindInternalModule)
947 return QObject::tr("Bind Int. Module");
948 else if (func == FuncBindExternalModule)
949 return QObject::tr("Bind Ext. Module");
950 else {
951 return QString("???"); // Highlight unknown functions with output of question marks.(BTW should not happen that we do not know what a function is)
955 void CustomFunctionData::populateResetParams(const ModelData * model, QComboBox * b, unsigned int value = 0)
957 int val = 0;
958 Firmware * firmware = Firmware::getCurrentVariant();
959 Board::Type board = firmware->getBoard();
961 b->addItem(QObject::tr("Timer1"), val++);
962 b->addItem(QObject::tr("Timer2"), val++);
963 if (IS_ARM(board)) {
964 b->addItem( QObject::tr("Timer3"), val++);
966 b->addItem(QObject::tr("Flight"), val++);
967 b->addItem(QObject::tr("Telemetry"), val++);
968 int reCount = firmware->getCapability(RotaryEncoders);
969 if (reCount == 1) {
970 b->addItem(QObject::tr("Rotary Encoder"), val++);
972 else if (reCount == 2) {
973 b->addItem(QObject::tr("REa"), val++);
974 b->addItem(QObject::tr("REb"), val++);
976 if ((int)value < b->count()) {
977 b->setCurrentIndex(value);
979 if (model && IS_ARM(board)) {
980 for (int i=0; i<CPN_MAX_SENSORS; ++i) {
981 if (model->sensorData[i].isAvailable()) {
982 RawSource item = RawSource(SOURCE_TYPE_TELEMETRY, 3*i);
983 b->addItem(item.toString(model), val+i);
984 if ((int)value == val+i) {
985 b->setCurrentIndex(b->count()-1);
992 void CustomFunctionData::populatePlaySoundParams(QStringList & qs)
994 qs <<"Beep 1" << "Beep 2" << "Beep 3" << "Warn1" << "Warn2" << "Cheep" << "Ratata" << "Tick" << "Siren" << "Ring" ;
995 qs << "SciFi" << "Robot" << "Chirp" << "Tada" << "Crickt" << "AlmClk" ;
998 void CustomFunctionData::populateHapticParams(QStringList & qs)
1000 qs << "0" << "1" << "2" << "3";
1003 QString CustomFunctionData::paramToString(const ModelData * model) const
1005 QStringList qs;
1006 if (func <= FuncInstantTrim) {
1007 return QString("%1").arg(param);
1009 else if (func==FuncLogs) {
1010 return QString("%1").arg(param/10.0) + QObject::tr("s");
1012 else if (func==FuncPlaySound) {
1013 CustomFunctionData::populatePlaySoundParams(qs);
1014 if (param>=0 && param<(int)qs.count())
1015 return qs.at(param);
1016 else
1017 return QObject::tr("<font color=red><b>Inconsistent parameter</b></font>");
1019 else if (func==FuncPlayHaptic) {
1020 CustomFunctionData::populateHapticParams(qs);
1021 if (param>=0 && param<(int)qs.count())
1022 return qs.at(param);
1023 else
1024 return QObject::tr("<font color=red><b>Inconsistent parameter</b></font>");
1026 else if (func==FuncReset) {
1027 QComboBox cb;
1028 CustomFunctionData::populateResetParams(model, &cb);
1029 int pos = cb.findData(param);
1030 if (pos >= 0)
1031 return cb.itemText(pos);
1032 else
1033 return QObject::tr("<font color=red><b>Inconsistent parameter</b></font>");
1035 else if ((func==FuncVolume)|| (func==FuncPlayValue)) {
1036 RawSource item(param);
1037 return item.toString(model);
1039 else if ((func==FuncPlayPrompt) || (func==FuncPlayBoth)) {
1040 if ( getCurrentFirmware()->getCapability(VoicesAsNumbers)) {
1041 return QString("%1").arg(param);
1043 else {
1044 return paramarm;
1047 else if ((func>=FuncAdjustGV1) && (func<FuncCount)) {
1048 switch (adjustMode) {
1049 case FUNC_ADJUST_GVAR_CONSTANT:
1050 return QObject::tr("Value ")+QString("%1").arg(param);
1051 case FUNC_ADJUST_GVAR_SOURCE:
1052 case FUNC_ADJUST_GVAR_GVAR:
1053 return RawSource(param).toString();
1054 case FUNC_ADJUST_GVAR_INCDEC:
1055 if (param==0) return QObject::tr("Decr:") + " -1";
1056 else return QObject::tr("Incr:") + " +1";
1059 return "";
1062 QString CustomFunctionData::repeatToString() const
1064 if (repeatParam == -1) {
1065 return QObject::tr("played once, not during startup");
1067 else if (repeatParam == 0) {
1068 return "";
1070 else {
1071 unsigned int step = IS_ARM(getCurrentBoard()) ? 1 : 10;
1072 return QObject::tr("repeat(%1s)").arg(step*repeatParam);
1076 QString CustomFunctionData::enabledToString() const
1078 if ((func>=FuncOverrideCH1 && func<=FuncOverrideCH32) ||
1079 (func>=FuncAdjustGV1 && func<=FuncAdjustGVLast) ||
1080 (func==FuncReset) ||
1081 (func>=FuncSetTimer1 && func<=FuncSetTimer2) ||
1082 (func==FuncVolume) ||
1083 (func <= FuncInstantTrim)) {
1084 if (!enabled) {
1085 return QObject::tr("DISABLED");
1088 return "";
1091 CurveData::CurveData()
1093 clear(5);
1096 void CurveData::clear(int count)
1098 memset(this, 0, sizeof(CurveData));
1099 this->count = count;
1102 bool CurveData::isEmpty() const
1104 for (int i=0; i<count; i++) {
1105 if (points[i].y != 0) {
1106 return false;
1109 return true;
1112 QString CurveData::nameToString(const int idx) const
1114 return getElementName(QCoreApplication::translate("Curve", "CV"), idx + 1, name);
1117 QString LimitData::minToString() const
1119 return QString::number((qreal)min/10);
1122 QString LimitData::maxToString() const
1124 return QString::number((qreal)max/10);
1127 QString LimitData::revertToString() const
1129 return revert ? QObject::tr("INV") : QObject::tr("NOR");
1132 QString LimitData::offsetToString() const
1134 return QString::number((qreal)offset/10, 'f', 1);
1137 void LimitData::clear()
1139 memset(this, 0, sizeof(LimitData));
1140 min = -1000;
1141 max = +1000;
1144 bool GeneralSettings::switchPositionAllowedTaranis(int index) const
1146 if (index == 0)
1147 return true;
1149 div_t qr = div(abs(index)-1, 3);
1151 if (index < 0 && switchConfig[qr.quot] != Board::SWITCH_3POS)
1152 return false;
1153 else if (qr.rem == 1)
1154 return switchConfig[qr.quot] == Board::SWITCH_3POS;
1155 else
1156 return switchConfig[qr.quot] != Board::SWITCH_NOT_AVAILABLE;
1159 bool GeneralSettings::switchSourceAllowedTaranis(int index) const
1161 return switchConfig[index] != Board::SWITCH_NOT_AVAILABLE;
1164 bool GeneralSettings::isPotAvailable(int index) const
1166 if (index<0 || index>getBoardCapability(getCurrentBoard(), Board::Pots)) return false;
1167 return potConfig[index] != Board::POT_NONE;
1170 bool GeneralSettings::isSliderAvailable(int index) const
1172 if (index<0 || index>getBoardCapability(getCurrentBoard(), Board::Sliders)) return false;
1173 return sliderConfig[index] != Board::SLIDER_NONE;
1176 GeneralSettings::GeneralSettings()
1178 memset(this, 0, sizeof(GeneralSettings));
1180 contrast = 25;
1181 vBatWarn = 90;
1183 for (int i=0; i < CPN_MAX_ANALOGS; ++i) {
1184 calibMid[i] = 0x200;
1185 calibSpanNeg[i] = 0x180;
1186 calibSpanPos[i] = 0x180;
1189 Firmware * firmware = Firmware::getCurrentVariant();
1190 Board::Type board = firmware->getBoard();
1192 for (int i=0; i<getBoardCapability(board, Board::FactoryInstalledSwitches); i++) {
1193 switchConfig[i] = Boards::getSwitchInfo(board, i).config;
1196 backlightMode = 3; // keys and sticks
1197 // backlightBright = 0; // 0 = 100%
1199 if (IS_HORUS(board)) {
1200 backlightOffBright = 20;
1203 if (IS_HORUS(board)) {
1204 potConfig[0] = Board::POT_WITH_DETENT;
1205 potConfig[1] = Board::POT_MULTIPOS_SWITCH;
1206 potConfig[2] = Board::POT_WITH_DETENT;
1208 else if (IS_TARANIS_X7(board)) {
1209 potConfig[0] = Board::POT_WITHOUT_DETENT;
1210 potConfig[1] = Board::POT_WITH_DETENT;
1212 else if (IS_TARANIS(board)) {
1213 potConfig[0] = Board::POT_WITH_DETENT;
1214 potConfig[1] = Board::POT_WITH_DETENT;
1216 else {
1217 potConfig[0] = Board::POT_WITHOUT_DETENT;
1218 potConfig[1] = Board::POT_WITHOUT_DETENT;
1219 potConfig[2] = Board::POT_WITHOUT_DETENT;
1222 if (IS_HORUS(board) || IS_TARANIS_X9E(board)) {
1223 sliderConfig[0] = Board::SLIDER_WITH_DETENT;
1224 sliderConfig[1] = Board::SLIDER_WITH_DETENT;
1225 sliderConfig[2] = Board::SLIDER_WITH_DETENT;
1226 sliderConfig[3] = Board::SLIDER_WITH_DETENT;
1228 else if (IS_TARANIS(board) && !IS_TARANIS_X7(board)) {
1229 sliderConfig[0] = Board::SLIDER_WITH_DETENT;
1230 sliderConfig[1] = Board::SLIDER_WITH_DETENT;
1233 if (IS_ARM(board)) {
1234 speakerVolume = 12;
1237 if (IS_TARANIS_X9E(board)) {
1238 strcpy(bluetoothName, "Taranis");
1241 templateSetup = g.profile[g.sessionId()].channelOrder();
1242 stickMode = g.profile[g.sessionId()].defaultMode();
1244 QString t_calib = g.profile[g.sessionId()].stickPotCalib();
1245 int potsnum = getBoardCapability(getCurrentBoard(), Board::Pots);
1246 if (!t_calib.isEmpty()) {
1247 QString t_trainercalib=g.profile[g.sessionId()].trainerCalib();
1248 int8_t t_txVoltageCalibration=(int8_t)g.profile[g.sessionId()].txVoltageCalibration();
1249 int8_t t_txCurrentCalibration=(int8_t)g.profile[g.sessionId()].txCurrentCalibration();
1250 int8_t t_PPM_Multiplier=(int8_t)g.profile[g.sessionId()].ppmMultiplier();
1251 uint8_t t_stickMode=(uint8_t)g.profile[g.sessionId()].gsStickMode();
1252 uint8_t t_vBatWarn=(uint8_t)g.profile[g.sessionId()].vBatWarn();
1253 QString t_DisplaySet=g.profile[g.sessionId()].display();
1254 QString t_BeeperSet=g.profile[g.sessionId()].beeper();
1255 QString t_HapticSet=g.profile[g.sessionId()].haptic();
1256 QString t_SpeakerSet=g.profile[g.sessionId()].speaker();
1257 QString t_CountrySet=g.profile[g.sessionId()].countryCode();
1259 if ((t_calib.length()==(CPN_MAX_STICKS+potsnum)*12) && (t_trainercalib.length()==16)) {
1260 QString Byte;
1261 int16_t byte16;
1262 bool ok;
1263 for (int i=0; i<(CPN_MAX_STICKS+potsnum); i++) {
1264 Byte=t_calib.mid(i*12,4);
1265 byte16=(int16_t)Byte.toInt(&ok,16);
1266 if (ok)
1267 calibMid[i]=byte16;
1268 Byte=t_calib.mid(4+i*12,4);
1269 byte16=(int16_t)Byte.toInt(&ok,16);
1270 if (ok)
1271 calibSpanNeg[i]=byte16;
1272 Byte=t_calib.mid(8+i*12,4);
1273 byte16=(int16_t)Byte.toInt(&ok,16);
1274 if (ok)
1275 calibSpanPos[i]=byte16;
1277 for (int i=0; i<4; i++) {
1278 Byte=t_trainercalib.mid(i*4,4);
1279 byte16=(int16_t)Byte.toInt(&ok,16);
1280 if (ok)
1281 trainer.calib[i]=byte16;
1283 txCurrentCalibration=t_txCurrentCalibration;
1284 txVoltageCalibration=t_txVoltageCalibration;
1285 vBatWarn=t_vBatWarn;
1286 PPM_Multiplier=t_PPM_Multiplier;
1287 stickMode = t_stickMode;
1289 if ((t_DisplaySet.length()==6) && (t_BeeperSet.length()==4) && (t_HapticSet.length()==6) && (t_SpeakerSet.length()==6)) {
1290 uint8_t byte8u;
1291 int8_t byte8;
1292 bool ok;
1293 byte8=(int8_t)t_DisplaySet.mid(0,2).toInt(&ok,16);
1294 if (ok)
1295 optrexDisplay=(byte8==1 ? true : false);
1296 byte8u=(uint8_t)t_DisplaySet.mid(2,2).toUInt(&ok,16);
1297 if (ok)
1298 contrast=byte8u;
1299 byte8u=(uint8_t)t_DisplaySet.mid(4,2).toUInt(&ok,16);
1300 if (ok)
1301 backlightBright=byte8u;
1302 byte8=(int8_t)t_BeeperSet.mid(0,2).toUInt(&ok,16);
1303 if (ok)
1304 beeperMode=(BeeperMode)byte8;
1305 byte8=(int8_t)t_BeeperSet.mid(2,2).toInt(&ok,16);
1306 if (ok)
1307 beeperLength=byte8;
1308 byte8=(int8_t)t_HapticSet.mid(0,2).toUInt(&ok,16);
1309 if (ok)
1310 hapticMode=(BeeperMode)byte8;
1311 byte8=(int8_t)t_HapticSet.mid(2,2).toInt(&ok,16);
1312 if (ok)
1313 hapticStrength=byte8;
1314 byte8=(int8_t)t_HapticSet.mid(4,2).toInt(&ok,16);
1315 if (ok)
1316 hapticLength=byte8;
1317 byte8u=(uint8_t)t_SpeakerSet.mid(0,2).toUInt(&ok,16);
1318 if (ok)
1319 speakerMode=byte8u;
1320 byte8u=(uint8_t)t_SpeakerSet.mid(2,2).toUInt(&ok,16);
1321 if (ok)
1322 speakerPitch=byte8u;
1323 byte8u=(uint8_t)t_SpeakerSet.mid(4,2).toUInt(&ok,16);
1324 if (ok)
1325 speakerVolume=byte8u;
1326 if (t_CountrySet.length()==6) {
1327 byte8u=(uint8_t)t_CountrySet.mid(0,2).toUInt(&ok,16);
1328 if (ok)
1329 countryCode=byte8u;
1330 byte8u=(uint8_t)t_CountrySet.mid(2,2).toUInt(&ok,16);
1331 if (ok)
1332 imperial=byte8u;
1333 QString chars = t_CountrySet.mid(4, 2);
1334 ttsLanguage[0] = chars[0].toLatin1();
1335 ttsLanguage[1] = chars[1].toLatin1();
1340 strcpy(themeName, "default");
1341 ThemeOptionData option1 = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 };
1342 memcpy(&themeOptionValue[0], option1, sizeof(ThemeOptionData));
1343 ThemeOptionData option2 = { 0x03, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 };
1344 memcpy(&themeOptionValue[1], option2, sizeof(ThemeOptionData));
1347 int GeneralSettings::getDefaultStick(unsigned int channel) const
1349 if (channel >= CPN_MAX_STICKS)
1350 return -1;
1351 else
1352 return chout_ar[4*templateSetup + channel] - 1;
1355 RawSource GeneralSettings::getDefaultSource(unsigned int channel) const
1357 int stick = getDefaultStick(channel);
1358 if (stick >= 0)
1359 return RawSource(SOURCE_TYPE_STICK, stick);
1360 else
1361 return RawSource(SOURCE_TYPE_NONE);
1364 int GeneralSettings::getDefaultChannel(unsigned int stick) const
1366 for (int i=0; i<4; i++){
1367 if (getDefaultStick(i) == (int)stick)
1368 return i;
1370 return -1;
1373 float FrSkyChannelData::getRatio() const
1375 if (type==0 || type==1 || type==2)
1376 return float(ratio << multiplier) / 10.0;
1377 else
1378 return ratio << multiplier;
1381 RawSourceRange FrSkyChannelData::getRange() const
1383 RawSourceRange result;
1384 float ratio = getRatio();
1385 if (type==0 || type==1 || type==2)
1386 result.decimals = 2;
1387 else
1388 result.decimals = 0;
1389 result.step = ratio / 255;
1390 result.min = offset * result.step;
1391 result.max = ratio + result.min;
1392 result.unit = QObject::tr("V");
1393 return result;
1396 void FrSkyScreenData::clear()
1398 memset(this, 0, sizeof(FrSkyScreenData));
1399 if (!IS_ARM(getCurrentBoard())) {
1400 type = TELEMETRY_SCREEN_NUMBERS;
1404 void FrSkyData::clear()
1406 usrProto = 0;
1407 voltsSource = 0;
1408 altitudeSource = 0;
1409 currentSource = 0;
1410 varioMin = 0;
1411 varioCenterMin = 0; // if increment in 0.2m/s = 3.0m/s max
1412 varioCenterMax = 0;
1413 varioMax = 0;
1414 mAhPersistent = 0;
1415 storedMah = 0;
1416 fasOffset = 0;
1417 for (int i=0; i<4; i++)
1418 screens[i].clear();
1419 varioSource = 2/*VARIO*/;
1420 blades = 2;
1423 ModelData::ModelData()
1425 clear();
1428 ModelData::ModelData(const ModelData & src)
1430 *this = src;
1433 ModelData & ModelData::operator = (const ModelData & src)
1435 memcpy(this, &src, sizeof(ModelData));
1436 return *this;
1439 ExpoData * ModelData::insertInput(const int idx)
1441 memmove(&expoData[idx+1], &expoData[idx], (CPN_MAX_EXPOS-(idx+1))*sizeof(ExpoData));
1442 expoData[idx].clear();
1443 return &expoData[idx];
1446 bool ModelData::isInputValid(const unsigned int idx) const
1448 for (int i=0; i<CPN_MAX_EXPOS; i++) {
1449 const ExpoData * expo = &expoData[i];
1450 if (expo->mode == 0) break;
1451 if (expo->chn == idx)
1452 return true;
1454 return false;
1457 bool ModelData::hasExpos(uint8_t inputIdx) const
1459 for (int i=0; i<CPN_MAX_EXPOS; i++) {
1460 const ExpoData & expo = expoData[i];
1461 if (expo.chn==inputIdx && expo.mode!=0) {
1462 return true;
1465 return false;
1468 bool ModelData::hasMixes(uint8_t channelIdx) const
1470 channelIdx += 1;
1471 for (int i=0; i<CPN_MAX_MIXERS; i++) {
1472 if (mixData[i].destCh == channelIdx) {
1473 return true;
1476 return false;
1479 QVector<const ExpoData *> ModelData::expos(int input) const
1481 QVector<const ExpoData *> result;
1482 for (int i=0; i<CPN_MAX_EXPOS; i++) {
1483 const ExpoData * ed = &expoData[i];
1484 if ((int)ed->chn==input && ed->mode!=0) {
1485 result << ed;
1488 return result;
1491 QVector<const MixData *> ModelData::mixes(int channel) const
1493 QVector<const MixData *> result;
1494 for (int i=0; i<CPN_MAX_MIXERS; i++) {
1495 const MixData * md = &mixData[i];
1496 if ((int)md->destCh == channel+1) {
1497 result << md;
1500 return result;
1503 void ModelData::removeInput(const int idx)
1505 unsigned int chn = expoData[idx].chn;
1507 memmove(&expoData[idx], &expoData[idx+1], (CPN_MAX_EXPOS-(idx+1))*sizeof(ExpoData));
1508 expoData[CPN_MAX_EXPOS-1].clear();
1510 //also remove input name if removing last line for this input
1511 bool found = false;
1512 for (int i=0; i<CPN_MAX_EXPOS; i++) {
1513 if (expoData[i].mode==0) continue;
1514 if (expoData[i].chn==chn) {
1515 found = true;
1516 break;
1519 if (!found) inputNames[chn][0] = 0;
1522 void ModelData::clearInputs()
1524 for (int i=0; i<CPN_MAX_EXPOS; i++)
1525 expoData[i].clear();
1527 //clear all input names
1528 if (getCurrentFirmware()->getCapability(VirtualInputs)) {
1529 for (int i=0; i<CPN_MAX_INPUTS; i++) {
1530 inputNames[i][0] = 0;
1535 void ModelData::clearMixes()
1537 for (int i=0; i<CPN_MAX_MIXERS; i++)
1538 mixData[i].clear();
1541 void ModelData::clear()
1543 memset(this, 0, sizeof(ModelData));
1544 modelIndex = -1; // an invalid index, this is managed by the TreeView data model
1545 moduleData[0].channelsCount = 8;
1546 moduleData[1].channelsStart = 0;
1547 moduleData[1].channelsCount = 8;
1548 moduleData[0].ppm.delay = 300;
1549 moduleData[1].ppm.delay = 300;
1550 moduleData[2].ppm.delay = 300;
1551 int board = getCurrentBoard();
1552 if (IS_HORUS_OR_TARANIS(board)) {
1553 moduleData[0].protocol = PULSES_PXX_XJT_X16;
1554 moduleData[1].protocol = PULSES_OFF;
1556 else if (IS_SKY9X(board)) {
1557 moduleData[0].protocol = PULSES_PPM;
1558 moduleData[1].protocol = PULSES_PPM;
1560 else {
1561 moduleData[0].protocol = PULSES_PPM;
1562 moduleData[1].protocol = PULSES_OFF;
1564 for (int i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
1565 flightModeData[i].clear(i);
1567 clearInputs();
1568 clearMixes();
1569 for (int i=0; i<CPN_MAX_CHNOUT; i++)
1570 limitData[i].clear();
1571 for (int i=0; i<CPN_MAX_STICKS; i++)
1572 expoData[i].clear();
1573 for (int i=0; i<CPN_MAX_LOGICAL_SWITCHES; i++)
1574 logicalSw[i].clear();
1575 for (int i=0; i<CPN_MAX_SPECIAL_FUNCTIONS; i++)
1576 customFn[i].clear();
1577 for (int i=0; i<CPN_MAX_CURVES; i++)
1578 curves[i].clear(5);
1579 for (int i=0; i<CPN_MAX_TIMERS; i++)
1580 timers[i].clear();
1581 swashRingData.clear();
1582 frsky.clear();
1583 rssiAlarms.clear();
1584 for (int i=0; i<CPN_MAX_SENSORS; i++)
1585 sensorData[i].clear();
1587 static const uint8_t blob[] = { 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x32, 0x50, 0x31, 0x00, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x42, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1588 memcpy(customScreenData[0], blob, sizeof(blob));
1591 bool ModelData::isEmpty() const
1593 return !used;
1596 QString removeAccents(const QString & str)
1598 QString result = str;
1600 // UTF-8 ASCII Table
1601 const QString tA[] = { "á", "â", "ã", "à", "ä" };
1602 const QString tE[] = { "é", "è", "ê", "ě" };
1603 const QString tI[] = { "í" };
1604 const QString tO[] = { "ó", "ô", "õ", "ö" };
1605 const QString tU[] = { "ú", "ü" };
1606 const QString tC[] = { "ç" };
1607 const QString tY[] = { "ý" };
1608 const QString tS[] = { "Å¡" };
1609 const QString tR[] = { "Å™" };
1611 for (unsigned int i = 0; i < DIM(tA); i++) result.replace(tA[i], "a");
1612 for (unsigned int i = 0; i < DIM(tE); i++) result.replace(tE[i], "e");
1613 for (unsigned int i = 0; i < DIM(tI); i++) result.replace(tI[i], "i");
1614 for (unsigned int i = 0; i < DIM(tO); i++) result.replace(tO[i], "o");
1615 for (unsigned int i = 0; i < DIM(tU); i++) result.replace(tU[i], "u");
1616 for (unsigned int i = 0; i < DIM(tC); i++) result.replace(tC[i], "c");
1617 for (unsigned int i = 0; i < DIM(tY); i++) result.replace(tY[i], "y");
1618 for (unsigned int i = 0; i < DIM(tS); i++) result.replace(tS[i], "s");
1619 for (unsigned int i = 0; i < DIM(tR); i++) result.replace(tR[i], "r");
1621 return result;
1624 void ModelData::setDefaultInputs(const GeneralSettings & settings)
1626 Board::Type board = getCurrentBoard();
1627 if (IS_ARM(board)) {
1628 for (int i=0; i<CPN_MAX_STICKS; i++) {
1629 ExpoData * expo = &expoData[i];
1630 expo->chn = i;
1631 expo->mode = INPUT_MODE_BOTH;
1632 expo->srcRaw = settings.getDefaultSource(i);
1633 expo->weight = 100;
1634 strncpy(inputNames[i], removeAccents(expo->srcRaw.toString(this)).toLatin1().constData(), sizeof(inputNames[i])-1);
1639 void ModelData::setDefaultMixes(const GeneralSettings & settings)
1641 Board::Type board = getCurrentBoard();
1642 if (IS_ARM(board)) {
1643 setDefaultInputs(settings);
1646 for (int i=0; i<CPN_MAX_STICKS; i++) {
1647 MixData * mix = &mixData[i];
1648 mix->destCh = i+1;
1649 mix->weight = 100;
1650 if (IS_ARM(board)) {
1651 mix->srcRaw = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, i);
1653 else {
1654 mix->srcRaw = RawSource(SOURCE_TYPE_STICK, i);
1659 void ModelData::setDefaultValues(unsigned int id, const GeneralSettings & settings)
1661 clear();
1662 used = true;
1663 sprintf(name, "MODEL%02d", id+1);
1664 for (int i=0; i<CPN_MAX_MODULES; i++) {
1665 moduleData[i].modelId = id+1;
1667 setDefaultMixes(settings);
1670 int ModelData::getTrimValue(int phaseIdx, int trimIdx)
1672 int result = 0;
1673 for (int i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
1674 FlightModeData & phase = flightModeData[phaseIdx];
1675 if (phase.trimMode[trimIdx] < 0) {
1676 return result;
1678 else {
1679 if (phase.trimRef[trimIdx] == phaseIdx || phaseIdx == 0) {
1680 return result + phase.trim[trimIdx];
1682 else {
1683 phaseIdx = phase.trimRef[trimIdx];
1684 if (phase.trimMode[trimIdx] != 0)
1685 result += phase.trim[trimIdx];
1689 return 0;
1692 bool ModelData::isGVarLinked(int phaseIdx, int gvarIdx)
1694 return flightModeData[phaseIdx].gvars[gvarIdx] > 1024;
1697 int ModelData::getGVarFieldValue(int phaseIdx, int gvarIdx)
1699 int idx = flightModeData[phaseIdx].gvars[gvarIdx];
1700 for (int i=0; idx>1024 && i<CPN_MAX_FLIGHT_MODES; i++) {
1701 int nextPhase = idx - 1025;
1702 if (nextPhase >= phaseIdx) nextPhase += 1;
1703 phaseIdx = nextPhase;
1704 idx = flightModeData[phaseIdx].gvars[gvarIdx];
1706 return idx;
1709 void ModelData::setTrimValue(int phaseIdx, int trimIdx, int value)
1711 for (uint8_t i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
1712 FlightModeData & phase = flightModeData[phaseIdx];
1713 int mode = phase.trimMode[trimIdx];
1714 int p = phase.trimRef[trimIdx];
1715 int & trim = phase.trim[trimIdx];
1716 if (mode < 0)
1717 return;
1718 if (p == phaseIdx || phaseIdx == 0) {
1719 trim = value;
1720 break;;
1722 else if (mode == 0) {
1723 phaseIdx = p;
1725 else {
1726 trim = value - getTrimValue(p, trimIdx);
1727 if (trim < -500)
1728 trim = -500;
1729 if (trim > 500)
1730 trim = 500;
1731 break;
1736 void ModelData::removeGlobalVar(int & var)
1738 if (var >= 126 && var <= 130)
1739 var = flightModeData[0].gvars[var-126];
1740 else if (var <= -126 && var >= -130)
1741 var = - flightModeData[0].gvars[-126-var];
1744 ModelData ModelData::removeGlobalVars()
1746 ModelData result = *this;
1748 for (int i=0; i<CPN_MAX_MIXERS; i++) {
1749 removeGlobalVar(mixData[i].weight);
1750 removeGlobalVar(mixData[i].curve.value);
1751 removeGlobalVar(mixData[i].sOffset);
1754 for (int i=0; i<CPN_MAX_EXPOS; i++) {
1755 removeGlobalVar(expoData[i].weight);
1756 removeGlobalVar(expoData[i].curve.value);
1759 return result;
1762 int ModelData::getChannelsMax(bool forceExtendedLimits) const
1764 if (forceExtendedLimits || extendedLimits)
1765 return IS_HORUS_OR_TARANIS(getCurrentBoard()) ? 150 : 125;
1766 else
1767 return 100;
1770 bool ModelData::isAvailable(const RawSwitch & swtch) const
1772 unsigned index = abs(swtch.index) - 1;
1774 if (swtch.type == SWITCH_TYPE_VIRTUAL) {
1775 return logicalSw[index].func != LS_FN_OFF;
1777 else if (swtch.type == SWITCH_TYPE_FLIGHT_MODE) {
1778 return index == 0 || flightModeData[index].swtch.type != SWITCH_TYPE_NONE;
1780 else if (swtch.type == SWITCH_TYPE_SENSOR) {
1781 return strlen(sensorData[index].label) > 0;
1783 else {
1784 return true;
1788 QList<EEPROMInterface *> eepromInterfaces;
1790 void unregisterEEpromInterfaces()
1792 foreach(EEPROMInterface * intf, eepromInterfaces) {
1793 // qDebug() << "UnregisterEepromInterfaces(): deleting " << QString::number( reinterpret_cast<uint64_t>(intf), 16 );
1794 delete intf;
1796 OpenTxEepromCleanup();
1799 void ShowEepromErrors(QWidget *parent, const QString &title, const QString &mainMessage, unsigned long errorsFound)
1801 std::bitset<NUM_ERRORS> errors((unsigned long long)errorsFound);
1802 QStringList errorsList;
1804 errorsList << QT_TRANSLATE_NOOP("EepromInterface", "Possible causes for this:");
1806 if (errors.test(UNSUPPORTED_NEWER_VERSION)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is from a newer version of OpenTX"); }
1807 if (errors.test(NOT_OPENTX)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from OpenTX"); }
1808 if (errors.test(NOT_TH9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from Th9X"); }
1809 if (errors.test(NOT_GRUVIN9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from Gruvin9X"); }
1810 if (errors.test(NOT_ERSKY9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from ErSky9X"); }
1811 if (errors.test(NOT_ER9X)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is not from Er9X"); }
1812 if (errors.test(WRONG_SIZE)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom size is invalid"); }
1813 if (errors.test(WRONG_FILE_SYSTEM)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom file system is invalid"); }
1814 if (errors.test(UNKNOWN_BOARD)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is from a unknown board"); }
1815 if (errors.test(WRONG_BOARD)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom is from the wrong board"); }
1816 if (errors.test(BACKUP_NOT_SUPPORTED)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Eeprom backup not supported"); }
1818 if (errors.test(UNKNOWN_ERROR)) { errorsList << QT_TRANSLATE_NOOP("EepromInterface", "- Something that couldn't be guessed, sorry"); }
1820 if (errors.test(HAS_WARNINGS)) {
1821 errorsList << QT_TRANSLATE_NOOP("EepromInterface", "Warning:");
1822 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"); }
1825 QMessageBox msgBox(parent);
1826 msgBox.setWindowTitle(title);
1827 msgBox.setIcon(QMessageBox::Critical);
1828 msgBox.setText(mainMessage);
1829 msgBox.setInformativeText(errorsList.join("\n"));
1830 msgBox.setStandardButtons(QMessageBox::Ok);
1831 msgBox.exec();
1834 void ShowEepromWarnings(QWidget *parent, const QString &title, unsigned long errorsFound)
1836 std::bitset<NUM_ERRORS> errors((unsigned long long)errorsFound);
1837 QStringList warningsList;
1838 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"); }
1839 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."); }
1841 QMessageBox msgBox(parent);
1842 msgBox.setWindowTitle(title);
1843 msgBox.setIcon(QMessageBox::Warning);
1844 msgBox.setText(QT_TRANSLATE_NOOP("EepromInterface", "Warnings!"));
1845 msgBox.setInformativeText(warningsList.join("\n"));
1846 msgBox.setStandardButtons(QMessageBox::Ok);
1847 msgBox.exec();
1850 // static
1851 QVector<Firmware *> Firmware::registeredFirmwares;
1852 Firmware * Firmware::defaultVariant = NULL;
1853 Firmware * Firmware::currentVariant = NULL;
1855 // static
1856 Firmware * Firmware::getFirmwareForId(const QString & id)
1858 foreach(Firmware * firmware, registeredFirmwares) {
1859 Firmware * result = firmware->getFirmwareVariant(id);
1860 if (result) {
1861 return result;
1865 return defaultVariant;
1868 void Firmware::addOption(const char *option, QString tooltip, uint32_t variant)
1870 Option options[] = { { option, tooltip, variant }, { NULL } };
1871 addOptions(options);
1874 unsigned int Firmware::getVariantNumber()
1876 unsigned int result = 0;
1877 const Firmware * base = getFirmwareBase();
1878 QStringList options = id.mid(base->getId().length()+1).split("-", QString::SkipEmptyParts);
1879 foreach(QString option, options) {
1880 foreach(QList<Option> group, base->opts) {
1881 foreach(Option opt, group) {
1882 if (opt.name == option) {
1883 result += opt.variant;
1888 return result;
1891 void Firmware::addLanguage(const char *lang)
1893 languages.push_back(lang);
1896 void Firmware::addTTSLanguage(const char *lang)
1898 ttslanguages.push_back(lang);
1901 void Firmware::addOptions(Option options[])
1903 QList<Option> opts;
1904 for (int i=0; options[i].name; i++) {
1905 opts.push_back(options[i]);
1907 this->opts.push_back(opts);
1910 void FlightModeData::clear(const int phase)
1912 memset(this, 0, sizeof(FlightModeData));
1913 if (phase != 0) {
1914 for (int idx=0; idx<CPN_MAX_GVARS; idx++) {
1915 gvars[idx] = 1025;
1917 for (int idx=0; idx<CPN_MAX_ENCODERS; idx++) {
1918 rotaryEncoders[idx] = 1025;