Initial version.
[opentx.git] / companion / src / modelprinter.cpp
blob9d3b1214d24f3aacc579bb321801b3ce3b8f04de
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 "helpers.h"
22 #include "modelprinter.h"
23 #include "multiprotocols.h"
24 #include "boards.h"
25 #include "helpers_html.h"
26 #include "multiprotocols.h"
28 #include <QApplication>
29 #include <QPainter>
30 #include <QFile>
31 #include <QUrl>
33 QString changeColor(const QString & input, const QString & to, const QString & from)
35 QString result = input;
36 return result.replace("color="+from, "color="+to);
39 ModelPrinter::ModelPrinter(Firmware * firmware, const GeneralSettings & generalSettings, const ModelData & model):
40 firmware(firmware),
41 generalSettings(generalSettings),
42 model(model)
46 ModelPrinter::~ModelPrinter()
50 QString formatTitle(const QString & name)
52 return QString("<b>" + name + "</b>&nbsp;");
55 void debugHtml(const QString & html)
57 QFile file("foo.html");
58 file.open(QIODevice::Truncate | QIODevice::WriteOnly);
59 file.write(html.toUtf8());
60 file.close();
63 QString addFont(const QString & input, const QString & color, const QString & size, const QString & face)
65 QString colorStr;
66 if (!color.isEmpty()) {
67 colorStr = "color=" + color;
69 QString sizeStr;
70 if (!size.isEmpty()) {
71 sizeStr = "size=" + size;
73 QString faceStr;
74 if (!face.isEmpty()) {
75 faceStr = "face='" + face + "'";
77 return "<font " + sizeStr + " " + faceStr + " " + colorStr + ">" + input + "</font>";
80 QString ModelPrinter::printLabelValue(const QString & lbl, const QString & val, const bool sep) {
81 return QString("<b>%1:</b> %2%3 ").arg(lbl, val, (sep ? ";" : ""));
84 QString ModelPrinter::printLabelValues(const QString & lbl, const QStringList & vals, const bool sep) {
85 QString str;
86 if (vals.count() > 1)
87 str.append("(");
88 for (int i=0;i<vals.count();i++) {
89 str.append(vals.at(i));
90 if (i<(vals.count()-1))
91 str.append(", ");
93 if (vals.count() > 1)
94 str.append(")");
96 return printLabelValue(lbl, str, sep);
99 #define MASK_TIMEVALUE_HRSMINS 1
100 #define MASK_TIMEVALUE_ZEROHRS 2
101 #define MASK_TIMEVALUE_PADSIGN 3
103 QString ModelPrinter::printTimeValue(const int value, const unsigned int mask)
105 QString result;
106 int sign = 1;
107 int val = value;
108 if (val < 0) {
109 val = -val;
110 sign = -1;
112 result = (sign < 0 ? QString("-") : ((mask & MASK_TIMEVALUE_PADSIGN) ? QString(" ") : QString("")));
113 if (mask & MASK_TIMEVALUE_HRSMINS) {
114 int hours = val / 3600;
115 if (hours > 0 || (mask & MASK_TIMEVALUE_ZEROHRS)) {
116 val -= hours * 3600;
117 result.append(QString("%1:").arg(hours, 2, 10, QLatin1Char('0')));
120 int minutes = val / 60;
121 int seconds = val % 60;
122 result.append(QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')));
123 return result;
126 #define BOOLEAN_ENABLEDISABLE 1
127 #define BOOLEAN_TRUEFALSE 2
128 #define BOOLEAN_YESNO 3
129 #define BOOLEAN_YN 4
130 #define BOOLEAN_ONOFF 5
132 QString ModelPrinter::printBoolean(const bool val, const int typ)
134 switch (typ) {
135 case BOOLEAN_ENABLEDISABLE:
136 return (val ? tr("Enable") : tr("Disable"));
137 case BOOLEAN_TRUEFALSE:
138 return (val ? tr("True") : tr("False"));
139 case BOOLEAN_YESNO:
140 return (val ? tr("Yes") : tr("No"));
141 case BOOLEAN_YN:
142 return (val ? tr("Y") : tr("N"));
143 case BOOLEAN_ONOFF:
144 return (val ? tr("ON") : tr("OFF"));
145 default:
146 return tr("???");
150 QString ModelPrinter::printEEpromSize()
152 return QString("%1 ").arg(getCurrentEEpromInterface()->getSize(model)) + tr("bytes");
155 QString ModelPrinter::printChannelName(int idx)
157 QString str = RawSource(SOURCE_TYPE_CH, idx).toString(&model, &generalSettings);
158 if (firmware->getCapability(ChannelsName)) {
159 str = str.leftJustified(firmware->getCapability(ChannelsName) + 5, ' ', false);
161 str.append(' ');
162 return str.toHtmlEscaped();
165 QString ModelPrinter::printTrimIncrementMode()
167 switch (model.trimInc) {
168 case -2:
169 return tr("Exponential");
170 case -1:
171 return tr("Extra Fine");
172 case 0:
173 return tr("Fine");
174 case 1:
175 return tr("Medium");
176 case 2:
177 return tr("Coarse");
178 default:
179 return tr("Unknown");
183 QString ModelPrinter::printModuleProtocol(unsigned int protocol)
185 static const char * strings[] = {
186 "OFF",
187 "PPM",
188 "Silverlit A", "Silverlit B", "Silverlit C",
189 "CTP1009",
190 "LP45", "DSM2", "DSMX",
191 "PPM16", "PPMsim",
192 "FrSky XJT (D16)", "FrSky XJT (D8)", "FrSky XJT (LR12)", "FrSky DJT",
193 "Crossfire",
194 "DIY Multiprotocol Module",
195 "FrSky R9M Module",
196 "SBUS output at VBat"
199 return CHECK_IN_ARRAY(strings, protocol);
202 QString ModelPrinter::printMultiRfProtocol(int rfProtocol, bool custom)
204 static const char * strings[] = {
205 "FlySky", "Hubsan", "FrSky", "Hisky", "V2x2", "DSM", "Devo", "YD717", "KN", "SymaX", "SLT", "CX10", "CG023",
206 "Bayang", "ESky", "MT99XX", "MJXQ", "Shenqi", "FY326", "SFHSS", "J6 PRO","FQ777","Assan","Hontai","OLRS",
207 "FlySky AFHDS2A", "Q2x2", "Walkera", "Q303", "GW008", "DM002", "CABELL", "Esky 150", "H8 3D", "Corona", "CFlie"
209 if (custom)
210 return QObject::tr("Custom - proto %1)").arg(QString::number(rfProtocol));
211 else
212 return CHECK_IN_ARRAY(strings, rfProtocol);
215 QString ModelPrinter::printMultiSubType(unsigned rfProtocol, bool custom, unsigned int subType) {
216 /* custom protocols */
218 if (custom)
219 rfProtocol = MM_RF_CUSTOM_SELECTED;
221 Multiprotocols::MultiProtocolDefinition pdef = multiProtocols.getProtocol(rfProtocol);
223 if (subType < (unsigned int) pdef.subTypeStrings.size())
224 return qApp->translate("Multiprotocols", qPrintable(pdef.subTypeStrings[subType]));
225 else
226 return "???";
229 QString ModelPrinter::printR9MPowerValue(unsigned subType, unsigned val, bool telem)
231 QStringList strFCC;
232 QStringList strLBT;
234 if (IS_TARANIS_XLITE(firmware->getBoard())) {
235 strFCC = QStringList() << tr("100mW - 16CH");
236 strLBT = QStringList() << tr("25mW - 8CH") << tr("25mW - 16CH") << tr("100mW 16CH");
238 else {
239 strFCC = QStringList() << tr("10mW") << tr("100mW") << tr("500mW") << tr("1W");
240 strLBT = QStringList() << tr("25mW - 8CH") << tr("25mW - 16CH") << tr("200mW 16CH") << tr("500mW 16CH");
244 if (subType == R9M_FCC && (int)val < strFCC.size())
245 return strFCC.at(val);
246 else if (subType == R9M_LBT)
247 return (telem ? strLBT.at(0) : strLBT.at(1));
248 else
249 return "???";
252 QString ModelPrinter::printModuleSubType(unsigned protocol, unsigned subType, unsigned rfProtocol, bool custom)
254 static const char * strings[] = {
255 "FCC",
256 "LBT(EU)"
259 switch (protocol) {
260 case PULSES_MULTIMODULE:
261 return printMultiSubType(rfProtocol, custom, subType);
263 case PULSES_PXX_R9M:
264 return CHECK_IN_ARRAY(strings, subType);
266 default:
267 return "???";
271 QString ModelPrinter::printModule(int idx)
273 QStringList str;
274 QString result;
275 ModuleData module = model.moduleData[(idx<0 ? CPN_MAX_MODULES : idx)];
276 if (idx < 0) {
277 str << printLabelValue(tr("Mode"), printTrainerMode());
278 if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
279 if (model.trainerMode == TRAINER_SLAVE_JACK) {
280 str << printLabelValue(tr("Channels"), QString("%1-%2").arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount));
281 str << printLabelValue(tr("Frame length"), QString("%1ms").arg(printPPMFrameLength(module.ppm.frameLength)));
282 str << printLabelValue(tr("PPM delay"), QString("%1us").arg(module.ppm.delay));
283 str << printLabelValue(tr("Polarity"), module.polarityToString());
286 result = str.join(" ");
288 else {
289 str << printLabelValue(tr("Protocol"), printModuleProtocol(module.protocol));
290 if (module.protocol) {
291 str << printLabelValue(tr("Channels"), QString("%1-%2").arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount));
292 if (module.protocol == PULSES_PPM || module.protocol == PULSES_SBUS) {
293 str << printLabelValue(tr("Frame length"), QString("%1ms").arg(printPPMFrameLength(module.ppm.frameLength)));
294 str << printLabelValue(tr("Polarity"), module.polarityToString());
295 if (module.protocol == PULSES_PPM)
296 str << printLabelValue(tr("Delay"), QString("%1us").arg(module.ppm.delay));
298 else {
299 if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_SBUS)) {
300 str << printLabelValue(tr("Receiver"), QString::number(module.modelId));
302 if (module.protocol == PULSES_MULTIMODULE) {
303 str << printLabelValue(tr("Radio protocol"), printMultiRfProtocol(module.multi.rfProtocol, module.multi.customProto));
304 str << printLabelValue(tr("Subtype"), printMultiSubType(module.multi.rfProtocol, module.multi.customProto, module.subType));
305 str << printLabelValue(tr("Option value"), QString::number(module.multi.optionValue));
307 if (module.protocol == PULSES_PXX_R9M) {
308 str << printLabelValue(tr("Sub Type"), printModuleSubType(module.protocol, module.subType));
309 str << printLabelValue(tr("RF Output Power"), printR9MPowerValue(module.subType, module.pxx.power, module.pxx.sport_out));
310 str << printLabelValue(tr("Telemetry"), printBoolean(module.pxx.sport_out, BOOLEAN_ENABLEDISABLE));
314 result = str.join(" ");
315 if (((PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_XJT_X16 || (PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_R9M)
316 && firmware->getCapability(HasFailsafe))
317 result.append("<br/>" + printFailsafe(idx));
319 return result;
322 QString ModelPrinter::printTrainerMode()
324 QString result;
325 switch (model.trainerMode) {
326 case TRAINER_MASTER_JACK:
327 result = tr("Master/Jack");
328 break;
329 case TRAINER_SLAVE_JACK:
330 result = tr("Slave/Jack");
331 break;
332 case TRAINER_MASTER_SBUS_MODULE:
333 result = tr("Master/SBUS Module");
334 break;
335 case TRAINER_MASTER_CPPM_MODULE:
336 result = tr("Master/CPPM Module");
337 break;
338 case TRAINER_MASTER_SBUS_BATT_COMPARTMENT:
339 result = tr("Master/SBUS in battery compartment");
340 break;
341 default:
342 result = tr("????");
344 return result;
347 QString ModelPrinter::printHeliSwashType ()
349 switch (model.swashRingData.type) {
350 case HELI_SWASH_TYPE_90:
351 return tr("90");
352 case HELI_SWASH_TYPE_120:
353 return tr("120");
354 case HELI_SWASH_TYPE_120X:
355 return tr("120X");
356 case HELI_SWASH_TYPE_140:
357 return tr("140");
358 case HELI_SWASH_TYPE_NONE:
359 return tr("Off");
360 default:
361 return "???";
365 QString ModelPrinter::printCenterBeep()
367 QStringList strl;
368 if (model.beepANACenter & 0x01)
369 strl << tr("Rudder");
370 if (model.beepANACenter & 0x02)
371 strl << tr("Elevator");
372 if (model.beepANACenter & 0x04)
373 strl << tr("Throttle");
374 if (model.beepANACenter & 0x08)
375 strl << tr("Aileron");
376 if (IS_HORUS(firmware->getBoard())) {
377 // TODO
378 qDebug() << "ModelPrinter::printCenterBeep() TODO";
380 else if (IS_TARANIS(firmware->getBoard())) {
381 if (model.beepANACenter & 0x10)
382 strl << "S1";
383 if (model.beepANACenter & 0x20)
384 strl << "S2";
385 if (model.beepANACenter & 0x40)
386 strl << "S3";
387 if (model.beepANACenter & 0x80)
388 strl << "LS";
389 if (model.beepANACenter & 0x100)
390 strl << "RS";
392 else {
393 if (model.beepANACenter & 0x10)
394 strl << "P1";
395 if (model.beepANACenter & 0x20)
396 strl << "P2";
397 if (model.beepANACenter & 0x40)
398 strl << "P3";
400 return (strl.isEmpty() ? tr("None") : strl.join(" "));
403 QString ModelPrinter::printTimer(int idx)
405 return printTimer(model.timers[idx]);
408 QString ModelPrinter::printTimer(const TimerData & timer)
410 QStringList result;
411 if (firmware->getCapability(TimersName) && timer.name[0])
412 result += tr("Name") + QString("(%1)").arg(timer.name);
413 result += printTimeValue(timer.val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS);
414 result += timer.mode.toString();
415 if (timer.countdownBeep)
416 result += tr("Countdown") + QString("(%1)").arg(printTimerCountdownBeep(timer.countdownBeep));
417 if (timer.minuteBeep)
418 result += tr("Minute call");
419 if (timer.persistent)
420 result += tr("Persistent") + QString("(%1)").arg(printTimerPersistent(timer.persistent));
421 return result.join(", ");
424 QString ModelPrinter::printTrim(int flightModeIndex, int stickIndex)
426 const FlightModeData & fm = model.flightModeData[flightModeIndex];
428 if (fm.trimMode[stickIndex] == -1) {
429 return tr("OFF");
431 else {
432 if (fm.trimRef[stickIndex] == flightModeIndex) {
433 return QString("%1").arg(fm.trim[stickIndex]);
435 else {
436 if (fm.trimMode[stickIndex] == 0) {
437 return tr("FM%1").arg(fm.trimRef[stickIndex]);
439 else {
440 if (fm.trim[stickIndex] < 0)
441 return tr("FM%1%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]);
442 else
443 return tr("FM%1+%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]);
449 QString ModelPrinter::printGlobalVar(int flightModeIndex, int gvarIndex)
451 const FlightModeData & fm = model.flightModeData[flightModeIndex];
453 if (fm.gvars[gvarIndex] <= 1024) {
454 return QString("%1").arg(fm.gvars[gvarIndex] * model.gvarData[gvarIndex].multiplierGet());
456 else {
457 int num = fm.gvars[gvarIndex] - 1025;
458 if (num >= flightModeIndex) num++;
459 return tr("FM%1").arg(num);
463 QString ModelPrinter::printRotaryEncoder(int flightModeIndex, int reIndex)
465 const FlightModeData & fm = model.flightModeData[flightModeIndex];
467 if (fm.rotaryEncoders[reIndex] <= 1024) {
468 return QString("%1").arg(fm.rotaryEncoders[reIndex]);
470 else {
471 int num = fm.rotaryEncoders[reIndex] - 1025;
472 if (num >= flightModeIndex) num++;
473 return tr("FM%1").arg(num);
477 QString ModelPrinter::printInputName(int idx)
479 RawSourceType srcType = (firmware->getCapability(VirtualInputs) ? SOURCE_TYPE_VIRTUAL_INPUT : SOURCE_TYPE_STICK);
480 return RawSource(srcType, idx).toString(&model, &generalSettings).toHtmlEscaped();
483 QString ModelPrinter::printInputLine(int idx)
485 return printInputLine(model.expoData[idx]);
488 QString ModelPrinter::printInputLine(const ExpoData & input)
490 QString str = "&nbsp;";
492 switch (input.mode) {
493 case (1): str += "&lt;-&nbsp;"; break;
494 case (2): str += "-&gt;&nbsp;"; break;
495 default: str += "&nbsp;&nbsp;&nbsp;"; break;
498 if (firmware->getCapability(VirtualInputs)) {
499 str += input.srcRaw.toString(&model, &generalSettings).toHtmlEscaped();
502 str += " " + tr("Weight").toHtmlEscaped() + QString("(%1)").arg(Helpers::getAdjustmentString(input.weight, &model, true).toHtmlEscaped());
503 if (input.curve.value)
504 str += " " + input.curve.toString(&model).toHtmlEscaped();
506 QString flightModesStr = printFlightModes(input.flightModes);
507 if (!flightModesStr.isEmpty())
508 str += " " + flightModesStr.toHtmlEscaped();
510 if (input.swtch.type != SWITCH_TYPE_NONE)
511 str += " " + tr("Switch").toHtmlEscaped() + QString("(%1)").arg(input.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped();
514 if (firmware->getCapability(VirtualInputs)) {
515 if (input.carryTrim>0)
516 str += " " + tr("NoTrim").toHtmlEscaped();
517 else if (input.carryTrim<0)
518 str += " " + RawSource(SOURCE_TYPE_TRIM, (-(input.carryTrim)-1)).toString(&model, &generalSettings).toHtmlEscaped();
521 if (input.offset)
522 str += " " + tr("Offset(%1)").arg(Helpers::getAdjustmentString(input.offset, &model)).toHtmlEscaped();
524 if (firmware->getCapability(HasExpoNames) && input.name[0])
525 str += QString(" [%1]").arg(input.name).toHtmlEscaped();
527 return str;
530 QString ModelPrinter::printMixerLine(const MixData & mix, bool showMultiplex, int highlightedSource)
532 QString str = "&nbsp;";
534 if (showMultiplex) {
535 switch(mix.mltpx) {
536 case (1): str += "*="; break;
537 case (2): str += ":="; break;
538 default: str += "+="; break;
541 else {
542 str += "&nbsp;&nbsp;";
544 // highlight source if needed
545 QString source = mix.srcRaw.toString(&model, &generalSettings).toHtmlEscaped();
546 if ( (mix.srcRaw.type == SOURCE_TYPE_CH) && (mix.srcRaw.index+1 == (int)highlightedSource) ) {
547 source = "<b>" + source + "</b>";
549 str += "&nbsp;" + source;
551 if (mix.mltpx == MLTPX_MUL && !showMultiplex)
552 str += " " + tr("MULT!").toHtmlEscaped();
553 else
554 str += " " + tr("Weight") + QString("(%1)").arg(Helpers::getAdjustmentString(mix.weight, &model, true)).toHtmlEscaped();
556 QString flightModesStr = printFlightModes(mix.flightModes);
557 if (!flightModesStr.isEmpty())
558 str += " " + flightModesStr.toHtmlEscaped();
560 if (mix.swtch.type != SWITCH_TYPE_NONE)
561 str += " " + tr("Switch") + QString("(%1)").arg(mix.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped();
563 if (mix.carryTrim > 0)
564 str += " " + tr("NoTrim").toHtmlEscaped();
565 else if (mix.carryTrim < 0)
566 str += " " + RawSource(SOURCE_TYPE_TRIM, (-(mix.carryTrim)-1)).toString(&model, &generalSettings);
568 if (firmware->getCapability(HasNoExpo) && mix.noExpo)
569 str += " " + tr("No DR/Expo").toHtmlEscaped();
570 if (mix.sOffset)
571 str += " " + tr("Offset") + QString("(%1)").arg(Helpers::getAdjustmentString(mix.sOffset, &model)).toHtmlEscaped();
572 if (mix.curve.value)
573 str += " " + mix.curve.toString(&model).toHtmlEscaped();
574 int scale = firmware->getCapability(SlowScale);
575 if (scale == 0)
576 scale = 1;
577 if (mix.delayDown || mix.delayUp)
578 str += " " + tr("Delay") + QString("(u%1:d%2)").arg((double)mix.delayUp/scale).arg((double)mix.delayDown/scale).toHtmlEscaped();
579 if (mix.speedDown || mix.speedUp)
580 str += " " + tr("Slow") + QString("(u%1:d%2)").arg((double)mix.speedUp/scale).arg((double)mix.speedDown/scale).toHtmlEscaped();
581 if (mix.mixWarn)
582 str += " " + tr("Warn") + QString("(%1)").arg(mix.mixWarn).toHtmlEscaped();
583 if (firmware->getCapability(HasMixerNames) && mix.name[0])
584 str += QString(" [%1]").arg(mix.name).toHtmlEscaped();
585 return str;
588 QString ModelPrinter::printFlightModeSwitch(const RawSwitch & swtch)
590 return swtch.toString(getCurrentBoard(), &generalSettings);
593 QString ModelPrinter::printFlightModeName(int index)
595 return model.flightModeData[index].nameToString(index);
598 QString ModelPrinter::printFlightModes(unsigned int flightModes)
600 int numFlightModes = firmware->getCapability(FlightModes);
601 if (numFlightModes && flightModes) {
602 if (flightModes == (unsigned int)(1<<numFlightModes) - 1) {
603 return tr("Disabled in all flight modes");
605 else {
606 QStringList list;
607 for (int i=0; i<numFlightModes; i++) {
608 if (!(flightModes & (1<<i))) {
609 list << printFlightModeName(i);
612 return (list.size() > 1 ? tr("Flight modes") : tr("Flight mode")) + QString("(%1)").arg(list.join(", "));
615 else
616 return "";
619 QString ModelPrinter::printInputFlightModes(unsigned int flightModes)
621 int numFlightModes = firmware->getCapability(FlightModes);
622 if (numFlightModes && flightModes) {
623 if (flightModes == (unsigned int)(1<<numFlightModes) - 1) {
624 return tr("None");
626 else {
627 QStringList list;
628 for (int i=0; i<numFlightModes; i++) {
629 if (!(flightModes & (1<<i))) {
630 list << printFlightModeName(i);
633 return QString("%1").arg(list.join(" "));
636 else
637 return tr("All");
640 QString ModelPrinter::printLogicalSwitchLine(int idx)
642 QString result = "";
643 const LogicalSwitchData & ls = model.logicalSw[idx];
644 const QString sw1Name = RawSwitch(ls.val1).toString(getCurrentBoard(), &generalSettings);
645 const QString sw2Name = RawSwitch(ls.val2).toString(getCurrentBoard(), &generalSettings);
647 if (ls.isEmpty())
648 return result;
650 if (ls.andsw!=0) {
651 result +="( ";
653 switch (ls.getFunctionFamily()) {
654 case LS_FAMILY_EDGE:
655 result += tr("Edge") + QString("(%1, [%2:%3])").arg(sw1Name).arg(ValToTim(ls.val2)).arg(ls.val3<0 ? tr("instant") : QString("%1").arg(ValToTim(ls.val2+ls.val3)));
656 break;
657 case LS_FAMILY_STICKY:
658 result += tr("Sticky") + QString("(%1, %2)").arg(sw1Name).arg(sw2Name);
659 break;
660 case LS_FAMILY_TIMER:
661 result += tr("Timer") + QString("(%1, %2)").arg(ValToTim(ls.val1)).arg(ValToTim(ls.val2));
662 break;
663 case LS_FAMILY_VOFS: {
664 RawSource source = RawSource(ls.val1);
665 RawSourceRange range = source.getRange(&model, generalSettings);
666 QString res;
667 if (ls.val1)
668 res += source.toString(&model, &generalSettings);
669 else
670 res += "0";
671 res.remove(" ");
672 if (ls.func == LS_FN_APOS || ls.func == LS_FN_ANEG)
673 res = "|" + res + "|";
674 else if (ls.func == LS_FN_DAPOS)
675 res = "|d(" + res + ")|";
676 else if (ls.func == LS_FN_DPOS)
677 result = "d(" + res + ")";
678 result += res;
679 if (ls.func == LS_FN_VEQUAL)
680 result += " = ";
681 else if (ls.func == LS_FN_APOS || ls.func == LS_FN_VPOS || ls.func == LS_FN_DPOS || ls.func == LS_FN_DAPOS)
682 result += " &gt; ";
683 else if (ls.func == LS_FN_ANEG || ls.func == LS_FN_VNEG)
684 result += " &lt; ";
685 else if (ls.func == LS_FN_VALMOSTEQUAL)
686 result += " ~ ";
687 else
688 result += tr(" missing");
689 result += QString::number(range.step * (ls.val2 /*TODO+ source.getRawOffset(model)*/) + range.offset);
690 break;
692 case LS_FAMILY_VBOOL:
693 result += sw1Name;
694 switch (ls.func) {
695 case LS_FN_AND:
696 result += " AND ";
697 break;
698 case LS_FN_OR:
699 result += " OR ";
700 break;
701 case LS_FN_XOR:
702 result += " XOR ";
703 break;
704 default:
705 result += " bar ";
706 break;
708 result += sw2Name;
709 break;
711 case LS_FAMILY_VCOMP:
712 if (ls.val1)
713 result += RawSource(ls.val1).toString(&model, &generalSettings);
714 else
715 result += "0";
716 switch (ls.func) {
717 case LS_FN_EQUAL:
718 case LS_FN_VEQUAL:
719 result += " = ";
720 break;
721 case LS_FN_NEQUAL:
722 result += " != ";
723 break;
724 case LS_FN_GREATER:
725 result += " &gt; ";
726 break;
727 case LS_FN_LESS:
728 result += " &lt; ";
729 break;
730 case LS_FN_EGREATER:
731 result += " &gt;= ";
732 break;
733 case LS_FN_ELESS:
734 result += " &lt;= ";
735 break;
736 default:
737 result += " foo ";
738 break;
740 if (ls.val2)
741 result += RawSource(ls.val2).toString(&model, &generalSettings);
742 else
743 result += "0";
744 break;
747 if (ls.andsw != 0) {
748 result +=" ) AND ";
749 result += RawSwitch(ls.andsw).toString(getCurrentBoard(), &generalSettings);
752 if (firmware->getCapability(LogicalSwitchesExt)) {
753 if (ls.duration)
754 result += " " + tr("Duration") + QString("(%1s)").arg(ls.duration/10.0);
755 if (ls.delay)
756 result += " " + tr("Delay") + QString("(%1s)").arg(ls.delay/10.0);
759 return result;
762 QString ModelPrinter::printCustomFunctionLine(int idx)
764 QString result;
765 const CustomFunctionData & cf = model.customFn[idx];
766 if (cf.swtch.type == SWITCH_TYPE_NONE)
767 return result;
769 result += cf.swtch.toString(getCurrentBoard(), &generalSettings) + " - ";
770 result += cf.funcToString(&model) + " (";
771 result += cf.paramToString(&model) + ")";
772 if (!cf.repeatToString().isEmpty())
773 result += " " + cf.repeatToString();
774 if (!cf.enabledToString().isEmpty())
775 result += " " + cf.enabledToString();
776 return result;
779 QString ModelPrinter::printCurveName(int idx)
781 return model.curves[idx].nameToString(idx).toHtmlEscaped();
784 QString ModelPrinter::printCurve(int idx)
786 QString result;
787 const CurveData & curve = model.curves[idx];
788 result += (curve.type == CurveData::CURVE_TYPE_CUSTOM) ? tr("Custom") : tr("Standard");
789 result += ", [";
790 if (curve.type == CurveData::CURVE_TYPE_CUSTOM) {
791 for (int j=0; j<curve.count; j++) {
792 if (j != 0)
793 result += ", ";
794 result += QString("(%1, %2)").arg(curve.points[j].x).arg(curve.points[j].y);
797 else {
798 for (int j=0; j<curve.count; j++) {
799 if (j != 0)
800 result += ", ";
801 result += QString("%1").arg(curve.points[j].y);
804 result += "]";
805 return result;
808 CurveImage::CurveImage():
809 size(200),
810 image(size+1, size+1, QImage::Format_RGB32),
811 painter(&image)
813 painter.setBrush(QBrush("#FFFFFF"));
814 painter.setPen(QColor(0, 0, 0));
815 painter.drawRect(0, 0, size, size);
817 painter.setPen(QColor(0, 0, 0));
818 painter.drawLine(0, size/2, size, size/2);
819 painter.drawLine(size/2, 0, size/2, size);
820 for (int i=0; i<21; i++) {
821 painter.drawLine(size/2-5, (size*i)/(20), size/2+5, (size*i)/(20));
822 painter.drawLine((size*i)/(20), size/2-5, (size*i)/(20), size/2+5);
826 void CurveImage::drawCurve(const CurveData & curve, QColor color)
828 painter.setPen(QPen(color, 2, Qt::SolidLine));
829 for (int j=1; j<curve.count; j++) {
830 if (curve.type == CurveData::CURVE_TYPE_CUSTOM)
831 painter.drawLine(size/2+(size*curve.points[j-1].x)/200, size/2-(size*curve.points[j-1].y)/200, size/2+(size*curve.points[j].x)/200, size/2-(size*curve.points[j].y)/200);
832 else
833 painter.drawLine(size*(j-1)/(curve.count-1), size/2-(size*curve.points[j-1].y)/200, size*(j)/(curve.count-1), size/2-(size*curve.points[j].y)/200);
837 QString ModelPrinter::createCurveImage(int idx, QTextDocument * document)
839 CurveImage image;
840 image.drawCurve(model.curves[idx], colors[idx]);
841 QString filename = QString("mydata://curve-%1-%2.png").arg((uint64_t)this).arg(idx);
842 if (document)
843 document->addResource(QTextDocument::ImageResource, QUrl(filename), image.get());
844 // qDebug() << "ModelPrinter::createCurveImage()" << idx << filename;
845 return filename;
848 QString ModelPrinter::printGlobalVarUnit(int idx)
850 return model.gvarData[idx].unitToString().toHtmlEscaped();
853 QString ModelPrinter::printGlobalVarPrec(int idx)
855 return model.gvarData[idx].precToString().toHtmlEscaped();
858 QString ModelPrinter::printGlobalVarMin(int idx)
860 return QString::number(model.gvarData[idx].getMinPrec());
863 QString ModelPrinter::printGlobalVarMax(int idx)
865 return QString::number(model.gvarData[idx].getMaxPrec());
868 QString ModelPrinter::printGlobalVarPopup(int idx)
870 return printBoolean(model.gvarData[idx].popup, BOOLEAN_YN);
873 QString ModelPrinter::printOutputValueGVar(int val)
875 QString result = "";
876 if (abs(val) > 10000) {
877 if (val < 0)
878 result = "-";
879 result.append(RawSource(SOURCE_TYPE_GVAR, abs(val)-10001).toString(&model));
881 else {
882 if (val >= 0)
883 result = "+";
884 result.append(QString::number((qreal)val/10, 'f', 1) + "%");
886 return result;
889 QString ModelPrinter::printOutputOffset(int idx)
891 return printOutputValueGVar(model.limitData[idx].offset);
894 QString ModelPrinter::printOutputMin(int idx)
896 return printOutputValueGVar(model.limitData[idx].min);
899 QString ModelPrinter::printOutputMax(int idx)
901 return printOutputValueGVar(model.limitData[idx].max);
904 QString ModelPrinter::printOutputRevert(int idx)
906 return model.limitData[idx].revertToString();
909 QString ModelPrinter::printOutputPpmCenter(int idx)
911 return QString::number(model.limitData[idx].ppmCenter + 1500);
914 QString ModelPrinter::printOutputCurve(int idx)
916 return CurveReference(CurveReference::CURVE_REF_CUSTOM, model.limitData[idx].curve.value).toString(&model, false);
919 QString ModelPrinter::printOutputSymetrical(int idx)
921 return printBoolean(model.limitData[idx].symetrical, BOOLEAN_YN);
924 QString ModelPrinter::printSettingsOther()
926 QStringList str;
927 str << printLabelValue(tr("Extended Limits"), printBoolean(model.extendedLimits, BOOLEAN_YESNO));
928 if (firmware->getCapability(HasDisplayText))
929 str << printLabelValue(tr("Display Checklist"), printBoolean(model.displayChecklist, BOOLEAN_YESNO));
930 if (firmware->getCapability(GlobalFunctions))
931 str << printLabelValue(tr("Global Functions"), printBoolean(!model.noGlobalFunctions, BOOLEAN_YESNO));
932 return str.join(" ");
935 QString ModelPrinter::printSwitchWarnings()
937 QStringList str;
938 Boards board = firmware->getBoard();
939 uint64_t switchStates = model.switchWarningStates;
940 uint64_t value;
942 for (int idx=0; idx<board.getCapability(Board::Switches); idx++) {
943 Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board.getBoardType(), idx);
944 switchInfo.config = Board::SwitchType(generalSettings.switchConfig[idx]);
945 if (switchInfo.config == Board::SWITCH_NOT_AVAILABLE || switchInfo.config == Board::SWITCH_TOGGLE) {
946 continue;
948 if (!(model.switchWarningEnable & (1 << idx))) {
949 if (IS_HORUS_OR_TARANIS(board.getBoardType())) {
950 value = (switchStates >> (2*idx)) & 0x03;
952 else {
953 value = (idx==0 ? switchStates & 0x3 : switchStates & 0x1);
954 switchStates >>= (idx==0 ? 2 : 1);
956 str += RawSwitch(SWITCH_TYPE_SWITCH, 1+idx*3+value).toString(board.getBoardType(), &generalSettings, &model);
959 return (str.isEmpty() ? tr("None") : str.join(" ")) ;
962 QString ModelPrinter::printPotWarnings()
964 QStringList str;
965 int genAryIdx = 0;
966 Boards board = firmware->getBoard();
967 if (model.potsWarningMode) {
968 for (int i=0; i<board.getCapability(Board::Pots)+board.getCapability(Board::Sliders); i++) {
969 RawSource src(SOURCE_TYPE_STICK, CPN_MAX_STICKS + i);
970 if ((src.isPot(&genAryIdx) && generalSettings.isPotAvailable(genAryIdx)) || (src.isSlider(&genAryIdx) && generalSettings.isSliderAvailable(genAryIdx))) {
971 if (!model.potsWarningEnabled[i])
972 str += src.toString(&model, &generalSettings);
976 str << printLabelValue(tr("Mode"), printPotsWarningMode());
977 return str.join(" ");
980 QString ModelPrinter::printPotsWarningMode()
982 switch (model.potsWarningMode) {
983 case 0:
984 return tr("OFF");
985 case 1:
986 return tr("Manual");
987 case 2:
988 return tr("Auto");
989 default:
990 return tr("????");
994 QString ModelPrinter::printFailsafe(int idx)
996 QStringList strl;
997 ModuleData module = model.moduleData[idx];
998 strl << printLabelValue(tr("Failsafe Mode"), printFailsafeMode(module.failsafeMode));
999 if (module.failsafeMode == FAILSAFE_CUSTOM) {
1000 for (int i=0; i<module.channelsCount; i++) {
1001 //strl << QString("%1(%2)").arg(printChannelName(module.channelsStart + i).trimmed()).arg(printFailsafeValue(module.failsafeChannels[i]));
1002 strl << printLabelValue(printChannelName(module.channelsStart + i).trimmed(), printFailsafeValue(module.failsafeChannels[i]));
1005 return strl.join(" ");
1008 QString ModelPrinter::printFailsafeValue(int val)
1010 switch (val) {
1011 case 2000:
1012 return tr("Hold");
1013 case 2001:
1014 return tr("No Pulse");
1015 default:
1016 return QString("%1%").arg(QString::number(divRoundClosest(val * 1000, 1024) / 10.0));
1020 QString ModelPrinter::printFailsafeMode(unsigned int fsmode)
1022 switch (fsmode) {
1023 case FAILSAFE_NOT_SET:
1024 return tr("Not set");
1025 case FAILSAFE_HOLD:
1026 return tr("Hold");
1027 case FAILSAFE_CUSTOM:
1028 return tr("Custom");
1029 case FAILSAFE_NOPULSES:
1030 return tr("No pulses");
1031 case FAILSAFE_RECEIVER:
1032 return tr("Receiver");
1033 default:
1034 return tr("???");
1038 QString ModelPrinter::printTimerCountdownBeep(unsigned int countdownBeep)
1040 switch (countdownBeep) {
1041 case TimerData::COUNTDOWN_SILENT:
1042 return tr("Silent");
1043 case TimerData::COUNTDOWN_BEEPS:
1044 return tr("Beeps");
1045 case TimerData::COUNTDOWN_VOICE:
1046 return tr("Voice");
1047 case TimerData::COUNTDOWN_HAPTIC:
1048 return tr("Haptic");
1049 default:
1050 return tr("???");
1054 QString ModelPrinter::printTimerPersistent(unsigned int persistent)
1056 switch (persistent) {
1057 case 0:
1058 return tr("OFF");
1059 case 1:
1060 return tr("Flight");
1061 case 2:
1062 return tr("Manual reset");
1063 default:
1064 return tr("???");
1068 QString ModelPrinter::printSettingsTrim()
1070 QStringList str;
1071 str << printLabelValue(tr("Step"), printTrimIncrementMode());
1072 if (IS_ARM(firmware->getBoard()))
1073 str << printLabelValue(tr("Display"), printTrimsDisplayMode());
1074 str << printLabelValue(tr("Extended"), printBoolean(model.extendedTrims, BOOLEAN_YESNO));
1075 return str.join(" ");
1078 QString ModelPrinter::printThrottleSource(int idx)
1080 Boards board = firmware->getBoard();
1081 int chnstart = board.getCapability(Board::Pots)+board.getCapability(Board::Sliders);
1082 if (idx == 0)
1083 return "THR";
1084 else if (idx < (chnstart+1))
1085 return firmware->getAnalogInputName(idx+board.getCapability(Board::Sticks)-1);
1086 else
1087 return RawSource(SOURCE_TYPE_CH, idx-chnstart-1).toString(&model, &generalSettings);
1090 QString ModelPrinter::printTrimsDisplayMode()
1092 switch (model.trimsDisplay) {
1093 case 0:
1094 return tr("Never");
1095 case 1:
1096 return tr("On Change");
1097 case 2:
1098 return tr("Always");
1099 default:
1100 return tr("???");
1104 QString ModelPrinter::printModuleType(int idx)
1106 if (idx < 0)
1107 return tr("Trainer Port");
1108 else if (firmware->getCapability(NumModules) > 1)
1109 if (IS_HORUS_OR_TARANIS(firmware->getBoard()))
1110 if (idx == 0)
1111 return tr("Internal Radio System");
1112 else
1113 return tr("External Radio Module");
1114 else if (idx == 0)
1115 return tr("Radio System");
1116 else
1117 return tr("Extra Radio System");
1118 else
1119 return tr("Radio System");
1122 QString ModelPrinter::printPxxPower(int power)
1124 static const char *strings[] = {
1125 "10mW", "100mW", "500mW", "3W"
1127 return CHECK_IN_ARRAY(strings, power);
1130 QString ModelPrinter::printThrottle()
1132 QStringList result;
1133 result << printLabelValue(tr("Source"), printThrottleSource(model.thrTraceSrc));
1134 result << printLabelValue(tr("Trim idle only"), printBoolean(model.thrTrim, BOOLEAN_YESNO));
1135 result << printLabelValue(tr("Warning"), printBoolean(!model.disableThrottleWarning, BOOLEAN_YESNO));
1136 result << printLabelValue(tr("Reversed"), printBoolean(model.throttleReversed, BOOLEAN_YESNO));
1137 return result.join(" ");
1140 QString ModelPrinter::printPPMFrameLength(int ppmFL)
1142 double result = (((double)ppmFL * 5) + 225) / 10;
1143 return QString::number(result);
1146 QString ModelPrinter::printTimerName(int idx)
1148 QString result;
1149 result = tr("Tmr") + QString("%1").arg(idx+1);
1150 if (firmware->getCapability(TimersName) && model.timers[idx].name[0])
1151 result.append(":" + QString(model.timers[idx].name));
1153 return result;
1156 QString ModelPrinter::printTimerTimeValue(unsigned int val)
1158 return printTimeValue(val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS);
1161 QString ModelPrinter::printTimerMinuteBeep(bool mb)
1163 return printBoolean(mb, BOOLEAN_YESNO);
1166 QString ModelPrinter::printTelemetryProtocol(unsigned int val)
1168 switch (val) {
1169 case 0:
1170 return tr("FrSky S.PORT");
1171 case 1:
1172 return tr("FrSky D");
1173 case 2:
1174 return tr("FrSky D (cable)");
1175 default:
1176 return tr("???");
1180 QString ModelPrinter::printRssiAlarmsDisabled(bool mb)
1182 return printBoolean(!mb, BOOLEAN_ENABLEDISABLE);
1185 QString ModelPrinter::printTelemetrySource(int val)
1187 QStringList strings = QStringList() << tr("None");
1189 for (int i=1; i<=CPN_MAX_SENSORS; ++i) {
1190 strings << QString("%1").arg(model.sensorData[i-1].label);
1193 return QString("%1%2").arg((val < 0 ? "-" : "")).arg(strings.value(abs(val)));
1196 QString ModelPrinter::printVarioSource(unsigned int val)
1198 switch (val) {
1199 case TELEMETRY_VARIO_SOURCE_ALTI:
1200 return tr("Alti");
1201 case TELEMETRY_VARIO_SOURCE_ALTI_PLUS:
1202 return tr("Alti+");
1203 case TELEMETRY_VARIO_SOURCE_VSPEED:
1204 return tr("VSpeed");
1205 case TELEMETRY_VARIO_SOURCE_A1:
1206 return tr("A1");
1207 case TELEMETRY_VARIO_SOURCE_A2:
1208 return tr("A2");
1209 default:
1210 return tr("???");
1214 QString ModelPrinter::printVarioCenterSilent(bool mb)
1216 return printBoolean(mb, BOOLEAN_YESNO);
1219 QString ModelPrinter::printVoltsSource(unsigned int val)
1221 switch (val) {
1222 case TELEMETRY_VOLTS_SOURCE_A1:
1223 return tr("A1");
1224 case TELEMETRY_VOLTS_SOURCE_A2:
1225 return tr("A2");
1226 case TELEMETRY_VOLTS_SOURCE_A3:
1227 return tr("A3");
1228 case TELEMETRY_VOLTS_SOURCE_A4:
1229 return tr("A4");
1230 case TELEMETRY_VOLTS_SOURCE_FAS:
1231 return tr("FAS");
1232 case TELEMETRY_VOLTS_SOURCE_CELLS:
1233 return tr("Cells");
1234 default:
1235 return tr("???");
1239 QString ModelPrinter::printCurrentSource(unsigned int val)
1241 switch (val) {
1242 case TELEMETRY_CURRENT_SOURCE_NONE:
1243 return tr("None");
1244 case TELEMETRY_CURRENT_SOURCE_A1:
1245 return tr("A1");
1246 case TELEMETRY_CURRENT_SOURCE_A2:
1247 return tr("A2");
1248 case TELEMETRY_CURRENT_SOURCE_A3:
1249 return tr("A3");
1250 case TELEMETRY_CURRENT_SOURCE_A4:
1251 return tr("A4");
1252 case TELEMETRY_CURRENT_SOURCE_FAS:
1253 return tr("FAS");
1254 default:
1255 return tr("???");
1259 QString ModelPrinter::printMahPersistent(bool mb)
1261 return printBoolean(mb, BOOLEAN_YESNO);
1264 QString ModelPrinter::printIgnoreSensorIds(bool mb)
1266 return printBoolean(mb, BOOLEAN_ENABLEDISABLE);
1269 QString ModelPrinter::printSensorType(unsigned int val)
1271 switch (val) {
1272 case SensorData::TELEM_TYPE_CUSTOM:
1273 return tr("Custom");
1274 case SensorData::TELEM_TYPE_CALCULATED:
1275 return tr("Calculated");
1276 default:
1277 return tr("???");
1281 QString ModelPrinter::printSensorFormula(unsigned int val)
1283 switch (val) {
1284 case SensorData::TELEM_FORMULA_ADD:
1285 return tr("Add");
1286 case SensorData::TELEM_FORMULA_AVERAGE:
1287 return tr("Average");
1288 case SensorData::TELEM_FORMULA_MIN:
1289 return tr("Min");
1290 case SensorData::TELEM_FORMULA_MAX:
1291 return tr("Max");
1292 case SensorData::TELEM_FORMULA_MULTIPLY:
1293 return tr("Multiply");
1294 case SensorData::TELEM_FORMULA_TOTALIZE:
1295 return tr("Totalise");
1296 case SensorData::TELEM_FORMULA_CELL:
1297 return tr("Cell");
1298 case SensorData::TELEM_FORMULA_CONSUMPTION:
1299 return tr("Consumption");
1300 case SensorData::TELEM_FORMULA_DIST:
1301 return tr("Distance");
1302 default:
1303 return tr("???");
1307 QString ModelPrinter::printSensorCells(unsigned int val)
1309 QStringList strings;
1311 strings << tr("Lowest");
1312 for (int i=1; i<=6; i++)
1313 strings << tr("Cell %1").arg(i);
1314 strings << tr("Highest") << tr("Delta");
1316 return strings.value(val);
1319 QString ModelPrinter::printSensorTypeCond(unsigned int idx)
1321 if (!model.sensorData[idx].isAvailable())
1322 return "";
1323 else
1324 return printSensorType(model.sensorData[idx].type);
1327 QString ModelPrinter::printSensorDetails(unsigned int idx)
1329 QString str = "";
1330 SensorData sensor = model.sensorData[idx];
1332 if (!sensor.isAvailable())
1333 return str;
1335 bool isConfigurable = false;
1336 bool gpsFieldsPrinted = false;
1337 bool cellsFieldsPrinted = false;
1338 bool consFieldsPrinted = false;
1339 bool ratioFieldsPrinted = false;
1340 bool totalizeFieldsPrinted = false;
1341 bool sources12FieldsPrinted = false;
1342 bool sources34FieldsPrinted = false;
1344 str.append(doTableCell(printSensorTypeCond(idx)));
1346 QString tc = "";
1347 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
1348 isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL);
1349 gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST);
1350 cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL);
1351 consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION);
1352 sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY);
1353 sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY);
1354 totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE);
1356 tc.append(printLabelValue(tr("Formula"), printSensorFormula(sensor.formula)));
1358 else {
1359 isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL;
1360 ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL);
1362 tc.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper()));
1363 tc.append(printLabelValue(tr("Instance"), QString::number(sensor.instance)));
1365 if (cellsFieldsPrinted) {
1366 tc.append(printLabelValue(tr("Sensor"), QString("%1 > %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index))));
1368 if (sources12FieldsPrinted) {
1369 QStringList srcs;
1370 for (int i=0;i<4;i++) {
1371 if (i < 2 || sources34FieldsPrinted) {
1372 srcs << printTelemetrySource(sensor.sources[i]);
1375 tc.append(printLabelValues(tr("Sources"), srcs));
1377 if (consFieldsPrinted || totalizeFieldsPrinted)
1378 tc.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps)));
1379 if (gpsFieldsPrinted) {
1380 tc.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps)));
1381 tc.append(printLabelValue(tr("Alt."), printTelemetrySource(sensor.alt)));
1383 if (ratioFieldsPrinted && sensor.unit == SensorData::UNIT_RPMS) {
1384 tc.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio)));
1385 tc.append(printLabelValue(tr("Multi."), QString::number(sensor.offset)));
1387 str.append(doTableCell(tc));
1389 tc = sensor.unitString();
1390 tc = tc.trimmed() == "" ? "-" : tc;
1391 str.append(doTableCell(tc));
1393 if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT)
1394 tc = QString::number(sensor.prec);
1395 else
1396 tc = "";
1397 str.append(doTableCell(tc));
1399 if (!ratioFieldsPrinted) {
1400 str.append(doTableCell(""));
1401 str.append(doTableCell(""));
1403 else if (sensor.unit != SensorData::UNIT_RPMS) {
1404 int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec);
1405 str.append(doTableCell(QString::number((float)sensor.ratio / prec)));
1406 str.append(doTableCell(QString::number((float)sensor.offset / prec)));
1409 if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable)
1410 str.append(doTableCell(printBoolean(sensor.autoOffset, BOOLEAN_YN)));
1411 else
1412 str.append(doTableCell(""));
1414 if (isConfigurable)
1415 str.append(doTableCell(printBoolean(sensor.filter, BOOLEAN_YN)));
1416 else
1417 str.append(doTableCell(""));
1419 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED)
1420 str.append(doTableCell(printBoolean(sensor.persistent, BOOLEAN_YN)));
1421 else
1422 str.append(doTableCell(""));
1424 str.append(doTableCell(printBoolean(sensor.onlyPositive, BOOLEAN_YN)));
1425 str.append(doTableCell(printBoolean(sensor.logs, BOOLEAN_YN), false));
1426 return str;
1429 QString ModelPrinter::printSensorParams(unsigned int idx)
1431 QString str = "";
1432 SensorData sensor = model.sensorData[idx];
1434 if (!sensor.isAvailable())
1435 return str;
1437 bool isConfigurable = false;
1438 bool gpsFieldsPrinted = false;
1439 bool cellsFieldsPrinted = false;
1440 bool consFieldsPrinted = false;
1441 bool ratioFieldsPrinted = false;
1442 bool totalizeFieldsPrinted = false;
1443 bool sources12FieldsPrinted = false;
1444 bool sources34FieldsPrinted = false;
1446 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
1447 isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL);
1448 gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST);
1449 cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL);
1450 consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION);
1451 sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY);
1452 sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY);
1453 totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE);
1455 str.append(printLabelValue(tr("F"), printSensorFormula(sensor.formula)));
1457 else {
1458 isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL;
1459 ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL);
1461 str.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper()));
1462 str.append(printLabelValue(tr("Inst"), QString::number(sensor.instance)));
1464 if (cellsFieldsPrinted) {
1465 str.append(printLabelValue(tr("Sensor"), QString("%1 %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index))));
1467 if (sources12FieldsPrinted) {
1468 QStringList srcs;
1469 for (int i=0;i<4;i++) {
1470 if (i < 2 || sources34FieldsPrinted) {
1471 srcs << printTelemetrySource(sensor.sources[i]);
1474 str.append(printLabelValues(tr("Sources"), srcs));
1476 if (consFieldsPrinted || totalizeFieldsPrinted)
1477 str.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps)));
1478 if (gpsFieldsPrinted) {
1479 str.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps)));
1480 str.append(printLabelValue(tr("Alt"), printTelemetrySource(sensor.alt)));
1482 QString u = sensor.unitString();
1483 u = u.trimmed() == "" ? "-" : u;
1484 str.append(printLabelValue(tr("Unit"), u));
1485 if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT)
1486 str.append(printLabelValue(tr("Prec"), QString::number(sensor.prec)));
1487 if (ratioFieldsPrinted) {
1488 if (sensor.unit != SensorData::UNIT_RPMS) {
1489 int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec);
1490 str.append(printLabelValue(tr("Ratio"), QString::number((float)sensor.ratio / prec)));
1491 str.append(printLabelValue(tr("Offset"), QString::number((float)sensor.offset / prec)));
1493 else if (sensor.unit == SensorData::UNIT_RPMS) {
1494 str.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio)));
1495 str.append(printLabelValue(tr("Multi"), QString::number(sensor.offset)));
1498 if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable)
1499 str.append(printLabelValue(tr("A/Offset"), printBoolean(sensor.autoOffset, BOOLEAN_YN)));
1500 if (isConfigurable)
1501 str.append(printLabelValue(tr("Filter"), printBoolean(sensor.filter, BOOLEAN_YN)));
1502 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED)
1503 str.append(printLabelValue(tr("Persist"), printBoolean(sensor.persistent, BOOLEAN_YN)));
1504 str.append(printLabelValue(tr("Positive"), printBoolean(sensor.onlyPositive, BOOLEAN_YN)));
1505 str.append(printLabelValue(tr("Log"), printBoolean(sensor.logs, BOOLEAN_YN), false));
1506 return str;
1509 QString ModelPrinter::printTelemetryScreenType(unsigned int val)
1511 switch (val) {
1512 case TelemetryScreenEnum::TELEMETRY_SCREEN_NONE:
1513 return tr("None");
1514 case TelemetryScreenEnum::TELEMETRY_SCREEN_NUMBERS:
1515 return tr("Numbers");
1516 case TelemetryScreenEnum::TELEMETRY_SCREEN_BARS:
1517 return tr("Bars");
1518 case TelemetryScreenEnum::TELEMETRY_SCREEN_SCRIPT:
1519 return tr("Script");
1520 default:
1521 return tr("???");
1525 QString ModelPrinter::printTelemetryScreen(unsigned int idx, unsigned int line, unsigned int width)
1527 QStringList strl;
1528 QStringList hd;
1529 FrSkyScreenData screen = model.frsky.screens[idx];
1530 hd << ""; // blank 1st column
1531 strl << "";
1532 if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_NUMBERS) {
1533 if (line == 0) {
1534 for (int c=0; c<firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
1535 hd << tr("Source");
1538 for (int c=0; c<firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
1539 RawSource source = screen.body.lines[line].source[c];
1540 strl << source.toString(&model, &generalSettings);
1543 else if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_BARS) {
1544 if (line == 0) {
1545 hd << tr("Source") << tr("Min") << tr("Max");
1547 RawSource source = screen.body.bars[line].source;
1548 RawSourceRange range = source.getRange(&model, generalSettings);
1549 strl << source.toString(&model, &generalSettings);
1550 QString unit;
1551 QString minstr;
1552 QString maxstr;
1553 if (source.isTimeBased()){
1554 minstr = printTimeValue((float)screen.body.bars[line].barMin, MASK_TIMEVALUE_HRSMINS);
1555 maxstr = printTimeValue((float)screen.body.bars[line].barMax, MASK_TIMEVALUE_HRSMINS);
1557 else {
1558 minstr = QString::number(range.getValue(screen.body.bars[line].barMin));
1559 maxstr = QString::number(range.getValue(screen.body.bars[line].barMax));
1560 unit = range.unit;
1562 strl << QString("%1%2").arg(minstr).arg(unit);
1563 strl << QString("%1%2").arg(maxstr).arg(unit);
1565 else if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_SCRIPT && line == 0) {
1566 hd << tr("Filename");
1567 strl << QString("%1.lua").arg(screen.body.script.filename);
1569 return (hd.count() > 1 ? doTableRow(hd, width / hd.count(), "left", "", true) : "" ) + doTableRow(strl, width / strl.count());