Files
edgetx-gx12/companion/src/firmwares/sensordata.cpp
T
Neil HorneandGitHub a9812688b0 Companion 2.5 (#555)
* Introduce yaml in Companion

* EdgeTX Companion
Replace some OpenTX refs
Data conversion to v220
Update Timers ui to cater for mode field split

* Fix some simulators crashing when started from Companion

* Disable firmware and app update checking and downloads from companion

* Some more rebranding

* Disable manual download actions and another rebrand

* Fix cmake warning

* Fix mode ON conversion gtests and enable switch in model edit

* Stop update message when updates turned off

* OpenTX #8580 Fixes missing negative sign for telemetry sources

* OpenTX #8604 fix curves tooltip colors

* OpenTX #8616 Fix release notes url references

* OpenTX #8548 Align file sync folder references

* OpenTX #8548 Link field once

* OpenTX #8550 Confirmation prompt before write models and settings to radio

* OpenTX #8550 Translate strings

* Title and splash images

* Change DEB package maintainer

* Start NV14

* NV14 continued

* NV14 sim

* Timer countdownStart data conversion and ui fixes

* Fix screen width

* Merge OpenTX PR 8675

* NV14 sim add computer keys and mouse to activate some functions

* Only allow supported protocols for each module on conversion

* Clear mismatched modules on conversion

* Replace colorlcd theme and layout blobs with detailed data structs in companion

* NV14 has 2 topbar zones

* Fix layout init

* Fix layout init again

* Reset to eepromimportexport.h to main branch

* Import/export does not like enum fields

* Model edit add custom screens tab for color and bw telemetry

* Free memory
2021-09-08 10:56:39 +10:00

515 lines
13 KiB
C++

/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "sensordata.h"
#include "modeldata.h"
#include "eeprominterface.h"
#include "compounditemmodels.h"
void SensorData::updateUnit()
{
if (type == TELEM_TYPE_CALCULATED) {
if (formula == TELEM_FORMULA_CONSUMPTION)
unit = UNIT_MAH;
}
}
QString SensorData::nameToString(int index) const
{
return DataHelpers::getElementName(tr("TELE"), index + 1, label);
}
QString SensorData::getOrigin(const ModelData * model) const
{
if (type != TELEM_TYPE_CUSTOM || !id)
return QString();
const ModuleData & module = model->moduleData[moduleIdx];
if (module.isPxx2Module() && rxIdx <= 2 && module.access.receivers & (1 << rxIdx)) {
return QString(module.access.receiverName[rxIdx]);
}
else {
return QString();
}
}
bool SensorData::isEmpty() const
{
return (!isAvailable() && type == 0 && id == 0 && subid == 0 && instance == 0 && rxIdx == 0 && moduleIdx == 0 && unit == 0 && ratio == 0 && prec == 0 && offset == 0 && autoOffset == 0 && filter == 0 && onlyPositive == 0 && logs == 0);
}
QString SensorData::idToString() const
{
return idToString(id);
}
QString SensorData::typeToString() const
{
return typeToString(type);
}
QString SensorData::formulaToString() const
{
return formulaToString(formula);
}
QString SensorData::cellIndexToString() const
{
return cellIndexToString(index);
}
QString SensorData::unitToString(bool hideRaw) const
{
return unitToString(unit, hideRaw);
}
QString SensorData::precToString() const
{
return precToString(prec);
}
int SensorData::getMask() const
{
int mask = 0;
if (type == TELEM_TYPE_CALCULATED) {
if (formula < TELEM_FORMULA_CELL)
mask |= SENSOR_ISCONFIGURABLE | SENSOR_HAS_POSITIVE;
if (formula == TELEM_FORMULA_DIST)
mask |= SENSOR_HAS_GPS;
if (formula == TELEM_FORMULA_CELL)
mask |= SENSOR_HAS_CELLS;
if (formula == TELEM_FORMULA_CONSUMPTION)
mask |= SENSOR_HAS_CONSUMPTION;
if (formula <= TELEM_FORMULA_MULTIPLY)
mask |= SENSOR_HAS_SOURCES_12;
if (formula < TELEM_FORMULA_MULTIPLY)
mask |= SENSOR_HAS_SOURCES_34;
if (formula == TELEM_FORMULA_TOTALIZE)
mask |= SENSOR_HAS_TOTALIZE;
}
else {
if (unit < UNIT_FIRST_VIRTUAL)
mask |= (SENSOR_ISCONFIGURABLE | SENSOR_HAS_RATIO | SENSOR_HAS_POSITIVE);
}
if (mask & SENSOR_ISCONFIGURABLE && unit != UNIT_FAHRENHEIT)
mask |= SENSOR_HAS_PRECISION;
return mask;
}
constexpr char FMT_LABEL[] { "<b>%1:</b> " };
constexpr char FMT_VALUE[] { "%1 " };
constexpr char FMT_LABEL_VALUE[] { "<b>%1:</b> %2 " };
QString SensorData::paramsToString(const ModelData * model) const
{
if (!isAvailable())
return "";
QString str;
int mask = getMask();
if (type == TELEM_TYPE_CALCULATED) {
str.append(QString(FMT_LABEL_VALUE).arg(tr("Formula")).arg(formulaToString()));
}
else {
str.append(QString(FMT_LABEL_VALUE).arg(tr("Id")).arg(idToString()));
str.append(QString(FMT_LABEL_VALUE).arg(tr("Instance")).arg(instance));
}
if (mask & SENSOR_HAS_CELLS) {
str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, source)));
str.append(QString(FMT_VALUE).arg(cellIndexToString()));
}
if (mask & SENSOR_HAS_SOURCES_12) {
str.append(QString(FMT_LABEL).arg(tr("Sources")));
for (int i = 0; i < 4; i++) {
if (i < 2 || mask & SENSOR_HAS_SOURCES_34) {
str.append(QString(FMT_VALUE).arg(sourceToString(model, sources[i])));
}
}
}
if (mask & SENSOR_HAS_CONSUMPTION || mask & SENSOR_HAS_TOTALIZE)
str.append(QString(FMT_LABEL_VALUE).arg(tr("Sensor")).arg(sourceToString(model, amps)));
if (mask & SENSOR_HAS_GPS) {
str.append(QString(FMT_LABEL_VALUE).arg(tr("GPS")).arg(sourceToString(model, gps)));
str.append(QString(FMT_LABEL_VALUE).arg(tr("Alt")).arg(sourceToString(model, alt)));
}
if (mask & SENSOR_ISCONFIGURABLE)
str.append(QString(FMT_LABEL_VALUE).arg(tr("Unit")).arg(unitToString()));
if (mask & SENSOR_HAS_PRECISION)
str.append(QString(FMT_LABEL_VALUE).arg(tr("Precision")).arg(precToString()));
if (mask & SENSOR_HAS_RATIO) {
if (unit != UNIT_RPMS) {
int precsn = prec == 0 ? 1 : pow(10, prec);
str.append(QString(FMT_LABEL_VALUE).arg(tr("Ratio")).arg((float)ratio / 10));
str.append(QString(FMT_LABEL_VALUE).arg(tr("Offset")).arg(QString::number((float)offset / precsn, 'f', prec)));
str.append(QString(FMT_LABEL_VALUE).arg(tr("Auto Offset")).arg(DataHelpers::boolToString(autoOffset, DataHelpers::BOOL_FMT_YN)));
}
else {
str.append(QString(FMT_LABEL_VALUE).arg(tr("Blades")).arg(ratio)); // TODO refactor to dedicated RPMS field
str.append(QString(FMT_LABEL_VALUE).arg(tr("Multiplier")).arg(offset)); // TODO refactor to dedicated RPMS field
}
}
if (mask & SENSOR_ISCONFIGURABLE)
str.append(QString(FMT_LABEL_VALUE).arg(tr("Filter")).arg(DataHelpers::boolToString(filter, DataHelpers::BOOL_FMT_YN)));
if (type == TELEM_TYPE_CALCULATED)
str.append(QString(FMT_LABEL_VALUE).arg(tr("Persist")).arg(DataHelpers::boolToString(persistent, DataHelpers::BOOL_FMT_YN)));
if (mask & SENSOR_HAS_POSITIVE)
str.append(QString(FMT_LABEL_VALUE).arg(tr("Positive")).arg(DataHelpers::boolToString(onlyPositive, DataHelpers::BOOL_FMT_YN)));
str.append(QString(FMT_LABEL_VALUE).arg(tr("Log")).arg(DataHelpers::boolToString(logs, DataHelpers::BOOL_FMT_YN)));
return str;
}
FieldRange SensorData::getRatioRange() const
{
FieldRange result;
if (unit == SensorData::UNIT_RPMS) {
result.decimals = 0;
result.max = 30000;
result.min = 1;
result.step = 1;
}
else {
result.decimals = 1;
result.max = 30000;
result.min = 0;
result.step = 0.1;
}
return result;
}
FieldRange SensorData::getOffsetRange() const
{
FieldRange result;
if (unit == SensorData::UNIT_RPMS) {
result.decimals = 0;
result.max = 30000;
result.min = 1;
result.step = 1;
}
else {
result.decimals = prec;
result.max = 30000.0f / powf(10.0f, prec);
result.min = -result.max;
result.step = pow(0.1, prec);
}
return result;
}
void SensorData::formulaChanged()
{
switch (formula) {
case TELEM_FORMULA_CELL:
prec = 2;
unit = UNIT_VOLTS;
break;
case TELEM_FORMULA_CONSUMPTION:
prec = 0;
unit = UNIT_MAH;
break;
case TELEM_FORMULA_DIST:
prec = 0;
unit = UNIT_METERS;
break;
}
}
void SensorData::unitChanged()
{
if (unit == UNIT_FAHRENHEIT)
prec = 0;
}
// static
QString SensorData::idToString(const int value)
{
return QString::number(value, 16).toUpper();
}
// static
QString SensorData::typeToString(const int value)
{
switch(value) {
case TELEM_TYPE_CUSTOM:
return tr("Custom");
case TELEM_TYPE_CALCULATED:
return tr("Calculated");
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
// static
QString SensorData::formulaToString(const int value)
{
switch(value) {
case TELEM_FORMULA_ADD:
return tr("Add");
case TELEM_FORMULA_AVERAGE:
return tr("Average");
case TELEM_FORMULA_MIN:
return tr("Minimum");
case TELEM_FORMULA_MAX:
return tr("Maximum");
case TELEM_FORMULA_MULTIPLY:
return tr("Multiply");
case TELEM_FORMULA_TOTALIZE:
return tr("Totalize");
case TELEM_FORMULA_CELL:
return tr("Cell");
case TELEM_FORMULA_CONSUMPTION:
return tr("Consumption");
case TELEM_FORMULA_DIST:
return tr("Distance");
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
// static
QString SensorData::cellIndexToString(const int value)
{
if (value == TELEM_CELL_INDEX_LOWEST)
return tr("Lowest");
if (value > TELEM_CELL_INDEX_LOWEST && value < TELEM_CELL_INDEX_HIGHEST)
return tr("Cell %1").arg(value - TELEM_CELL_INDEX_LOWEST);
if (value== TELEM_CELL_INDEX_HIGHEST)
return tr("Highest");
if (value == TELEM_CELL_INDEX_DELTA)
return tr("Delta");
return CPN_STR_UNKNOWN_ITEM;
}
// static
QString SensorData::precToString(const int value)
{
return QString("0.%1").arg(QString(value, '0'));
}
// static
QString SensorData::sourceToString(const ModelData * model, const int index, const bool positivesign)
{
if (model) {
const QString prfx = index < 0 ? "-" : positivesign ? "+" : "";
if (abs(index) > 0) {
const SensorData &sd = model->sensorData[abs(index) - 1];
if (sd.type == SensorData::TELEM_TYPE_CUSTOM)
return QString("%1%2 (%3)").arg(prfx).arg(sd.label).arg(sd.getOrigin(model));
else
return QString("%1%2").arg(prfx).arg(sd.label);
}
else
return CPN_STR_NONE_ITEM;
}
return "";
}
// static
QString SensorData::unitToString(const int value, bool hideRaw)
{
switch (value) {
case UNIT_RAW:
return hideRaw ? QString() : tr("Raw (-)");
case UNIT_VOLTS:
return tr("V");
case UNIT_AMPS:
return tr("A");
case UNIT_MILLIAMPS:
return tr("mA");
case UNIT_KTS:
return tr("kts");
case UNIT_METERS_PER_SECOND:
return tr("m/s");
case UNIT_KMH:
return tr("km/h");
case UNIT_MPH:
return tr("mph");
case UNIT_METERS:
return tr("m");
case UNIT_FEET:
return tr("f");
case UNIT_CELSIUS:
return tr("°C");
case UNIT_FAHRENHEIT:
return tr("°F");
case UNIT_PERCENT:
return tr("%");
case UNIT_MAH:
return tr("mAh");
case UNIT_WATTS:
return tr("W");
case UNIT_MILLIWATTS:
return tr("mW");
case UNIT_DB:
return tr("dB");
case UNIT_RPMS:
return tr("rpms");
case UNIT_G:
return tr("g");
case UNIT_DEGREE:
return tr("°");
case UNIT_RADIANS:
return tr("Rad");
case UNIT_HOURS:
return tr("hours");
case UNIT_MINUTES:
return tr("minutes");
case UNIT_SECONDS:
return tr("seconds");
case UNIT_CELLS:
return tr("V");
case UNIT_MILLILITERS_PER_MINUTE:
return tr("ml/minute");
case UNIT_HERZ:
return tr("Hertz");
case UNIT_MS:
return tr("mS");
case UNIT_US:
return tr("uS");
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
// static
bool SensorData::isSourceAvailable(const ModelData * model, const int index)
{
Firmware * firmware = getCurrentFirmware();
const int sensorcnt = firmware->getCapability(Sensors);
const int i = abs(index);
if (model && sensorcnt > 0 && i <= sensorcnt) {
if (i > 0)
return model->sensorData[i - 1].isAvailable();
else
return true;
}
return false;
}
#define RSSI_ID 0xF101
// static
bool SensorData::isRssiSensorAvailable(const ModelData * model, const int value)
{
if (value == 0)
return true;
else {
const SensorData &sensor = model->sensorData[abs(value) - 1];
return (sensor.isAvailable() && sensor.id == RSSI_ID);
}
}
// static
QString SensorData::rssiSensorToString(const ModelData * model, const int value)
{
if (value == 0)
return tr("(default)");
else
return sourceToString(model, value);
}
// static
AbstractStaticItemModel * SensorData::typeItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("sensordata.type");
for (int i = 0; i <= TELEM_TYPE_LAST; i++) {
mdl->appendToItemList(typeToString(i), i);
}
mdl->loadItemList();
return mdl;
}
// static
AbstractStaticItemModel * SensorData::formulaItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("sensordata.formula");
for (int i = 0; i <= TELEM_FORMULA_LAST; i++) {
mdl->appendToItemList(formulaToString(i), i);
}
mdl->loadItemList();
return mdl;
}
// static
AbstractStaticItemModel * SensorData::cellIndexItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("sensordata.cellindex");
for (int i = 0; i <= TELEM_CELL_INDEX_LAST; i++) {
mdl->appendToItemList(cellIndexToString(i), i);
}
mdl->loadItemList();
return mdl;
}
// static
AbstractStaticItemModel * SensorData::unitItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("sensordata.unit");
for (int i = 0; i <= UNIT_MAX; i++) {
QString str = unitToString(i, false);
if (str != CPN_STR_UNKNOWN_ITEM)
mdl->appendToItemList(str, i);
}
mdl->loadItemList();
return mdl;
}
// static
PrecisionItemModel * SensorData::precisionItemModel()
{
PrecisionItemModel * mdl = new PrecisionItemModel(0, 2);
mdl->setName("sensordata.precision");
return mdl;
}