Standalone simulator startup options (#4164)
[opentx.git] / companion / src / helpers.cpp
blob3fae62153db53318603ebb7b9f4cc279cad35ab8
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 <QtGui>
22 #if defined _MSC_VER
23 #include <io.h>
24 #include <stdio.h>
25 #elif defined __GNUC__
26 #include <unistd.h>
27 #endif
28 #if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO)
29 #include "windows.h"
30 #endif
32 #include "appdata.h"
33 #include "helpers.h"
34 #include "simulatordialog.h"
35 #include "simulatorinterface.h"
36 #include "firmwareinterface.h"
37 #include "storage/storage_sdcard.h"
39 Stopwatch gStopwatch("global");
41 const QColor colors[CPN_MAX_CURVES] = {
42 QColor(0,0,127),
43 QColor(0,127,0),
44 QColor(127,0,0),
45 QColor(0,127,127),
46 QColor(127,0,127),
47 QColor(127,127,0),
48 QColor(127,127,127),
49 QColor(0,0,255),
50 QColor(0,127,255),
51 QColor(127,0,255),
52 QColor(0,255,0),
53 QColor(0,255,127),
54 QColor(127,255,0),
55 QColor(255,0,0),
56 QColor(255,0,127),
57 QColor(255,127,0),
58 QColor(0,0,127),
59 QColor(0,127,0),
60 QColor(127,0,0),
61 QColor(0,127,127),
62 QColor(127,0,127),
63 QColor(127,127,0),
64 QColor(127,127,127),
65 QColor(0,0,255),
66 QColor(0,127,255),
67 QColor(127,0,255),
68 QColor(0,255,0),
69 QColor(0,255,127),
70 QColor(127,255,0),
71 QColor(255,0,0),
72 QColor(255,0,127),
73 QColor(255,127,0),
76 void populateGvSourceCB(QComboBox *b, int value)
78 QString strings[] = { QObject::tr("---"), QObject::tr("Rud Trim"), QObject::tr("Ele Trim"), QObject::tr("Thr Trim"), QObject::tr("Ail Trim"), QObject::tr("Rot Enc"), QObject::tr("Rud"), QObject::tr("Ele"), QObject::tr("Thr"), QObject::tr("Ail"), QObject::tr("P1"), QObject::tr("P2"), QObject::tr("P3")};
79 b->clear();
80 for (int i=0; i<= 12; i++) {
81 b->addItem(strings[i]);
83 b->setCurrentIndex(value);
86 void populateFileComboBox(QComboBox * b, const QSet<QString> & set, const QString & current)
88 b->clear();
89 b->addItem("----");
91 bool added = false;
92 // Convert set into list and sort it alphabetically case insensitive
93 QStringList list = QStringList::fromSet(set);
94 qSort(list.begin(), list.end(), caseInsensitiveLessThan);
95 foreach (QString entry, list) {
96 b->addItem(entry);
97 if (entry == current) {
98 b->setCurrentIndex(b->count()-1);
99 added = true;
103 if (!added && !current.isEmpty()) {
104 b->addItem(current);
105 b->setCurrentIndex(b->count()-1);
109 void getFileComboBoxValue(QComboBox * b, char * dest, int length)
111 memset(dest, 0, length+1);
112 if (b->currentText() != "----") {
113 strncpy(dest, b->currentText().toLatin1(), length);
117 void populatePhasesCB(QComboBox *b, int value)
119 for (int i=-GetCurrentFirmware()->getCapability(FlightModes); i<=GetCurrentFirmware()->getCapability(FlightModes); i++) {
120 if (i < 0)
121 b->addItem(QObject::tr("!Flight mode %1").arg(-i-1), i);
122 else if (i > 0)
123 b->addItem(QObject::tr("Flight mode %1").arg(i-1), i);
124 else
125 b->addItem(QObject::tr("----"), 0);
127 b->setCurrentIndex(value + GetCurrentFirmware()->getCapability(FlightModes));
130 GVarGroup::GVarGroup(QCheckBox * weightGV, QAbstractSpinBox * weightSB, QComboBox * weightCB, int & weight, const ModelData & model, const int deflt, const int mini, const int maxi, const double step, bool allowGvars):
131 QObject(),
132 weightGV(weightGV),
133 weightSB(weightSB),
134 sb(dynamic_cast<QSpinBox *>(weightSB)),
135 dsb(dynamic_cast<QDoubleSpinBox *>(weightSB)),
136 weightCB(weightCB),
137 weight(weight),
138 step(step),
139 lock(true)
141 if (allowGvars && GetCurrentFirmware()->getCapability(Gvars)) {
142 populateGVCB(*weightCB, weight, model);
143 connect(weightGV, SIGNAL(stateChanged(int)), this, SLOT(gvarCBChanged(int)));
144 connect(weightCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged()));
146 else {
147 weightGV->hide();
148 if (weight > maxi || weight < mini) {
149 weight = deflt;
153 int val;
155 if (weight>maxi || weight<mini) {
156 val = deflt;
157 weightGV->setChecked(true);
158 weightSB->hide();
159 weightCB->show();
161 else {
162 val = weight;
163 weightGV->setChecked(false);
164 weightSB->show();
165 weightCB->hide();
168 if (sb) {
169 sb->setMinimum(mini);
170 sb->setMaximum(maxi);
171 sb->setValue(val);
173 else {
174 dsb->setMinimum(mini*step);
175 dsb->setMaximum(maxi*step);
176 dsb->setValue(val*step);
179 connect(weightSB, SIGNAL(editingFinished()), this, SLOT(valuesChanged()));
181 lock = false;
184 void GVarGroup::gvarCBChanged(int state)
186 weightCB->setVisible(state);
187 if (weightSB)
188 weightSB->setVisible(!state);
189 else
190 weightSB->setVisible(!state);
191 valuesChanged();
194 void GVarGroup::valuesChanged()
196 if (!lock) {
197 if (weightGV->isChecked())
198 weight = weightCB->itemData(weightCB->currentIndex()).toInt();
199 else if (sb)
200 weight = sb->value();
201 else
202 weight = round(dsb->value()/step);
206 CurveGroup::CurveGroup(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QComboBox * curveValueCB, QSpinBox * curveValueSB, CurveReference & curve, const ModelData & model, unsigned int flags):
207 QObject(),
208 curveTypeCB(curveTypeCB),
209 curveGVarCB(curveGVarCB),
210 curveValueCB(curveValueCB),
211 curveValueSB(curveValueSB),
212 curve(curve),
213 model(model),
214 flags(flags),
215 lock(false),
216 lastType(-1)
218 if (!(flags & HIDE_DIFF)) curveTypeCB->addItem(tr("Diff"), 0);
219 if (!(flags & HIDE_EXPO)) curveTypeCB->addItem(tr("Expo"), 1);
220 curveTypeCB->addItem(tr("Func"), 2);
221 curveTypeCB->addItem(tr("Curve"), 3);
223 curveValueCB->setMaxVisibleItems(10);
225 connect(curveTypeCB, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int)));
226 connect(curveGVarCB, SIGNAL(stateChanged(int)), this, SLOT(gvarCBChanged(int)));
227 connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged()));
228 connect(curveValueSB, SIGNAL(editingFinished()), this, SLOT(valuesChanged()));
230 update();
233 void CurveGroup::update()
235 lock = true;
237 int found = curveTypeCB->findData(curve.type);
238 if (found < 0) found = 0;
239 curveTypeCB->setCurrentIndex(found);
241 if (curve.type == CurveReference::CURVE_REF_DIFF || curve.type == CurveReference::CURVE_REF_EXPO) {
242 curveGVarCB->setVisible(GetCurrentFirmware()->getCapability(Gvars));
243 if (curve.value > 100 || curve.value < -100) {
244 curveGVarCB->setChecked(true);
245 if (lastType != CurveReference::CURVE_REF_DIFF && lastType != CurveReference::CURVE_REF_EXPO) {
246 lastType = curve.type;
247 populateGVCB(*curveValueCB, curve.value, model);
249 curveValueCB->show();
250 curveValueSB->hide();
252 else {
253 curveGVarCB->setChecked(false);
254 curveValueSB->setMinimum(-100);
255 curveValueSB->setMaximum(100);
256 curveValueSB->setValue(curve.value);
257 curveValueSB->show();
258 curveValueCB->hide();
261 else {
262 curveGVarCB->hide();
263 curveValueSB->hide();
264 curveValueCB->show();
265 switch (curve.type) {
266 case CurveReference::CURVE_REF_FUNC:
267 if (lastType != curve.type) {
268 lastType = curve.type;
269 curveValueCB->clear();
270 for (int i=0; i<=6/*TODO constant*/; i++) {
271 curveValueCB->addItem(CurveReference(CurveReference::CURVE_REF_FUNC, i).toString());
274 curveValueCB->setCurrentIndex(curve.value);
275 break;
276 case CurveReference::CURVE_REF_CUSTOM:
278 int numcurves = GetCurrentFirmware()->getCapability(NumCurves);
279 if (lastType != curve.type) {
280 lastType = curve.type;
281 curveValueCB->clear();
282 for (int i= ((flags & HIDE_NEGATIVE_CURVES) ? 0 : -numcurves); i<=numcurves; i++) {
283 curveValueCB->addItem(CurveReference(CurveReference::CURVE_REF_CUSTOM, i).toString(), i);
284 if (i == curve.value) {
285 curveValueCB->setCurrentIndex(curveValueCB->count() - 1);
289 break;
291 default:
292 break;
296 lock = false;
299 void CurveGroup::gvarCBChanged(int state)
301 if (!lock) {
302 if (state) {
303 curve.value = 10000+1; // TODO constant in EEpromInterface ...
304 lastType = -1; // quickfix for issue #3518: force refresh of curveValueCB at next update() to set current index to GV1
306 else {
307 curve.value = 0; // TODO could be better
310 update();
314 void CurveGroup::typeChanged(int value)
316 if (!lock) {
317 int type = curveTypeCB->itemData(curveTypeCB->currentIndex()).toInt();
318 switch (type) {
319 case 0:
320 curve = CurveReference(CurveReference::CURVE_REF_DIFF, 0);
321 break;
322 case 1:
323 curve = CurveReference(CurveReference::CURVE_REF_EXPO, 0);
324 break;
325 case 2:
326 curve = CurveReference(CurveReference::CURVE_REF_FUNC, 0);
327 break;
328 case 3:
329 curve = CurveReference(CurveReference::CURVE_REF_CUSTOM, 0);
330 break;
333 update();
337 void CurveGroup::valuesChanged()
339 if (!lock) {
340 switch (curveTypeCB->itemData(curveTypeCB->currentIndex()).toInt()) {
341 case 0:
342 case 1:
344 int value;
345 if (curveGVarCB->isChecked())
346 value = curveValueCB->itemData(curveValueCB->currentIndex()).toInt();
347 else
348 value = curveValueSB->value();
349 curve = CurveReference(curveTypeCB->itemData(curveTypeCB->currentIndex()).toInt() == 0 ? CurveReference::CURVE_REF_DIFF : CurveReference::CURVE_REF_EXPO, value);
350 break;
352 case 2:
353 curve = CurveReference(CurveReference::CURVE_REF_FUNC, curveValueCB->currentIndex());
354 break;
355 case 3:
356 curve = CurveReference(CurveReference::CURVE_REF_CUSTOM, curveValueCB->itemData(curveValueCB->currentIndex()).toInt());
357 break;
360 update();
364 void populateGvarUseCB(QComboBox *b, unsigned int phase)
366 b->addItem(QObject::tr("Own value"));
367 for (int i=0; i<GetCurrentFirmware()->getCapability(FlightModes); i++) {
368 if (i != (int)phase) {
369 b->addItem(QObject::tr("Flight mode %1 value").arg(i));
374 void populateSwitchCB(QComboBox *b, const RawSwitch & value, const GeneralSettings & generalSettings, SwitchContext context)
376 RawSwitch item;
378 b->clear();
380 if (context != MixesContext && context != GlobalFunctionsContext) {
381 // !FMx
382 if (IS_ARM(GetCurrentFirmware()->getBoard())) {
383 for (int i=-GetCurrentFirmware()->getCapability(FlightModes); i<0; i++) {
384 item = RawSwitch(SWITCH_TYPE_FLIGHT_MODE, i);
385 b->addItem(item.toString(), item.toValue());
386 if (item == value) b->setCurrentIndex(b->count()-1);
391 if (context != GlobalFunctionsContext) {
392 for (int i=-GetCurrentFirmware()->getCapability(LogicalSwitches); i<0; i++) {
393 item = RawSwitch(SWITCH_TYPE_VIRTUAL, i);
394 b->addItem(item.toString(), item.toValue());
395 if (item == value) b->setCurrentIndex(b->count()-1);
399 for (int i=-GetCurrentFirmware()->getCapability(RotaryEncoders); i<0; i++) {
400 item = RawSwitch(SWITCH_TYPE_ROTARY_ENCODER, i);
401 b->addItem(item.toString(), item.toValue());
402 if (item == value) b->setCurrentIndex(b->count()-1);
405 for (int i=-8; i<0; i++) {
406 item = RawSwitch(SWITCH_TYPE_TRIM, i);
407 b->addItem(item.toString(), item.toValue());
408 if (item == value) b->setCurrentIndex(b->count()-1);
411 for (int i=GetCurrentFirmware()->getCapability(MultiposPots)-1; i>=0; i--) {
412 if (generalSettings.potConfig[i] == GeneralSettings::POT_MULTIPOS_SWITCH) {
413 for (int j=-GetCurrentFirmware()->getCapability(MultiposPotsPositions); j<0; j++) {
414 item = RawSwitch(SWITCH_TYPE_MULTIPOS_POT, -i*GetCurrentFirmware()->getCapability(MultiposPotsPositions)+j);
415 b->addItem(item.toString(), item.toValue());
416 if (item == value) b->setCurrentIndex(b->count()-1);
421 for (int i=-GetCurrentFirmware()->getCapability(SwitchesPositions); i<0; i++) {
422 item = RawSwitch(SWITCH_TYPE_SWITCH, i);
423 if (IS_TARANIS(GetCurrentFirmware()->getBoard()) && !generalSettings.switchPositionAllowedTaranis(i)){
424 continue;
426 b->addItem(item.toString(), item.toValue());
427 if (item == value) b->setCurrentIndex(b->count()-1);
430 if (context == TimersContext) {
431 for (int i=0; i<5; i++) {
432 item = RawSwitch(SWITCH_TYPE_TIMER_MODE, i);
433 b->addItem(item.toString(), item.toValue());
434 if (item == value) b->setCurrentIndex(b->count()-1);
437 else {
438 item = RawSwitch(SWITCH_TYPE_NONE);
439 b->addItem(item.toString(), item.toValue());
440 if (item == value) b->setCurrentIndex(b->count()-1);
443 for (int i=1; i<=GetCurrentFirmware()->getCapability(SwitchesPositions); i++) {
444 item = RawSwitch(SWITCH_TYPE_SWITCH, i);
445 if (IS_TARANIS(GetCurrentFirmware()->getBoard()) && !generalSettings.switchPositionAllowedTaranis(i)){
446 continue;
448 b->addItem(item.toString(), item.toValue());
449 if (item == value) b->setCurrentIndex(b->count()-1);
452 for (int i=0; i<GetCurrentFirmware()->getCapability(MultiposPots); i++) {
453 if (generalSettings.potConfig[i] == GeneralSettings::POT_MULTIPOS_SWITCH) {
454 for (int j=1; j<=GetCurrentFirmware()->getCapability(MultiposPotsPositions); j++) {
455 item = RawSwitch(SWITCH_TYPE_MULTIPOS_POT, i*GetCurrentFirmware()->getCapability(MultiposPotsPositions)+j);
456 b->addItem(item.toString(), item.toValue());
457 if (item == value) b->setCurrentIndex(b->count()-1);
462 for (int i=1; i<=8; i++) {
463 item = RawSwitch(SWITCH_TYPE_TRIM, i);
464 b->addItem(item.toString(), item.toValue());
465 if (item == value) b->setCurrentIndex(b->count()-1);
468 for (int i=1; i<=GetCurrentFirmware()->getCapability(RotaryEncoders); i++) {
469 item = RawSwitch(SWITCH_TYPE_ROTARY_ENCODER, i);
470 b->addItem(item.toString(), item.toValue());
471 if (item == value) b->setCurrentIndex(b->count()-1);
474 if (context != GlobalFunctionsContext) {
475 for (int i=1; i<=GetCurrentFirmware()->getCapability(LogicalSwitches); i++) {
476 item = RawSwitch(SWITCH_TYPE_VIRTUAL, i);
477 b->addItem(item.toString(), item.toValue());
478 if (item == value) b->setCurrentIndex(b->count()-1);
482 if (context == SpecialFunctionsContext || context == GlobalFunctionsContext) {
483 // ON
484 item = RawSwitch(SWITCH_TYPE_ON);
485 b->addItem(item.toString(), item.toValue());
486 if (item == value) b->setCurrentIndex(b->count()-1);
487 // One
488 item = RawSwitch(SWITCH_TYPE_ONE, 1);
489 b->addItem(item.toString(), item.toValue());
490 if (item == value) b->setCurrentIndex(b->count()-1);
493 // FMx
494 if (context != MixesContext && context != GlobalFunctionsContext) {
495 if (IS_ARM(GetCurrentFirmware()->getBoard())) {
496 for (int i=1; i<=GetCurrentFirmware()->getCapability(FlightModes); i++) {
497 item = RawSwitch(SWITCH_TYPE_FLIGHT_MODE, i);
498 b->addItem(item.toString(), item.toValue());
499 if (item == value) b->setCurrentIndex(b->count()-1);
504 b->setMaxVisibleItems(10);
507 void populateGVCB(QComboBox & b, int value, const ModelData & model)
509 bool selected = false;
511 b.clear();
513 int count = GetCurrentFirmware()->getCapability(Gvars);
514 for (int i=-count; i<=-1; i++) {
515 int16_t gval = (int16_t)(-10000+i);
516 if (strlen(model.gvars_names[-i-1]) > 0)
517 b.addItem(QObject::tr("-GV%1 (%2)").arg(-i).arg(model.gvars_names[-i-1]), gval);
518 else
519 b.addItem(QObject::tr("-GV%1").arg(-i), gval);
520 if (value == gval) {
521 b.setCurrentIndex(b.count()-1);
522 selected = true;
526 for (int i=1; i<=count; i++) {
527 int16_t gval = (int16_t)(10000+i);
528 if (strlen(model.gvars_names[i-1]) > 0)
529 b.addItem(QObject::tr("GV%1 (%2)").arg(i).arg(model.gvars_names[i-1]), gval);
530 else
531 b.addItem(QObject::tr("GV%1").arg(i), gval);
532 if (value == gval) {
533 b.setCurrentIndex(b.count()-1);
534 selected = true;
538 if (!selected) {
539 b.setCurrentIndex(count);
543 void populateSourceCB(QComboBox *b, const RawSource & source, const GeneralSettings generalSettings, const ModelData * model, unsigned int flags)
545 BoardEnum board = GetCurrentFirmware()->getBoard();
546 RawSource item;
548 b->clear();
550 if (flags & POPULATE_NONE) {
551 item = RawSource(SOURCE_TYPE_NONE);
552 b->addItem(item.toString(model), item.toValue());
553 if (item == source) b->setCurrentIndex(b->count()-1);
556 if (flags & POPULATE_SCRIPT_OUTPUTS) {
557 for (int i=0; i<GetCurrentFirmware()->getCapability(LuaScripts); i++) {
558 for (int j=0; j<GetCurrentFirmware()->getCapability(LuaOutputsPerScript); j++) {
559 item = RawSource(SOURCE_TYPE_LUA_OUTPUT, i*16+j);
560 b->addItem(item.toString(model), item.toValue());
561 if (item == source) b->setCurrentIndex(b->count()-1);
566 if (model && (flags & POPULATE_VIRTUAL_INPUTS)) {
567 int virtualInputs = GetCurrentFirmware()->getCapability(VirtualInputs);
568 for (int i=0; i<virtualInputs; i++) {
569 if (model->isInputValid(i)) {
570 item = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, i);
571 b->addItem(item.toString(model), item.toValue());
572 if (item == source) b->setCurrentIndex(b->count()-1);
577 if (flags & POPULATE_SOURCES) {
578 for (int i=0; i<CPN_MAX_STICKS+GetCurrentFirmware()->getCapability(Pots)+GetCurrentFirmware()->getCapability(Sliders); i++) {
579 item = RawSource(SOURCE_TYPE_STICK, i);
580 // skip unavailable pots and sliders
581 if (item.isPot() && !generalSettings.isPotAvailable(i-CPN_MAX_STICKS)) continue;
582 if (item.isSlider() && !generalSettings.isSliderAvailable(i-CPN_MAX_STICKS-GetCurrentFirmware()->getCapability(Pots))) continue;
583 b->addItem(item.toString(model), item.toValue());
584 if (item == source) b->setCurrentIndex(b->count()-1);
586 for (int i=0; i<GetCurrentFirmware()->getCapability(RotaryEncoders); i++) {
587 item = RawSource(SOURCE_TYPE_ROTARY_ENCODER, i);
588 b->addItem(item.toString(model), item.toValue());
589 if (item == source) b->setCurrentIndex(b->count()-1);
593 if (flags & POPULATE_TRIMS) {
594 for (int i=0; i<4; i++) {
595 item = RawSource(SOURCE_TYPE_TRIM, i);
596 b->addItem(item.toString(model), item.toValue());
597 if (item == source) b->setCurrentIndex(b->count()-1);
601 if (flags & POPULATE_SOURCES) {
602 item = RawSource(SOURCE_TYPE_MAX);
603 b->addItem(item.toString(model), item.toValue());
604 if (item == source) b->setCurrentIndex(b->count()-1);
607 if (flags & POPULATE_SWITCHES) {
608 for (int i=0; i<GetCurrentFirmware()->getCapability(Switches); i++) {
609 item = RawSource(SOURCE_TYPE_SWITCH, i);
610 b->addItem(item.toString(model), item.toValue());
611 if (IS_TARANIS(GetCurrentFirmware()->getBoard()) && !generalSettings.switchSourceAllowedTaranis(i)) {
612 QModelIndex index = b->model()->index(b->count()-1, 0);
613 QVariant v(0);
614 b->model()->setData(index, v, Qt::UserRole - 1);
616 if (item == source) b->setCurrentIndex(b->count()-1);
619 for (int i=0; i<GetCurrentFirmware()->getCapability(LogicalSwitches); i++) {
620 item = RawSource(SOURCE_TYPE_CUSTOM_SWITCH, i);
621 b->addItem(item.toString(model), item.toValue());
622 if (item == source) b->setCurrentIndex(b->count()-1);
626 if (flags & POPULATE_SOURCES) {
627 for (int i=0; i<CPN_MAX_CYC; i++) {
628 item = RawSource(SOURCE_TYPE_CYC, i);
629 b->addItem(item.toString(model), item.toValue());
630 if (item == source) b->setCurrentIndex(b->count()-1);
633 for (int i=0; i<GetCurrentFirmware()->getCapability(TrainerInputs); i++) {
634 item = RawSource(SOURCE_TYPE_PPM, i);
635 b->addItem(item.toString(model), item.toValue());
636 if (item == source) b->setCurrentIndex(b->count()-1);
639 for (int i=0; i<GetCurrentFirmware()->getCapability(Outputs); i++) {
640 item = RawSource(SOURCE_TYPE_CH, i);
641 b->addItem(item.toString(model), item.toValue());
642 if (item == source) b->setCurrentIndex(b->count()-1);
646 if (flags & POPULATE_TELEMETRY) {
647 if (IS_ARM(GetCurrentFirmware()->getBoard())) {
648 for (int i=0; i<5; ++i) {
649 item = RawSource(SOURCE_TYPE_SPECIAL, i);
650 b->addItem(item.toString(model), item.toValue());
651 if (item == source) b->setCurrentIndex(b->count()-1);
653 for (int i=0; i<CPN_MAX_SENSORS; ++i) {
654 if (model && model->sensorData[i].isAvailable()) { //this conditon must be false if we populate Global Functions where model = 0
655 for (int j=0; j<3; ++j) {
656 item = RawSource(SOURCE_TYPE_TELEMETRY, 3*i+j);
657 b->addItem(item.toString(model), item.toValue());
658 // qDebug() << item.toString(model) << source.toString(model);
659 if (item == source) b->setCurrentIndex(b->count()-1);
664 else {
665 for (int i=0; i<(flags & POPULATE_TELEMETRYEXT ? TELEMETRY_SOURCES_STATUS_COUNT : TELEMETRY_SOURCES_COUNT); i++) {
666 if (i==TELEMETRY_SOURCE_TX_TIME && !GetCurrentFirmware()->getCapability(RtcTime))
667 continue;
668 if (i==TELEMETRY_SOURCE_SWR && !GetCurrentFirmware()->getCapability(SportTelemetry))
669 continue;
670 if (i==TELEMETRY_SOURCE_TIMER3 && !IS_ARM(board))
671 continue;
672 item = RawSource(SOURCE_TYPE_TELEMETRY, i);
673 b->addItem(item.toString(model), item.toValue());
674 if (item == source) b->setCurrentIndex(b->count()-1);
679 if (flags & POPULATE_GVARS) {
680 for (int i=0; i<GetCurrentFirmware()->getCapability(Gvars); i++) {
681 item = RawSource(SOURCE_TYPE_GVAR, i);
682 b->addItem(item.toString(model), item.toValue());
683 if (item == source) b->setCurrentIndex(b->count()-1);
687 b->setMaxVisibleItems(10);
690 QString image2qstring(QImage image)
692 if (image.isNull())
693 return "";
694 QBuffer buffer;
695 image.save(&buffer, "PNG");
696 QString ImageStr;
697 int b=0;
698 int size=buffer.data().size();
699 for (int j = 0; j < size; j++) {
700 b=buffer.data().at(j);
701 ImageStr += QString("%1").arg(b&0xff, 2, 16, QChar('0'));
703 return ImageStr;
706 int findmult(float value, float base)
708 int vvalue = value*10;
709 int vbase = base*10;
710 vvalue--;
712 int mult = 0;
713 for (int i=8; i>=0; i--) {
714 if (vvalue/vbase >= (1<<i)) {
715 mult = i+1;
716 break;
720 return mult;
723 QString getFrSkyAlarmType(int alarm)
725 switch (alarm) {
726 case 1:
727 return QObject::tr("Yellow");
728 case 2:
729 return QObject::tr("Orange");
730 case 3:
731 return QObject::tr("Red");
732 default:
733 return "----";
737 QString getFrSkyUnits(int units)
739 switch(units) {
740 case 1:
741 return QObject::tr("---");
742 default:
743 return "V";
747 QString getFrSkyProtocol(int protocol)
749 switch(protocol) {
750 case 2:
751 if ((GetCurrentFirmware()->getCapability(Telemetry) & TM_HASWSHH))
752 return QObject::tr("Winged Shadow How High");
753 else
754 return QObject::tr("Winged Shadow How High (not supported)");
755 case 1:
756 return QObject::tr("FrSky Sensor Hub");
757 default:
758 return QObject::tr("None");
762 QString getFrSkyMeasure(int units)
764 switch(units) {
765 case 1:
766 return QObject::tr("Imperial");
767 default:
768 return QObject::tr("Metric");
772 QString getFrSkySrc(int index)
774 return RawSource(SOURCE_TYPE_TELEMETRY, index-1).toString();
777 QString getTheme()
779 int theme_set = g.theme();
780 QString Theme;
781 switch(theme_set) {
782 case 0:
783 Theme="classic";
784 break;
785 case 2:
786 Theme="monowhite";
787 break;
788 case 3:
789 Theme="monochrome";
790 break;
791 case 4:
792 Theme="monoblue";
793 break;
794 default:
795 Theme="yerico";
796 break;
798 return Theme;
801 CompanionIcon::CompanionIcon(const QString &baseimage)
803 static QString theme = getTheme();
804 addFile(":/themes/"+theme+"/16/"+baseimage, QSize(16,16));
805 addFile(":/themes/"+theme+"/24/"+baseimage, QSize(24,24));
806 addFile(":/themes/"+theme+"/32/"+baseimage, QSize(32,32));
807 addFile(":/themes/"+theme+"/48/"+baseimage, QSize(48,48));
810 void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
812 Firmware * firmware = GetCurrentFirmware();
813 SimulatorInterface * simulator = GetCurrentFirmwareSimulator();
814 if (simulator) {
815 #if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO)
816 AllocConsole();
817 SetConsoleTitle("Companion Console");
818 freopen("conin$", "r", stdin);
819 freopen("conout$", "w", stdout);
820 freopen("conout$", "w", stderr);
821 #endif
822 RadioData * simuData = new RadioData(radioData);
823 unsigned int flags = 0;
824 if (modelIdx >= 0) {
825 flags |= SIMULATOR_FLAGS_NOTX;
826 simuData->setCurrentModel(modelIdx);
828 if (radioData.generalSettings.stickMode & 1) {
829 flags |= SIMULATOR_FLAGS_STICK_MODE_LEFT;
831 BoardEnum board = GetCurrentFirmware()->getBoard();
832 SimulatorDialog * dialog;
834 if (board == BOARD_HORUS && HORUS_READY_FOR_RELEASE()) {
835 dialog = new SimulatorDialogHorus(parent, simulator, flags);
836 GetEepromInterface()->saveFile(*simuData, g.profile[g.id()].sdPath());
837 dialog->start(NULL);
839 else if (board == BOARD_FLAMENCO) {
840 dialog = new SimulatorDialogFlamenco(parent, simulator, flags);
841 QByteArray eeprom(GetEepromInterface()->getEEpromSize(), 0);
842 firmware->saveEEPROM((uint8_t *)eeprom.data(), *simuData);
843 dialog->start(eeprom);
845 else if (board == BOARD_TARANIS_X9D || board == BOARD_TARANIS_X9DP || board == BOARD_TARANIS_X9E) {
846 for (int i=0; i<GetCurrentFirmware()->getCapability(Pots); i++) {
847 if (radioData.generalSettings.isPotAvailable(i)) {
848 flags |= (SIMULATOR_FLAGS_S1 << i);
849 if (radioData.generalSettings.potConfig[1] == GeneralSettings::POT_MULTIPOS_SWITCH ) {
850 flags |= (SIMULATOR_FLAGS_S1_MULTI << i);
854 dialog = new SimulatorDialogTaranis(parent, simulator, flags);
855 QByteArray eeprom(GetEepromInterface()->getEEpromSize(), 0);
856 firmware->saveEEPROM((uint8_t *)eeprom.data(), *simuData);
857 dialog->start(eeprom);
859 else {
860 dialog = new SimulatorDialog9X(parent, simulator, flags);
861 QByteArray eeprom(GetEepromInterface()->getEEpromSize(), 0);
862 firmware->saveEEPROM((uint8_t *)eeprom.data(), *simuData, 0, firmware->getCapability(SimulatorVariant));
863 dialog->start(eeprom);
866 dialog->exec();
867 delete dialog;
868 delete simuData;
869 #if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO)
870 FreeConsole();
871 #endif
873 else {
874 QMessageBox::warning(NULL,
875 QObject::tr("Warning"),
876 QObject::tr("Simulator for this firmware is not yet available"));
880 QPixmap makePixMap(const QImage & image)
882 Firmware * firmware = GetCurrentFirmware();
883 QImage result = image.scaled(firmware->getCapability(LcdWidth), firmware->getCapability(LcdHeight));
884 if (firmware->getCapability(LcdDepth) == 4) {
885 result = result.convertToFormat(QImage::Format_RGB32);
886 for (int i = 0; i < image.width(); ++i) {
887 for (int j = 0; j < image.height(); ++j) {
888 QRgb col = result.pixel(i, j);
889 int gray = qGray(col);
890 result.setPixel(i, j, qRgb(gray, gray, gray));
894 else {
895 result = result.convertToFormat(QImage::Format_Mono);
898 return QPixmap::fromImage(result);
901 int version2index(const QString & version)
903 int result = 999;
904 QStringList parts = version.split("N");
905 if (parts.size() > 1)
906 result = parts[1].toInt(); // nightly build
907 parts = parts[0].split('.');
908 if (parts.size() > 2)
909 result += 1000 * parts[2].toInt();
910 if (parts.size() > 1)
911 result += 100000 * parts[1].toInt();
912 if (parts.size() > 0)
913 result += 10000000 * parts[0].toInt();
914 return result;
917 QString index2version(int index)
919 if (index >= 19900000) {
920 int nightly = index % 1000;
921 index /= 1000;
922 int revision = index % 100;
923 index /= 100;
924 int minor = index % 100;
925 int major = index / 100;
926 QString result = QString("%1.%2.%3").arg(major).arg(minor).arg(revision);
927 if (nightly > 0 && nightly < 999) {
928 result += QString("N%1").arg(nightly);
930 return result;
932 else if (index >= 19900) {
933 int revision = index % 100;
934 index /= 100;
935 int minor = index % 100;
936 int major = index / 100;
937 return QString("%1.%2.%3").arg(major).arg(minor).arg(revision);
939 else {
940 return QString();
944 int qunlink(const QString & fileName)
946 QByteArray ba = fileName.toLatin1();
947 return unlink(ba.constData());
950 QString generateProcessUniqueTempFileName(const QString & fileName)
952 QString sanitizedFileName = fileName;
953 sanitizedFileName.remove('/');
954 return QDir::tempPath() + QString("/%1-").arg(QCoreApplication::applicationPid()) + sanitizedFileName;
957 bool isTempFileName(const QString & fileName)
959 return fileName.startsWith(QDir::tempPath());
962 QString getSoundsPath(const GeneralSettings &generalSettings)
964 QString path = g.profile[g.id()].sdPath() + "/SOUNDS/";
965 QString lang = generalSettings.ttsLanguage;
966 if (lang.isEmpty())
967 lang = "en";
968 path.append(lang);
969 return path;
972 QSet<QString> getFilesSet(const QString &path, const QStringList &filter, int maxLen)
974 QSet<QString> result;
975 QDir dir(path);
976 if (dir.exists()) {
977 foreach (QString filename, dir.entryList(filter, QDir::Files)) {
978 QFileInfo file(filename);
979 QString name = file.completeBaseName();
980 if (name.length() <= maxLen) {
981 result.insert(name);
985 return result;
988 bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
990 return s1.toLower() < s2.toLower();
993 bool GpsGlitchFilter::isGlitch(GpsCoord coord)
995 if ((fabs(coord.latitude) < 0.1) && (fabs(coord.longitude) < 0.1)) {
996 return true;
999 if (lastValid) {
1000 if (fabs(coord.latitude - lastLat) > 0.01) {
1001 // qDebug() << "GpsGlitchFilter(): latitude glitch " << coord.latitude << lastLat;
1002 if ( ++glitchCount < 10) {
1003 return true;
1006 if (fabs(coord.longitude - lastLon) > 0.01) {
1007 // qDebug() << "GpsGlitchFilter(): longitude glitch " << coord.longitude << lastLon;
1008 if ( ++glitchCount < 10) {
1009 return true;
1013 lastLat = coord.latitude;
1014 lastLon = coord.longitude;
1015 lastValid = true;
1016 glitchCount = 0;
1017 return false;
1020 bool GpsLatLonFilter::isValid(GpsCoord coord)
1022 if (lastLat == coord.latitude) {
1023 return false;
1025 if (lastLon == coord.longitude) {
1026 return false;
1028 lastLat = coord.latitude;
1029 lastLon = coord.longitude;
1030 return true;
1033 double toDecimalCoordinate(const QString & value)
1035 if (value.isEmpty()) return 0.0;
1036 double temp = int(value.left(value.length()-1).toDouble() / 100);
1037 double result = temp + (value.left(value.length() - 1).toDouble() - temp * 100) / 60.0;
1038 QChar direction = value.at(value.size()-1);
1039 if ((direction == 'S') || (direction == 'W')) {
1040 result = -result;
1042 return result;
1045 GpsCoord extractGpsCoordinates(const QString & position)
1047 GpsCoord result;
1048 QStringList parts = position.split(' ');
1049 if (parts.size() == 2) {
1050 QString value = parts.at(0).trimmed();
1051 QChar direction = value.at(value.size()-1);
1052 if (direction == 'E' || direction == 'W') {
1053 // OpenTX 2.1 format: "NNN.MMM[E|W] NNN.MMM[N|S]" <longitude> <latitude>
1054 result.latitude = toDecimalCoordinate(parts.at(1).trimmed());
1055 result.longitude = toDecimalCoordinate(parts.at(0).trimmed());
1057 else {
1058 // OpenTX 2.2 format: "DD.DDDDDD DD.DDDDDD" <latitude> <longitude> both in Signed degrees format (DDD.dddd)
1059 // Precede South latitudes and West longitudes with a minus sign.
1060 // Latitudes range from -90 to 90.
1061 // Longitudes range from -180 to 180.
1062 result.latitude = parts.at(0).trimmed().toDouble();
1063 result.longitude = parts.at(1).trimmed().toDouble();
1066 return result;
1069 TableLayout::TableLayout(QWidget * parent, int rowCount, const QStringList & headerLabels)
1071 #if defined(TABLE_LAYOUT)
1072 tableWidget = new QTableWidget(parent);
1073 QVBoxLayout * layout = new QVBoxLayout();
1074 layout->addWidget(tableWidget);
1075 layout->setContentsMargins(0, 0, 0, 0);
1076 parent->setLayout(layout);
1078 tableWidget->setRowCount(rowCount);
1079 tableWidget->setColumnCount(headerLabels.size());
1080 tableWidget->setShowGrid(false);
1081 tableWidget->verticalHeader()->setVisible(false);
1082 tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
1083 tableWidget->setSelectionMode(QAbstractItemView::NoSelection);
1084 tableWidget->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
1085 tableWidget->setStyleSheet("QTableWidget {background-color: transparent;}");
1086 tableWidget->setHorizontalHeaderLabels(headerLabels);
1087 #else
1088 gridWidget = new QGridLayout(parent);
1090 int col = 0;
1091 foreach(QString text, headerLabels) {
1092 QLabel *label = new QLabel();
1093 label->setFrameShape(QFrame::Panel);
1094 label->setFrameShadow(QFrame::Raised);
1095 label->setMidLineWidth(0);
1096 label->setAlignment(Qt::AlignCenter);
1097 label->setMargin(5);
1098 label->setText(text);
1099 // if (!minimize)
1100 // label->setMinimumWidth(100);
1101 gridWidget->addWidget(label, 0, col++);
1103 #endif
1106 void TableLayout::addWidget(int row, int column, QWidget * widget)
1108 #if defined(TABLE_LAYOUT)
1109 QHBoxLayout * layout = new QHBoxLayout(tableWidget);
1110 layout->addWidget(widget);
1111 addLayout(row, column, layout);
1112 #else
1113 gridWidget->addWidget(widget, row + 1, column);
1114 #endif
1117 void TableLayout::addLayout(int row, int column, QLayout * layout)
1119 #if defined(TABLE_LAYOUT)
1120 layout->setContentsMargins(1, 3, 1, 3);
1121 QWidget * containerWidget = new QWidget(tableWidget);
1122 containerWidget->setLayout(layout);
1123 tableWidget->setCellWidget(row, column, containerWidget);
1124 #else
1125 gridWidget->addLayout(layout, row + 1, column);
1126 #endif
1129 void TableLayout::resizeColumnsToContents()
1131 #if defined(TABLE_LAYOUT)
1132 tableWidget->resizeColumnsToContents();
1133 #else
1134 #endif
1137 void TableLayout::setColumnWidth(int col, int width)
1139 #if defined(TABLE_LAYOUT)
1140 tableWidget->setColumnWidth(col, width);
1141 #else
1142 #endif
1145 void TableLayout::pushRowsUp(int row)
1147 #if defined(TABLE_LAYOUT)
1148 #else
1149 // Push the rows up
1150 QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding );
1151 gridWidget->addItem(spacer, row, 0);
1152 #endif
1153 // Push rows upward
1154 // addDoubleSpring(gridLayout, 5, num_fsw+1);