Fix doc path
[opentx.git] / companion / src / modelprinter.cpp
blob91a81eaa24606f3f0e99ebaea82cb6d4721d658c
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 CPN_STR_UNKNOWN_ITEM;
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::printModule(int idx)
185 QStringList str;
186 QString result;
187 ModuleData module = model.moduleData[(idx<0 ? CPN_MAX_MODULES : idx)];
188 if (idx < 0) {
189 str << printLabelValue(tr("Mode"), printTrainerMode());
190 if (IS_HORUS_OR_TARANIS(firmware->getBoard())) {
191 if (model.trainerMode == TRAINER_SLAVE_JACK) {
192 str << printLabelValue(tr("Channels"), QString("%1-%2").arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount));
193 str << printLabelValue(tr("Frame length"), QString("%1ms").arg(printPPMFrameLength(module.ppm.frameLength)));
194 str << printLabelValue(tr("PPM delay"), QString("%1us").arg(module.ppm.delay));
195 str << printLabelValue(tr("Polarity"), module.polarityToString());
198 result = str.join(" ");
200 else {
201 str << printLabelValue(tr("Protocol"), ModuleData::protocolToString(module.protocol));
202 if (module.protocol) {
203 str << printLabelValue(tr("Channels"), QString("%1-%2").arg(module.channelsStart + 1).arg(module.channelsStart + module.channelsCount));
204 if (module.protocol == PULSES_PPM || module.protocol == PULSES_SBUS) {
205 str << printLabelValue(tr("Frame length"), QString("%1ms").arg(printPPMFrameLength(module.ppm.frameLength)));
206 str << printLabelValue(tr("Polarity"), module.polarityToString());
207 if (module.protocol == PULSES_PPM)
208 str << printLabelValue(tr("Delay"), QString("%1us").arg(module.ppm.delay));
210 else {
211 if (!(module.protocol == PULSES_PXX_XJT_D8 || module.protocol == PULSES_CROSSFIRE || module.protocol == PULSES_SBUS)) {
212 str << printLabelValue(tr("Receiver"), QString::number(module.modelId));
214 if (module.protocol == PULSES_MULTIMODULE) {
215 str << printLabelValue(tr("Radio protocol"), module.rfProtocolToString());
216 str << printLabelValue(tr("Subtype"), module.subTypeToString());
217 str << printLabelValue(tr("Option value"), QString::number(module.multi.optionValue));
219 if (module.protocol == PULSES_PXX_R9M) {
220 str << printLabelValue(tr("Sub Type"), module.subTypeToString());
221 str << printLabelValue(tr("RF Output Power"), module.powerValueToString(firmware));
222 str << printLabelValue(tr("Telemetry"), printBoolean(module.pxx.sport_out, BOOLEAN_ENABLEDISABLE));
226 result = str.join(" ");
227 if (((PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_XJT_X16 || (PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_R9M)
228 && firmware->getCapability(HasFailsafe))
229 result.append("<br/>" + printFailsafe(idx));
231 return result;
234 QString ModelPrinter::printTrainerMode()
236 QString result;
237 switch (model.trainerMode) {
238 case TRAINER_MASTER_JACK:
239 result = tr("Master/Jack");
240 break;
241 case TRAINER_SLAVE_JACK:
242 result = tr("Slave/Jack");
243 break;
244 case TRAINER_MASTER_SBUS_MODULE:
245 result = tr("Master/SBUS Module");
246 break;
247 case TRAINER_MASTER_CPPM_MODULE:
248 result = tr("Master/CPPM Module");
249 break;
250 case TRAINER_MASTER_SBUS_BATT_COMPARTMENT:
251 result = tr("Master/SBUS in battery compartment");
252 break;
253 default:
254 result = CPN_STR_UNKNOWN_ITEM;
256 return result;
259 QString ModelPrinter::printHeliSwashType ()
261 switch (model.swashRingData.type) {
262 case HELI_SWASH_TYPE_90:
263 return tr("90");
264 case HELI_SWASH_TYPE_120:
265 return tr("120");
266 case HELI_SWASH_TYPE_120X:
267 return tr("120X");
268 case HELI_SWASH_TYPE_140:
269 return tr("140");
270 case HELI_SWASH_TYPE_NONE:
271 return tr("Off");
272 default:
273 return CPN_STR_UNKNOWN_ITEM;
277 QString ModelPrinter::printCenterBeep()
279 QStringList strl;
280 if (model.beepANACenter & 0x01)
281 strl << tr("Rudder");
282 if (model.beepANACenter & 0x02)
283 strl << tr("Elevator");
284 if (model.beepANACenter & 0x04)
285 strl << tr("Throttle");
286 if (model.beepANACenter & 0x08)
287 strl << tr("Aileron");
288 if (IS_HORUS(firmware->getBoard())) {
289 // TODO
290 qDebug() << "ModelPrinter::printCenterBeep() TODO";
292 else if (IS_TARANIS(firmware->getBoard())) {
293 if (model.beepANACenter & 0x10)
294 strl << "S1";
295 if (model.beepANACenter & 0x20)
296 strl << "S2";
297 if (model.beepANACenter & 0x40)
298 strl << "S3";
299 if (model.beepANACenter & 0x80)
300 strl << "LS";
301 if (model.beepANACenter & 0x100)
302 strl << "RS";
304 else {
305 if (model.beepANACenter & 0x10)
306 strl << "P1";
307 if (model.beepANACenter & 0x20)
308 strl << "P2";
309 if (model.beepANACenter & 0x40)
310 strl << "P3";
312 return (strl.isEmpty() ? tr("None") : strl.join(" "));
315 QString ModelPrinter::printTimer(int idx)
317 return printTimer(model.timers[idx]);
320 QString ModelPrinter::printTimer(const TimerData & timer)
322 QStringList result;
323 if (firmware->getCapability(TimersName) && timer.name[0])
324 result += tr("Name") + QString("(%1)").arg(timer.name);
325 result += printTimeValue(timer.val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS);
326 result += timer.mode.toString();
327 if (timer.countdownBeep)
328 result += tr("Countdown") + QString("(%1)").arg(printTimerCountdownBeep(timer.countdownBeep));
329 if (timer.minuteBeep)
330 result += tr("Minute call");
331 if (timer.persistent)
332 result += tr("Persistent") + QString("(%1)").arg(printTimerPersistent(timer.persistent));
333 return result.join(", ");
336 QString ModelPrinter::printTrim(int flightModeIndex, int stickIndex)
338 const FlightModeData & fm = model.flightModeData[flightModeIndex];
340 if (fm.trimMode[stickIndex] == -1) {
341 return tr("OFF");
343 else {
344 if (fm.trimRef[stickIndex] == flightModeIndex) {
345 return QString("%1").arg(fm.trim[stickIndex]);
347 else {
348 if (fm.trimMode[stickIndex] == 0) {
349 return tr("FM%1").arg(fm.trimRef[stickIndex]);
351 else {
352 if (fm.trim[stickIndex] < 0)
353 return tr("FM%1%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]);
354 else
355 return tr("FM%1+%2").arg(fm.trimRef[stickIndex]).arg(fm.trim[stickIndex]);
361 QString ModelPrinter::printGlobalVar(int flightModeIndex, int gvarIndex)
363 const FlightModeData & fm = model.flightModeData[flightModeIndex];
365 if (fm.gvars[gvarIndex] <= 1024) {
366 return QString("%1").arg(fm.gvars[gvarIndex] * model.gvarData[gvarIndex].multiplierGet());
368 else {
369 int num = fm.gvars[gvarIndex] - 1025;
370 if (num >= flightModeIndex) num++;
371 return tr("FM%1").arg(num);
375 QString ModelPrinter::printRotaryEncoder(int flightModeIndex, int reIndex)
377 const FlightModeData & fm = model.flightModeData[flightModeIndex];
379 if (fm.rotaryEncoders[reIndex] <= 1024) {
380 return QString("%1").arg(fm.rotaryEncoders[reIndex]);
382 else {
383 int num = fm.rotaryEncoders[reIndex] - 1025;
384 if (num >= flightModeIndex) num++;
385 return tr("FM%1").arg(num);
389 QString ModelPrinter::printInputName(int idx)
391 RawSourceType srcType = (firmware->getCapability(VirtualInputs) ? SOURCE_TYPE_VIRTUAL_INPUT : SOURCE_TYPE_STICK);
392 return RawSource(srcType, idx).toString(&model, &generalSettings).toHtmlEscaped();
395 QString ModelPrinter::printInputLine(int idx)
397 return printInputLine(model.expoData[idx]);
400 QString ModelPrinter::printInputLine(const ExpoData & input)
402 QString str = "&nbsp;";
404 switch (input.mode) {
405 case (1): str += "&lt;-&nbsp;"; break;
406 case (2): str += "-&gt;&nbsp;"; break;
407 default: str += "&nbsp;&nbsp;&nbsp;"; break;
410 if (firmware->getCapability(VirtualInputs)) {
411 str += input.srcRaw.toString(&model, &generalSettings).toHtmlEscaped();
414 str += " " + tr("Weight").toHtmlEscaped() + QString("(%1)").arg(Helpers::getAdjustmentString(input.weight, &model, true).toHtmlEscaped());
415 if (input.curve.value)
416 str += " " + input.curve.toString(&model).toHtmlEscaped();
418 QString flightModesStr = printFlightModes(input.flightModes);
419 if (!flightModesStr.isEmpty())
420 str += " " + flightModesStr.toHtmlEscaped();
422 if (input.swtch.type != SWITCH_TYPE_NONE)
423 str += " " + tr("Switch").toHtmlEscaped() + QString("(%1)").arg(input.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped();
426 if (firmware->getCapability(VirtualInputs)) {
427 if (input.carryTrim>0)
428 str += " " + tr("NoTrim").toHtmlEscaped();
429 else if (input.carryTrim<0)
430 str += " " + RawSource(SOURCE_TYPE_TRIM, (-(input.carryTrim)-1)).toString(&model, &generalSettings).toHtmlEscaped();
433 if (input.offset)
434 str += " " + tr("Offset(%1)").arg(Helpers::getAdjustmentString(input.offset, &model)).toHtmlEscaped();
436 if (firmware->getCapability(HasExpoNames) && input.name[0])
437 str += QString(" [%1]").arg(input.name).toHtmlEscaped();
439 return str;
442 QString ModelPrinter::printMixerLine(const MixData & mix, bool showMultiplex, int highlightedSource)
444 QString str = "&nbsp;";
446 if (showMultiplex) {
447 switch(mix.mltpx) {
448 case (1): str += "*="; break;
449 case (2): str += ":="; break;
450 default: str += "+="; break;
453 else {
454 str += "&nbsp;&nbsp;";
456 // highlight source if needed
457 QString source = mix.srcRaw.toString(&model, &generalSettings).toHtmlEscaped();
458 if ( (mix.srcRaw.type == SOURCE_TYPE_CH) && (mix.srcRaw.index+1 == (int)highlightedSource) ) {
459 source = "<b>" + source + "</b>";
461 str += "&nbsp;" + source;
463 if (mix.mltpx == MLTPX_MUL && !showMultiplex)
464 str += " " + tr("MULT!").toHtmlEscaped();
465 else
466 str += " " + tr("Weight") + QString("(%1)").arg(Helpers::getAdjustmentString(mix.weight, &model, true)).toHtmlEscaped();
468 QString flightModesStr = printFlightModes(mix.flightModes);
469 if (!flightModesStr.isEmpty())
470 str += " " + flightModesStr.toHtmlEscaped();
472 if (mix.swtch.type != SWITCH_TYPE_NONE)
473 str += " " + tr("Switch") + QString("(%1)").arg(mix.swtch.toString(getCurrentBoard(), &generalSettings)).toHtmlEscaped();
475 if (mix.carryTrim > 0)
476 str += " " + tr("NoTrim").toHtmlEscaped();
477 else if (mix.carryTrim < 0)
478 str += " " + RawSource(SOURCE_TYPE_TRIM, (-(mix.carryTrim)-1)).toString(&model, &generalSettings);
480 if (firmware->getCapability(HasNoExpo) && mix.noExpo)
481 str += " " + tr("No DR/Expo").toHtmlEscaped();
482 if (mix.sOffset)
483 str += " " + tr("Offset") + QString("(%1)").arg(Helpers::getAdjustmentString(mix.sOffset, &model)).toHtmlEscaped();
484 if (mix.curve.value)
485 str += " " + mix.curve.toString(&model).toHtmlEscaped();
486 int scale = firmware->getCapability(SlowScale);
487 if (scale == 0)
488 scale = 1;
489 if (mix.delayDown || mix.delayUp)
490 str += " " + tr("Delay") + QString("(u%1:d%2)").arg((double)mix.delayUp/scale).arg((double)mix.delayDown/scale).toHtmlEscaped();
491 if (mix.speedDown || mix.speedUp)
492 str += " " + tr("Slow") + QString("(u%1:d%2)").arg((double)mix.speedUp/scale).arg((double)mix.speedDown/scale).toHtmlEscaped();
493 if (mix.mixWarn)
494 str += " " + tr("Warn") + QString("(%1)").arg(mix.mixWarn).toHtmlEscaped();
495 if (firmware->getCapability(HasMixerNames) && mix.name[0])
496 str += QString(" [%1]").arg(mix.name).toHtmlEscaped();
497 return str;
500 QString ModelPrinter::printFlightModeSwitch(const RawSwitch & swtch)
502 return swtch.toString(getCurrentBoard(), &generalSettings);
505 QString ModelPrinter::printFlightModeName(int index)
507 return model.flightModeData[index].nameToString(index);
510 QString ModelPrinter::printFlightModes(unsigned int flightModes)
512 int numFlightModes = firmware->getCapability(FlightModes);
513 if (numFlightModes && flightModes) {
514 if (flightModes == (unsigned int)(1<<numFlightModes) - 1) {
515 return tr("Disabled in all flight modes");
517 else {
518 QStringList list;
519 for (int i=0; i<numFlightModes; i++) {
520 if (!(flightModes & (1<<i))) {
521 list << printFlightModeName(i);
524 return (list.size() > 1 ? tr("Flight modes") : tr("Flight mode")) + QString("(%1)").arg(list.join(", "));
527 else
528 return "";
531 QString ModelPrinter::printInputFlightModes(unsigned int flightModes)
533 int numFlightModes = firmware->getCapability(FlightModes);
534 if (numFlightModes && flightModes) {
535 if (flightModes == (unsigned int)(1<<numFlightModes) - 1) {
536 return tr("None");
538 else {
539 QStringList list;
540 for (int i=0; i<numFlightModes; i++) {
541 if (!(flightModes & (1<<i))) {
542 list << printFlightModeName(i);
545 return QString("%1").arg(list.join(" "));
548 else
549 return tr("All");
552 QString ModelPrinter::printLogicalSwitchLine(int idx)
554 QString result = "";
555 const LogicalSwitchData & ls = model.logicalSw[idx];
556 const QString sw1Name = RawSwitch(ls.val1).toString(getCurrentBoard(), &generalSettings);
557 const QString sw2Name = RawSwitch(ls.val2).toString(getCurrentBoard(), &generalSettings);
559 if (ls.isEmpty())
560 return result;
562 if (ls.andsw!=0) {
563 result +="( ";
565 switch (ls.getFunctionFamily()) {
566 case LS_FAMILY_EDGE:
567 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)));
568 break;
569 case LS_FAMILY_STICKY:
570 result += tr("Sticky") + QString("(%1, %2)").arg(sw1Name).arg(sw2Name);
571 break;
572 case LS_FAMILY_TIMER:
573 result += tr("Timer") + QString("(%1, %2)").arg(ValToTim(ls.val1)).arg(ValToTim(ls.val2));
574 break;
575 case LS_FAMILY_VOFS: {
576 RawSource source = RawSource(ls.val1);
577 RawSourceRange range = source.getRange(&model, generalSettings);
578 QString res;
579 if (ls.val1)
580 res += source.toString(&model, &generalSettings);
581 else
582 res += "0";
583 res.remove(" ");
584 if (ls.func == LS_FN_APOS || ls.func == LS_FN_ANEG)
585 res = "|" + res + "|";
586 else if (ls.func == LS_FN_DAPOS)
587 res = "|d(" + res + ")|";
588 else if (ls.func == LS_FN_DPOS)
589 result = "d(" + res + ")";
590 result += res;
591 if (ls.func == LS_FN_VEQUAL)
592 result += " = ";
593 else if (ls.func == LS_FN_APOS || ls.func == LS_FN_VPOS || ls.func == LS_FN_DPOS || ls.func == LS_FN_DAPOS)
594 result += " &gt; ";
595 else if (ls.func == LS_FN_ANEG || ls.func == LS_FN_VNEG)
596 result += " &lt; ";
597 else if (ls.func == LS_FN_VALMOSTEQUAL)
598 result += " ~ ";
599 else
600 result += tr(" missing");
601 result += QString::number(range.step * (ls.val2 /*TODO+ source.getRawOffset(model)*/) + range.offset);
602 break;
604 case LS_FAMILY_VBOOL:
605 result += sw1Name;
606 switch (ls.func) {
607 case LS_FN_AND:
608 result += " AND ";
609 break;
610 case LS_FN_OR:
611 result += " OR ";
612 break;
613 case LS_FN_XOR:
614 result += " XOR ";
615 break;
616 default:
617 result += " bar ";
618 break;
620 result += sw2Name;
621 break;
623 case LS_FAMILY_VCOMP:
624 if (ls.val1)
625 result += RawSource(ls.val1).toString(&model, &generalSettings);
626 else
627 result += "0";
628 switch (ls.func) {
629 case LS_FN_EQUAL:
630 case LS_FN_VEQUAL:
631 result += " = ";
632 break;
633 case LS_FN_NEQUAL:
634 result += " != ";
635 break;
636 case LS_FN_GREATER:
637 result += " &gt; ";
638 break;
639 case LS_FN_LESS:
640 result += " &lt; ";
641 break;
642 case LS_FN_EGREATER:
643 result += " &gt;= ";
644 break;
645 case LS_FN_ELESS:
646 result += " &lt;= ";
647 break;
648 default:
649 result += " foo ";
650 break;
652 if (ls.val2)
653 result += RawSource(ls.val2).toString(&model, &generalSettings);
654 else
655 result += "0";
656 break;
659 if (ls.andsw != 0) {
660 result +=" ) AND ";
661 result += RawSwitch(ls.andsw).toString(getCurrentBoard(), &generalSettings);
664 if (firmware->getCapability(LogicalSwitchesExt)) {
665 if (ls.duration)
666 result += " " + tr("Duration") + QString("(%1s)").arg(ls.duration/10.0);
667 if (ls.delay)
668 result += " " + tr("Delay") + QString("(%1s)").arg(ls.delay/10.0);
671 return result;
674 QString ModelPrinter::printCustomFunctionLine(int idx)
676 QString result;
677 const CustomFunctionData & cf = model.customFn[idx];
678 if (cf.swtch.type == SWITCH_TYPE_NONE)
679 return result;
681 result += cf.swtch.toString(getCurrentBoard(), &generalSettings) + " - ";
682 result += cf.funcToString(&model) + " (";
683 result += cf.paramToString(&model) + ")";
684 if (!cf.repeatToString().isEmpty())
685 result += " " + cf.repeatToString();
686 if (!cf.enabledToString().isEmpty())
687 result += " " + cf.enabledToString();
688 return result;
691 QString ModelPrinter::printCurveName(int idx)
693 return model.curves[idx].nameToString(idx).toHtmlEscaped();
696 QString ModelPrinter::printCurve(int idx)
698 QString result;
699 const CurveData & curve = model.curves[idx];
700 result += (curve.type == CurveData::CURVE_TYPE_CUSTOM) ? tr("Custom") : tr("Standard");
701 result += ", [";
702 if (curve.type == CurveData::CURVE_TYPE_CUSTOM) {
703 for (int j=0; j<curve.count; j++) {
704 if (j != 0)
705 result += ", ";
706 result += QString("(%1, %2)").arg(curve.points[j].x).arg(curve.points[j].y);
709 else {
710 for (int j=0; j<curve.count; j++) {
711 if (j != 0)
712 result += ", ";
713 result += QString("%1").arg(curve.points[j].y);
716 result += "]";
717 return result;
720 CurveImage::CurveImage():
721 size(200),
722 image(size+1, size+1, QImage::Format_RGB32),
723 painter(&image)
725 painter.setBrush(QBrush("#FFFFFF"));
726 painter.setPen(QColor(0, 0, 0));
727 painter.drawRect(0, 0, size, size);
729 painter.setPen(QColor(0, 0, 0));
730 painter.drawLine(0, size/2, size, size/2);
731 painter.drawLine(size/2, 0, size/2, size);
732 for (int i=0; i<21; i++) {
733 painter.drawLine(size/2-5, (size*i)/(20), size/2+5, (size*i)/(20));
734 painter.drawLine((size*i)/(20), size/2-5, (size*i)/(20), size/2+5);
738 void CurveImage::drawCurve(const CurveData & curve, QColor color)
740 painter.setPen(QPen(color, 2, Qt::SolidLine));
741 for (int j=1; j<curve.count; j++) {
742 if (curve.type == CurveData::CURVE_TYPE_CUSTOM)
743 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);
744 else
745 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);
749 QString ModelPrinter::createCurveImage(int idx, QTextDocument * document)
751 CurveImage image;
752 image.drawCurve(model.curves[idx], colors[idx]);
753 QString filename = QString("mydata://curve-%1-%2.png").arg((uint64_t)this).arg(idx);
754 if (document)
755 document->addResource(QTextDocument::ImageResource, QUrl(filename), image.get());
756 // qDebug() << "ModelPrinter::createCurveImage()" << idx << filename;
757 return filename;
760 QString ModelPrinter::printGlobalVarUnit(int idx)
762 return model.gvarData[idx].unitToString().toHtmlEscaped();
765 QString ModelPrinter::printGlobalVarPrec(int idx)
767 return model.gvarData[idx].precToString().toHtmlEscaped();
770 QString ModelPrinter::printGlobalVarMin(int idx)
772 return QString::number(model.gvarData[idx].getMinPrec());
775 QString ModelPrinter::printGlobalVarMax(int idx)
777 return QString::number(model.gvarData[idx].getMaxPrec());
780 QString ModelPrinter::printGlobalVarPopup(int idx)
782 return printBoolean(model.gvarData[idx].popup, BOOLEAN_YN);
785 QString ModelPrinter::printOutputValueGVar(int val)
787 QString result = "";
788 if (abs(val) > 10000) {
789 if (val < 0)
790 result = "-";
791 result.append(RawSource(SOURCE_TYPE_GVAR, abs(val)-10001).toString(&model));
793 else {
794 if (val >= 0)
795 result = "+";
796 result.append(QString::number((qreal)val/10, 'f', 1) + "%");
798 return result;
801 QString ModelPrinter::printOutputOffset(int idx)
803 return printOutputValueGVar(model.limitData[idx].offset);
806 QString ModelPrinter::printOutputMin(int idx)
808 return printOutputValueGVar(model.limitData[idx].min);
811 QString ModelPrinter::printOutputMax(int idx)
813 return printOutputValueGVar(model.limitData[idx].max);
816 QString ModelPrinter::printOutputRevert(int idx)
818 return model.limitData[idx].revertToString();
821 QString ModelPrinter::printOutputPpmCenter(int idx)
823 return QString::number(model.limitData[idx].ppmCenter + 1500);
826 QString ModelPrinter::printOutputCurve(int idx)
828 return CurveReference(CurveReference::CURVE_REF_CUSTOM, model.limitData[idx].curve.value).toString(&model, false);
831 QString ModelPrinter::printOutputSymetrical(int idx)
833 return printBoolean(model.limitData[idx].symetrical, BOOLEAN_YN);
836 QString ModelPrinter::printSettingsOther()
838 QStringList str;
839 str << printLabelValue(tr("Extended Limits"), printBoolean(model.extendedLimits, BOOLEAN_YESNO));
840 if (firmware->getCapability(HasDisplayText))
841 str << printLabelValue(tr("Display Checklist"), printBoolean(model.displayChecklist, BOOLEAN_YESNO));
842 if (firmware->getCapability(GlobalFunctions))
843 str << printLabelValue(tr("Global Functions"), printBoolean(!model.noGlobalFunctions, BOOLEAN_YESNO));
844 return str.join(" ");
847 QString ModelPrinter::printSwitchWarnings()
849 QStringList str;
850 Boards board = firmware->getBoard();
851 uint64_t switchStates = model.switchWarningStates;
852 uint64_t value;
854 for (int idx=0; idx<board.getCapability(Board::Switches); idx++) {
855 Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board.getBoardType(), idx);
856 switchInfo.config = Board::SwitchType(generalSettings.switchConfig[idx]);
857 if (switchInfo.config == Board::SWITCH_NOT_AVAILABLE || switchInfo.config == Board::SWITCH_TOGGLE) {
858 continue;
860 if (!(model.switchWarningEnable & (1 << idx))) {
861 if (IS_HORUS_OR_TARANIS(board.getBoardType())) {
862 value = (switchStates >> (2*idx)) & 0x03;
864 else {
865 value = (idx==0 ? switchStates & 0x3 : switchStates & 0x1);
866 switchStates >>= (idx==0 ? 2 : 1);
868 str += RawSwitch(SWITCH_TYPE_SWITCH, 1+idx*3+value).toString(board.getBoardType(), &generalSettings, &model);
871 return (str.isEmpty() ? tr("None") : str.join(" ")) ;
874 QString ModelPrinter::printPotWarnings()
876 QStringList str;
877 int genAryIdx = 0;
878 Boards board = firmware->getBoard();
879 if (model.potsWarningMode) {
880 for (int i=0; i<board.getCapability(Board::Pots)+board.getCapability(Board::Sliders); i++) {
881 RawSource src(SOURCE_TYPE_STICK, CPN_MAX_STICKS + i);
882 if ((src.isPot(&genAryIdx) && generalSettings.isPotAvailable(genAryIdx)) || (src.isSlider(&genAryIdx) && generalSettings.isSliderAvailable(genAryIdx))) {
883 if (!model.potsWarningEnabled[i])
884 str += src.toString(&model, &generalSettings);
888 str << printLabelValue(tr("Mode"), printPotsWarningMode());
889 return str.join(" ");
892 QString ModelPrinter::printPotsWarningMode()
894 switch (model.potsWarningMode) {
895 case 0:
896 return tr("OFF");
897 case 1:
898 return tr("Manual");
899 case 2:
900 return tr("Auto");
901 default:
902 return CPN_STR_UNKNOWN_ITEM;
906 QString ModelPrinter::printFailsafe(int idx)
908 QStringList strl;
909 ModuleData module = model.moduleData[idx];
910 strl << printLabelValue(tr("Failsafe Mode"), printFailsafeMode(module.failsafeMode));
911 if (module.failsafeMode == FAILSAFE_CUSTOM) {
912 for (int i=0; i<module.channelsCount; i++) {
913 //strl << QString("%1(%2)").arg(printChannelName(module.channelsStart + i).trimmed()).arg(printFailsafeValue(module.failsafeChannels[i]));
914 strl << printLabelValue(printChannelName(module.channelsStart + i).trimmed(), printFailsafeValue(module.failsafeChannels[i]));
917 return strl.join(" ");
920 QString ModelPrinter::printFailsafeValue(int val)
922 switch (val) {
923 case 2000:
924 return tr("Hold");
925 case 2001:
926 return tr("No Pulse");
927 default:
928 return QString("%1%").arg(QString::number(divRoundClosest(val * 1000, 1024) / 10.0));
932 QString ModelPrinter::printFailsafeMode(unsigned int fsmode)
934 switch (fsmode) {
935 case FAILSAFE_NOT_SET:
936 return tr("Not set");
937 case FAILSAFE_HOLD:
938 return tr("Hold");
939 case FAILSAFE_CUSTOM:
940 return tr("Custom");
941 case FAILSAFE_NOPULSES:
942 return tr("No pulses");
943 case FAILSAFE_RECEIVER:
944 return tr("Receiver");
945 default:
946 return CPN_STR_UNKNOWN_ITEM;
950 QString ModelPrinter::printTimerCountdownBeep(unsigned int countdownBeep)
952 switch (countdownBeep) {
953 case TimerData::COUNTDOWN_SILENT:
954 return tr("Silent");
955 case TimerData::COUNTDOWN_BEEPS:
956 return tr("Beeps");
957 case TimerData::COUNTDOWN_VOICE:
958 return tr("Voice");
959 case TimerData::COUNTDOWN_HAPTIC:
960 return tr("Haptic");
961 default:
962 return CPN_STR_UNKNOWN_ITEM;
966 QString ModelPrinter::printTimerPersistent(unsigned int persistent)
968 switch (persistent) {
969 case 0:
970 return tr("OFF");
971 case 1:
972 return tr("Flight");
973 case 2:
974 return tr("Manual reset");
975 default:
976 return CPN_STR_UNKNOWN_ITEM;
980 QString ModelPrinter::printSettingsTrim()
982 QStringList str;
983 str << printLabelValue(tr("Step"), printTrimIncrementMode());
984 if (IS_ARM(firmware->getBoard()))
985 str << printLabelValue(tr("Display"), printTrimsDisplayMode());
986 str << printLabelValue(tr("Extended"), printBoolean(model.extendedTrims, BOOLEAN_YESNO));
987 return str.join(" ");
990 QString ModelPrinter::printThrottleSource(int idx)
992 Boards board = firmware->getBoard();
993 int chnstart = board.getCapability(Board::Pots)+board.getCapability(Board::Sliders);
994 if (idx == 0)
995 return "THR";
996 else if (idx < (chnstart+1))
997 return firmware->getAnalogInputName(idx+board.getCapability(Board::Sticks)-1);
998 else
999 return RawSource(SOURCE_TYPE_CH, idx-chnstart-1).toString(&model, &generalSettings);
1002 QString ModelPrinter::printTrimsDisplayMode()
1004 switch (model.trimsDisplay) {
1005 case 0:
1006 return tr("Never");
1007 case 1:
1008 return tr("On Change");
1009 case 2:
1010 return tr("Always");
1011 default:
1012 return CPN_STR_UNKNOWN_ITEM;
1016 QString ModelPrinter::printModuleType(int idx)
1018 return ModuleData::indexToString(idx, firmware);
1021 QString ModelPrinter::printPxxPower(int power)
1023 static const char *strings[] = {
1024 "10mW", "100mW", "500mW", "3W"
1026 return CHECK_IN_ARRAY(strings, power);
1029 QString ModelPrinter::printThrottle()
1031 QStringList result;
1032 result << printLabelValue(tr("Source"), printThrottleSource(model.thrTraceSrc));
1033 result << printLabelValue(tr("Trim idle only"), printBoolean(model.thrTrim, BOOLEAN_YESNO));
1034 result << printLabelValue(tr("Warning"), printBoolean(!model.disableThrottleWarning, BOOLEAN_YESNO));
1035 result << printLabelValue(tr("Reversed"), printBoolean(model.throttleReversed, BOOLEAN_YESNO));
1036 return result.join(" ");
1039 QString ModelPrinter::printPPMFrameLength(int ppmFL)
1041 double result = (((double)ppmFL * 5) + 225) / 10;
1042 return QString::number(result);
1045 QString ModelPrinter::printTimerName(int idx)
1047 QString result;
1048 result = tr("Tmr") + QString("%1").arg(idx+1);
1049 if (firmware->getCapability(TimersName) && model.timers[idx].name[0])
1050 result.append(":" + QString(model.timers[idx].name));
1052 return result;
1055 QString ModelPrinter::printTimerTimeValue(unsigned int val)
1057 return printTimeValue(val, MASK_TIMEVALUE_HRSMINS | MASK_TIMEVALUE_ZEROHRS);
1060 QString ModelPrinter::printTimerMinuteBeep(bool mb)
1062 return printBoolean(mb, BOOLEAN_YESNO);
1065 QString ModelPrinter::printTelemetryProtocol(unsigned int val)
1067 switch (val) {
1068 case 0:
1069 return tr("FrSky S.PORT");
1070 case 1:
1071 return tr("FrSky D");
1072 case 2:
1073 return tr("FrSky D (cable)");
1074 default:
1075 return CPN_STR_UNKNOWN_ITEM;
1079 QString ModelPrinter::printRssiAlarmsDisabled(bool mb)
1081 return printBoolean(!mb, BOOLEAN_ENABLEDISABLE);
1084 QString ModelPrinter::printTelemetrySource(int val)
1086 QStringList strings = QStringList() << tr("None");
1088 for (int i=1; i<=CPN_MAX_SENSORS; ++i) {
1089 strings << QString("%1").arg(model.sensorData[i-1].label);
1092 return QString("%1%2").arg((val < 0 ? "-" : "")).arg(strings.value(abs(val)));
1095 QString ModelPrinter::printVarioSource(unsigned int val)
1097 switch (val) {
1098 case TELEMETRY_VARIO_SOURCE_ALTI:
1099 return tr("Alti");
1100 case TELEMETRY_VARIO_SOURCE_ALTI_PLUS:
1101 return tr("Alti+");
1102 case TELEMETRY_VARIO_SOURCE_VSPEED:
1103 return tr("VSpeed");
1104 case TELEMETRY_VARIO_SOURCE_A1:
1105 return tr("A1");
1106 case TELEMETRY_VARIO_SOURCE_A2:
1107 return tr("A2");
1108 default:
1109 return CPN_STR_UNKNOWN_ITEM;
1113 QString ModelPrinter::printVarioCenterSilent(bool mb)
1115 return printBoolean(mb, BOOLEAN_YESNO);
1118 QString ModelPrinter::printVoltsSource(unsigned int val)
1120 switch (val) {
1121 case TELEMETRY_VOLTS_SOURCE_A1:
1122 return tr("A1");
1123 case TELEMETRY_VOLTS_SOURCE_A2:
1124 return tr("A2");
1125 case TELEMETRY_VOLTS_SOURCE_A3:
1126 return tr("A3");
1127 case TELEMETRY_VOLTS_SOURCE_A4:
1128 return tr("A4");
1129 case TELEMETRY_VOLTS_SOURCE_FAS:
1130 return tr("FAS");
1131 case TELEMETRY_VOLTS_SOURCE_CELLS:
1132 return tr("Cells");
1133 default:
1134 return CPN_STR_UNKNOWN_ITEM;
1138 QString ModelPrinter::printCurrentSource(unsigned int val)
1140 switch (val) {
1141 case TELEMETRY_CURRENT_SOURCE_NONE:
1142 return tr("None");
1143 case TELEMETRY_CURRENT_SOURCE_A1:
1144 return tr("A1");
1145 case TELEMETRY_CURRENT_SOURCE_A2:
1146 return tr("A2");
1147 case TELEMETRY_CURRENT_SOURCE_A3:
1148 return tr("A3");
1149 case TELEMETRY_CURRENT_SOURCE_A4:
1150 return tr("A4");
1151 case TELEMETRY_CURRENT_SOURCE_FAS:
1152 return tr("FAS");
1153 default:
1154 return CPN_STR_UNKNOWN_ITEM;
1158 QString ModelPrinter::printMahPersistent(bool mb)
1160 return printBoolean(mb, BOOLEAN_YESNO);
1163 QString ModelPrinter::printIgnoreSensorIds(bool mb)
1165 return printBoolean(mb, BOOLEAN_ENABLEDISABLE);
1168 QString ModelPrinter::printSensorType(unsigned int val)
1170 switch (val) {
1171 case SensorData::TELEM_TYPE_CUSTOM:
1172 return tr("Custom");
1173 case SensorData::TELEM_TYPE_CALCULATED:
1174 return tr("Calculated");
1175 default:
1176 return CPN_STR_UNKNOWN_ITEM;
1180 QString ModelPrinter::printSensorFormula(unsigned int val)
1182 switch (val) {
1183 case SensorData::TELEM_FORMULA_ADD:
1184 return tr("Add");
1185 case SensorData::TELEM_FORMULA_AVERAGE:
1186 return tr("Average");
1187 case SensorData::TELEM_FORMULA_MIN:
1188 return tr("Min");
1189 case SensorData::TELEM_FORMULA_MAX:
1190 return tr("Max");
1191 case SensorData::TELEM_FORMULA_MULTIPLY:
1192 return tr("Multiply");
1193 case SensorData::TELEM_FORMULA_TOTALIZE:
1194 return tr("Totalise");
1195 case SensorData::TELEM_FORMULA_CELL:
1196 return tr("Cell");
1197 case SensorData::TELEM_FORMULA_CONSUMPTION:
1198 return tr("Consumption");
1199 case SensorData::TELEM_FORMULA_DIST:
1200 return tr("Distance");
1201 default:
1202 return CPN_STR_UNKNOWN_ITEM;
1206 QString ModelPrinter::printSensorCells(unsigned int val)
1208 QStringList strings;
1210 strings << tr("Lowest");
1211 for (int i=1; i<=6; i++)
1212 strings << tr("Cell %1").arg(i);
1213 strings << tr("Highest") << tr("Delta");
1215 return strings.value(val);
1218 QString ModelPrinter::printSensorTypeCond(unsigned int idx)
1220 if (!model.sensorData[idx].isAvailable())
1221 return "";
1222 else
1223 return printSensorType(model.sensorData[idx].type);
1226 QString ModelPrinter::printSensorDetails(unsigned int idx)
1228 QString str = "";
1229 SensorData sensor = model.sensorData[idx];
1231 if (!sensor.isAvailable())
1232 return str;
1234 bool isConfigurable = false;
1235 bool gpsFieldsPrinted = false;
1236 bool cellsFieldsPrinted = false;
1237 bool consFieldsPrinted = false;
1238 bool ratioFieldsPrinted = false;
1239 bool totalizeFieldsPrinted = false;
1240 bool sources12FieldsPrinted = false;
1241 bool sources34FieldsPrinted = false;
1243 str.append(doTableCell(printSensorTypeCond(idx)));
1245 QString tc = "";
1246 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
1247 isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL);
1248 gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST);
1249 cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL);
1250 consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION);
1251 sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY);
1252 sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY);
1253 totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE);
1255 tc.append(printLabelValue(tr("Formula"), printSensorFormula(sensor.formula)));
1257 else {
1258 isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL;
1259 ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL);
1261 tc.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper()));
1262 tc.append(printLabelValue(tr("Instance"), QString::number(sensor.instance)));
1264 if (cellsFieldsPrinted) {
1265 tc.append(printLabelValue(tr("Sensor"), QString("%1 > %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index))));
1267 if (sources12FieldsPrinted) {
1268 QStringList srcs;
1269 for (int i=0;i<4;i++) {
1270 if (i < 2 || sources34FieldsPrinted) {
1271 srcs << printTelemetrySource(sensor.sources[i]);
1274 tc.append(printLabelValues(tr("Sources"), srcs));
1276 if (consFieldsPrinted || totalizeFieldsPrinted)
1277 tc.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps)));
1278 if (gpsFieldsPrinted) {
1279 tc.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps)));
1280 tc.append(printLabelValue(tr("Alt."), printTelemetrySource(sensor.alt)));
1282 if (ratioFieldsPrinted && sensor.unit == SensorData::UNIT_RPMS) {
1283 tc.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio)));
1284 tc.append(printLabelValue(tr("Multi."), QString::number(sensor.offset)));
1286 str.append(doTableCell(tc));
1288 tc = sensor.unitString();
1289 tc = tc.trimmed() == "" ? "-" : tc;
1290 str.append(doTableCell(tc));
1292 if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT)
1293 tc = QString::number(sensor.prec);
1294 else
1295 tc = "";
1296 str.append(doTableCell(tc));
1298 if (!ratioFieldsPrinted) {
1299 str.append(doTableCell(""));
1300 str.append(doTableCell(""));
1302 else if (sensor.unit != SensorData::UNIT_RPMS) {
1303 int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec);
1304 str.append(doTableCell(QString::number((float)sensor.ratio / prec)));
1305 str.append(doTableCell(QString::number((float)sensor.offset / prec)));
1308 if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable)
1309 str.append(doTableCell(printBoolean(sensor.autoOffset, BOOLEAN_YN)));
1310 else
1311 str.append(doTableCell(""));
1313 if (isConfigurable)
1314 str.append(doTableCell(printBoolean(sensor.filter, BOOLEAN_YN)));
1315 else
1316 str.append(doTableCell(""));
1318 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED)
1319 str.append(doTableCell(printBoolean(sensor.persistent, BOOLEAN_YN)));
1320 else
1321 str.append(doTableCell(""));
1323 str.append(doTableCell(printBoolean(sensor.onlyPositive, BOOLEAN_YN)));
1324 str.append(doTableCell(printBoolean(sensor.logs, BOOLEAN_YN), false));
1325 return str;
1328 QString ModelPrinter::printSensorParams(unsigned int idx)
1330 QString str = "";
1331 SensorData sensor = model.sensorData[idx];
1333 if (!sensor.isAvailable())
1334 return str;
1336 bool isConfigurable = false;
1337 bool gpsFieldsPrinted = false;
1338 bool cellsFieldsPrinted = false;
1339 bool consFieldsPrinted = false;
1340 bool ratioFieldsPrinted = false;
1341 bool totalizeFieldsPrinted = false;
1342 bool sources12FieldsPrinted = false;
1343 bool sources34FieldsPrinted = false;
1345 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
1346 isConfigurable = (sensor.formula < SensorData::TELEM_FORMULA_CELL);
1347 gpsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_DIST);
1348 cellsFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CELL);
1349 consFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_CONSUMPTION);
1350 sources12FieldsPrinted = (sensor.formula <= SensorData::TELEM_FORMULA_MULTIPLY);
1351 sources34FieldsPrinted = (sensor.formula < SensorData::TELEM_FORMULA_MULTIPLY);
1352 totalizeFieldsPrinted = (sensor.formula == SensorData::TELEM_FORMULA_TOTALIZE);
1354 str.append(printLabelValue(tr("F"), printSensorFormula(sensor.formula)));
1356 else {
1357 isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL;
1358 ratioFieldsPrinted = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL);
1360 str.append(printLabelValue(tr("Id"), QString::number(sensor.id,16).toUpper()));
1361 str.append(printLabelValue(tr("Inst"), QString::number(sensor.instance)));
1363 if (cellsFieldsPrinted) {
1364 str.append(printLabelValue(tr("Sensor"), QString("%1 %2").arg(printTelemetrySource(sensor.source), false).arg(printSensorCells(sensor.index))));
1366 if (sources12FieldsPrinted) {
1367 QStringList srcs;
1368 for (int i=0;i<4;i++) {
1369 if (i < 2 || sources34FieldsPrinted) {
1370 srcs << printTelemetrySource(sensor.sources[i]);
1373 str.append(printLabelValues(tr("Sources"), srcs));
1375 if (consFieldsPrinted || totalizeFieldsPrinted)
1376 str.append(printLabelValue(tr("Sensor"), printTelemetrySource(sensor.amps)));
1377 if (gpsFieldsPrinted) {
1378 str.append(printLabelValue(tr("GPS"), printTelemetrySource(sensor.gps)));
1379 str.append(printLabelValue(tr("Alt"), printTelemetrySource(sensor.alt)));
1381 QString u = sensor.unitString();
1382 u = u.trimmed() == "" ? "-" : u;
1383 str.append(printLabelValue(tr("Unit"), u));
1384 if (isConfigurable && sensor.unit != SensorData::UNIT_FAHRENHEIT)
1385 str.append(printLabelValue(tr("Prec"), QString::number(sensor.prec)));
1386 if (ratioFieldsPrinted) {
1387 if (sensor.unit != SensorData::UNIT_RPMS) {
1388 int prec = sensor.prec == 0 ? 1 : pow(10, sensor.prec);
1389 str.append(printLabelValue(tr("Ratio"), QString::number((float)sensor.ratio / prec)));
1390 str.append(printLabelValue(tr("Offset"), QString::number((float)sensor.offset / prec)));
1392 else if (sensor.unit == SensorData::UNIT_RPMS) {
1393 str.append(printLabelValue(tr("Blades"), QString::number(sensor.ratio)));
1394 str.append(printLabelValue(tr("Multi"), QString::number(sensor.offset)));
1397 if (sensor.unit != SensorData::UNIT_RPMS && isConfigurable)
1398 str.append(printLabelValue(tr("A/Offset"), printBoolean(sensor.autoOffset, BOOLEAN_YN)));
1399 if (isConfigurable)
1400 str.append(printLabelValue(tr("Filter"), printBoolean(sensor.filter, BOOLEAN_YN)));
1401 if (sensor.type == SensorData::TELEM_TYPE_CALCULATED)
1402 str.append(printLabelValue(tr("Persist"), printBoolean(sensor.persistent, BOOLEAN_YN)));
1403 str.append(printLabelValue(tr("Positive"), printBoolean(sensor.onlyPositive, BOOLEAN_YN)));
1404 str.append(printLabelValue(tr("Log"), printBoolean(sensor.logs, BOOLEAN_YN), false));
1405 return str;
1408 QString ModelPrinter::printTelemetryScreenType(unsigned int val)
1410 switch (val) {
1411 case TelemetryScreenEnum::TELEMETRY_SCREEN_NONE:
1412 return tr("None");
1413 case TelemetryScreenEnum::TELEMETRY_SCREEN_NUMBERS:
1414 return tr("Numbers");
1415 case TelemetryScreenEnum::TELEMETRY_SCREEN_BARS:
1416 return tr("Bars");
1417 case TelemetryScreenEnum::TELEMETRY_SCREEN_SCRIPT:
1418 return tr("Script");
1419 default:
1420 return CPN_STR_UNKNOWN_ITEM;
1424 QString ModelPrinter::printTelemetryScreen(unsigned int idx, unsigned int line, unsigned int width)
1426 QStringList strl;
1427 QStringList hd;
1428 FrSkyScreenData screen = model.frsky.screens[idx];
1429 hd << ""; // blank 1st column
1430 strl << "";
1431 if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_NUMBERS) {
1432 if (line == 0) {
1433 for (int c=0; c<firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
1434 hd << tr("Source");
1437 for (int c=0; c<firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
1438 RawSource source = screen.body.lines[line].source[c];
1439 strl << source.toString(&model, &generalSettings);
1442 else if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_BARS) {
1443 if (line == 0) {
1444 hd << tr("Source") << tr("Min") << tr("Max");
1446 RawSource source = screen.body.bars[line].source;
1447 RawSourceRange range = source.getRange(&model, generalSettings);
1448 strl << source.toString(&model, &generalSettings);
1449 QString unit;
1450 QString minstr;
1451 QString maxstr;
1452 if (source.isTimeBased()){
1453 minstr = printTimeValue((float)screen.body.bars[line].barMin, MASK_TIMEVALUE_HRSMINS);
1454 maxstr = printTimeValue((float)screen.body.bars[line].barMax, MASK_TIMEVALUE_HRSMINS);
1456 else {
1457 minstr = QString::number(range.getValue(screen.body.bars[line].barMin));
1458 maxstr = QString::number(range.getValue(screen.body.bars[line].barMax));
1459 unit = range.unit;
1461 strl << QString("%1%2").arg(minstr).arg(unit);
1462 strl << QString("%1%2").arg(maxstr).arg(unit);
1464 else if (screen.type == TelemetryScreenEnum::TELEMETRY_SCREEN_SCRIPT && line == 0) {
1465 hd << tr("Filename");
1466 strl << QString("%1.lua").arg(screen.body.script.filename);
1468 return (hd.count() > 1 ? doTableRow(hd, width / hd.count(), "left", "", true) : "" ) + doTableRow(strl, width / strl.count());