Make TX volatge for simu more flexible (#7124)
[opentx.git] / companion / src / modelprinter.cpp
blobac9f6f0667366cb158ed5792cf19150420c3b821
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"
27 #include "appdata.h"
29 #include <QApplication>
30 #include <QPainter>
31 #include <QFile>
32 #include <QUrl>
33 #include <QTextStream>
35 extern AppData g;
37 QString changeColor(const QString & input, const QString & to, const QString & from)
39 QString result = input;
40 return result.replace("color="+from, "color="+to);
43 ModelPrinter::ModelPrinter(Firmware * firmware, const GeneralSettings & generalSettings, const ModelData & model):
44 firmware(firmware),
45 generalSettings(generalSettings),
46 model(model)
50 ModelPrinter::~ModelPrinter()
54 QString formatTitle(const QString & name)
56 return QString("<b>" + name + "</b>&nbsp;");
59 void debugHtml(const QString & html)
61 QFile file("foo.html");
62 file.open(QIODevice::Truncate | QIODevice::WriteOnly);
63 file.write(html.toUtf8());
64 file.close();
67 QString addFont(const QString & input, const QString & color, const QString & size, const QString & face)
69 QString colorStr;
70 if (!color.isEmpty()) {
71 colorStr = "color=" + color;
73 QString sizeStr;
74 if (!size.isEmpty()) {
75 sizeStr = "size=" + size;
77 QString faceStr;
78 if (!face.isEmpty()) {
79 faceStr = "face='" + face + "'";
81 return "<font " + sizeStr + " " + faceStr + " " + colorStr + ">" + input + "</font>";
84 QString ModelPrinter::printLabelValue(const QString & lbl, const QString & val, const bool sep) {
85 return QString("<b>%1:</b> %2%3 ").arg(lbl, val, (sep ? ";" : ""));
88 QString ModelPrinter::printLabelValues(const QString & lbl, const QStringList & vals, const bool sep) {
89 QString str;
90 if (vals.count() > 1)
91 str.append("(");
92 for (int i=0;i<vals.count();i++) {
93 str.append(vals.at(i));
94 if (i<(vals.count()-1))
95 str.append(", ");
97 if (vals.count() > 1)
98 str.append(")");
100 return printLabelValue(lbl, str, sep);
103 #define MASK_TIMEVALUE_HRSMINS 1
104 #define MASK_TIMEVALUE_ZEROHRS 2
105 #define MASK_TIMEVALUE_PADSIGN 3
107 QString ModelPrinter::printTimeValue(const int value, const unsigned int mask)
109 QString result;
110 int sign = 1;
111 int val = value;
112 if (val < 0) {
113 val = -val;
114 sign = -1;
116 result = (sign < 0 ? QString("-") : ((mask & MASK_TIMEVALUE_PADSIGN) ? QString(" ") : QString("")));
117 if (mask & MASK_TIMEVALUE_HRSMINS) {
118 int hours = val / 3600;
119 if (hours > 0 || (mask & MASK_TIMEVALUE_ZEROHRS)) {
120 val -= hours * 3600;
121 result.append(QString("%1:").arg(hours, 2, 10, QLatin1Char('0')));
124 int minutes = val / 60;
125 int seconds = val % 60;
126 result.append(QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')));
127 return result;
130 #define BOOLEAN_ENABLEDISABLE 1
131 #define BOOLEAN_TRUEFALSE 2
132 #define BOOLEAN_YESNO 3
133 #define BOOLEAN_YN 4
134 #define BOOLEAN_ONOFF 5
136 QString ModelPrinter::printBoolean(const bool val, const int typ)
138 switch (typ) {
139 case BOOLEAN_ENABLEDISABLE:
140 return (val ? tr("Enable") : tr("Disable"));
141 case BOOLEAN_TRUEFALSE:
142 return (val ? tr("True") : tr("False"));
143 case BOOLEAN_YESNO:
144 return (val ? tr("Yes") : tr("No"));
145 case BOOLEAN_YN:
146 return (val ? tr("Y") : tr("N"));
147 case BOOLEAN_ONOFF:
148 return (val ? tr("ON") : tr("OFF"));
149 default:
150 return CPN_STR_UNKNOWN_ITEM;
154 QString ModelPrinter::printEEpromSize()
156 return QString("%1 ").arg(getCurrentEEpromInterface()->getSize(model)) + tr("bytes");
159 QString ModelPrinter::printChannelName(int idx)
161 QString str = RawSource(SOURCE_TYPE_CH, idx).toString(&model, &generalSettings);
162 if (firmware->getCapability(ChannelsName)) {
163 str = str.leftJustified(firmware->getCapability(ChannelsName) + 5, ' ', false);
165 str.append(' ');
166 return str.toHtmlEscaped();
169 QString ModelPrinter::printTrimIncrementMode()
171 switch (model.trimInc) {
172 case -2:
173 return tr("Exponential");
174 case -1:
175 return tr("Extra Fine");
176 case 0:
177 return tr("Fine");
178 case 1:
179 return tr("Medium");
180 case 2:
181 return tr("Coarse");
182 default:
183 return tr("Unknown");
187 QString ModelPrinter::printModule(int idx)
189 QStringList str;
190 QString result;
191 ModuleData module = model.moduleData[(idx<0 ? CPN_MAX_MODULES : idx)];
192 if (idx < 0) {
193 str << printLabelValue(tr("Mode"), printTrainerMode());
194 if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
195 if (model.trainerMode == TRAINER_SLAVE_JACK) {
196 str << printLabelValue(tr("Channels"), QString("%1-%2").arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount));
197 str << printLabelValue(tr("Frame length"), QString("%1ms").arg(printPPMFrameLength(module.ppm.frameLength)));
198 str << printLabelValue(tr("PPM delay"), QString("%1us").arg(module.ppm.delay));
199 str << printLabelValue(tr("Polarity"), module.polarityToString());
202 result = str.join(" ");
204 else {
205 str << printLabelValue(tr("Protocol"), ModuleData::protocolToString(module.protocol));
206 if (module.protocol) {
207 str << printLabelValue(tr("Channels"), QString("%1-%2").arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount));
208 if (module.protocol == PULSES_PPM || module.protocol == PULSES_SBUS) {
209 str << printLabelValue(tr("Frame length"), QString("%1ms").arg(printPPMFrameLength(module.ppm.frameLength)));
210 str << printLabelValue(tr("Polarity"), module.polarityToString());
211 if (module.protocol == PULSES_PPM)
212 str << printLabelValue(tr("Delay"), QString("%1us").arg(module.ppm.delay));
214 else {
215 if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_SBUS)) {
216 str << printLabelValue(tr("Receiver"), QString::number(module.modelId));
218 if (module.protocol == PULSES_MULTIMODULE) {
219 str << printLabelValue(tr("Radio protocol"), module.rfProtocolToString());
220 str << printLabelValue(tr("Subtype"), module.subTypeToString());
221 str << printLabelValue(tr("Option value"), QString::number(module.multi.optionValue));
223 if (module.protocol == PULSES_PXX_R9M) {
224 str << printLabelValue(tr("Sub Type"), module.subTypeToString());
225 str << printLabelValue(tr("RF Output Power"), module.powerValueToString(firmware));
229 result = str.join(" ");
230 if (((PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_XJT_X16 || (PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_R9M)
231 && firmware->getCapability(HasFailsafe))
232 result.append("<br/>" + printFailsafe(idx));
234 return result;
237 QString ModelPrinter::printTrainerMode()
239 QString result;
240 switch (model.trainerMode) {
241 case TRAINER_MASTER_JACK:
242 result = tr("Master/Jack");
243 break;
244 case TRAINER_SLAVE_JACK:
245 result = tr("Slave/Jack");
246 break;
247 case TRAINER_MASTER_SBUS_MODULE:
248 result = tr("Master/SBUS Module");
249 break;
250 case TRAINER_MASTER_CPPM_MODULE:
251 result = tr("Master/CPPM Module");
252 break;
253 case TRAINER_MASTER_SBUS_BATT_COMPARTMENT:
254 result = tr("Master/SBUS in battery compartment");
255 break;
256 default:
257 result = CPN_STR_UNKNOWN_ITEM;
259 return result;
262 QString ModelPrinter::printHeliSwashType ()
264 switch (model.swashRingData.type) {
265 case HELI_SWASH_TYPE_90:
266 return tr("90");
267 case HELI_SWASH_TYPE_120:
268 return tr("120");
269 case HELI_SWASH_TYPE_120X:
270 return tr("120X");
271 case HELI_SWASH_TYPE_140:
272 return tr("140");
273 case HELI_SWASH_TYPE_NONE:
274 return tr("Off");
275 default:
276 return CPN_STR_UNKNOWN_ITEM;
280 QString ModelPrinter::printCenterBeep()
282 QStringList strl;
283 Board::Type board = firmware->getBoard();
284 int analogs = CPN_MAX_STICKS + getBoardCapability(board, Board::Pots) + getBoardCapability(board, Board::Sliders);
286 for (int i=0; i < analogs + firmware->getCapability(RotaryEncoders); i++) {
287 RawSource src((i < analogs) ? SOURCE_TYPE_STICK : SOURCE_TYPE_ROTARY_ENCODER, (i < analogs) ? i : analogs - i);
288 if (model.beepANACenter & (0x01 << i)) {
289 strl << src.toString(&model, &generalSettings);
293 return (strl.isEmpty() ? tr("None") : strl.join(" "));
296 QString ModelPrinter::printTimer(int idx)
298 return printTimer(model.timers[idx]);
301 QString ModelPrinter::printTimer(const TimerData & timer)
303 QStringList result;
304 if (firmware->getCapability(TimersName) && timer.name[0])
305 result += tr("Name") + QString("(%1)").arg(timer.name);
306 result += printTimeValue(timer.val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS);
307 result += timer.mode.toString();
308 if (timer.countdownBeep)
309 result += tr("Countdown") + QString("(%1)").arg(printTimerCountdownBeep(timer.countdownBeep));
310 if (timer.minuteBeep)
311 result += tr("Minute call");
312 if (timer.persistent)
313 result += tr("Persistent") + QString("(%1)").arg(printTimerPersistent(timer.persistent));
314 return result.join(", ");
317 QString ModelPrinter::printTrim(int flightModeIndex, int stickIndex)
319 const FlightModeData & fm = model.flightModeData[flightModeIndex];
321 if (fm.trimMode[stickIndex] == -1) {
322 return tr("OFF");
324 else {
325 if (fm.trimRef[stickIndex] == flightModeIndex) {
326 return QString("%1").arg(fm.trim[stickIndex]);
328 else {
329 if (fm.trimMode[stickIndex] == 0) {
330 return tr("FM%1").arg(fm.trimRef[stickIndex]);
332 else {
333 if (fm.trim[stickIndex] < 0)
334 return tr("FM%1%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]);
335 else
336 return tr("FM%1+%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]);
342 QString ModelPrinter::printGlobalVar(int flightModeIndex, int gvarIndex)
344 const FlightModeData & fm = model.flightModeData[flightModeIndex];
346 if (fm.gvars[gvarIndex] <= 1024) {
347 return QString("%1").arg(fm.gvars[gvarIndex] * model.gvarData[gvarIndex].multiplierGet());
349 else {
350 int num = fm.gvars[gvarIndex] - 1025;
351 if (num >= flightModeIndex) num++;
352 return tr("FM%1").arg(num);
356 QString ModelPrinter::printRotaryEncoder(int flightModeIndex, int reIndex)
358 const FlightModeData & fm = model.flightModeData[flightModeIndex];
360 if (fm.rotaryEncoders[reIndex] <= 1024) {
361 return QString("%1").arg(fm.rotaryEncoders[reIndex]);
363 else {
364 int num = fm.rotaryEncoders[reIndex] - 1025;
365 if (num >= flightModeIndex) num++;
366 return tr("FM%1").arg(num);
370 QString ModelPrinter::printInputName(int idx)
372 RawSourceType srcType = (firmware->getCapability(VirtualInputs) ? SOURCE_TYPE_VIRTUAL_INPUT : SOURCE_TYPE_STICK);
373 return RawSource(srcType, idx).toString(&model, &generalSettings).toHtmlEscaped();
376 QString ModelPrinter::printInputLine(int idx)
378 return printInputLine(model.expoData[idx]);
381 QString ModelPrinter::printInputLine(const ExpoData & input)
383 QString str = "&nbsp;";
385 switch (input.mode) {
386 case (1): str += "&lt;-&nbsp;"; break;
387 case (2): str += "-&gt;&nbsp;"; break;
388 default: str += "&nbsp;&nbsp;&nbsp;"; break;
391 if (firmware->getCapability(VirtualInputs)) {
392 str += input.srcRaw.toString(&model, &generalSettings).toHtmlEscaped();
395 str += " " + tr("Weight").toHtmlEscaped() + QString("(%1)").arg(Helpers::getAdjustmentString(input.weight, &model, true).toHtmlEscaped());
396 if (input.curve.value)
397 str += " " + input.curve.toString(&model).toHtmlEscaped();
399 QString flightModesStr = printFlightModes(input.flightModes);
400 if (!flightModesStr.isEmpty())
401 str += " " + flightModesStr.toHtmlEscaped();
403 if (input.swtch.type != SWITCH_TYPE_NONE)
404 str += " " + tr("Switch").toHtmlEscaped() + QString("(%1)").arg(input.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped();
407 if (firmware->getCapability(VirtualInputs)) {
408 if (input.carryTrim>0)
409 str += " " + tr("NoTrim").toHtmlEscaped();
410 else if (input.carryTrim<0)
411 str += " " + RawSource(SOURCE_TYPE_TRIM, (-(input.carryTrim)-1)).toString(&model, &generalSettings).toHtmlEscaped();
414 if (input.offset)
415 str += " " + tr("Offset(%1)").arg(Helpers::getAdjustmentString(input.offset, &model)).toHtmlEscaped();
417 if (firmware->getCapability(HasExpoNames) && input.name[0])
418 str += QString(" [%1]").arg(input.name).toHtmlEscaped();
420 return str;
423 QString ModelPrinter::printMixerLine(const MixData & mix, bool showMultiplex, int highlightedSource)
425 QString str = "&nbsp;";
427 if (showMultiplex) {
428 switch(mix.mltpx) {
429 case (1): str += "*="; break;
430 case (2): str += ":="; break;
431 default: str += "+="; break;
434 else {
435 str += "&nbsp;&nbsp;";
437 // highlight source if needed
438 QString source = mix.srcRaw.toString(&model, &generalSettings).toHtmlEscaped();
439 if ( (mix.srcRaw.type == SOURCE_TYPE_CH) && (mix.srcRaw.index+1 == (int)highlightedSource) ) {
440 source = "<b>" + source + "</b>";
442 str += "&nbsp;" + source;
444 if (mix.mltpx == MLTPX_MUL && !showMultiplex)
445 str += " " + tr("MULT!").toHtmlEscaped();
446 else
447 str += " " + tr("Weight") + QString("(%1)").arg(Helpers::getAdjustmentString(mix.weight, &model, true)).toHtmlEscaped();
449 QString flightModesStr = printFlightModes(mix.flightModes);
450 if (!flightModesStr.isEmpty())
451 str += " " + flightModesStr.toHtmlEscaped();
453 if (mix.swtch.type != SWITCH_TYPE_NONE)
454 str += " " + tr("Switch") + QString("(%1)").arg(mix.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped();
456 if (mix.carryTrim > 0)
457 str += " " + tr("NoTrim").toHtmlEscaped();
458 else if (mix.carryTrim < 0)
459 str += " " + RawSource(SOURCE_TYPE_TRIM, (-(mix.carryTrim)-1)).toString(&model, &generalSettings);
461 if (firmware->getCapability(HasNoExpo) && mix.noExpo)
462 str += " " + tr("No DR/Expo").toHtmlEscaped();
463 if (mix.sOffset)
464 str += " " + tr("Offset") + QString("(%1)").arg(Helpers::getAdjustmentString(mix.sOffset, &model)).toHtmlEscaped();
465 if (mix.curve.value)
466 str += " " + mix.curve.toString(&model).toHtmlEscaped();
467 int scale = firmware->getCapability(SlowScale);
468 if (scale == 0)
469 scale = 1;
470 if (mix.delayDown || mix.delayUp)
471 str += " " + tr("Delay") + QString("(u%1:d%2)").arg((double)mix.delayUp/scale).arg((double)mix.delayDown/scale).toHtmlEscaped();
472 if (mix.speedDown || mix.speedUp)
473 str += " " + tr("Slow") + QString("(u%1:d%2)").arg((double)mix.speedUp/scale).arg((double)mix.speedDown/scale).toHtmlEscaped();
474 if (mix.mixWarn)
475 str += " " + tr("Warn") + QString("(%1)").arg(mix.mixWarn).toHtmlEscaped();
476 if (firmware->getCapability(HasMixerNames) && mix.name[0])
477 str += QString(" [%1]").arg(mix.name).toHtmlEscaped();
478 return str;
481 QString ModelPrinter::printFlightModeSwitch(const RawSwitch & swtch)
483 return swtch.toString(getCurrentBoard(), &generalSettings);
486 QString ModelPrinter::printFlightModeName(int index)
488 return model.flightModeData[index].nameToString(index);
491 QString ModelPrinter::printFlightModes(unsigned int flightModes)
493 int numFlightModes = firmware->getCapability(FlightModes);
494 if (numFlightModes && flightModes) {
495 if (flightModes == (unsigned int)(1<<numFlightModes) - 1) {
496 return tr("Disabled in all flight modes");
498 else {
499 QStringList list;
500 for (int i=0; i<numFlightModes; i++) {
501 if (!(flightModes & (1<<i))) {
502 list << printFlightModeName(i);
505 return (list.size() > 1 ? tr("Flight modes") : tr("Flight mode")) + QString("(%1)").arg(list.join(", "));
508 else
509 return "";
512 QString ModelPrinter::printInputFlightModes(unsigned int flightModes)
514 int numFlightModes = firmware->getCapability(FlightModes);
515 if (numFlightModes && flightModes) {
516 if (flightModes == (unsigned int)(1<<numFlightModes) - 1) {
517 return tr("None");
519 else {
520 QStringList list;
521 for (int i=0; i<numFlightModes; i++) {
522 if (!(flightModes & (1<<i))) {
523 list << printFlightModeName(i);
526 return QString("%1").arg(list.join(" "));
529 else
530 return tr("All");
533 QString ModelPrinter::printLogicalSwitchLine(int idx)
535 QString result = "";
536 const LogicalSwitchData & ls = model.logicalSw[idx];
537 const QString sw1Name = RawSwitch(ls.val1).toString(getCurrentBoard(), &generalSettings);
538 const QString sw2Name = RawSwitch(ls.val2).toString(getCurrentBoard(), &generalSettings);
540 if (ls.isEmpty())
541 return result;
543 if (ls.andsw!=0) {
544 result +="( ";
546 switch (ls.getFunctionFamily()) {
547 case LS_FAMILY_EDGE:
548 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)));
549 break;
550 case LS_FAMILY_STICKY:
551 result += tr("Sticky") + QString("(%1, %2)").arg(sw1Name).arg(sw2Name);
552 break;
553 case LS_FAMILY_TIMER:
554 result += tr("Timer") + QString("(%1, %2)").arg(ValToTim(ls.val1)).arg(ValToTim(ls.val2));
555 break;
556 case LS_FAMILY_VOFS: {
557 RawSource source = RawSource(ls.val1);
558 RawSourceRange range = source.getRange(&model, generalSettings);
559 QString res;
560 if (ls.val1)
561 res += source.toString(&model, &generalSettings);
562 else
563 res += "0";
564 res.remove(" ");
565 if (ls.func == LS_FN_APOS || ls.func == LS_FN_ANEG)
566 res = "|" + res + "|";
567 else if (ls.func == LS_FN_DAPOS)
568 res = "|d(" + res + ")|";
569 else if (ls.func == LS_FN_DPOS)
570 result = "d(" + res + ")";
571 result += res;
572 if (ls.func == LS_FN_VEQUAL)
573 result += " = ";
574 else if (ls.func == LS_FN_APOS || ls.func == LS_FN_VPOS || ls.func == LS_FN_DPOS || ls.func == LS_FN_DAPOS)
575 result += " &gt; ";
576 else if (ls.func == LS_FN_ANEG || ls.func == LS_FN_VNEG)
577 result += " &lt; ";
578 else if (ls.func == LS_FN_VALMOSTEQUAL)
579 result += " ~ ";
580 else
581 result += tr(" missing");
582 result += QString::number(range.step * (ls.val2 /*TODO+ source.getRawOffset(model)*/) + range.offset);
583 break;
585 case LS_FAMILY_VBOOL:
586 result += sw1Name;
587 switch (ls.func) {
588 case LS_FN_AND:
589 result += " AND ";
590 break;
591 case LS_FN_OR:
592 result += " OR ";
593 break;
594 case LS_FN_XOR:
595 result += " XOR ";
596 break;
597 default:
598 result += " bar ";
599 break;
601 result += sw2Name;
602 break;
604 case LS_FAMILY_VCOMP:
605 if (ls.val1)
606 result += RawSource(ls.val1).toString(&model, &generalSettings);
607 else
608 result += "0";
609 switch (ls.func) {
610 case LS_FN_EQUAL:
611 case LS_FN_VEQUAL:
612 result += " = ";
613 break;
614 case LS_FN_NEQUAL:
615 result += " != ";
616 break;
617 case LS_FN_GREATER:
618 result += " &gt; ";
619 break;
620 case LS_FN_LESS:
621 result += " &lt; ";
622 break;
623 case LS_FN_EGREATER:
624 result += " &gt;= ";
625 break;
626 case LS_FN_ELESS:
627 result += " &lt;= ";
628 break;
629 default:
630 result += " foo ";
631 break;
633 if (ls.val2)
634 result += RawSource(ls.val2).toString(&model, &generalSettings);
635 else
636 result += "0";
637 break;
640 if (ls.andsw != 0) {
641 result +=" ) AND ";
642 result += RawSwitch(ls.andsw).toString(getCurrentBoard(), &generalSettings);
645 if (firmware->getCapability(LogicalSwitchesExt)) {
646 if (ls.duration)
647 result += " " + tr("Duration") + QString("(%1s)").arg(ls.duration/10.0);
648 if (ls.delay)
649 result += " " + tr("Delay") + QString("(%1s)").arg(ls.delay/10.0);
652 return result;
655 QString ModelPrinter::printCustomFunctionLine(int idx, bool gfunc)
657 QString result;
658 CustomFunctionData cf;
659 if (gfunc) {
660 if (model.noGlobalFunctions)
661 return result;
662 cf = generalSettings.customFn[idx];
664 else
665 cf = model.customFn[idx];
666 if (cf.swtch.type == SWITCH_TYPE_NONE)
667 return result;
669 result += cf.swtch.toString(getCurrentBoard(), &generalSettings) + " - ";
670 result += cf.funcToString(&model) + " (";
671 result += cf.paramToString(&model) + ")";
672 if (!cf.repeatToString().isEmpty())
673 result += " " + cf.repeatToString();
674 if (!cf.enabledToString().isEmpty())
675 result += " " + cf.enabledToString();
676 return result;
679 QString ModelPrinter::printCurveName(int idx)
681 return model.curves[idx].nameToString(idx).toHtmlEscaped();
684 QString ModelPrinter::printCurve(int idx)
686 QString result;
687 const CurveData & curve = model.curves[idx];
688 result += (curve.type == CurveData::CURVE_TYPE_CUSTOM) ? tr("Custom") : tr("Standard");
689 result += ", [";
690 if (curve.type == CurveData::CURVE_TYPE_CUSTOM) {
691 for (int j=0; j<curve.count; j++) {
692 if (j != 0)
693 result += ", ";
694 result += QString("(%1, %2)").arg(curve.points[j].x).arg(curve.points[j].y);
697 else {
698 for (int j=0; j<curve.count; j++) {
699 if (j != 0)
700 result += ", ";
701 result += QString("%1").arg(curve.points[j].y);
704 result += "]";
705 return result;
708 CurveImage::CurveImage():
709 size(200),
710 image(size+1, size+1, QImage::Format_RGB32),
711 painter(&image)
713 painter.setBrush(QBrush("#FFFFFF"));
714 painter.setPen(QColor(0, 0, 0));
715 painter.drawRect(0, 0, size, size);
717 painter.setPen(QColor(0, 0, 0));
718 painter.drawLine(0, size/2, size, size/2);
719 painter.drawLine(size/2, 0, size/2, size);
720 for (int i=0; i<21; i++) {
721 painter.drawLine(size/2-5, (size*i)/(20), size/2+5, (size*i)/(20));
722 painter.drawLine((size*i)/(20), size/2-5, (size*i)/(20), size/2+5);
726 void CurveImage::drawCurve(const CurveData & curve, QColor color)
728 painter.setPen(QPen(color, 2, Qt::SolidLine));
729 for (int j=1; j<curve.count; j++) {
730 if (curve.type == CurveData::CURVE_TYPE_CUSTOM)
731 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);
732 else
733 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);
737 QString ModelPrinter::createCurveImage(int idx, QTextDocument * document)
739 CurveImage image;
740 image.drawCurve(model.curves[idx], colors[idx]);
741 QString filename = QString("mydata://curve-%1-%2.png").arg((uint64_t)this).arg(idx);
742 if (document)
743 document->addResource(QTextDocument::ImageResource, QUrl(filename), image.get());
744 // qDebug() << "ModelPrinter::createCurveImage()" << idx << filename;
745 return filename;
748 QString ModelPrinter::printGlobalVarUnit(int idx)
750 return model.gvarData[idx].unitToString().toHtmlEscaped();
753 QString ModelPrinter::printGlobalVarPrec(int idx)
755 return model.gvarData[idx].precToString().toHtmlEscaped();
758 QString ModelPrinter::printGlobalVarMin(int idx)
760 return QString::number(model.gvarData[idx].getMinPrec());
763 QString ModelPrinter::printGlobalVarMax(int idx)
765 return QString::number(model.gvarData[idx].getMaxPrec());
768 QString ModelPrinter::printGlobalVarPopup(int idx)
770 return printBoolean(model.gvarData[idx].popup, BOOLEAN_YN);
773 QString ModelPrinter::printOutputValueGVar(int val)
775 QString result = "";
776 if (abs(val) > 10000) {
777 if (val < 0)
778 result = "-";
779 result.append(RawSource(SOURCE_TYPE_GVAR, abs(val)-10001).toString(&model));
781 else {
782 if (val >= 0)
783 result = "+";
784 result.append(QString::number((qreal)val/10, 'f', 1) + "%");
786 return result;
789 QString ModelPrinter::printOutputOffset(int idx)
791 return printOutputValueGVar(model.limitData[idx].offset);
794 QString ModelPrinter::printOutputMin(int idx)
796 return printOutputValueGVar(model.limitData[idx].min);
799 QString ModelPrinter::printOutputMax(int idx)
801 return printOutputValueGVar(model.limitData[idx].max);
804 QString ModelPrinter::printOutputRevert(int idx)
806 return model.limitData[idx].revertToString();
809 QString ModelPrinter::printOutputPpmCenter(int idx)
811 return QString::number(model.limitData[idx].ppmCenter + 1500);
814 QString ModelPrinter::printOutputCurve(int idx)
816 return CurveReference(CurveReference::CURVE_REF_CUSTOM, model.limitData[idx].curve.value).toString(&model, false);
819 QString ModelPrinter::printOutputSymetrical(int idx)
821 return printBoolean(model.limitData[idx].symetrical, BOOLEAN_YN);
824 QString ModelPrinter::printSettingsOther()
826 QStringList str;
827 str << printLabelValue(tr("Extended Limits"), printBoolean(model.extendedLimits, BOOLEAN_YESNO));
828 if (firmware->getCapability(HasDisplayText))
829 str << printLabelValue(tr("Display Checklist"), printBoolean(model.displayChecklist, BOOLEAN_YESNO));
830 if (firmware->getCapability(GlobalFunctions))
831 str << printLabelValue(tr("Global Functions"), printBoolean(!model.noGlobalFunctions, BOOLEAN_YESNO));
832 return str.join(" ");
835 QString ModelPrinter::printSwitchWarnings()
837 QStringList str;
838 Boards board = firmware->getBoard();
839 uint64_t switchStates = model.switchWarningStates;
840 uint64_t value;
842 for (int idx=0; idx<board.getCapability(Board::Switches); idx++) {
843 Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board.getBoardType(), idx);
844 switchInfo.config = Board::SwitchType(generalSettings.switchConfig[idx]);
845 if (switchInfo.config == Board::SWITCH_NOT_AVAILABLE || switchInfo.config == Board::SWITCH_TOGGLE) {
846 continue;
848 if (!(model.switchWarningEnable & (1 << idx))) {
849 if (IS_HORUS_OR_TARANIS(board.getBoardType())) {
850 value = (switchStates >> (2*idx)) & 0x03;
852 else {
853 value = (idx==0 ? switchStates & 0x3 : switchStates & 0x1);
854 switchStates >>= (idx==0 ? 2 : 1);
856 str += RawSwitch(SWITCH_TYPE_SWITCH, 1+idx*3+value).toString(board.getBoardType(), &generalSettings, &model);
859 return (str.isEmpty() ? tr("None") : str.join(" ")) ;
862 QString ModelPrinter::printPotWarnings()
864 QStringList str;
865 int genAryIdx = 0;
866 Boards board = firmware->getBoard();
867 if (model.potsWarningMode) {
868 for (int i=0; i<board.getCapability(Board::Pots)+board.getCapability(Board::Sliders); i++) {
869 RawSource src(SOURCE_TYPE_STICK, CPN_MAX_STICKS + i);
870 if ((src.isPot(&genAryIdx) && generalSettings.isPotAvailable(genAryIdx)) || (src.isSlider(&genAryIdx) && generalSettings.isSliderAvailable(genAryIdx))) {
871 if (!model.potsWarnEnabled[i])
872 str += src.toString(&model, &generalSettings);
876 str << printLabelValue(tr("Mode"), printPotsWarningMode());
877 return str.join(" ");
880 QString ModelPrinter::printPotsWarningMode()
882 switch (model.potsWarningMode) {
883 case 0:
884 return tr("OFF");
885 case 1:
886 return tr("Manual");
887 case 2:
888 return tr("Auto");
889 default:
890 return CPN_STR_UNKNOWN_ITEM;
894 QString ModelPrinter::printFailsafe(int idx)
896 QStringList strl;
897 ModuleData module = model.moduleData[idx];
898 strl << printLabelValue(tr("Failsafe Mode"), printFailsafeMode(module.failsafeMode));
899 if (module.failsafeMode == FAILSAFE_CUSTOM) {
900 for (int i=0; i<module.channelsCount; i++) {
901 //strl << QString("%1(%2)").arg(printChannelName(module.channelsStart + i).trimmed()).arg(printFailsafeValue(module.failsafeChannels[i]));
902 strl << printLabelValue(printChannelName(module.channelsStart + i).trimmed(), printFailsafeValue(module.failsafeChannels[i]));
905 return strl.join(" ");
908 QString ModelPrinter::printFailsafeValue(int val)
910 switch (val) {
911 case 2000:
912 return tr("Hold");
913 case 2001:
914 return tr("No Pulse");
915 default:
916 return QString("%1%").arg(QString::number(divRoundClosest(val * 1000, 1024) / 10.0));
920 QString ModelPrinter::printFailsafeMode(unsigned int fsmode)
922 switch (fsmode) {
923 case FAILSAFE_NOT_SET:
924 return tr("Not set");
925 case FAILSAFE_HOLD:
926 return tr("Hold");
927 case FAILSAFE_CUSTOM:
928 return tr("Custom");
929 case FAILSAFE_NOPULSES:
930 return tr("No pulses");
931 case FAILSAFE_RECEIVER:
932 return tr("Receiver");
933 default:
934 return CPN_STR_UNKNOWN_ITEM;
938 QString ModelPrinter::printTimerCountdownBeep(unsigned int countdownBeep)
940 switch (countdownBeep) {
941 case TimerData::COUNTDOWN_SILENT:
942 return tr("Silent");
943 case TimerData::COUNTDOWN_BEEPS:
944 return tr("Beeps");
945 case TimerData::COUNTDOWN_VOICE:
946 return tr("Voice");
947 case TimerData::COUNTDOWN_HAPTIC:
948 return tr("Haptic");
949 default:
950 return CPN_STR_UNKNOWN_ITEM;
954 QString ModelPrinter::printTimerPersistent(unsigned int persistent)
956 switch (persistent) {
957 case 0:
958 return tr("OFF");
959 case 1:
960 return tr("Flight");
961 case 2:
962 return tr("Manual reset");
963 default:
964 return CPN_STR_UNKNOWN_ITEM;
968 QString ModelPrinter::printSettingsTrim()
970 QStringList str;
971 str << printLabelValue(tr("Step"), printTrimIncrementMode());
972 if (IS_ARM(firmware->getBoard()))
973 str << printLabelValue(tr("Display"), printTrimsDisplayMode());
974 str << printLabelValue(tr("Extended"), printBoolean(model.extendedTrims, BOOLEAN_YESNO));
975 return str.join(" ");
978 QString ModelPrinter::printThrottleSource(int idx)
980 Boards board = firmware->getBoard();
981 int chnstart = board.getCapability(Board::Pots)+board.getCapability(Board::Sliders);
982 if (idx == 0)
983 return "THR";
984 else if (idx < (chnstart+1))
985 return firmware->getAnalogInputName(idx+board.getCapability(Board::Sticks)-1);
986 else
987 return RawSource(SOURCE_TYPE_CH, idx-chnstart-1).toString(&model, &generalSettings);
990 QString ModelPrinter::printTrimsDisplayMode()
992 switch (model.trimsDisplay) {
993 case 0:
994 return tr("Never");
995 case 1:
996 return tr("On Change");
997 case 2:
998 return tr("Always");
999 default:
1000 return CPN_STR_UNKNOWN_ITEM;
1004 QString ModelPrinter::printModuleType(int idx)
1006 return ModuleData::indexToString(idx, firmware);
1009 QString ModelPrinter::printPxxPower(int power)
1011 static const char *strings[] = {
1012 "10mW", "100mW", "500mW", "3W"
1014 return CHECK_IN_ARRAY(strings, power);
1017 QString ModelPrinter::printThrottle()
1019 QStringList result;
1020 result << printLabelValue(tr("Source"), printThrottleSource(model.thrTraceSrc));
1021 result << printLabelValue(tr("Trim idle only"), printBoolean(model.thrTrim, BOOLEAN_YESNO));
1022 result << printLabelValue(tr("Warning"), printBoolean(!model.disableThrottleWarning, BOOLEAN_YESNO));
1023 result << printLabelValue(tr("Reversed"), printBoolean(model.throttleReversed, BOOLEAN_YESNO));
1024 return result.join(" ");
1027 QString ModelPrinter::printPPMFrameLength(int ppmFL)
1029 double result = (((double)ppmFL * 5) + 225) / 10;
1030 return QString::number(result);
1033 QString ModelPrinter::printTimerName(int idx)
1035 QString result;
1036 result = tr("Tmr") + QString("%1").arg(idx+1);
1037 if (firmware->getCapability(TimersName) && model.timers[idx].name[0])
1038 result.append(":" + QString(model.timers[idx].name));
1040 return result;
1043 QString ModelPrinter::printTimerTimeValue(unsigned int val)
1045 return printTimeValue(val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS);
1048 QString ModelPrinter::printTimerMinuteBeep(bool mb)
1050 return printBoolean(mb, BOOLEAN_YESNO);
1053 QString ModelPrinter::printTelemetryProtocol(unsigned int val)
1055 switch (val) {
1056 case 0:
1057 return tr("FrSky S.PORT");
1058 case 1:
1059 return tr("FrSky D");
1060 case 2:
1061 return tr("FrSky D (cable)");
1062 default:
1063 return CPN_STR_UNKNOWN_ITEM;
1067 QString ModelPrinter::printRssiAlarmsDisabled(bool mb)
1069 return printBoolean(!mb, BOOLEAN_ENABLEDISABLE);
1072 QString ModelPrinter::printTelemetrySource(int val)
1074 QStringList strings = QStringList() << tr("None");
1076 for (unsigned i=1; i<=CPN_MAX_SENSORS; ++i) {
1077 strings << QString("%1").arg(model.sensorData[i-1].label);
1080 return QString("%1%2").arg((val < 0 ? "-" : "")).arg(strings.value(abs(val)));
1083 QString ModelPrinter::printVarioSource(unsigned int val)
1085 switch (val) {
1086 case TELEMETRY_VARIO_SOURCE_ALTI:
1087 return tr("Alti");
1088 case TELEMETRY_VARIO_SOURCE_ALTI_PLUS:
1089 return tr("Alti+");
1090 case TELEMETRY_VARIO_SOURCE_VSPEED:
1091 return tr("VSpeed");
1092 case TELEMETRY_VARIO_SOURCE_A1:
1093 return tr("A1");
1094 case TELEMETRY_VARIO_SOURCE_A2:
1095 return tr("A2");
1096 default:
1097 return CPN_STR_UNKNOWN_ITEM;
1101 QString ModelPrinter::printVarioCenterSilent(bool mb)
1103 return printBoolean(mb, BOOLEAN_YESNO);
1106 QString ModelPrinter::printVoltsSource(unsigned int val)
1108 switch (val) {
1109 case TELEMETRY_VOLTS_SOURCE_A1:
1110 return tr("A1");
1111 case TELEMETRY_VOLTS_SOURCE_A2:
1112 return tr("A2");
1113 case TELEMETRY_VOLTS_SOURCE_A3:
1114 return tr("A3");
1115 case TELEMETRY_VOLTS_SOURCE_A4:
1116 return tr("A4");
1117 case TELEMETRY_VOLTS_SOURCE_FAS:
1118 return tr("FAS");
1119 case TELEMETRY_VOLTS_SOURCE_CELLS:
1120 return tr("Cells");
1121 default:
1122 return CPN_STR_UNKNOWN_ITEM;
1126 QString ModelPrinter::printCurrentSource(unsigned int val)
1128 switch (val) {
1129 case TELEMETRY_CURRENT_SOURCE_NONE:
1130 return tr("None");
1131 case TELEMETRY_CURRENT_SOURCE_A1:
1132 return tr("A1");
1133 case TELEMETRY_CURRENT_SOURCE_A2:
1134 return tr("A2");
1135 case TELEMETRY_CURRENT_SOURCE_A3:
1136 return tr("A3");
1137 case TELEMETRY_CURRENT_SOURCE_A4:
1138 return tr("A4");
1139 case TELEMETRY_CURRENT_SOURCE_FAS:
1140 return tr("FAS");
1141 default:
1142 return CPN_STR_UNKNOWN_ITEM;
1146 QString ModelPrinter::printMahPersistent(bool mb)
1148 return printBoolean(mb, BOOLEAN_YESNO);
1151 QString ModelPrinter::printIgnoreSensorIds(bool mb)
1153 return printBoolean(mb, BOOLEAN_ENABLEDISABLE);
1156 QString ModelPrinter::printSensorType(unsigned int val)
1158 switch (val) {
1159 case SensorData::TELEM_TYPE_CUSTOM:
1160 return tr("Custom");
1161 case SensorData::TELEM_TYPE_CALCULATED:
1162 return tr("Calculated");
1163 default:
1164 return CPN_STR_UNKNOWN_ITEM;
1168 QString ModelPrinter::printSensorFormula(unsigned int val)
1170 switch (val) {
1171 case SensorData::TELEM_FORMULA_ADD:
1172 return tr("Add");
1173 case SensorData::TELEM_FORMULA_AVERAGE:
1174 return tr("Average");
1175 case SensorData::TELEM_FORMULA_MIN:
1176 return tr("Min");
1177 case SensorData::TELEM_FORMULA_MAX:
1178 return tr("Max");
1179 case SensorData::TELEM_FORMULA_MULTIPLY:
1180 return tr("Multiply");
1181 case SensorData::TELEM_FORMULA_TOTALIZE:
1182 return tr("Totalise");
1183 case SensorData::TELEM_FORMULA_CELL:
1184 return tr("Cell");
1185 case SensorData::TELEM_FORMULA_CONSUMPTION:
1186 return tr("Consumption");
1187 case SensorData::TELEM_FORMULA_DIST:
1188 return tr("Distance");
1189 default:
1190 return CPN_STR_UNKNOWN_ITEM;
1194 QString ModelPrinter::printSensorCells(unsigned int val)
1196 QStringList strings;
1198 strings << tr("Lowest");
1199 for (int i=1; i<=6; i++)
1200 strings << tr("Cell %1").arg(i);
1201 strings << tr("Highest") << tr("Delta");
1203 return strings.value(val);
1206 QString ModelPrinter::printSensorTypeCond(unsigned int idx)
1208 if (!model.sensorData[idx].isAvailable())
1209 return "";
1210 else
1211 return printSensorType(model.sensorData[idx].type);
1214 QString ModelPrinter::printSensorDetails(unsigned int idx)
1216 QString str = "";
1217 SensorData sensor = model.sensorData[idx];
1219 if (!sensor.isAvailable())
1220 return str;
1222 bool isConfigurable = false;
1223 bool gpsFieldsPrinted = false;
1224 bool cellsFieldsPrinted = false;
1225 bool consFieldsPrinted = false;
1226 bool ratioFieldsPrinted = false;
1227 bool totalizeFieldsPrinted = false;
1228 bool sources12FieldsPrinted = false;
1229 bool sources34FieldsPrinted = false;
1231 str.append(doTableCell(printSensorTypeCond(idx)));
1233 QString tc = "";
1234 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
1235 isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL);
1236 gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST);
1237 cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL);
1238 consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION);
1239 sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY);
1240 sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY);
1241 totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE);
1243 tc.append(printLabelValue(tr("Formula"), printSensorFormula(sensor.formula)));
1245 else {
1246 isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL;
1247 ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL);
1249 tc.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper()));
1250 tc.append(printLabelValue(tr("Instance"), QString::number(sensor.instance)));
1252 if (cellsFieldsPrinted) {
1253 tc.append(printLabelValue(tr("Sensor"), QString("%1 > %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index))));
1255 if (sources12FieldsPrinted) {
1256 QStringList srcs;
1257 for (int i=0;i<4;i++) {
1258 if (i < 2 || sources34FieldsPrinted) {
1259 srcs << printTelemetrySource(sensor.sources[i]);
1262 tc.append(printLabelValues(tr("Sources"), srcs));
1264 if (consFieldsPrinted || totalizeFieldsPrinted)
1265 tc.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps)));
1266 if (gpsFieldsPrinted) {
1267 tc.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps)));
1268 tc.append(printLabelValue(tr("Alt."), printTelemetrySource(sensor.alt)));
1270 if (ratioFieldsPrinted && sensor.unit == SensorData::UNIT_RPMS) {
1271 tc.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio)));
1272 tc.append(printLabelValue(tr("Multi."), QString::number(sensor.offset)));
1274 str.append(doTableCell(tc));
1276 tc = sensor.unitString();
1277 tc = tc.trimmed() == "" ? "-" : tc;
1278 str.append(doTableCell(tc));
1280 if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT)
1281 tc = QString::number(sensor.prec);
1282 else
1283 tc = "";
1284 str.append(doTableCell(tc));
1286 if (!ratioFieldsPrinted) {
1287 str.append(doTableCell(""));
1288 str.append(doTableCell(""));
1290 else if (sensor.unit != SensorData::UNIT_RPMS) {
1291 int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec);
1292 str.append(doTableCell(QString::number((float)sensor.ratio / prec)));
1293 str.append(doTableCell(QString::number((float)sensor.offset / prec)));
1296 if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable)
1297 str.append(doTableCell(printBoolean(sensor.autoOffset, BOOLEAN_YN)));
1298 else
1299 str.append(doTableCell(""));
1301 if (isConfigurable)
1302 str.append(doTableCell(printBoolean(sensor.filter, BOOLEAN_YN)));
1303 else
1304 str.append(doTableCell(""));
1306 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED)
1307 str.append(doTableCell(printBoolean(sensor.persistent, BOOLEAN_YN)));
1308 else
1309 str.append(doTableCell(""));
1311 str.append(doTableCell(printBoolean(sensor.onlyPositive, BOOLEAN_YN)));
1312 str.append(doTableCell(printBoolean(sensor.logs, BOOLEAN_YN), false));
1313 return str;
1316 QString ModelPrinter::printSensorParams(unsigned int idx)
1318 QString str = "";
1319 SensorData sensor = model.sensorData[idx];
1321 if (!sensor.isAvailable())
1322 return str;
1324 bool isConfigurable = false;
1325 bool gpsFieldsPrinted = false;
1326 bool cellsFieldsPrinted = false;
1327 bool consFieldsPrinted = false;
1328 bool ratioFieldsPrinted = false;
1329 bool totalizeFieldsPrinted = false;
1330 bool sources12FieldsPrinted = false;
1331 bool sources34FieldsPrinted = false;
1333 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
1334 isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL);
1335 gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST);
1336 cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL);
1337 consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION);
1338 sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY);
1339 sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY);
1340 totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE);
1342 str.append(printLabelValue(tr("F"), printSensorFormula(sensor.formula)));
1344 else {
1345 isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL;
1346 ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL);
1348 str.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper()));
1349 str.append(printLabelValue(tr("Inst"), QString::number(sensor.instance)));
1351 if (cellsFieldsPrinted) {
1352 str.append(printLabelValue(tr("Sensor"), QString("%1 %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index))));
1354 if (sources12FieldsPrinted) {
1355 QStringList srcs;
1356 for (int i=0;i<4;i++) {
1357 if (i < 2 || sources34FieldsPrinted) {
1358 srcs << printTelemetrySource(sensor.sources[i]);
1361 str.append(printLabelValues(tr("Sources"), srcs));
1363 if (consFieldsPrinted || totalizeFieldsPrinted)
1364 str.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps)));
1365 if (gpsFieldsPrinted) {
1366 str.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps)));
1367 str.append(printLabelValue(tr("Alt"), printTelemetrySource(sensor.alt)));
1369 QString u = sensor.unitString();
1370 u = u.trimmed() == "" ? "-" : u;
1371 str.append(printLabelValue(tr("Unit"), u));
1372 if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT)
1373 str.append(printLabelValue(tr("Prec"), QString::number(sensor.prec)));
1374 if (ratioFieldsPrinted) {
1375 if (sensor.unit != SensorData::UNIT_RPMS) {
1376 int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec);
1377 str.append(printLabelValue(tr("Ratio"), QString::number((float)sensor.ratio / prec)));
1378 str.append(printLabelValue(tr("Offset"), QString::number((float)sensor.offset / prec)));
1380 else if (sensor.unit == SensorData::UNIT_RPMS) {
1381 str.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio)));
1382 str.append(printLabelValue(tr("Multi"), QString::number(sensor.offset)));
1385 if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable)
1386 str.append(printLabelValue(tr("A/Offset"), printBoolean(sensor.autoOffset, BOOLEAN_YN)));
1387 if (isConfigurable)
1388 str.append(printLabelValue(tr("Filter"), printBoolean(sensor.filter, BOOLEAN_YN)));
1389 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED)
1390 str.append(printLabelValue(tr("Persist"), printBoolean(sensor.persistent, BOOLEAN_YN)));
1391 str.append(printLabelValue(tr("Positive"), printBoolean(sensor.onlyPositive, BOOLEAN_YN)));
1392 str.append(printLabelValue(tr("Log"), printBoolean(sensor.logs, BOOLEAN_YN), false));
1393 return str;
1396 QString ModelPrinter::printTelemetryScreenType(unsigned int val)
1398 switch (val) {
1399 case TelemetryScreenEnum::TELEMETRY_SCREEN_NONE:
1400 return tr("None");
1401 case TelemetryScreenEnum::TELEMETRY_SCREEN_NUMBERS:
1402 return tr("Numbers");
1403 case TelemetryScreenEnum::TELEMETRY_SCREEN_BARS:
1404 return tr("Bars");
1405 case TelemetryScreenEnum::TELEMETRY_SCREEN_SCRIPT:
1406 return tr("Script");
1407 default:
1408 return CPN_STR_UNKNOWN_ITEM;
1412 QString ModelPrinter::printTelemetryScreen(unsigned int idx, unsigned int line, unsigned int width)
1414 QStringList strl;
1415 QStringList hd;
1416 FrSkyScreenData screen = model.frsky.screens[idx];
1417 hd << ""; // blank 1st column
1418 strl << "";
1419 if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_NUMBERS) {
1420 if (line == 0) {
1421 for (int c=0; c<firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
1422 hd << tr("Source");
1425 for (int c=0; c<firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
1426 RawSource source = screen.body.lines[line].source[c];
1427 strl << source.toString(&model, &generalSettings);
1430 else if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_BARS) {
1431 if (line == 0) {
1432 hd << tr("Source") << tr("Min") << tr("Max");
1434 RawSource source = screen.body.bars[line].source;
1435 RawSourceRange range = source.getRange(&model, generalSettings);
1436 strl << source.toString(&model, &generalSettings);
1437 QString unit;
1438 QString minstr;
1439 QString maxstr;
1440 if (source.isTimeBased()){
1441 minstr = printTimeValue((float)screen.body.bars[line].barMin, MASK_TIMEVALUE_HRSMINS);
1442 maxstr = printTimeValue((float)screen.body.bars[line].barMax, MASK_TIMEVALUE_HRSMINS);
1444 else {
1445 minstr = QString::number(range.getValue(screen.body.bars[line].barMin));
1446 maxstr = QString::number(range.getValue(screen.body.bars[line].barMax));
1447 unit = range.unit;
1449 strl << QString("%1%2").arg(minstr).arg(unit);
1450 strl << QString("%1%2").arg(maxstr).arg(unit);
1452 else if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_SCRIPT && line == 0) {
1453 hd << tr("Filename");
1454 strl << QString("%1.lua").arg(screen.body.script.filename);
1456 return (hd.count() > 1 ? doTableRow(hd, width / hd.count(), "left", "", true) : "" ) + doTableRow(strl, width / strl.count());
1459 QString ModelPrinter::printChecklist()
1461 if (!model.displayChecklist)
1462 return "";
1463 QString str = tr("Error: Unable to open or read file!");
1464 QFile file(Helpers::getChecklistFilePath(&model));
1465 if (file.open(QFile::ReadOnly | QFile::Text)) {
1466 QTextStream in(&file);
1467 if (in.status() == QTextStream::Ok) {
1468 str = in.readAll();
1469 str.replace("\n", "<br />");
1470 str.remove("\r");
1472 file.close();
1474 return str;