From 005bd19588705bc76075a38fdaf1690a639efb14 Mon Sep 17 00:00:00 2001 From: Nigel Williams <8898943+nrw505@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:43:44 +1000 Subject: [PATCH] feat(cpn): support more than just FrSky S.Port telemetry simulation (#5410) --- companion/src/simulation/CMakeLists.txt | 3 + companion/src/simulation/simulatedgps.cpp | 160 + companion/src/simulation/simulatedgps.h | 68 + companion/src/simulation/simulatorinterface.h | 10 +- companion/src/simulation/telemetryprovider.h | 50 + .../simulation/telemetryprovidercrossfire.cpp | 536 +++ .../simulation/telemetryprovidercrossfire.h | 75 + .../simulation/telemetryprovidercrossfire.ui | 1596 +++++++++ .../src/simulation/telemetryproviderfrsky.cpp | 954 +++++ .../src/simulation/telemetryproviderfrsky.h | 112 + .../src/simulation/telemetryproviderfrsky.ui | 2669 ++++++++++++++ companion/src/simulation/telemetrysimu.cpp | 1161 ++----- companion/src/simulation/telemetrysimu.h | 125 +- companion/src/simulation/telemetrysimu.ui | 3072 ++--------------- radio/src/targets/simu/opentxsimulator.cpp | 35 +- radio/src/targets/simu/opentxsimulator.h | 4 +- radio/util/find_clang.py | 2 + 17 files changed, 6717 insertions(+), 3915 deletions(-) create mode 100644 companion/src/simulation/simulatedgps.cpp create mode 100644 companion/src/simulation/simulatedgps.h create mode 100644 companion/src/simulation/telemetryprovider.h create mode 100644 companion/src/simulation/telemetryprovidercrossfire.cpp create mode 100644 companion/src/simulation/telemetryprovidercrossfire.h create mode 100644 companion/src/simulation/telemetryprovidercrossfire.ui create mode 100644 companion/src/simulation/telemetryproviderfrsky.cpp create mode 100644 companion/src/simulation/telemetryproviderfrsky.h create mode 100644 companion/src/simulation/telemetryproviderfrsky.ui diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index c53fff8813e..5dcfe27be9a 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -10,7 +10,10 @@ set(${PROJECT_NAME}_NAMES simulatormainwindow simulatorstartupdialog simulatorwidget + simulatedgps telemetrysimu + telemetryprovidercrossfire + telemetryproviderfrsky trainersimu widgets/lcdwidget widgets/radiowidget diff --git a/companion/src/simulation/simulatedgps.cpp b/companion/src/simulation/simulatedgps.cpp new file mode 100644 index 00000000000..60d26fda081 --- /dev/null +++ b/companion/src/simulation/simulatedgps.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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 "simulatedgps.h" +#include +#include + +SimulatedGPS::SimulatedGPS() +{ + lat = 0; + lon = 0; + courseDegrees = 0; + speedKMH = 0; + altitude = 0; + satellites = 0; + dt = QDateTime::currentDateTime(); + running = false; + timer.setInterval(250); + connect(&timer, &QTimer::timeout, this, &SimulatedGPS::update); +} + +SimulatedGPS::~SimulatedGPS() +{ +} + +void SimulatedGPS::setDateTime(QDateTime dateTime) +{ + dt = dateTime; +} + +void SimulatedGPS::setDateTime(QString dateTime) +{ + if (dateTime.startsWith("*")) { + dt = QDateTime::currentDateTime(); + } else { + dt = QDateTime::fromString(dateTime, Qt::ISODate); + } +} + +void SimulatedGPS::setLatLon(QString latLon) +{ + QStringList coords = latLon.split(","); + if (coords.length() < 2) { + coords = latLon.split(" "); + } + lat = 0.0; + lon = 0.0; + if (coords.length() > 1) { + lat = coords[0].simplified().toDouble(); + lon = coords[1].simplified().toDouble(); + } else { + stop(); + } +} + +void SimulatedGPS::setCourseDegrees(double course) +{ + courseDegrees = course; +} + +void SimulatedGPS::setSpeedKMH(double speed) +{ + speedKMH = speed; +} + +void SimulatedGPS::setAltitude(double altitude) +{ + this->altitude = altitude; +} + +void SimulatedGPS::setSatelliteCount(int sats) +{ + satellites = sats; +} + +void SimulatedGPS::start() +{ + running = true; + timer.start(); +} + +void SimulatedGPS::stop() +{ + running = false; + timer.stop(); +} + +void SimulatedGPS::update() +{ + if (!running) { + return; + } + + dt = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); + emitDateTimeChange(); + + double b2 = lat; + double c2 = lon; + double d3 = speedKMH / 14400; + double f3 = courseDegrees; + double j2 = 6378.1; + double b3 = qRadiansToDegrees(qAsin( qSin(qDegreesToRadians(b2))*qCos(d3/j2) + qCos(qDegreesToRadians(b2))*qSin(d3/j2)*qCos(qDegreesToRadians(f3)))); + double bb3 = b3; + if (bb3 < 0) { + bb3 = bb3 * -1; + } + if (bb3 > 89.99) { + f3 = f3 + 180; + if (f3 > 360) { + f3 = f3 - 360; + } + courseDegrees = f3; + emit courseDegreesChanged(courseDegrees); + } + double c3 = qRadiansToDegrees(qDegreesToRadians(c2) + qAtan2(qSin(qDegreesToRadians(f3))*qSin(d3/j2)*qCos(qDegreesToRadians(b2)),qCos(d3/j2)-qSin(qDegreesToRadians(b2))*qSin(qDegreesToRadians(b3)))); + if (c3 > 180) { + c3 = c3 - 360; + } + if (c3 < -180) { + c3 = c3 + 360; + } + lat = b3; + lon = c3; + + emitPositionChange(); +} + +void SimulatedGPS::emitPositionChange() +{ + QString lats = QString::number(lat, 'f', 8); + QString lons = QString::number(lon, 'f', 8); + QString qs = lats + "," + lons; + + emit positionChanged(qs); +} + +void SimulatedGPS::emitDateTimeChange() +{ + QString formatted = dt.toString(Qt::ISODate); + emit dateTimeChanged(formatted); + emit dateTimeChanged(dt); +} diff --git a/companion/src/simulation/simulatedgps.h b/companion/src/simulation/simulatedgps.h new file mode 100644 index 00000000000..4c2b9bb6235 --- /dev/null +++ b/companion/src/simulation/simulatedgps.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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. + */ + +#pragma once + +#include +#include + +class SimulatedGPS : public QObject +{ + Q_OBJECT + + public: + SimulatedGPS(); + ~SimulatedGPS(); + + QDateTime dt; + double lat; + double lon; + double courseDegrees; + double speedKMH; + double altitude; // in meters + uint8_t satellites; + bool running; + + signals: + void positionChanged(const QString latLon); + void dateTimeChanged(const QString dateTime); + void dateTimeChanged(const QDateTime dateTime); + void courseDegreesChanged(double course); + + public slots: + void setDateTime(QDateTime dateTime); + void setDateTime(QString dateTime); + void setLatLon(QString latLon); + void setCourseDegrees(double course); + void setSpeedKMH(double speed); + void setAltitude(double altitude); + void setSatelliteCount(int sats); + void start(); + void stop(); + + protected slots: + void update(); + void emitPositionChange(); + void emitDateTimeChange(); + + private: + QTimer timer; +}; diff --git a/companion/src/simulation/simulatorinterface.h b/companion/src/simulation/simulatorinterface.h index 9613022b434..8546910da36 100644 --- a/companion/src/simulation/simulatorinterface.h +++ b/companion/src/simulation/simulatorinterface.h @@ -37,6 +37,13 @@ #define SIMULATOR_INTERFACE_HEARTBEAT_PERIOD 1000 // ms +enum SimulatorTelemetryProtocol { + SIMU_TELEMETRY_PROTOCOL_FRSKY_SPORT = 0, + SIMU_TELEMETRY_PROTOCOL_FRSKY_HUB, + SIMU_TELEMETRY_PROTOCOL_CROSSFIRE, + SIMU_TELEMETRY_PROTOCOL_COUNT +}; + class SimulatorInterface : public QObject { Q_OBJECT @@ -148,7 +155,8 @@ class SimulatorInterface : public QObject virtual void touchEvent(int type, int x, int y) = 0; virtual void lcdFlushed() = 0; virtual void setTrainerTimeout(uint16_t ms) = 0; - virtual void sendTelemetry(const QByteArray data) = 0; + virtual void sendInternalModuleTelemetry(const quint8 protocol, const QByteArray data) = 0; + virtual void sendExternalModuleTelemetry(const quint8 protocol, const QByteArray data) = 0; virtual void setLuaStateReloadPermanentScripts() = 0; virtual void addTracebackDevice(QIODevice * device) = 0; virtual void removeTracebackDevice(QIODevice * device) = 0; diff --git a/companion/src/simulation/telemetryprovider.h b/companion/src/simulation/telemetryprovider.h new file mode 100644 index 00000000000..a382e628500 --- /dev/null +++ b/companion/src/simulation/telemetryprovider.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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. + */ + +#pragma once + +#include "simulatorinterface.h" +#include + +class TelemetryProvider +{ + public: + virtual ~TelemetryProvider() {} + + virtual void resetRssi() = 0; + + // Simulator has been updated externally, update the UI to match + virtual void loadUiFromSimulator(SimulatorInterface * simulator) = 0; + + // Load a telemetry item from the log replay. value must be in the correct units, + // see supportLogItems + virtual void loadItemFromLog(QString itemName, QString value) = 0; + + // Tell the log replayer what items we can accept and what units we need them in + // - returns e.g. { "Alt" => "m", "GSpd" => "kmh" } + virtual QHash * getSupportedLogItems() = 0; + + // In hopes of being able to load a file and select the appropriate provider in one step + virtual QString getLogfileIdentifier() = 0; + + // do the work every however often + virtual void generateTelemetryFrame(SimulatorInterface * simulator) = 0; +}; diff --git a/companion/src/simulation/telemetryprovidercrossfire.cpp b/companion/src/simulation/telemetryprovidercrossfire.cpp new file mode 100644 index 00000000000..d1b44368171 --- /dev/null +++ b/companion/src/simulation/telemetryprovidercrossfire.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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 +#include + +#include "appdata.h" +#include "telemetryprovidercrossfire.h" +#include "ui_telemetryprovidercrossfire.h" +#include "telem_data.h" + +template t LIMIT(t mi, t x, t ma) { return std::min(std::max(mi, x), ma); } + +TelemetryProviderCrossfire::TelemetryProviderCrossfire(QWidget * parent): + QWidget(parent), + ui(new Ui::TelemetryProviderCrossfire) +{ + ui->setupUi(this); + + // Set default values from UI definition into GPS + gps.setLatLon(ui->input_gps->text()); + gps.setCourseDegrees(ui->input_hdg->value()); + gps.setSpeedKMH(ui->input_gspd->value()); + gps.setSatelliteCount(ui->input_sats->value()); + gps.setAltitude(ui->input_alt->value()); + + connect(ui->input_hdg, QOverload::of(&QDoubleSpinBox::valueChanged), &gps, &SimulatedGPS::setCourseDegrees); + connect(ui->input_gps, &QLineEdit::textChanged, &gps, &SimulatedGPS::setLatLon); + connect(ui->input_gspd, QOverload::of(&QDoubleSpinBox::valueChanged), &gps, &SimulatedGPS::setSpeedKMH); + connect(ui->input_alt, QOverload::of(&QDoubleSpinBox::valueChanged), &gps, &SimulatedGPS::setAltitude); + connect(ui->input_sats, QOverload::of(&QSpinBox::valueChanged), &gps, &SimulatedGPS::setSatelliteCount); + + connect(&gps, &SimulatedGPS::positionChanged, ui->input_gps, &QLineEdit::setText); + connect(&gps, &SimulatedGPS::courseDegreesChanged, ui->input_hdg, QOverload::of(&QDoubleSpinBox::setValue)); + + // Create this once + supportedLogItems.clear(); + supportedLogItems.insert("1RSS", "dB"); + supportedLogItems.insert("2RSS", "dB"); + supportedLogItems.insert("RQly", "%"); + supportedLogItems.insert("RSNR", "dB"); + supportedLogItems.insert("ANT", ""); + supportedLogItems.insert("RFMD", ""); + supportedLogItems.insert("TPWR", "mW"); + supportedLogItems.insert("TRSS", "dB"); + supportedLogItems.insert("TQly", "%"); + supportedLogItems.insert("TSNR", "dB"); + supportedLogItems.insert("RxBt", "V"); + supportedLogItems.insert("Curr", "A"); + supportedLogItems.insert("Capa", "mAh"); + supportedLogItems.insert("Bat%", "%"); + supportedLogItems.insert("GPS", ""); + supportedLogItems.insert("GSpd", "kmh"); + supportedLogItems.insert("Hdg", "°"); + supportedLogItems.insert("Alt", "m"); + supportedLogItems.insert("Sats", ""); + supportedLogItems.insert("Ptch", "rad"); + supportedLogItems.insert("Roll", "rad"); + supportedLogItems.insert("Yaw", "rad"); + supportedLogItems.insert("FM", ""); + supportedLogItems.insert("Alt", "m"); + supportedLogItems.insert("VSpd", "m/s"); +} + +TelemetryProviderCrossfire::~TelemetryProviderCrossfire() +{ + delete ui; +} + +QHash * TelemetryProviderCrossfire::getSupportedLogItems() +{ + return &supportedLogItems; +} + +QString TelemetryProviderCrossfire::getLogfileIdentifier() +{ + return QString("TELEMETRY_DATA: CRSF"); +} + +void TelemetryProviderCrossfire::loadItemFromLog(QString item, QString value) +{ + if (item == "1RSS") ui->input_1rss->setValue(value.toInt()); + if (item == "2RSS") ui->input_2rss->setValue(value.toInt()); + if (item == "RQly") ui->input_rqly->setValue(value.toInt()); + if (item == "RSNR") ui->input_rsnr->setValue(value.toInt()); + if (item == "TRSS") ui->input_trss->setValue(value.toInt()); + if (item == "TPWR") ui->input_tpwr->setCurrentText(value + "mW"); + if (item == "RFMD") ui->input_rfmd->setValue(value.toInt()); + if (item == "ANT") ui->input_ant->setValue(value.toInt()); + if (item == "TQly") ui->input_tqly->setValue(value.toInt()); + if (item == "TSNR") ui->input_tsnr->setValue(value.toInt()); + if (item == "RRSP") ui->input_rrsp->setValue(value.toInt()); + if (item == "RPWR") ui->input_rpwr->setValue(value.toInt()); + if (item == "TRSP") ui->input_trsp->setValue(value.toInt()); + if (item == "RxBt") ui->input_rxbt->setValue(value.toDouble()); + if (item == "Curr") ui->input_curr->setValue(value.toDouble()); + if (item == "Capa") ui->input_capa->setValue(value.toInt()); + if (item == "Bat%") ui->input_batpercent->setValue(value.toInt()); + if (item == "GPS") ui->input_gps->setText(value); + if (item == "GSpd") ui->input_gspd->setValue(value.toDouble()); + if (item == "Hdg") ui->input_hdg->setValue(value.toDouble()); + if (item == "Sats") ui->input_sats->setValue(value.toInt()); + if (item == "Ptch") ui->input_ptch->setValue(value.toDouble()); + if (item == "Roll") ui->input_roll->setValue(value.toDouble()); + if (item == "Yaw") ui->input_yaw->setValue(value.toDouble()); + if (item == "FM") ui->input_fm->setText(value.remove(QChar('"'))); + if (item == "VSpd") ui->input_vspd->setValue(value.toDouble()); + if (item == "Alt") ui->input_alt->setValue(value.toDouble()); +} + +void TelemetryProviderCrossfire::resetRssi() +{ + uint8_t buffer[CROSSFIRE_PACKET_SIZE] = {0}; + generateTelemetryLinkStatisticsFrame(buffer, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + emit telemetryDataChanged(SIMU_TELEMETRY_PROTOCOL_CROSSFIRE, QByteArray((char *)buffer, CROSSFIRE_PACKET_SIZE)); +} + +void TelemetryProviderCrossfire::loadUiFromSimulator(SimulatorInterface * simulator) +{ + // Nothing to do for crossfire +} + +uint8_t dropdownToTPWRMap[] = { 0, 1, 2, 8, 3, 7, 4, 5, 6, }; + +void TelemetryProviderCrossfire::generateTelemetryFrame(SimulatorInterface *simulator) +{ + static int item = 0; + uint8_t buffer[CROSSFIRE_PACKET_SIZE] = {0}; + + switch (item++) { + case 0: + // Always generate link stats + if (true) { + // just want a block here to put these variables in + uint8_t rssi1 = ui->input_1rss->value(); + uint8_t rssi2 = ui->input_2rss->value(); + uint8_t rqly = ui->input_rqly->value(); + int8_t rsnr = ui->input_rsnr->value(); + uint8_t ant = ui->input_ant->value(); + uint8_t rfmd = ui->input_rfmd->value(); + uint8_t tpwr = dropdownToTPWRMap[ui->input_tpwr->currentIndex()]; + uint8_t trss = ui->input_trss->value(); + uint8_t tqly = ui->input_tqly->value(); + int8_t tsnr = ui->input_tsnr->value(); + + generateTelemetryLinkStatisticsFrame(buffer, rssi1, rssi2, rqly, rsnr, ant, rfmd, tpwr, trss, tqly, tsnr); + } + break; + case 1: + if (ui->enabled_battery->isChecked()) { + double voltage = ui->input_rxbt->value(); + double current = ui->input_curr->value(); + int32_t used_capacity = ui->input_capa->value(); + int8_t battery_percent = ui->input_batpercent->value(); + + generateTelemetryBatterySensorFrame(buffer, voltage, current, used_capacity, battery_percent); + } + break; + case 2: + if (ui->enabled_gps->isChecked()) { + generateTelemetryGPSFrame(buffer, gps.lat, gps.lon, gps.speedKMH, gps.courseDegrees, gps.altitude, gps.satellites); + } + break; + case 3: + if (ui->enabled_attitude->isChecked()) { + double pitch = ui->input_ptch->value(); + double roll = ui->input_roll->value(); + double yaw = ui->input_yaw->value(); + + generateTelemetryAttitudeFrame(buffer, pitch, roll, yaw); + } + break; + case 4: + if (ui->enabled_flightcontroller->isChecked()) { + QString mode = ui->input_fm->text(); + + generateTelemetryFlightModeFrame(buffer, mode); + } + break; + case 5: + if (ui->enabled_barometer->isChecked()) { + double altitude = ui->input_alt->value(); + double vspeed = ui->input_vspd->value(); + + generateTelemetryBarometerFrame(buffer, altitude, vspeed); + } + break; + default: + item = 0; + return; + } + + if (buffer[0]) { + // If we put anything in the buffer, send it + QByteArray ba((char *)buffer, CROSSFIRE_PACKET_SIZE); + emit telemetryDataChanged(SIMU_TELEMETRY_PROTOCOL_CROSSFIRE, ba); + } +} + +void TelemetryProviderCrossfire::generateTelemetryLinkStatisticsFrame(uint8_t *packet, uint8_t rss1, uint8_t rss2, uint8_t rqly, int8_t rsnr, uint8_t ant, uint8_t rfmd, uint8_t tpwr, uint8_t trss, uint8_t tqly, int8_t tsnr) +{ + packet[0] = 0xc8; // SYNC + packet[1] = 13; // LEN (11 + 2 CRC) + packet[2] = 0x14; // CRSF_FRAMETYPE_LINK_STATISTICS + packet[3] = rss1; + packet[4] = rss2; + packet[5] = rqly; + packet[6] = rsnr; + packet[7] = ant; + packet[8] = rfmd; + packet[9] = tpwr; + packet[10] = trss; + packet[11] = tqly; + packet[12] = tsnr; + // Don't bother calculating the CRC, the telemetry consumer doesn't check it +} + +void TelemetryProviderCrossfire::generateTelemetryBatterySensorFrame(uint8_t *packet, double voltage, double current, int32_t used_capacity, int8_t battery_percent) +{ + int32_t tmp; + + packet[0] = 0xc8; // SYNC + packet[1] = 10; // LEN (8 + 2 CRC) + packet[2] = 0x08; // CRSF_FRAMETYPE_BATTERY_SENSOR + + tmp = (voltage * 10); + packet[3] = (tmp & 0xff00) >> 8; + packet[4] = tmp & 0xff; + + tmp = (current * 10); + packet[5] = (tmp & 0xff00) >> 8; + packet[6] = tmp & 0xff; + + packet[7] = (used_capacity & 0xff0000) >> 16; + packet[8] = (used_capacity & 0xff00) >> 8; + packet[9] = (used_capacity & 0xff); + + packet[10] = battery_percent; + // Don't bother calculating the CRC, the telemetry consumer doesn't check it +} + +void TelemetryProviderCrossfire::generateTelemetryAttitudeFrame(uint8_t *packet, double pitch, double roll, double yaw) +{ + int16_t tmp; + + packet[0] = 0xc8; // SYNC + packet[1] = 8; // LEN (6 + 2 CRC) + packet[2] = 0x1e; // CRSF_FRAMETYPE_ATTITUDE + + tmp = pitch * 10000; + packet[3] = (tmp & 0xff00) >> 8; + packet[4] = tmp & 0xff; + + tmp = roll * 10000; + packet[5] = (tmp & 0xff00) >> 8; + packet[6] = tmp & 0xff; + + tmp = yaw * 10000; + packet[7] = (tmp & 0xff00) >> 8; + packet[8] = tmp & 0xff; + // Don't bother calculating the CRC, the telemetry consumer doesn't check it +} + +void TelemetryProviderCrossfire::generateTelemetryFlightModeFrame(uint8_t *packet, const QString &mode) +{ + QByteArray snipped = mode.toUtf8().left(13); + int len = snipped.length(); + + packet[0] = 0xc8; // SYNC + packet[1] = 3 + len; // LEN (string + 2 CRC) + packet[2] = 0x21; // CRSF_FRAMETYPE_FLIGHT_MODE + + int i; + for (i = 0; i < len; i++) { + packet[3+i] = snipped[i]; + } + // Null terminate + packet[3+i] = 0; + // Don't bother calculating the CRC, the telemetry consumer doesn't check it +} + +uint16_t encodeAltitude(double altitude) +{ + uint16_t res; + + if (altitude > 2276.7) { + res = altitude; + res |= 0x8000; // set high bit to indicate it's integer meters + } else { + res = (altitude * 10) + 10000; + res &= 0x7fff; // clear high bit to indicate it's decimeters above -1000m + } + + return res; +} + +void TelemetryProviderCrossfire::generateTelemetryBarometerFrame(uint8_t *packet, double altitude, double vspeed) +{ + uint16_t tmp; + + packet[0] = 0xc8; // SYNC + packet[1] = 6; // LEN (4 + 2 CRC) + packet[2] = 0x09; // CRSF_FRAMETYPE_BARO_ALTITUDE + + tmp = encodeAltitude(altitude); + packet[3] = (tmp & 0xff00) >> 8; + packet[4] = tmp & 0xff; + + tmp = vspeed * 100; // cm/sec + packet[5] = (tmp & 0xff00) >> 8; + packet[6] = tmp & 0xff; + // Don't bother calculating the CRC, the telemetry consumer doesn't check it +} + +void TelemetryProviderCrossfire::generateTelemetryGPSFrame(uint8_t *packet, double latitude, double longitude, double ground_speed, double ground_course, double altitude, int satellite_count) +{ + int32_t tmp; + int16_t tmp2; + uint16_t tmp3; + + packet[0] = 0xc8; + packet[1] = 17; // LEN(15 + 2 CRC) + packet[2] = 0x02; // CRSF_FRAMETYPE_GPS + + tmp = latitude * 10000000; + packet[3] = (tmp & 0xff000000) >> 24; + packet[4] = (tmp & 0xff0000) >> 16; + packet[5] = (tmp & 0xff00) >> 8; + packet[6] = tmp & 0xff; + + tmp = longitude * 10000000; + packet[7] = (tmp & 0xff000000) >> 24; + packet[8] = (tmp & 0xff0000) >> 16; + packet[9] = (tmp & 0xff00) >> 8; + packet[10] = tmp & 0xff; + + tmp2 = ground_speed * 10; // 10ths of a kph + packet[11] = (tmp2 & 0xff00) >> 8; + packet[12] = tmp2 & 0xff; + + tmp2 = ground_course; + if (ground_course > 180) + tmp2 = (ground_course - 360); // -180..180, not 0-360 + tmp2 *= 100; // 100ths of a degree + + packet[13] = (tmp2 & 0xff00) >> 8; + packet[14] = tmp2 & 0xff; + + tmp3 = altitude + 1000; + packet[15] = (tmp3 & 0xff00) >> 8; + packet[16] = tmp3 & 0xff; + + packet[17] = satellite_count & 0xff; + // Don't bother calculating the CRC, the telemetry consumer doesn't check it +} + +void TelemetryProviderCrossfire::on_button_gpsRunStop_clicked() +{ + if (gps.running) { + gps.stop(); + ui->button_gpsRunStop->setText(tr("Run")); + } else { + gps.start(); + ui->button_gpsRunStop->setText(tr("Stop")); + } +} + +void TelemetryProviderCrossfire::on_button_saveTelemetryValues_clicked() +{ + QString fldr = g.backupDir().trimmed(); + if (fldr.isEmpty()) + fldr = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + + QString idFileNameAndPath = QFileDialog::getSaveFileName(this, tr("Save Telemetry"), fldr % "/telemetry.tlm", tr(".tlm Files (*.tlm)")); + if (idFileNameAndPath.isEmpty()) + return; + + QFile file(idFileNameAndPath); + if (!file.open(QIODevice::WriteOnly)){ + QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open file for writing.\n%1").arg(file.errorString())); + return; + } + QTextStream out(&file); + + out << getLogfileIdentifier(); + out << "\r\n"; + out << ui->input_1rss->text(); + out << "\r\n"; + out << ui->input_2rss->text(); + out << "\r\n"; + out << ui->input_rqly->text(); + out << "\r\n"; + out << ui->input_rsnr->text(); + out << "\r\n"; + out << ui->input_trss->text(); + out << "\r\n"; + out << ui->input_tpwr->currentText(); + out << "\r\n"; + out << ui->input_rfmd->text(); + out << "\r\n"; + out << ui->input_ant->text(); + out << "\r\n"; + out << ui->input_tqly->text(); + out << "\r\n"; + out << ui->input_tsnr->text(); + out << "\r\n"; + out << ui->input_rrsp->text(); + out << "\r\n"; + out << ui->input_rpwr->text(); + out << "\r\n"; + out << ui->input_trsp->text(); + out << "\r\n"; + out << ui->enabled_battery->isChecked(); + out << "\r\n"; + out << ui->input_rxbt->text(); + out << "\r\n"; + out << ui->input_curr->text(); + out << "\r\n"; + out << ui->input_capa->text(); + out << "\r\n"; + out << ui->input_batpercent->text(); + out << "\r\n"; + out << ui->enabled_gps->isChecked(); + out << "\r\n"; + out << ui->input_gps->text(); + out << "\r\n"; + out << ui->input_gspd->text(); + out << "\r\n"; + out << ui->input_hdg->text(); + out << "\r\n"; + out << ui->input_sats->text(); + out << "\r\n"; + out << ui->enabled_attitude->isChecked(); + out << "\r\n"; + out << ui->input_ptch->text(); + out << "\r\n"; + out << ui->input_roll->text(); + out << "\r\n"; + out << ui->input_yaw->text(); + out << "\r\n"; + out << ui->enabled_flightcontroller->isChecked(); + out << "\r\n"; + out << ui->input_fm->text(); + out << "\r\n"; + out << ui->enabled_barometer->isChecked(); + out << "\r\n"; + out << ui->input_vspd->text(); + out << "\r\n"; + out << ui->input_alt->text(); + out << "\r\n"; + file.flush(); + file.close(); +} + +void TelemetryProviderCrossfire::on_button_loadTelemetryValues_clicked() +{ + QString fldr = g.backupDir().trimmed(); + if (fldr.isEmpty()) + fldr = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + + QString idFileNameAndPath = QFileDialog::getOpenFileName(this, tr("Open Telemetry File"), fldr % "/telemetry.tlm", tr(".tlm Files (*.tlm)")); + if (idFileNameAndPath.isEmpty()) + return; + + QFile file(idFileNameAndPath); + + if (!file.open(QIODevice::ReadOnly)){ + QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open file for reading.\n%1").arg(file.errorString())); + return; + } + + QTextStream in(&file); + + QString inputText; + double inputDouble; + int inputInt; + + inputText = in.readLine(); + if (inputText != getLogfileIdentifier()) { + QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Not a CRSF telemetry values file.")); + return; + } + + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_1rss->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_2rss->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_rqly->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_rsnr->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_trss->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_tpwr->setCurrentText(inputText); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_rfmd->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_ant->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_tqly->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_tsnr->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_rrsp->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_rpwr->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_trsp->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->enabled_battery->setChecked(inputInt); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_rxbt->setValue(inputDouble); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_curr->setValue(inputDouble); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_capa->setValue(inputDouble); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_batpercent->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->enabled_gps->setChecked(inputInt); + inputText = in.readLine(); ui->input_gps->setText(inputText); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_gspd->setValue(inputDouble); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_hdg->setValue(inputDouble); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->input_sats->setValue(inputInt); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->enabled_attitude->setChecked(inputInt); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_ptch->setValue(inputDouble); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_roll->setValue(inputDouble); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_yaw->setValue(inputDouble); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->enabled_flightcontroller->setChecked(inputInt); + inputText = in.readLine(); ui->input_fm->setText(inputText); + inputText = in.readLine(); inputInt = inputText.toInt(); ui->enabled_barometer->setChecked(inputInt); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_vspd->setValue(inputDouble); + inputText = in.readLine(); inputDouble = inputText.toDouble(); ui->input_alt->setValue(inputDouble); + + file.close(); +} diff --git a/companion/src/simulation/telemetryprovidercrossfire.h b/companion/src/simulation/telemetryprovidercrossfire.h new file mode 100644 index 00000000000..858f4a4d921 --- /dev/null +++ b/companion/src/simulation/telemetryprovidercrossfire.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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. + */ + +#pragma once + +#include +#include + +#include "simulatorinterface.h" +#include "telemetryprovider.h" +#include "simulatedgps.h" + +// Max telemetry packet size +#define CROSSFIRE_PACKET_SIZE 64 + +namespace Ui { + class TelemetryProviderCrossfire; +} + +class TelemetryProviderCrossfire : public QWidget, public TelemetryProvider +{ + Q_OBJECT + + public: + + explicit TelemetryProviderCrossfire(QWidget * parent); + virtual ~TelemetryProviderCrossfire(); + + signals: + void telemetryDataChanged(const quint8 protocol, const QByteArray data); + + public slots: + void generateTelemetryLinkStatisticsFrame(uint8_t *packet, uint8_t rss1, uint8_t rss2, uint8_t rqly, int8_t rsnr, uint8_t ant, uint8_t rfmd, uint8_t tpwr, uint8_t trss, uint8_t tqly, int8_t tsnr); + void generateTelemetryBatterySensorFrame(uint8_t *packet, double voltage, double current, int32_t used_capacity, int8_t battery_percent); + void generateTelemetryAttitudeFrame(uint8_t *packet, double pitch, double roll, double yaw); + void generateTelemetryFlightModeFrame(uint8_t *packet, const QString &mode); + void generateTelemetryBarometerFrame(uint8_t *packet, double altitude, double vspeed); + void generateTelemetryGPSFrame(uint8_t *packet, double latitude, double longitude, double ground_speed, double ground_course, double altitude, int satellite_count); + void resetRssi(); + QHash * getSupportedLogItems(); + void loadItemFromLog(QString itemName, QString value); + QString getLogfileIdentifier(); + + protected slots: + void generateTelemetryFrame(SimulatorInterface * simulator); + void loadUiFromSimulator(SimulatorInterface * simulator); + + protected: + Ui::TelemetryProviderCrossfire * ui; + SimulatedGPS gps; + QHash supportedLogItems; + + private slots: + void on_button_gpsRunStop_clicked(); + void on_button_saveTelemetryValues_clicked(); + void on_button_loadTelemetryValues_clicked(); +}; diff --git a/companion/src/simulation/telemetryprovidercrossfire.ui b/companion/src/simulation/telemetryprovidercrossfire.ui new file mode 100644 index 00000000000..068f31c66be --- /dev/null +++ b/companion/src/simulation/telemetryprovidercrossfire.ui @@ -0,0 +1,1596 @@ + + + TelemetryProviderCrossfire + + + + 0 + 0 + 311 + 1026 + + + + + 0 + 0 + + + + Form + + + + + + + 0 + 0 + + + + + 8 + + + + 13 + + + + + + + + 0 + 0 + + + + + 8 + + + + 1 + + + -1000.000000000000000 + + + 32768.000000000000000 + + + 33.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 2RSS + + + + + + + + 0 + 0 + + + + + 8 + + + + TQly + + + + + + + + 8 + + + + Sats + + + + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + RPWR + + + + + + + + 0 + 0 + + + + + 8 + + + + RxBt + + + + + + + + 0 + 0 + + + + + 8 + + + + dB + + + + + + + + 0 + 0 + + + + + 8 + + + + GPS + + + + + + + + 0 + 0 + + + + + 8 + + + + dBm + + + + + + + + 0 + 0 + + + + + 8 + + + + m + + + + + + + + 0 + 0 + + + + + 8 + + + + ANT + + + + + + + + 0 + 0 + + + + + 8 + + + + km/h + + + + + + + + 0 + 0 + + + + + 8 + + + + VSpd + + + + + + + + 0 + 0 + + + + + 8 + + + + Hdg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 8 + + + + RSNR + + + + + + + + 0 + 0 + + + + + 8 + + + + dB + + + + + + + + 0 + 0 + + + + + 8 + + + + % + + + + + + + + 0 + 0 + + + + + 8 + + + + -120 + + + 0 + + + -18 + + + + + + + + 0 + 0 + + + + + 8 + + + + % + + + + + + + + 8 + + + + Degrees + + + + + + + + 0 + 0 + + + + + 8 + + + + -120 + + + 0 + + + -31 + + + + + + + + 0 + 0 + + + + + 8 + + + + Capa + + + + + + + + 0 + 0 + + + + + 8 + + + + 1 + + + + + + + + 8 + + + + 2 + + + + 0mW + + + + + 10mW + + + + + 25mW + + + + + 50mW + + + + + 100mW + + + + + 250mW + + + + + 500mW + + + + + 1000mW + + + + + 2000mW + + + + + + + + + 8 + + + + -4.000000000000000 + + + 4.000000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + Bat% + + + + + + + + 0 + 0 + + + + + 8 + + + + dB + + + + + + + + 8 + + + + Save Telemetry Values + + + + + + + + 0 + 0 + + + + + 8 + + + + m/s + + + + + + + + 0 + 0 + + + + + 8 + + + + Lat,Lon +(dec.deg.) + + + false + + + + + + + + 8 + + + + 1 + + + 1200.000000000000000 + + + 31.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + GSpd + + + + + + + + 8 + + + + 360.000000000000000 + + + 340.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 100 + + + 100 + + + + + + + + 8 + + + + Yaw + + + + + + + + 0 + 0 + + + + + 8 + + + + FM + + + + + + + + 0 + 0 + + + + + 8 + + + + 5.130000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + V + + + + + + + + 0 + 0 + + + + + 8 + + + + TPWR + + + + + + + + 8 + + + + 13 + + + + + + + + 8 + + + + -4.000000000000000 + + + 4.000000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + Radians + + + + + + + + 0 + 0 + + + + + 8 + + + + 120 + + + 15 + + + + + + + + 0 + 0 + + + + + 8 + + + + TRSS + + + + + + + + 0 + 0 + + + + + 8 + + + + TSNR + + + + + + + + 0 + 0 + + + + + 8 + + + + 100 + + + 99 + + + + + + + + 0 + 0 + + + + + 8 + + + + dB + + + + + + + + 0 + 0 + + + + + 8 + + + + 100 + + + 83 + + + + + + + + 0 + 0 + + + + + 8 + + + + 2.540000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + RFMD + + + + + + + + 0 + 0 + + + + + 8 + + + + % + + + + + + + + 0 + 0 + + + + + 8 + + + + % + + + + + + + + 8 + + + + Battery Monitoring + + + + + + + + 0 + 0 + + + + + 8 + + + + Ptch + + + + + + + + 0 + 0 + + + + + 8 + + + + RQly + + + + + + + + 0 + 0 + + + + + 8 + + + + mAh + + + + + + + + 8 + + + + Attitude + + + + + + + + 1 + 0 + + + + + 8 + + + + -120 + + + 0 + + + -21 + + + + + + + + 0 + 0 + + + + + 8 + + + + 1 + + + + + + + + 8 + + + + -4.000000000000000 + + + 4.000000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 1RSS + + + + + + + + 8 + + + + GPS + + + + + + + + 0 + 0 + + + + + 8 + + + + Curr + + + + + + + + 0 + 0 + + + + + 8 + + + + TRSP + + + + + + + + 0 + 0 + + + + + 8 + + + + Roll + + + + + + + + 0 + 0 + + + + + 8 + + + + dB + + + + + + + + 8 + + + + Load Telemetry Values + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 8 + + + + Radians + + + + + + + + 0 + 0 + + + + + 8 + + + + 100 + + + 99 + + + + + + + + 8 + + + + Barometer + + + + + + + + 0 + 0 + + + + + 8 + + + + 120 + + + 12 + + + + + + + + 0 + 0 + + + + + 8 + + + + mw + + + + + + + + 0 + 0 + + + + + 8 + + + + Alt + + + + + + + + 0 + 0 + + + + + 8 + + + + A + + + + + + + + 0 + 0 + + + + + 8 + + + + % + + + + + + + + 0 + 0 + + + + + 8 + + + + Radians + + + + + + + + 0 + 0 + + + + + 8 + + + + 100 + + + 5 + + + + + + + + 0 + 0 + + + + + 8 + + + + 100 + + + 99 + + + + + + + + 0 + 0 + + + + + 8 + + + + RRSP + + + + + + + + 8 + + + + Flight Controller + + + + + + + + 8 + + + + GPS Sim + + + + + + + + 0 + 0 + + + + + 8 + + + + 10000 + + + 2762 + + + + + + + + 8 + + + + Run + + + + + + + + 8 + + + + 25.9973,-97.1572 + + + + + + + + diff --git a/companion/src/simulation/telemetryproviderfrsky.cpp b/companion/src/simulation/telemetryproviderfrsky.cpp new file mode 100644 index 00000000000..7702fd48c20 --- /dev/null +++ b/companion/src/simulation/telemetryproviderfrsky.cpp @@ -0,0 +1,954 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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 +#include + +#include "appdata.h" +#include "telemetryproviderfrsky.h" +#include "ui_telemetryproviderfrsky.h" +#include "telem_data.h" +#include "radio/src/telemetry/frsky_defs.h" + +template t LIMIT(t mi, t x, t ma) { return std::min(std::max(mi, x), ma); } + +TelemetryProviderFrSky::TelemetryProviderFrSky(QWidget * parent): + QWidget(parent), + ui(new Ui::TelemetryProviderFrSky) +{ + ui->setupUi(this); + + ui->A1->setSpecialValueText(" "); + ui->A2->setSpecialValueText(" "); + ui->A3->setSpecialValueText(" "); + ui->A4->setSpecialValueText(" "); + ui->rpm->setSpecialValueText(" "); + ui->fuel->setSpecialValueText(" "); + + ui->rxbt_ratio->setEnabled(false); + ui->A1_ratio->setEnabled(false); + ui->A2_ratio->setEnabled(false); + + gpsTimer.setInterval(250); + connect(&gpsTimer, &QTimer::timeout, this, &TelemetryProviderFrSky::updateGps); + + // Create this once + supportedLogItems.clear(); + supportedLogItems.insert("RxBt", "V"); + supportedLogItems.insert("RSSI", "dB"); + supportedLogItems.insert("A1", "V"); + supportedLogItems.insert("A2", "V"); + supportedLogItems.insert("A3", "V"); + supportedLogItems.insert("A4", "V"); + supportedLogItems.insert("Tmp1", "°C"); + supportedLogItems.insert("Tmp2", "°C"); + supportedLogItems.insert("RPM", "rpm"); + supportedLogItems.insert("Fuel", "%"); + supportedLogItems.insert("Fuel", "ml"); + supportedLogItems.insert("VSpd", "m/s"); + supportedLogItems.insert("Alt", "m"); + supportedLogItems.insert("VFAS", "V"); + supportedLogItems.insert("Curr", "A"); + supportedLogItems.insert("Cels", "V"); + supportedLogItems.insert("ASpd", "kmh"); + supportedLogItems.insert("GAlt", "m"); + supportedLogItems.insert("GSpd", "kmh"); + supportedLogItems.insert("Hdg", "°"); + supportedLogItems.insert("Date", ""); + supportedLogItems.insert("GPS", ""); + supportedLogItems.insert("AccX", "g"); + supportedLogItems.insert("AccY", "g"); + supportedLogItems.insert("AccZ", "g"); +} + +TelemetryProviderFrSky::~TelemetryProviderFrSky() +{ + delete ui; +} + + +void TelemetryProviderFrSky::resetRssi() +{ + if (!(ui && ui->rssi_inst)) + return; + + bool ok = false; + + const int id = ui->rssi_inst->text().toInt(&ok, 0); + if (!ok) + return; + + uint8_t buffer[FRSKY_SPORT_PACKET_SIZE] = {0}; + generateSportPacket(buffer, id, DATA_FRAME, RSSI_ID, 0); + emit telemetryDataChanged(SIMU_TELEMETRY_PROTOCOL_FRSKY_SPORT, QByteArray((char *)buffer, FRSKY_SPORT_PACKET_SIZE)); +} + +void TelemetryProviderFrSky::updateGps() +{ + int a = ui->gps_latlon->text().contains(","); + if (!a) { + QMessageBox::information(this, tr("Bad GPS Format"), tr("Must be decimal latitude,longitude")); + ui->gps_latlon->setText("000.00000000,000.00000000"); + ui->GPSpushButton->click(); + } + else + { + QStringList gpsLatLon = (ui->gps_latlon->text()).split(","); + + double b2 = gpsLatLon[0].toDouble(); + double c2 = gpsLatLon[1].toDouble(); + double d3 = ui->gps_speed->value() / 14400; + double f3 = ui->gps_course->value(); + double j2 = 6378.1; + double b3 = qRadiansToDegrees(qAsin( qSin(qDegreesToRadians(b2))*qCos(d3/j2) + qCos(qDegreesToRadians(b2))*qSin(d3/j2)*qCos(qDegreesToRadians(f3)))); + double bb3 = b3; + if (bb3 < 0) { + bb3 = bb3 * -1; + } + if (bb3 > 89.99) { + f3 = f3 + 180; + if (f3 > 360) { + f3 = f3 - 360; + } + ui->gps_course->setValue(f3); + } + double c3 = qRadiansToDegrees(qDegreesToRadians(c2) + qAtan2(qSin(qDegreesToRadians(f3))*qSin(d3/j2)*qCos(qDegreesToRadians(b2)),qCos(d3/j2)-qSin(qDegreesToRadians(b2))*qSin(qDegreesToRadians(b3)))); + if (c3 > 180) { + c3 = c3 - 360; + } + if (c3 < -180) { + c3 = c3 + 360; + } + QString lats = QString::number(b3, 'f', 8); + QString lons = QString::number(c3, 'f', 8); + QString qs = lats + "," + lons; + ui->gps_latlon->setText(qs); + } +} + +#define SET_INSTANCE(control, id, def) ui->control->setText(QString::number(simulator->getSensorInstance(id, ((def) & 0x1F)))) + +void TelemetryProviderFrSky::loadUiFromSimulator(SimulatorInterface * simulator) +{ + SET_INSTANCE(rxbt_inst, BATT_ID, 0); + SET_INSTANCE(rssi_inst, RSSI_ID, 24); + SET_INSTANCE(swr_inst, RAS_ID, 24); + SET_INSTANCE(a1_inst, ADC1_ID, 0); + SET_INSTANCE(a2_inst, ADC2_ID, 0); + SET_INSTANCE(a3_inst, A3_FIRST_ID, 0); + SET_INSTANCE(a4_inst, A4_FIRST_ID, 0); + SET_INSTANCE(t1_inst, T1_FIRST_ID, 0); + SET_INSTANCE(t2_inst, T2_FIRST_ID, 0); + SET_INSTANCE(rpm_inst, RPM_FIRST_ID, DATA_ID_RPM); + SET_INSTANCE(fuel_inst, FUEL_FIRST_ID, 0); + SET_INSTANCE(fuel_qty_inst, FUEL_QTY_FIRST_ID, 0); + SET_INSTANCE(aspd_inst, AIR_SPEED_FIRST_ID, 0); + SET_INSTANCE(vvspd_inst, VARIO_FIRST_ID, DATA_ID_VARIO); + SET_INSTANCE(valt_inst, ALT_FIRST_ID, DATA_ID_VARIO); + SET_INSTANCE(fasv_inst, VFAS_FIRST_ID, DATA_ID_FAS); + SET_INSTANCE(fasc_inst, CURR_FIRST_ID, DATA_ID_FAS); + SET_INSTANCE(cells_inst, CELLS_FIRST_ID, DATA_ID_FLVSS); + SET_INSTANCE(gpsa_inst, GPS_ALT_FIRST_ID, DATA_ID_GPS); + SET_INSTANCE(gpss_inst, GPS_SPEED_FIRST_ID, DATA_ID_GPS); + SET_INSTANCE(gpsc_inst, GPS_COURS_FIRST_ID, DATA_ID_GPS); + SET_INSTANCE(gpst_inst, GPS_TIME_DATE_FIRST_ID, DATA_ID_GPS); + SET_INSTANCE(gpsll_inst, GPS_LONG_LATI_FIRST_ID, DATA_ID_GPS); + SET_INSTANCE(accx_inst, ACCX_FIRST_ID, 0); + SET_INSTANCE(accy_inst, ACCY_FIRST_ID, 0); + SET_INSTANCE(accz_inst, ACCZ_FIRST_ID, 0); + + refreshSensorRatios(simulator); +} + +void TelemetryProviderFrSky::refreshSensorRatios(SimulatorInterface * simulator) +{ + ui->rxbt_ratio->setValue(simulator->getSensorRatio(BATT_ID) / 10.0); + ui->A1_ratio->setValue(simulator->getSensorRatio(ADC1_ID) / 10.0); + ui->A2_ratio->setValue(simulator->getSensorRatio(ADC2_ID) / 10.0); +} + +void TelemetryProviderFrSky::generateTelemetryFrame(SimulatorInterface *simulator) +{ + static int item = 0; + bool ok = true; + uint8_t buffer[FRSKY_SPORT_PACKET_SIZE] = {0}; + static FlvssEmulator *flvss = new FlvssEmulator(); + static GPSEmulator *gps = new GPSEmulator(); + + switch (item++) { + case 0: +#if defined(XJT_VERSION_ID) + generateSportPacket(buffer, 1, DATA_FRAME, XJT_VERSION_ID, 11); +#endif + refreshSensorRatios(simulator); // placed here in order to call this less often + break; + + case 1: + if (ui->rxbt->text().length()) { + generateSportPacket(buffer, ui->rxbt_inst->text().toInt(&ok, 0), DATA_FRAME, BATT_ID, LIMIT(0, ui->rxbt->value() * 255.0 / ui->rxbt_ratio->value(), 0xFFFFFFFF)); + } + break; + + case 2: + if (ui->Rssi->text().length()) + generateSportPacket(buffer, ui->rssi_inst->text().toInt(&ok, 0), DATA_FRAME, RSSI_ID, LIMIT(0, ui->Rssi->text().toInt(&ok, 0), 0xFF)); + break; + + case 3: + if (ui->Swr->text().length()) + generateSportPacket(buffer, ui->swr_inst->text().toInt(&ok, 0), DATA_FRAME, RAS_ID, LIMIT(0, ui->Swr->text().toInt(&ok, 0), 0xFFFF)); + break; + + case 4: + if (ui->A1->value() > 0) + generateSportPacket(buffer, ui->a1_inst->text().toInt(&ok, 0), DATA_FRAME, ADC1_ID, LIMIT(0, ui->A1->value() * 255.0 / ui->A1_ratio->value(), 0xFF)); + break; + + case 5: + if (ui->A2->value() > 0) + generateSportPacket(buffer, ui->a2_inst->text().toInt(&ok, 0), DATA_FRAME, ADC2_ID, LIMIT(0, ui->A2->value() * 255.0 / ui->A2_ratio->value(), 0xFF)); + break; + + case 6: + if (ui->A3->value() != 0) + generateSportPacket(buffer, ui->a3_inst->text().toInt(&ok, 0), DATA_FRAME, A3_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->A3->value() * 100.0, 0x7FFFFFFF)); + break; + + case 7: + if (ui->A4->value() != 0) + generateSportPacket(buffer, ui->a4_inst->text().toInt(&ok, 0), DATA_FRAME, A4_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->A4->value() * 100.0, 0x7FFFFFFF)); + break; + + case 8: + if (ui->T1->value() != 0) + generateSportPacket(buffer, ui->t1_inst->text().toInt(&ok, 0), DATA_FRAME, T1_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->T1->value(), 0x7FFFFFFF)); + break; + + case 9: + if (ui->T2->value() != 0) + generateSportPacket(buffer, ui->t2_inst->text().toInt(&ok, 0), DATA_FRAME, T2_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->T2->value(), 0x7FFFFFFF)); + break; + + case 10: + if (ui->rpm->value() > 0) + generateSportPacket(buffer, ui->rpm_inst->text().toInt(&ok, 0), DATA_FRAME, RPM_FIRST_ID, LIMIT(0, ui->rpm->value(), 0x7FFFFFFF)); + break; + + case 11: + if (ui->fuel->value() > 0) + generateSportPacket(buffer, ui->fuel_inst->text().toInt(&ok, 0), DATA_FRAME, FUEL_FIRST_ID, LIMIT(0, ui->fuel->value(), 0xFFFF)); + break; + + case 12: + if (ui->vspeed->value() != 0) + generateSportPacket(buffer, ui->vvspd_inst->text().toInt(&ok, 0), DATA_FRAME, VARIO_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->vspeed->value() * 100.0, 0x7FFFFFFF)); + break; + + case 13: + if (ui->valt->value() != 0) + generateSportPacket(buffer, ui->valt_inst->text().toInt(&ok, 0), DATA_FRAME, ALT_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->valt->value() * 100.0, 0x7FFFFFFF)); + break; + + case 14: + if (ui->vfas->value() != 0) + generateSportPacket(buffer, ui->fasv_inst->text().toInt(&ok, 0), DATA_FRAME, VFAS_FIRST_ID, LIMIT(0, ui->vfas->value() * 100.0, 0xFFFFFFFF)); + break; + + case 15: + if (ui->curr->value() != 0) + generateSportPacket(buffer, ui->fasc_inst->text().toInt(&ok, 0), DATA_FRAME, CURR_FIRST_ID, LIMIT(0, ui->curr->value() * 10.0, 0xFFFFFFFF)); + break; + + case 16: + double cellValues[FlvssEmulator::MAXCELLS]; + if (ui->cell1->value() > 0.009) { // ??? cell1 returning non-zero value when spin box is zero! + cellValues[0] = ui->cell1->value(); + cellValues[1] = ui->cell2->value(); + cellValues[2] = ui->cell3->value(); + cellValues[3] = ui->cell4->value(); + cellValues[4] = ui->cell5->value(); + cellValues[5] = ui->cell6->value(); + cellValues[6] = ui->cell7->value(); + cellValues[7] = ui->cell8->value(); + generateSportPacket(buffer, ui->cells_inst->text().toInt(&ok, 0), DATA_FRAME, CELLS_FIRST_ID, flvss->setAllCells_GetNextPair(cellValues)); + } + else { + cellValues[0] = 0; + flvss->setAllCells_GetNextPair(cellValues); + } + break; + + case 17: + if (ui->aspeed->value() > 0) + generateSportPacket(buffer, ui->aspd_inst->text().toInt(&ok, 0), DATA_FRAME, AIR_SPEED_FIRST_ID, LIMIT(0, ui->aspeed->value() * 5.39957, 0xFFFFFFFF)); + break; + + case 18: + if (ui->gps_alt->value() != 0) { + gps->setGPSAltitude(ui->gps_alt->value()); + generateSportPacket(buffer, ui->gpsa_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_ALT_FIRST_ID, gps->getNextPacketData(GPS_ALT_FIRST_ID)); + } + break; + + case 19: + if (ui->gps_speed->value() > 0) { + gps->setGPSSpeedKMH(ui->gps_speed->value()); + generateSportPacket(buffer, ui->gpss_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_SPEED_FIRST_ID, gps->getNextPacketData(GPS_SPEED_FIRST_ID)); + } + break; + + case 20: + if (ui->gps_course->value() != 0) { + gps->setGPSCourse(ui->gps_course->value()); + generateSportPacket(buffer, ui->gpsc_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_COURS_FIRST_ID, gps->getNextPacketData(GPS_COURS_FIRST_ID)); + } + break; + + case 21: + if (ui->gps_time->text().length()) { + gps->setGPSDateTime(ui->gps_time->text()); + generateSportPacket(buffer, ui->gpst_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_TIME_DATE_FIRST_ID, gps->getNextPacketData(GPS_TIME_DATE_FIRST_ID)); + } + break; + + case 22: + if (ui->gps_latlon->text().length()) { + gps->setGPSLatLon(ui->gps_latlon->text()); + generateSportPacket(buffer, ui->gpsll_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_LONG_LATI_FIRST_ID, gps->getNextPacketData(GPS_LONG_LATI_FIRST_ID)); + } + break; + + case 23: + if (ui->accx->value() != 0) + generateSportPacket(buffer, ui->accx_inst->text().toInt(&ok, 0), DATA_FRAME, ACCX_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->accx->value() * 100.0, 0x7FFFFFFF)); + break; + + case 24: + if (ui->accy->value() != 0) + generateSportPacket(buffer, ui->accy_inst->text().toInt(&ok, 0), DATA_FRAME, ACCY_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->accy->value() * 100.0, 0x7FFFFFFF)); + break; + + case 25: + if (ui->accz->value() != 0) + generateSportPacket(buffer, ui->accz_inst->text().toInt(&ok, 0), DATA_FRAME, ACCZ_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->accz->value() * 100.0, 0x7FFFFFFF)); + break; + + case 26: + if (ui->fuel_qty->value() > 0) + generateSportPacket(buffer, ui->fuel_qty_inst->text().toInt(&ok, 0), DATA_FRAME, FUEL_QTY_FIRST_ID, LIMIT(0, ui->fuel_qty->value() * 100.0, 0xFFFFFF)); + break; + + default: + item = 0; + return; + } + + if (ok && (buffer[2] || buffer[3])) { + QByteArray ba((char *)buffer, FRSKY_SPORT_PACKET_SIZE); + emit telemetryDataChanged(SIMU_TELEMETRY_PROTOCOL_FRSKY_SPORT, ba); + //qDebug("%02X %02X %02X %02X %02X %02X %02X %02X %02X", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8]); + } + else { + generateTelemetryFrame(simulator); + } +} + +void setSportPacketCrc(uint8_t * packet) +{ + short crc = 0; + for (int i=1; i> 8; //0-100 + crc &= 0x00ff; + crc += crc >> 8; //0-0FF + crc &= 0x00ff; + } + packet[FRSKY_SPORT_PACKET_SIZE-1] = 0xFF - (crc & 0x00ff); +} + +uint8_t getBit(uint8_t position, uint8_t value) +{ + return (value & (uint8_t)(1 << position)) ? 1 : 0; +} + +bool TelemetryProviderFrSky::generateSportPacket(uint8_t * packet, uint8_t dataId, uint8_t prim, uint16_t appId, uint32_t data) +{ + if (dataId > 0x1B ) return false; + + // generate Data ID field + uint8_t bit5 = getBit(0, dataId) ^ getBit(1, dataId) ^ getBit(2, dataId); + uint8_t bit6 = getBit(2, dataId) ^ getBit(3, dataId) ^ getBit(4, dataId); + uint8_t bit7 = getBit(0, dataId) ^ getBit(2, dataId) ^ getBit(4, dataId); + + packet[0] = (bit7 << 7) + (bit6 << 6) + (bit5 << 5) + dataId; + // qDebug("dataID: 0x%02x (%d)", packet[0], dataId); + packet[1] = prim; + *((uint16_t *)(packet+2)) = appId; + *((int32_t *)(packet+4)) = data; + setSportPacketCrc(packet); + return true; +} + +QHash * TelemetryProviderFrSky::getSupportedLogItems() +{ + return &supportedLogItems; +} + +QString TelemetryProviderFrSky::getLogfileIdentifier() +{ + return QString("TELEMETRY_DATA: FrSky S.Port"); +} + +QString convertGPSDate(QString input) +{ + QStringList dateTime = input.simplified().split(' '); + if (dateTime.size() < 2) { + return ""; // invalid format + } + QStringList dateParts = dateTime[0].split('-'); // input as yy-mm-dd + if (dateParts.size() < 3) { + return ""; // invalid format + } + // output is dd-MM-yyyy hh:mm:ss + QString localDateString = dateParts[2] + "-" + dateParts[1] + "-20" + dateParts[0] + " " + dateTime[1]; + QString format("dd-MM-yyyy hh:mm:ss"); + QDateTime utcDate = QDateTime::fromString(localDateString, format).toTimeSpec(Qt::UTC); + return utcDate.toString(format); +} + +void TelemetryProviderFrSky::loadItemFromLog(QString item, QString value) +{ + if (item == "RxBt") ui->rxbt->setValue(value.toDouble()); + if (item == "RSSI") ui->Rssi->setValue(value.toInt()); + // SWR doesn't get set as a telemetry item, it appears to just be + // used to alert when the transmitter / module antenna's broken. So + // we won't be setting it from the log. + if (item == "A1") ui->A1->setValue(value.toDouble()); + if (item == "A2") ui->A2->setValue(value.toDouble()); + if (item == "A3") ui->A3->setValue(value.toDouble()); + if (item == "A4") ui->A4->setValue(value.toDouble()); + if (item == "Tmp1") ui->T1->setValue(value.toInt()); + if (item == "Tmp2") ui->T2->setValue(value.toInt()); + if (item == "RPM") ui->rpm->setValue(value.toInt()); + if (item == "Fuel") { + // Both of these end up as "Fuel", so set them both and hope that + // nobody has both Fuel(%) and Fuel(ml) set up on the same + // vehicle. + ui->fuel->setValue(value.toInt()); + ui->fuel_qty->setValue(value.toInt()); + } + if (item == "VSpd") ui->vspeed->setValue(value.toDouble()); + if (item == "Alt") ui->valt->setValue(value.toDouble()); + if (item == "VFAS") ui->vfas->setValue(value.toDouble()); + if (item == "Curr") ui->curr->setValue(value.toDouble()); + if (item == "Cels") { + // Individual cell voltages don't get logged, just the total. So + // set each cell to be equal and add up to the value supplied + double cellV = value.toDouble() / 8; + ui->cell1->setValue(cellV); + ui->cell2->setValue(cellV); + ui->cell3->setValue(cellV); + ui->cell4->setValue(cellV); + ui->cell5->setValue(cellV); + ui->cell6->setValue(cellV); + ui->cell7->setValue(cellV); + ui->cell8->setValue(cellV); + } + if (item == "ASpd") ui->aspeed->setValue(value.toDouble()); + if (item == "GAlt") ui->gps_alt->setValue(value.toDouble()); + if (item == "GSpd") ui->gps_speed->setValue(value.toDouble()); + if (item == "Hdg") ui->gps_course->setValue(value.toDouble()); + if (item == "Date") { + // This is logged as YYYY-mm-dd hh:mm:ss, but we want dd-MM-yyyy + // hh:mm:ss in the UI + ui->gps_time->setText(convertGPSDate(value)); + } + if (item == "GPS") ui->gps_latlon->setText(value); + if (item == "AccX") ui->accx->setValue(value.toDouble()); + if (item == "AccY") ui->accy->setValue(value.toDouble()); + if (item == "AccZ") ui->accz->setValue(value.toDouble()); +} + +uint32_t TelemetryProviderFrSky::FlvssEmulator::encodeCellPair(uint8_t cellNum, uint8_t firstCellNo, double cell1, double cell2) +{ + uint16_t cell1Data = cell1 * 500.0; + uint16_t cell2Data = cell2 * 500.0; + uint32_t cellData = 0; + + cellData = cell2Data & 0x0FFF; + cellData <<= 12; + cellData |= cell1Data & 0x0FFF; + cellData <<= 4; + cellData |= cellNum & 0x0F; + cellData <<= 4; + cellData |= firstCellNo & 0x0F; + + return cellData; +} + +void TelemetryProviderFrSky::FlvssEmulator::encodeAllCells() +{ + cellData1 = encodeCellPair(numCells, 0, cellFloats[0], cellFloats[1]); + if (numCells > 2) cellData2 = encodeCellPair(numCells, 2, cellFloats[2], cellFloats[3]); else cellData2 = 0; + if (numCells > 4) cellData3 = encodeCellPair(numCells, 4, cellFloats[4], cellFloats[5]); else cellData3 = 0; + if (numCells > 6) cellData4 = encodeCellPair(numCells, 6, cellFloats[6], cellFloats[7]); else cellData4 = 0; +} + +void TelemetryProviderFrSky::FlvssEmulator::splitIntoCells(double totalVolts) +{ + numCells = qFloor((totalVolts / 3.7) + .5); + double avgVolts = totalVolts / numCells; + double remainder = (totalVolts - (avgVolts * numCells)); + for (uint32_t i = 0; (i < numCells) && ( i < MAXCELLS); i++) { + cellFloats[i] = avgVolts; + } + for (uint32_t i = numCells; i < MAXCELLS; i++) { + cellFloats[i] = 0; + } + cellFloats[0] += remainder; + numCells = numCells > MAXCELLS ? MAXCELLS : numCells; // force into valid cell count in case of input out of range +} + +uint32_t TelemetryProviderFrSky::FlvssEmulator::setAllCells_GetNextPair(double cellValues[MAXCELLS]) +{ + numCells = 0; + for (uint32_t i = 0; i < MAXCELLS; i++) { + if ((i == 0) && (cellValues[0] > 4.2)) { + splitIntoCells(cellValues[0]); + break; + } + if (cellValues[i] > 0) { + cellFloats[i] = cellValues[i]; + numCells++; + } + else { + // zero marks the last cell + for (uint32_t x = i; x < MAXCELLS; x++) { + cellFloats[x] = 0; + } + break; + } + } + + // encode the double values into telemetry format + encodeAllCells(); + + // return the value for the current pair + uint32_t cellData = 0; + if (nextCellNum >= numCells) { + nextCellNum = 0; + } + switch (nextCellNum) { + case 0: + cellData = cellData1; + break; + case 2: + cellData = cellData2; + break; + case 4: + cellData = cellData3; + break; + case 6: + cellData = cellData4; + break; + } + nextCellNum += 2; + return cellData; +} + +TelemetryProviderFrSky::GPSEmulator::GPSEmulator() +{ + lat = 0; + lon = 0; + dt = QDateTime::currentDateTime(); + sendLat = true; + sendDate = true; +} + + +uint32_t TelemetryProviderFrSky::GPSEmulator::encodeLatLon(double latLon, bool isLat) +{ + uint32_t data = (uint32_t)((latLon < 0 ? -latLon : latLon) * 60 * 10000) & 0x3FFFFFFF; + if (isLat == false) { + data |= 0x80000000; + } + if (latLon < 0) { + data |= 0x40000000; + } + return data; +} + +uint32_t TelemetryProviderFrSky::GPSEmulator::encodeDateTime(uint8_t yearOrHour, uint8_t monthOrMinute, uint8_t dayOrSecond, bool isDate) +{ + uint32_t data = yearOrHour; + data <<= 8; + data |= monthOrMinute; + data <<= 8; + data |= dayOrSecond; + data <<= 8; + if (isDate == true) { + data |= 0xFF; + } + return data; +} + +uint32_t TelemetryProviderFrSky::GPSEmulator::getNextPacketData(uint32_t packetType) +{ + switch (packetType) { + case GPS_LONG_LATI_FIRST_ID: + sendLat = !sendLat; + return sendLat ? encodeLatLon(lat, true) : encodeLatLon(lon, false); + break; + case GPS_TIME_DATE_FIRST_ID: + sendDate = !sendDate; + return sendDate ? encodeDateTime(dt.date().year() - 2000, dt.date().month(), dt.date().day(), true) : encodeDateTime(dt.time().hour(), dt.time().minute(), dt.time().second(), false); + break; + case GPS_ALT_FIRST_ID: + return (uint32_t) (altitude * 100); + break; + case GPS_SPEED_FIRST_ID: + return speedKNTS * 1000; + break; + case GPS_COURS_FIRST_ID: + return course * 100; + break; + } + return 0; +} + +void TelemetryProviderFrSky::GPSEmulator::setGPSDateTime(QString dateTime) +{ + dt = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); // default to current systemtime + if (!dateTime.startsWith('*')) { + QString format("dd-MM-yyyy hh:mm:ss"); + dt = QDateTime::fromString(dateTime, format); + } +} + +void TelemetryProviderFrSky::GPSEmulator::setGPSLatLon(QString latLon) +{ + QStringList coords = latLon.split(","); + lat = 0.0; + lon = 0.0; + if (coords.length() > 1) + { + lat = coords[0].toDouble(); + lon = coords[1].toDouble(); + } +} + +void TelemetryProviderFrSky::GPSEmulator::setGPSCourse(double course) +{ + this->course = course; +} + +void TelemetryProviderFrSky::GPSEmulator::setGPSSpeedKMH(double speedKMH) +{ + this->speedKNTS = speedKMH * 0.539957; +} + +void TelemetryProviderFrSky::GPSEmulator::setGPSAltitude(double altitude) +{ + this->altitude = altitude; +} + + +void TelemetryProviderFrSky::on_saveTelemetryvalues_clicked() +{ + QString fldr = g.backupDir().trimmed(); + if (fldr.isEmpty()) + fldr = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + + QString idFileNameAndPath = QFileDialog::getSaveFileName(this, tr("Save Telemetry"), fldr % "/telemetry.tlm", tr(".tlm Files (*.tlm)")); + if (idFileNameAndPath.isEmpty()) + return; + + QFile file(idFileNameAndPath); + if (!file.open(QIODevice::WriteOnly)){ + QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open file for writing.\n%1").arg(file.errorString())); + return; + } + QTextStream out(&file); + + out<< getLogfileIdentifier(); + out << "\r\n"; + out<< ui -> rxbt -> text(); + out<<"\r\n"; + out<< ui -> Rssi -> text(); + out<<"\r\n"; + out<< ui -> Swr -> text(); + out<<"\r\n"; + out << ui -> A1 -> text(); + out<<"\r\n"; + out<< ui -> A2 -> text(); + out<<"\r\n"; + out<< ui -> A3 -> text(); + out<<"\r\n"; + out << ui -> A4 -> text(); + out<<"\r\n"; + out << ui -> T1 -> text(); + out<<"\r\n"; + out << ui -> T2 -> text(); + out<<"\r\n"; + out << ui -> rpm -> text(); + out<<"\r\n"; + out << ui -> fuel -> text(); + out<<"\r\n"; + out << ui -> fuel_qty -> text(); + out<<"\r\n"; + out << ui -> vspeed -> text(); + out<<"\r\n"; + out << ui -> valt -> text(); + out<<"\r\n"; + out << ui -> vfas -> text(); + out<<"\r\n"; + out << ui -> curr -> text(); + out<<"\r\n"; + out << ui -> cell1 -> text(); + out<<"\r\n"; + out << ui -> cell2 -> text(); + out<<"\r\n"; + out << ui -> cell3 -> text(); + out<<"\r\n"; + out << ui -> cell4 -> text(); + out<<"\r\n"; + out << ui -> cell5 -> text(); + out<<"\r\n"; + out << ui -> cell6 -> text(); + out<<"\r\n"; + out << ui -> cell7 -> text(); + out<<"\r\n"; + out << ui -> cell8 -> text(); + out<<"\r\n"; + out << ui -> aspeed -> text(); + out<<"\r\n"; + out << ui -> gps_alt -> text(); + out<<"\r\n"; + out << ui -> gps_speed -> text(); + out<<"\r\n"; + out << ui -> gps_course -> text(); + out<<"\r\n"; + out << ui -> gps_time -> text(); + out<<"\r\n"; + out << ui -> gps_latlon -> text(); + out<<"\r\n"; + out << ui -> accx -> text(); + out<<"\r\n"; + out << ui -> accy -> text(); + out<<"\r\n"; + out << ui -> accz -> text(); + out<<"\r\n"; + file.flush(); + + file.close(); + +} + + +void TelemetryProviderFrSky::on_loadTelemetryvalues_clicked() +{ + QString fldr = g.backupDir().trimmed(); + if (fldr.isEmpty()) + fldr = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + + QString idFileNameAndPath = QFileDialog::getOpenFileName(this, tr("Open Telemetry File"), fldr % "/telemetry.tlm", tr(".tlm Files (*.tlm)")); + if (idFileNameAndPath.isEmpty()) + return; + + QFile file(idFileNameAndPath); + + if (!file.open(QIODevice::ReadOnly)){ + QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open file for reading.\n%1").arg(file.errorString())); + return; + } + + QTextStream in(&file); + + QString n = in.readLine(); + double ns; + + if (n == getLogfileIdentifier()) { + // It's not a legacy telemetry values file _AND_ it's one of ours + n = in.readLine(); + ns = n.toDouble(); + } else if (n.length() > 0 && n.at(0).isDigit()) { + // Legacy telemetry values file starts with numbers and have no header line + ns = n.toDouble(); + } else { + // It's not ours, it's not legacy, error. + QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Not a FrSky S.Port telemetry values file.")); + return; + } + ui -> rxbt -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> Rssi -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> Swr -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> A1 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> A2 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> A3 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> A4 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> T1 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> T2 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> rpm -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> fuel -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> fuel_qty -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> vspeed -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> valt -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> vfas -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> curr -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell1 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell2 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell3 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell4 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell5 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell6 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell7 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> cell8 -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> aspeed -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> gps_alt -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> gps_speed -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> gps_course -> setValue(ns); + + n = in.readLine(); + ui -> gps_time -> setText(n); + + n = in.readLine(); + ui -> gps_latlon -> setText(n); + + n = in.readLine(); + ns = n.toDouble(); + ui -> accx -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> accy -> setValue(ns); + + n = in.readLine(); + ns = n.toDouble(); + ui -> accz -> setValue(ns); + + file.close(); + +} + +void TelemetryProviderFrSky::on_GPSpushButton_clicked() +{ + if (ui->GPSpushButton->text() == tr("Run")) { + ui->GPSpushButton->setText(tr("Stop")); + gpsTimer.start(); + } + else + { + ui->GPSpushButton->setText(tr("Run")); + gpsTimer.stop(); + } +} + +void TelemetryProviderFrSky::on_gps_course_valueChanged(double arg1) +{ + if (ui->gps_course->value() > 360) { + ui->gps_course->setValue(1); + } + if (ui->gps_course->value() < 1) { + ui->gps_course->setValue(360); + } +} + diff --git a/companion/src/simulation/telemetryproviderfrsky.h b/companion/src/simulation/telemetryproviderfrsky.h new file mode 100644 index 00000000000..023cef11e5c --- /dev/null +++ b/companion/src/simulation/telemetryproviderfrsky.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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. + */ + +#pragma once + +#include +#include + +#include "simulatorinterface.h" +#include "telemetryprovider.h" + +namespace Ui { + class TelemetryProviderFrSky; +} + +class TelemetryProviderFrSky : public QWidget, public TelemetryProvider +{ + Q_OBJECT + + public: + + explicit TelemetryProviderFrSky(QWidget * parent); + virtual ~TelemetryProviderFrSky(); + + signals: + void telemetryDataChanged(const quint8 protocol, const QByteArray data); + + public slots: + bool generateSportPacket(uint8_t * packet, uint8_t dataId, uint8_t prim, uint16_t appId, uint32_t data); + void resetRssi(); + QHash * getSupportedLogItems(); + void loadItemFromLog(QString itemName, QString value); + QString getLogfileIdentifier(); + void loadUiFromSimulator(SimulatorInterface * simulator); + + protected slots: + void updateGps(); + void generateTelemetryFrame(SimulatorInterface * simulator); + void refreshSensorRatios(SimulatorInterface * simulator); + + protected: + Ui::TelemetryProviderFrSky * ui; + QTimer gpsTimer; + QHash supportedLogItems; + + class FlvssEmulator + { + public: + static const uint32_t MAXCELLS = 8; + uint32_t setAllCells_GetNextPair(double cellValues[MAXCELLS]); + + private: + void encodeAllCells(); + void splitIntoCells(double totalVolts); + static uint32_t encodeCellPair(uint8_t cellNum, uint8_t firstCellNo, double cell1, double cell2); + double cellFloats[MAXCELLS]; + uint32_t nextCellNum; + uint32_t numCells; + uint32_t cellData1; + uint32_t cellData2; + uint32_t cellData3; + uint32_t cellData4; + }; // FlvssEmulator + + class GPSEmulator + { + public: + GPSEmulator(); + uint32_t getNextPacketData(uint32_t packetType); + void setGPSDateTime(QString dateTime); + void setGPSLatLon(QString latLon); + void setGPSCourse(double course); + void setGPSSpeedKMH(double speed); + void setGPSAltitude(double altitude); + + private: + QDateTime dt; + bool sendLat; + bool sendDate; + double lat; + double lon; + double course; + double speedKNTS; + double altitude; // in meters + uint32_t encodeLatLon(double latLon, bool isLat); + uint32_t encodeDateTime(uint8_t yearOrHour, uint8_t monthOrMinute, uint8_t dayOrSecond, bool isDate); + }; // GPSEmulator + + private slots: + void on_saveTelemetryvalues_clicked(); + void on_loadTelemetryvalues_clicked(); + void on_GPSpushButton_clicked(); + void on_gps_course_valueChanged(double arg1); +}; diff --git a/companion/src/simulation/telemetryproviderfrsky.ui b/companion/src/simulation/telemetryproviderfrsky.ui new file mode 100644 index 00000000000..9d8e287a834 --- /dev/null +++ b/companion/src/simulation/telemetryproviderfrsky.ui @@ -0,0 +1,2669 @@ + + + TelemetryProviderFrSky + + + + 0 + 0 + 376 + 939 + + + + + 1 + 1 + + + + Form + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Cels + + + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 999.000000000000000 + + + 0.100000000000000 + + + 5.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 999.000000000000000 + + + 0.100000000000000 + + + + + + + + + + 0 + 0 + + + + + 8 + + + + Fuel Qty + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + GAlt + + + + + + + + 8 + + + + Save Telemetry Values + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + VFAS + + + + + + + + 0 + 0 + + + + + 8 + + + + ml + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 125.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + A4 + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + ASpd + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + -16777215 + + + 16777215 + + + 26 + + + + + + + + 0 + 0 + + + + + 8 + + + + 1 + + + 0.000000000000000 + + + 5000.000000000000000 + + + 1.000000000000000 + + + 40.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + °C + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 6.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + Volts + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + 65535 + + + 50 + + + + + + + + 0 + 0 + + + + + 8 + + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 5.200000000000000 + + + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 99.000000000000000 + + + 0.100000000000000 + + + 5.200000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 99.000000000000000 + + + 0.100000000000000 + + + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + -0.100000000000000 + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + G + + + + + + + + 8 + + + + Run/Stop + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 0.200000000000000 + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 999.000000000000000 + + + 0.100000000000000 + + + 5.100000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 999.000000000000000 + + + 0.100000000000000 + + + + + + + + + + 0 + 0 + + + + + 8 + + + + 2147483647 + + + 5000 + + + + + + + + + + 0 + 0 + + + + + 8 + + + + 0.100000000000000 + + + 3.700000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 3.800000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 3.900000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 4.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 4.100000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 4.200000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 4.200000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 8.190000000000000 + + + 0.100000000000000 + + + 4.200000000000000 + + + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Tmp1 + + + + + + + + 0 + 0 + + + + + 8 + + + + RxBt + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + GPS + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + 0.000000000000000 + + + 361.000000000000000 + + + 1.000000000000000 + + + 340.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + 75 + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + m/s + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + + + + + + 0 + 0 + + + + + 8 + + + + % + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 1125.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + Degrees + + + + + + + + 8 + + + + GPS sim + + + + + + + + 0 + 0 + + + + + 8 + + + + km/h + + + + + + + + 0 + 0 + + + + + 8 + + + + V / ratio + + + + + + + + 0 + 0 + + + + + 8 + + + + G + + + + + + + + 0 + 0 + + + + + 8 + + + + 100000 + + + 25 + + + + + + + + 0 + 0 + + + + + 8 + + + + * + + + + + + + + 0 + 0 + + + + + 8 + + + + AccZ + + + + + + + + 0 + 0 + + + + + 8 + + + + dd-MM-yyyy +hh:mm:ss + + + false + + + + + + + + 0 + 0 + + + + + 8 + + + + Meters + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + RAS + + + + + + + + 0 + 0 + + + + + 8 + + + + AccX + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Tmp2 + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + km/h + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + dB + + + + + + + + 0 + 0 + + + + + 8 + + + + RPM + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + A1 + + + + + + + + 0 + 0 + + + + + 8 + + + + AccY + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + VSpd + + + + + + + + 8 + + + + Load Telemetry Values + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 23.699999999999999 + + + + + + + + 0 + 0 + + + + + 8 + + + + G + + + + + + + + 0 + 0 + + + + + 8 + + + + A2 + + + + + + + + 0 + 0 + + + + + 8 + + + + Lat,Lon +(dec.deg.) + + + false + + + + + + + + 0 + 0 + + + + + 8 + + + + Meters + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + °C + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + -16777215 + + + 16777215 + + + 1 + + + 25 + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + A3 + + + + + + + + 0 + 0 + + + + + 8 + + + + Fuel + + + + + + + + 0 + 0 + + + + + 8 + + + + RSSI + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Hdg + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Curr + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + 30 + + + + + + + + 0 + 0 + + + + + 8 + + + + Amps + + + + + + + + 0 + 0 + + + + + 8 + + + + 25.9973,-97.1572 + + + + + + + + 0 + 0 + + + + + 8 + + + + 2 + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 0.100000000000000 + + + 35.000000000000000 + + + + + + + + 0 + 0 + + + + + 8 + + + + V / ratio + + + + + + + + 0 + 0 + + + + + 8 + + + + RPM + + + + + + + + 8 + + + + Run + + + + + + + + 0 + 0 + + + + + 8 + + + + -21474836.000000000000000 + + + 21474836.000000000000000 + + + 5.150000000000000 + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Alt + + + + + + + + 0 + 0 + + + + + 8 + + + + Volts + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + V / ratio + + + + + + + + 0 + 0 + + + + + 45 + 0 + + + + + 45 + 16777215 + + + + + 8 + + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + Date + + + + + + + + 0 + 0 + + + + + 8 + + + + <html><head/><body><p><br/></p></body></html> + + + GSpd + + + + + + + + 0 + 0 + + + + + 8 + + + + 1 + + + -214748364.000000000000000 + + + 214748364.000000000000000 + + + 0.100000000000000 + + + 0.100000000000000 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/companion/src/simulation/telemetrysimu.cpp b/companion/src/simulation/telemetrysimu.cpp index b44a0f05bb0..281931159c6 100644 --- a/companion/src/simulation/telemetrysimu.cpp +++ b/companion/src/simulation/telemetrysimu.cpp @@ -25,11 +25,13 @@ #include "simulatorinterface.h" #include "telem_data.h" #include "radio/src/telemetry/frsky_defs.h" +#include "telemetryproviderfrsky.h" +#include "telemetryprovidercrossfire.h" #include #include -#include // +#include TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator): QWidget(parent), @@ -37,32 +39,21 @@ TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * si simulator(simulator), m_simuStarted(false), m_logReplayEnable(false), - logPlayback(new LogPlaybackController(ui)) + logPlayback(new LogPlaybackController(ui, this)) { ui->setupUi(this); - ui->A1->setSpecialValueText(" "); - ui->A2->setSpecialValueText(" "); - ui->A3->setSpecialValueText(" "); - ui->A4->setSpecialValueText(" "); - ui->rpm->setSpecialValueText(" "); - ui->fuel->setSpecialValueText(" "); - - ui->rxbt_ratio->setEnabled(false); - ui->A1_ratio->setEnabled(false); - ui->A2_ratio->setEnabled(false); - ui->Simulate->setChecked(g.currentProfile().telemSimEnabled()); ui->cbPauseOnHide->setChecked(g.currentProfile().telemSimPauseOnHide()); ui->cbResetRssiOnStop->setChecked(g.currentProfile().telemSimResetRssiOnStop()); + internalProvider = NULL; + externalProvider = NULL; + timer.setInterval(10); connect(&timer, &QTimer::timeout, this, &TelemetrySimulator::generateTelemetryFrame); connect(&logTimer, &QTimer::timeout, this, &TelemetrySimulator::onLogTimerEvent); - gpsTimer.setInterval(250); - connect(&gpsTimer, &QTimer::timeout, this, &TelemetrySimulator::onGpsRunLoop); - connect(ui->Simulate, &QCheckBox::toggled, [&](bool on) { g.currentProfile().telemSimEnabled(on); }); connect(ui->cbPauseOnHide, &QCheckBox::toggled, [&](bool on) { g.currentProfile().telemSimPauseOnHide(on); }); connect(ui->cbResetRssiOnStop, &QCheckBox::toggled, [&](bool on) { g.currentProfile().telemSimResetRssiOnStop(on); }); @@ -76,7 +67,12 @@ TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * si connect(ui->positionIndicator, &QScrollBar::valueChanged, this, &TelemetrySimulator::onPositionIndicatorChanged); connect(ui->replayRate, &QSlider::valueChanged, this, &TelemetrySimulator::onReplayRateChanged); - connect(this, &TelemetrySimulator::telemetryDataChanged, simulator, &SimulatorInterface::sendTelemetry); + connect(ui->internalTelemetrySelector, QOverload::of(&QComboBox::currentIndexChanged), this, &TelemetrySimulator::onInternalTelemetrySelectorChanged); + connect(ui->externalTelemetrySelector, QOverload::of(&QComboBox::currentIndexChanged), this, &TelemetrySimulator::onExternalTelemetrySelectorChanged); + + connect(this, &TelemetrySimulator::internalTelemetryDataChanged, simulator, &SimulatorInterface::sendInternalModuleTelemetry); + connect(this, &TelemetrySimulator::externalTelemetryDataChanged, simulator, &SimulatorInterface::sendExternalModuleTelemetry); + connect(simulator, &SimulatorInterface::started, this, &TelemetrySimulator::onSimulatorStarted); connect(simulator, &SimulatorInterface::stopped, this, &TelemetrySimulator::onSimulatorStopped); connect(&g.currentProfile(), &Profile::telemSimEnabledChanged, this, &TelemetrySimulator::onSimulateToggled); @@ -89,6 +85,16 @@ TelemetrySimulator::~TelemetrySimulator() delete ui; } +TelemetryProvider * TelemetrySimulator::getInternalTelemetryProvider() +{ + return internalProvider; +} + +TelemetryProvider * TelemetrySimulator::getExternalTelemetryProvider() +{ + return externalProvider; +} + void TelemetrySimulator::hideEvent(QHideEvent *event) { if (g.currentProfile().telemSimPauseOnHide()) @@ -118,23 +124,17 @@ void TelemetrySimulator::stopTelemetry() m_logReplayEnable = logTimer.isActive(); onStop(); - if (!(g.currentProfile().telemSimResetRssiOnStop() && ui && ui->rssi_inst)) - return; - - bool ok = false; - const int id = ui->rssi_inst->text().toInt(&ok, 0); - if (!ok) + if (!g.currentProfile().telemSimResetRssiOnStop()) return; - - uint8_t buffer[FRSKY_SPORT_PACKET_SIZE] = {0}; - generateSportPacket(buffer, id, DATA_FRAME, RSSI_ID, 0); - emit telemetryDataChanged(QByteArray((char *)buffer, FRSKY_SPORT_PACKET_SIZE)); + if (internalProvider) + internalProvider->resetRssi(); + if (externalProvider) + externalProvider->resetRssi(); } void TelemetrySimulator::onSimulatorStarted() { m_simuStarted = true; - setupDataFields(); if (isVisible() && g.currentProfile().telemSimEnabled()) startTelemetry(); } @@ -153,53 +153,81 @@ void TelemetrySimulator::onSimulateToggled(bool isChecked) stopTelemetry(); } -void TelemetrySimulator::onLogTimerEvent() +TelemetryProvider * TelemetrySimulator::newTelemetryProviderFromDropdownChoice(int selectedIndex, QScrollArea * parent, bool isExternal) { - logPlayback->stepForward(false); -} + switch(selectedIndex) { + case 0: + return NULL; + case 1: + { + TelemetryProviderFrSky * newProvider = new TelemetryProviderFrSky(parent); + parent->setWidget(newProvider); + if (isExternal) { + connect(newProvider, &TelemetryProviderFrSky::telemetryDataChanged, this, &TelemetrySimulator::onExternalTelemetryProviderDataChanged); + } else { + connect(newProvider, &TelemetryProviderFrSky::telemetryDataChanged, this, &TelemetrySimulator::onInternalTelemetryProviderDataChanged); + } + // FrSky provider needs to set up sensor instances from the model's telemetry configuration + newProvider->loadUiFromSimulator(simulator); -void TelemetrySimulator::onGpsRunLoop() -{ - int a = ui->gps_latlon->text().contains(","); - if (!a) { - QMessageBox::information(this, tr("Bad GPS Format"), tr("Must be decimal latitude,longitude")); - ui->gps_latlon->setText("000.00000000,000.00000000"); - ui->GPSpushButton->click(); - } - else - { - QStringList gpsLatLon = (ui->gps_latlon->text()).split(","); - - double b2 = gpsLatLon[0].toDouble(); - double c2 = gpsLatLon[1].toDouble(); - double d3 = ui->gps_speed->value() / 14400; - double f3 = ui->gps_course->value(); - double j2 = 6378.1; - double b3 = qRadiansToDegrees(qAsin( qSin(qDegreesToRadians(b2))*qCos(d3/j2) + qCos(qDegreesToRadians(b2))*qSin(d3/j2)*qCos(qDegreesToRadians(f3)))); - double bb3 = b3; - if (bb3 < 0) { - bb3 = bb3 * -1; + return newProvider; } - if (bb3 > 89.99) { - f3 = f3 + 180; - if (f3 > 360) { - f3 = f3 - 360; + case 2: + { + TelemetryProviderCrossfire * newProvider = new TelemetryProviderCrossfire(parent); + parent->setWidget(newProvider); + if (isExternal) { + connect(newProvider, &TelemetryProviderCrossfire::telemetryDataChanged, this, &TelemetrySimulator::onExternalTelemetryProviderDataChanged); + } else { + connect(newProvider, &TelemetryProviderCrossfire::telemetryDataChanged, this, &TelemetrySimulator::onInternalTelemetryProviderDataChanged); } - ui->gps_course->setValue(f3); - } - double c3 = qRadiansToDegrees(qDegreesToRadians(c2) + qAtan2(qSin(qDegreesToRadians(f3))*qSin(d3/j2)*qCos(qDegreesToRadians(b2)),qCos(d3/j2)-qSin(qDegreesToRadians(b2))*qSin(qDegreesToRadians(b3)))); - if (c3 > 180) { - c3 = c3 - 360; + return newProvider; } - if (c3 < -180) { - c3 = c3 + 360; - } - QString lats = QString::number(b3, 'f', 8); - QString lons = QString::number(c3, 'f', 8); - QString qs = lats + "," + lons; - ui->gps_latlon->setText(qs); + default: + qDebug() << "Unimplemented telemetry provider " << selectedIndex; } + return NULL; } + +void TelemetrySimulator::onInternalTelemetrySelectorChanged(int selectedIndex) +{ + if (internalProvider) { + delete internalProvider; + } + internalProvider = newTelemetryProviderFromDropdownChoice(selectedIndex, ui->internalScrollArea, false); +} + +void TelemetrySimulator::onExternalTelemetrySelectorChanged(int selectedIndex) +{ + if (externalProvider) { + delete externalProvider; + } + externalProvider = newTelemetryProviderFromDropdownChoice(selectedIndex, ui->externalScrollArea, true); +} + +void TelemetrySimulator::onInternalTelemetryProviderDataChanged(const quint8 protocol, const QByteArray data) +{ + emit internalTelemetryDataChanged(protocol, data); +} + +void TelemetrySimulator::onExternalTelemetryProviderDataChanged(const quint8 protocol, const QByteArray data) +{ + emit externalTelemetryDataChanged(protocol, data); +} + +void TelemetrySimulator::generateTelemetryFrame() +{ + if (internalProvider) + internalProvider->generateTelemetryFrame(simulator); + if (externalProvider) + externalProvider->generateTelemetryFrame(simulator); +} + +void TelemetrySimulator::onLogTimerEvent() +{ + logPlayback->stepForward(false); +} + void TelemetrySimulator::onLoadLogFile() { onStop(); // in case we are in playback mode @@ -262,504 +290,116 @@ void TelemetrySimulator::onReplayRateChanged(int value) } } -#define SET_INSTANCE(control, id, def) ui->control->setText(QString::number(simulator->getSensorInstance(id, ((def) & 0x1F)))) -void TelemetrySimulator::setupDataFields() +TelemetrySimulator::LogPlaybackController::LogPlaybackController(Ui::TelemetrySimulator * ui, TelemetrySimulator * sim) { - SET_INSTANCE(rxbt_inst, BATT_ID, 0); - SET_INSTANCE(rssi_inst, RSSI_ID, 24); - SET_INSTANCE(swr_inst, RAS_ID, 24); - SET_INSTANCE(a1_inst, ADC1_ID, 0); - SET_INSTANCE(a2_inst, ADC2_ID, 0); - SET_INSTANCE(a3_inst, A3_FIRST_ID, 0); - SET_INSTANCE(a4_inst, A4_FIRST_ID, 0); - SET_INSTANCE(t1_inst, T1_FIRST_ID, 0); - SET_INSTANCE(t2_inst, T2_FIRST_ID, 0); - SET_INSTANCE(rpm_inst, RPM_FIRST_ID, DATA_ID_RPM); - SET_INSTANCE(fuel_inst, FUEL_FIRST_ID, 0); - SET_INSTANCE(fuel_qty_inst, FUEL_QTY_FIRST_ID, 0); - SET_INSTANCE(aspd_inst, AIR_SPEED_FIRST_ID, 0); - SET_INSTANCE(vvspd_inst, VARIO_FIRST_ID, DATA_ID_VARIO); - SET_INSTANCE(valt_inst, ALT_FIRST_ID, DATA_ID_VARIO); - SET_INSTANCE(fasv_inst, VFAS_FIRST_ID, DATA_ID_FAS); - SET_INSTANCE(fasc_inst, CURR_FIRST_ID, DATA_ID_FAS); - SET_INSTANCE(cells_inst, CELLS_FIRST_ID, DATA_ID_FLVSS); - SET_INSTANCE(gpsa_inst, GPS_ALT_FIRST_ID, DATA_ID_GPS); - SET_INSTANCE(gpss_inst, GPS_SPEED_FIRST_ID, DATA_ID_GPS); - SET_INSTANCE(gpsc_inst, GPS_COURS_FIRST_ID, DATA_ID_GPS); - SET_INSTANCE(gpst_inst, GPS_TIME_DATE_FIRST_ID, DATA_ID_GPS); - SET_INSTANCE(gpsll_inst, GPS_LONG_LATI_FIRST_ID, DATA_ID_GPS); - SET_INSTANCE(accx_inst, ACCX_FIRST_ID, 0); - SET_INSTANCE(accy_inst, ACCY_FIRST_ID, 0); - SET_INSTANCE(accz_inst, ACCZ_FIRST_ID, 0); - - refreshSensorRatios(); -} + TelemetrySimulator::LogPlaybackController::sim = sim; + TelemetrySimulator::LogPlaybackController::ui = ui; + stepping = false; + logFileGpsCordsInDecimalFormat = false; -void setSportPacketCrc(uint8_t * packet) -{ - short crc = 0; - for (int i=1; i> 8; //0-100 - crc &= 0x00ff; - crc += crc >> 8; //0-0FF - crc &= 0x00ff; - } - packet[FRSKY_SPORT_PACKET_SIZE-1] = 0xFF - (crc & 0x00ff); } -uint8_t getBit(uint8_t position, uint8_t value) +QString convertFeetToMeters(QString input) { - return (value & (uint8_t)(1 << position)) ? 1 : 0; + double meters = input.toDouble() * 0.3048; + return QString::number(meters); } -bool TelemetrySimulator::generateSportPacket(uint8_t * packet, uint8_t dataId, uint8_t prim, uint16_t appId, uint32_t data) +QString convertFahrenheitToCelsius(QString input) { - if (dataId > 0x1B ) return false; - - // generate Data ID field - uint8_t bit5 = getBit(0, dataId) ^ getBit(1, dataId) ^ getBit(2, dataId); - uint8_t bit6 = getBit(2, dataId) ^ getBit(3, dataId) ^ getBit(4, dataId); - uint8_t bit7 = getBit(0, dataId) ^ getBit(2, dataId) ^ getBit(4, dataId); - - packet[0] = (bit7 << 7) + (bit6 << 6) + (bit5 << 5) + dataId; - // qDebug("dataID: 0x%02x (%d)", packet[0], dataId); - packet[1] = prim; - *((uint16_t *)(packet+2)) = appId; - *((int32_t *)(packet+4)) = data; - setSportPacketCrc(packet); - return true; + double celsius = (input.toDouble() - 32.0) * 0.5556; + return QString::number(celsius); } -void TelemetrySimulator::refreshSensorRatios() +QString convertKnotsToKPH(QString input) { - ui->rxbt_ratio->setValue(simulator->getSensorRatio(BATT_ID) / 10.0); - ui->A1_ratio->setValue(simulator->getSensorRatio(ADC1_ID) / 10.0); - ui->A2_ratio->setValue(simulator->getSensorRatio(ADC2_ID) / 10.0); + double kph = input.toDouble() * 1.852; + return QString::number(kph); } -void TelemetrySimulator::generateTelemetryFrame() +QString convertMilesPerHourToKPH(QString input) { - static int item = 0; - bool ok = true; - uint8_t buffer[FRSKY_SPORT_PACKET_SIZE] = {0}; - static FlvssEmulator *flvss = new FlvssEmulator(); - static GPSEmulator *gps = new GPSEmulator(); - - if (!m_simuStarted) - return; - - switch (item++) { - case 0: -#if defined(XJT_VERSION_ID) - generateSportPacket(buffer, 1, DATA_FRAME, XJT_VERSION_ID, 11); -#endif - refreshSensorRatios(); // placed here in order to call this less often - break; - - case 1: - if (ui->rxbt->text().length()) { - generateSportPacket(buffer, ui->rxbt_inst->text().toInt(&ok, 0), DATA_FRAME, BATT_ID, LIMIT(0, ui->rxbt->value() * 255.0 / ui->rxbt_ratio->value(), 0xFFFFFFFF)); - } - break; - - case 2: - if (ui->Rssi->text().length()) - generateSportPacket(buffer, ui->rssi_inst->text().toInt(&ok, 0), DATA_FRAME, RSSI_ID, LIMIT(0, ui->Rssi->text().toInt(&ok, 0), 0xFF)); - break; - - case 3: - if (ui->Swr->text().length()) - generateSportPacket(buffer, ui->swr_inst->text().toInt(&ok, 0), DATA_FRAME, RAS_ID, LIMIT(0, ui->Swr->text().toInt(&ok, 0), 0xFFFF)); - break; - - case 4: - if (ui->A1->value() > 0) - generateSportPacket(buffer, ui->a1_inst->text().toInt(&ok, 0), DATA_FRAME, ADC1_ID, LIMIT(0, ui->A1->value() * 255.0 / ui->A1_ratio->value(), 0xFF)); - break; - - case 5: - if (ui->A2->value() > 0) - generateSportPacket(buffer, ui->a2_inst->text().toInt(&ok, 0), DATA_FRAME, ADC2_ID, LIMIT(0, ui->A2->value() * 255.0 / ui->A2_ratio->value(), 0xFF)); - break; - - case 6: - if (ui->A3->value() != 0) - generateSportPacket(buffer, ui->a3_inst->text().toInt(&ok, 0), DATA_FRAME, A3_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->A3->value() * 100.0, 0x7FFFFFFF)); - break; - - case 7: - if (ui->A4->value() != 0) - generateSportPacket(buffer, ui->a4_inst->text().toInt(&ok, 0), DATA_FRAME, A4_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->A4->value() * 100.0, 0x7FFFFFFF)); - break; - - case 8: - if (ui->T1->value() != 0) - generateSportPacket(buffer, ui->t1_inst->text().toInt(&ok, 0), DATA_FRAME, T1_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->T1->value(), 0x7FFFFFFF)); - break; - - case 9: - if (ui->T2->value() != 0) - generateSportPacket(buffer, ui->t2_inst->text().toInt(&ok, 0), DATA_FRAME, T2_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->T2->value(), 0x7FFFFFFF)); - break; - - case 10: - if (ui->rpm->value() > 0) - generateSportPacket(buffer, ui->rpm_inst->text().toInt(&ok, 0), DATA_FRAME, RPM_FIRST_ID, LIMIT(0, ui->rpm->value(), 0x7FFFFFFF)); - break; - - case 11: - if (ui->fuel->value() > 0) - generateSportPacket(buffer, ui->fuel_inst->text().toInt(&ok, 0), DATA_FRAME, FUEL_FIRST_ID, LIMIT(0, ui->fuel->value(), 0xFFFF)); - break; - - case 12: - if (ui->vspeed->value() != 0) - generateSportPacket(buffer, ui->vvspd_inst->text().toInt(&ok, 0), DATA_FRAME, VARIO_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->vspeed->value() * 100.0, 0x7FFFFFFF)); - break; - - case 13: - if (ui->valt->value() != 0) - generateSportPacket(buffer, ui->valt_inst->text().toInt(&ok, 0), DATA_FRAME, ALT_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->valt->value() * 100.0, 0x7FFFFFFF)); - break; - - case 14: - if (ui->vfas->value() != 0) - generateSportPacket(buffer, ui->fasv_inst->text().toInt(&ok, 0), DATA_FRAME, VFAS_FIRST_ID, LIMIT(0, ui->vfas->value() * 100.0, 0xFFFFFFFF)); - break; - - case 15: - if (ui->curr->value() != 0) - generateSportPacket(buffer, ui->fasc_inst->text().toInt(&ok, 0), DATA_FRAME, CURR_FIRST_ID, LIMIT(0, ui->curr->value() * 10.0, 0xFFFFFFFF)); - break; - - case 16: - double cellValues[FlvssEmulator::MAXCELLS]; - if (ui->cell1->value() > 0.009) { // ??? cell1 returning non-zero value when spin box is zero! - cellValues[0] = ui->cell1->value(); - cellValues[1] = ui->cell2->value(); - cellValues[2] = ui->cell3->value(); - cellValues[3] = ui->cell4->value(); - cellValues[4] = ui->cell5->value(); - cellValues[5] = ui->cell6->value(); - cellValues[6] = ui->cell7->value(); - cellValues[7] = ui->cell8->value(); - generateSportPacket(buffer, ui->cells_inst->text().toInt(&ok, 0), DATA_FRAME, CELLS_FIRST_ID, flvss->setAllCells_GetNextPair(cellValues)); - } - else { - cellValues[0] = 0; - flvss->setAllCells_GetNextPair(cellValues); - } - break; - - case 17: - if (ui->aspeed->value() > 0) - generateSportPacket(buffer, ui->aspd_inst->text().toInt(&ok, 0), DATA_FRAME, AIR_SPEED_FIRST_ID, LIMIT(0, ui->aspeed->value() * 5.39957, 0xFFFFFFFF)); - break; - - case 18: - if (ui->gps_alt->value() != 0) { - gps->setGPSAltitude(ui->gps_alt->value()); - generateSportPacket(buffer, ui->gpsa_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_ALT_FIRST_ID, gps->getNextPacketData(GPS_ALT_FIRST_ID)); - } - break; - - case 19: - if (ui->gps_speed->value() > 0) { - gps->setGPSSpeedKMH(ui->gps_speed->value()); - generateSportPacket(buffer, ui->gpss_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_SPEED_FIRST_ID, gps->getNextPacketData(GPS_SPEED_FIRST_ID)); - } - break; - - case 20: - if (ui->gps_course->value() != 0) { - gps->setGPSCourse(ui->gps_course->value()); - generateSportPacket(buffer, ui->gpsc_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_COURS_FIRST_ID, gps->getNextPacketData(GPS_COURS_FIRST_ID)); - } - break; - - case 21: - if (ui->gps_time->text().length()) { - gps->setGPSDateTime(ui->gps_time->text()); - generateSportPacket(buffer, ui->gpst_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_TIME_DATE_FIRST_ID, gps->getNextPacketData(GPS_TIME_DATE_FIRST_ID)); - } - break; - - case 22: - if (ui->gps_latlon->text().length()) { - gps->setGPSLatLon(ui->gps_latlon->text()); - generateSportPacket(buffer, ui->gpsll_inst->text().toInt(&ok, 0), DATA_FRAME, GPS_LONG_LATI_FIRST_ID, gps->getNextPacketData(GPS_LONG_LATI_FIRST_ID)); - } - break; - - case 23: - if (ui->accx->value() != 0) - generateSportPacket(buffer, ui->accx_inst->text().toInt(&ok, 0), DATA_FRAME, ACCX_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->accx->value() * 100.0, 0x7FFFFFFF)); - break; - - case 24: - if (ui->accy->value() != 0) - generateSportPacket(buffer, ui->accy_inst->text().toInt(&ok, 0), DATA_FRAME, ACCY_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->accy->value() * 100.0, 0x7FFFFFFF)); - break; - - case 25: - if (ui->accz->value() != 0) - generateSportPacket(buffer, ui->accz_inst->text().toInt(&ok, 0), DATA_FRAME, ACCZ_FIRST_ID, LIMIT(-0x7FFFFFFF, ui->accz->value() * 100.0, 0x7FFFFFFF)); - break; - - case 26: - if (ui->fuel_qty->value() > 0) - generateSportPacket(buffer, ui->fuel_qty_inst->text().toInt(&ok, 0), DATA_FRAME, FUEL_QTY_FIRST_ID, LIMIT(0, ui->fuel_qty->value() * 100.0, 0xFFFFFF)); - break; - - default: - item = 0; - return; - } - - if (ok && (buffer[2] || buffer[3])) { - QByteArray ba((char *)buffer, FRSKY_SPORT_PACKET_SIZE); - emit telemetryDataChanged(ba); - //qDebug("%02X %02X %02X %02X %02X %02X %02X %02X %02X", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8]); - } - else { - generateTelemetryFrame(); - } + double kph = input.toDouble() * 1.60934; + return QString::number(kph); } -uint32_t TelemetrySimulator::FlvssEmulator::encodeCellPair(uint8_t cellNum, uint8_t firstCellNo, double cell1, double cell2) +QString convertMetersPerSecondToKPH(QString input) { - uint16_t cell1Data = cell1 * 500.0; - uint16_t cell2Data = cell2 * 500.0; - uint32_t cellData = 0; - - cellData = cell2Data & 0x0FFF; - cellData <<= 12; - cellData |= cell1Data & 0x0FFF; - cellData <<= 4; - cellData |= cellNum & 0x0F; - cellData <<= 4; - cellData |= firstCellNo & 0x0F; - - return cellData; + double kph = input.toDouble() * 3.6; + return QString::number(kph); } -void TelemetrySimulator::FlvssEmulator::encodeAllCells() +QString convertFeetPerSecondToKPH(QString input) { - cellData1 = encodeCellPair(numCells, 0, cellFloats[0], cellFloats[1]); - if (numCells > 2) cellData2 = encodeCellPair(numCells, 2, cellFloats[2], cellFloats[3]); else cellData2 = 0; - if (numCells > 4) cellData3 = encodeCellPair(numCells, 4, cellFloats[4], cellFloats[5]); else cellData3 = 0; - if (numCells > 6) cellData4 = encodeCellPair(numCells, 6, cellFloats[6], cellFloats[7]); else cellData4 = 0; + double kph = input.toDouble() * 1.09728; + return QString::number(kph); } -void TelemetrySimulator::FlvssEmulator::splitIntoCells(double totalVolts) +QString convertDegreesToRadians(QString input) { - numCells = qFloor((totalVolts / 3.7) + .5); - double avgVolts = totalVolts / numCells; - double remainder = (totalVolts - (avgVolts * numCells)); - for (uint32_t i = 0; (i < numCells) && ( i < MAXCELLS); i++) { - cellFloats[i] = avgVolts; - } - for (uint32_t i = numCells; i < MAXCELLS; i++) { - cellFloats[i] = 0; - } - cellFloats[0] += remainder; - numCells = numCells > MAXCELLS ? MAXCELLS : numCells; // force into valid cell count in case of input out of range + double rad = input.toDouble() * 0.0174533; + return QString::number(rad); } -uint32_t TelemetrySimulator::FlvssEmulator::setAllCells_GetNextPair(double cellValues[MAXCELLS]) +QString convertRadiansToDegrees(QString input) { - numCells = 0; - for (uint32_t i = 0; i < MAXCELLS; i++) { - if ((i == 0) && (cellValues[0] > 4.2)) { - splitIntoCells(cellValues[0]); - break; - } - if (cellValues[i] > 0) { - cellFloats[i] = cellValues[i]; - numCells++; - } - else { - // zero marks the last cell - for (uint32_t x = i; x < MAXCELLS; x++) { - cellFloats[x] = 0; - } - break; - } - } - - // encode the double values into telemetry format - encodeAllCells(); - - // return the value for the current pair - uint32_t cellData = 0; - if (nextCellNum >= numCells) { - nextCellNum = 0; - } - switch (nextCellNum) { - case 0: - cellData = cellData1; - break; - case 2: - cellData = cellData2; - break; - case 4: - cellData = cellData3; - break; - case 6: - cellData = cellData4; - break; - } - nextCellNum += 2; - return cellData; + double deg = input.toDouble() * 57.2958; + return QString::number(deg); } -TelemetrySimulator::GPSEmulator::GPSEmulator() +QString convertWattToMilliwatt(QString input) { - lat = 0; - lon = 0; - dt = QDateTime::currentDateTime(); - sendLat = true; - sendDate = true; + double mw = input.toDouble() * 1000; + return QString::number(mw); } - -uint32_t TelemetrySimulator::GPSEmulator::encodeLatLon(double latLon, bool isLat) +QString convertFluidOuncesToMilliliters(QString input) { - uint32_t data = (uint32_t)((latLon < 0 ? -latLon : latLon) * 60 * 10000) & 0x3FFFFFFF; - if (isLat == false) { - data |= 0x80000000; - } - if (latLon < 0) { - data |= 0x40000000; - } - return data; + double ml = input.toDouble() * 29.5735; + return QString::number(ml); } -uint32_t TelemetrySimulator::GPSEmulator::encodeDateTime(uint8_t yearOrHour, uint8_t monthOrMinute, uint8_t dayOrSecond, bool isDate) +QString convertDBMToMilliwatts(QString input) { - uint32_t data = yearOrHour; - data <<= 8; - data |= monthOrMinute; - data <<= 8; - data |= dayOrSecond; - data <<= 8; - if (isDate == true) { - data |= 0xFF; - } - return data; + double dbm = input.toDouble(); + double mw = qPow(10, dbm/10); + return QString::number(mw); } -uint32_t TelemetrySimulator::GPSEmulator::getNextPacketData(uint32_t packetType) -{ - switch (packetType) { - case GPS_LONG_LATI_FIRST_ID: - sendLat = !sendLat; - return sendLat ? encodeLatLon(lat, true) : encodeLatLon(lon, false); - break; - case GPS_TIME_DATE_FIRST_ID: - sendDate = !sendDate; - return sendDate ? encodeDateTime(dt.date().year() - 2000, dt.date().month(), dt.date().day(), true) : encodeDateTime(dt.time().hour(), dt.time().minute(), dt.time().second(), false); - break; - case GPS_ALT_FIRST_ID: - return (uint32_t) (altitude * 100); - break; - case GPS_SPEED_FIRST_ID: - return speedKNTS * 1000; - break; - case GPS_COURS_FIRST_ID: - return course * 100; - break; - } - return 0; -} +struct unitConversion { + QString source; + QString destination; + QString (*converter)(QString); +} conversions[] = { + {QString("ft"), QString("m"), convertFeetToMeters}, + {QString("°F"), QString("°C"), convertFahrenheitToCelsius}, + {QString("kts"), QString("kmh"), convertKnotsToKPH}, + {QString("mph"), QString("kmh"), convertMilesPerHourToKPH}, + {QString("m/s"), QString("kmh"), convertMetersPerSecondToKPH}, + {QString("f/s"), QString("kmh"), convertFeetPerSecondToKPH}, + {QString("°"), QString("rad"), convertDegreesToRadians}, + {QString("rad"), QString("°"), convertRadiansToDegrees}, + {QString("W"), QString("mW"), convertWattToMilliwatt}, + {QString("fOz"), QString("ml"), convertFluidOuncesToMilliliters}, + {QString("dBm"), QString("mW"), convertDBMToMilliwatts}, + {NULL, NULL, NULL}, + }; -void TelemetrySimulator::GPSEmulator::setGPSDateTime(QString dateTime) +QString convertItemValue(QString sourceUnit, QString destUnit, QString value) { - dt = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); // default to current systemtime - if (!dateTime.startsWith('*')) { - QString format("dd-MM-yyyy hh:mm:ss"); - dt = QDateTime::fromString(dateTime, format); - } -} + if (sourceUnit == destUnit) + return value; -void TelemetrySimulator::GPSEmulator::setGPSLatLon(QString latLon) -{ - QStringList coords = latLon.split(","); - lat = 0.0; - lon = 0.0; - if (coords.length() > 1) - { - lat = coords[0].toDouble(); - lon = coords[1].toDouble(); + int i = 0; + while (conversions[i].source != NULL) { + if (conversions[i].source == sourceUnit && conversions[i].destination == destUnit) { + return ((conversions[i].converter)(value)); + } + i++; } -} - -void TelemetrySimulator::GPSEmulator::setGPSCourse(double course) -{ - this->course = course; -} - -void TelemetrySimulator::GPSEmulator::setGPSSpeedKMH(double speedKMH) -{ - this->speedKNTS = speedKMH * 0.539957; -} - -void TelemetrySimulator::GPSEmulator::setGPSAltitude(double altitude) -{ - this->altitude = altitude; -} - -TelemetrySimulator::LogPlaybackController::LogPlaybackController(Ui::TelemetrySimulator * ui) -{ - TelemetrySimulator::LogPlaybackController::ui = ui; - stepping = false; - logFileGpsCordsInDecimalFormat = false; - // initialize the map - TODO: how should this be localized? - colToFuncMap.clear(); - colToFuncMap.insert("RxBt(V)", RXBT_V); - colToFuncMap.insert("RSSI(dB)", RSSI); - colToFuncMap.insert("RAS", RAS); - colToFuncMap.insert("A1", A1); - colToFuncMap.insert("A1(V)", A1); - colToFuncMap.insert("A2", A2); - colToFuncMap.insert("A2(V)", A2); - colToFuncMap.insert("A3", A3); - colToFuncMap.insert("A3(V)", A3); - colToFuncMap.insert("A4", A4); - colToFuncMap.insert("A4(V)", A4); - colToFuncMap.insert("Tmp1(@C)", T1_DEGC); - colToFuncMap.insert("Tmp1(@F)", T1_DEGF); - colToFuncMap.insert("Tmp2(@C)", T2_DEGC); - colToFuncMap.insert("Tmp2(@F)", T2_DEGF); - colToFuncMap.insert("RPM(rpm)", RPM); - colToFuncMap.insert("Fuel(%)", FUEL); - colToFuncMap.insert("Fuel(ml)", FUEL_QTY); - colToFuncMap.insert("VSpd(m/s)", VSPD_MS); - colToFuncMap.insert("VSpd(f/s)", VSPD_FS); - colToFuncMap.insert("Alt(ft)", ALT_FEET); - colToFuncMap.insert("Alt(m)", ALT_METERS); - colToFuncMap.insert("VFAS(V)", FASV); - colToFuncMap.insert("Curr(A)", FASC); - colToFuncMap.insert("Cels(gRe)", CELS_GRE); - colToFuncMap.insert("Cels(V)", CELS_GRE); - colToFuncMap.insert("ASpd(kts)", ASPD_KTS); - colToFuncMap.insert("ASpd(kmh)", ASPD_KMH); - colToFuncMap.insert("ASpd(mph)", ASPD_MPH); - colToFuncMap.insert("GAlt(ft)", GALT_FEET); - colToFuncMap.insert("GAlt(m)", GALT_METERS); - colToFuncMap.insert("GSpd(kts)", GSPD_KNTS); - colToFuncMap.insert("GSpd(kmh)", GSPD_KMH); - colToFuncMap.insert("GSpd(mph)", GSPD_MPH); - colToFuncMap.insert("Hdg(@)", GHDG_DEG); - colToFuncMap.insert("Date", GDATE); - colToFuncMap.insert("GPS", G_LATLON); - colToFuncMap.insert("AccX(g)", ACCX); - colToFuncMap.insert("AccY(g)", ACCY); - colToFuncMap.insert("AccZ(g)", ACCZ); - - // ACCX Y and Z + qDebug() << "TelemetrySimulator::LogPlaybackController: failed to convert " << sourceUnit << " to " << destUnit; + return value; } QDateTime TelemetrySimulator::LogPlaybackController::parseTransmittterTimestamp(QString row) @@ -877,32 +517,15 @@ void TelemetrySimulator::LogPlaybackController::loadLogFile() ui->stop->setEnabled(true); ui->positionIndicator->setEnabled(true); ui->replayRate->setEnabled(true); - supportedCols.clear(); recordIndex = 1; calcLogFrequency(); checkGpsFormat(); } ui->logFileLabel->setText(QFileInfo(logFileNameAndPath).fileName()); - // iterate through all known mappings and add those that are used - QMapIterator it(colToFuncMap); - while (it.hasNext()) { - it.next(); - addColumnHash(it.key(), it.value()); - } rewind(); return; } -void TelemetrySimulator::LogPlaybackController::addColumnHash(QString key, CONVERT_TYPE functionIndex) -{ - DATA_TO_FUNC_XREF dfx; - if (columnNames.contains(key)) { - dfx.functionIndex = functionIndex; - dfx.dataIndex = columnNames.indexOf(key); - supportedCols.append(dfx); - } -} - void TelemetrySimulator::LogPlaybackController::play() { } @@ -950,57 +573,6 @@ void TelemetrySimulator::LogPlaybackController::stepBack() stepping = false; } -double TelemetrySimulator::LogPlaybackController::convertFeetToMeters(QString input) -{ - double meters100 = input.toDouble() * 0.3048; - return qFloor(meters100 + .005); -} - -QString TelemetrySimulator::LogPlaybackController::convertGPSDate(QString input) -{ - QStringList dateTime = input.simplified().split(' '); - if (dateTime.size() < 2) { - return ""; // invalid format - } - QStringList dateParts = dateTime[0].split('-'); // input as yy-mm-dd - if (dateParts.size() < 3) { - return ""; // invalid format - } - // output is dd-MM-yyyy hh:mm:ss - QString localDateString = dateParts[2] + "-" + dateParts[1] + "-20" + dateParts[0] + " " + dateTime[1]; - QString format("dd-MM-yyyy hh:mm:ss"); - QDateTime utcDate = QDateTime::fromString(localDateString, format).toTimeSpec(Qt::UTC); - return utcDate.toString(format); -} - -double TelemetrySimulator::LogPlaybackController::convertDegMin(QString input) -{ - double fInput = input.mid(0, input.length() - 1).toDouble(); - double degrees = qFloor(fInput / 100.0); - double minutes = fInput - (degrees * 100); - int32_t sign = ((input.endsWith('E')) || (input.endsWith('N'))) ? 1 : -1; - return (degrees + (minutes / 60)) * sign; -} - -QString TelemetrySimulator::LogPlaybackController::convertGPS(QString input) -{ - // input format is DDmm.mmmmH DDDmm.mmmmH (longitude latitude - degrees (2 places) minutes (2 places) decimal minutes (4 places)) - QStringList lonLat = input.simplified().split(' '); - if (lonLat.count() < 2) { - return ""; // invalid format - } - double lon, lat; - if (logFileGpsCordsInDecimalFormat) { - lat = lonLat[0].toDouble(); - lon = lonLat[1].toDouble(); - } - else { - lon = convertDegMin(lonLat[0]); - lat = convertDegMin(lonLat[1]); - } - return QString::number(lat, 'f', 6) + ", " + QString::number(lon, 'f', 6); -} - void TelemetrySimulator::LogPlaybackController::updatePositionLabel(int32_t percentage) { if ((percentage > 0) && (!stepping)) { @@ -1020,393 +592,52 @@ void TelemetrySimulator::LogPlaybackController::updatePositionLabel(int32_t perc } } -double TelemetrySimulator::LogPlaybackController::convertFahrenheitToCelsius(QString input) -{ - return (input.toDouble() - 32.0) * 0.5556; -} - -void TelemetrySimulator::LogPlaybackController::setUiDataValues() +QString unitFromColumnName(QString columnName) { - QStringList columnData = csvRecords[recordIndex].split(','); - Q_FOREACH(DATA_TO_FUNC_XREF info, supportedCols) { - if (info.dataIndex < columnData.size()) { - switch (info.functionIndex) { - case RXBT_V: - ui->rxbt->setValue(columnData[info.dataIndex].toDouble()); - break; - case RSSI: - ui->Rssi->setValue(columnData[info.dataIndex].toDouble()); - break; - case RAS: - ui->Swr->setValue(columnData[info.dataIndex].toDouble()); - break; - case A1: - ui->A1->setValue(columnData[info.dataIndex].toDouble()); - break; - case A2: - ui->A2->setValue(columnData[info.dataIndex].toDouble()); - break; - case A3: - ui->A3->setValue(columnData[info.dataIndex].toDouble()); - break; - case A4: - ui->A4->setValue(columnData[info.dataIndex].toDouble()); - break; - case T1_DEGC: - ui->T1->setValue(columnData[info.dataIndex].toDouble()); - break; - case T2_DEGC: - ui->T2->setValue(columnData[info.dataIndex].toDouble()); - break; - case T1_DEGF: - ui->T1->setValue(convertFahrenheitToCelsius(columnData[info.dataIndex])); - break; - case T2_DEGF: - ui->T2->setValue(convertFahrenheitToCelsius(columnData[info.dataIndex])); - break; - case RPM: - ui->rpm->setValue(columnData[info.dataIndex].toDouble()); - break; - case FUEL: - ui->fuel->setValue(columnData[info.dataIndex].toDouble()); - break; - case FUEL_QTY: - ui->fuel_qty->setValue(columnData[info.dataIndex].toDouble()); - break; - case VSPD_MS: - ui->vspeed->setValue(columnData[info.dataIndex].toDouble()); - break; - case VSPD_FS: - ui->vspeed->setValue(columnData[info.dataIndex].toDouble() * 0.3048); - break; - case ALT_FEET: - ui->valt->setValue(convertFeetToMeters(columnData[info.dataIndex])); - break; - case ALT_METERS: - ui->valt->setValue(columnData[info.dataIndex].toDouble()); - break; - case FASV: - ui->vfas->setValue(columnData[info.dataIndex].toDouble()); - break; - case FASC: - ui->curr->setValue(columnData[info.dataIndex].toDouble()); - break; - case CELS_GRE: - ui->cell1->setValue(columnData[info.dataIndex].toDouble()); - break; - case ASPD_KTS: - ui->aspeed->setValue(columnData[info.dataIndex].toDouble() * 1.8520008892119); - break; - case ASPD_KMH: - ui->aspeed->setValue(columnData[info.dataIndex].toDouble()); - break; - case ASPD_MPH: - ui->aspeed->setValue(columnData[info.dataIndex].toDouble() * 1.60934); - break; - case GALT_FEET: - ui->gps_alt->setValue(convertFeetToMeters(columnData[info.dataIndex])); - break; - case GALT_METERS: - ui->gps_alt->setValue(columnData[info.dataIndex].toDouble()); - break; - case GSPD_KNTS: - ui->gps_speed->setValue(columnData[info.dataIndex].toDouble() * 1.852); - break; - case GSPD_KMH: - ui->gps_speed->setValue(columnData[info.dataIndex].toDouble()); - break; - case GSPD_MPH: - ui->gps_speed->setValue(columnData[info.dataIndex].toDouble() * 1.60934); - break; - case GHDG_DEG: - ui->gps_course->setValue(columnData[info.dataIndex].toDouble()); - break; - case GDATE: - ui->gps_time->setText(convertGPSDate(columnData[info.dataIndex])); - break; - case G_LATLON: - ui->gps_latlon->setText(convertGPS(columnData[info.dataIndex])); - break; - case ACCX: - ui->accx->setValue(columnData[info.dataIndex].toDouble()); - break; - case ACCY: - ui->accy->setValue(columnData[info.dataIndex].toDouble()); - break; - case ACCZ: - ui->accz->setValue(columnData[info.dataIndex].toDouble()); - break; - } - } - else { - // file is corrupt - shut down with open logs, or log format changed mid-day - } + QStringList parts = columnName.split('('); + if (parts.count() == 1) { + return QString(""); } + parts = parts[1].split(')'); + return parts[0]; } -void TelemetrySimulator::on_saveTelemetryvalues_clicked() +QString itemFromColumnName(QString columnName) { - QString fldr = g.backupDir().trimmed(); - if (fldr.isEmpty()) - fldr = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - - QString idFileNameAndPath = QFileDialog::getSaveFileName(this, tr("Save Telemetry"), fldr % "/telemetry.tlm", tr(".tlm Files (*.tlm)")); - if (idFileNameAndPath.isEmpty()) - return; - - QFile file(idFileNameAndPath); - if (!file.open(QIODevice::WriteOnly)){ - QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open file for writing.\n%1").arg(file.errorString())); - return; - } - QTextStream out(&file); - - out<< ui -> rxbt -> text(); - out<<"\r\n"; - out<< ui -> Rssi -> text(); - out<<"\r\n"; - out<< ui -> Swr -> text(); - out<<"\r\n"; - out << ui -> A1 -> text(); - out<<"\r\n"; - out<< ui -> A2 -> text(); - out<<"\r\n"; - out<< ui -> A3 -> text(); - out<<"\r\n"; - out << ui -> A4 -> text(); - out<<"\r\n"; - out << ui -> T1 -> text(); - out<<"\r\n"; - out << ui -> T2 -> text(); - out<<"\r\n"; - out << ui -> rpm -> text(); - out<<"\r\n"; - out << ui -> fuel -> text(); - out<<"\r\n"; - out << ui -> fuel_qty -> text(); - out<<"\r\n"; - out << ui -> vspeed -> text(); - out<<"\r\n"; - out << ui -> valt -> text(); - out<<"\r\n"; - out << ui -> vfas -> text(); - out<<"\r\n"; - out << ui -> curr -> text(); - out<<"\r\n"; - out << ui -> cell1 -> text(); - out<<"\r\n"; - out << ui -> cell2 -> text(); - out<<"\r\n"; - out << ui -> cell3 -> text(); - out<<"\r\n"; - out << ui -> cell4 -> text(); - out<<"\r\n"; - out << ui -> cell5 -> text(); - out<<"\r\n"; - out << ui -> cell6 -> text(); - out<<"\r\n"; - out << ui -> cell7 -> text(); - out<<"\r\n"; - out << ui -> cell8 -> text(); - out<<"\r\n"; - out << ui -> aspeed -> text(); - out<<"\r\n"; - out << ui -> gps_alt -> text(); - out<<"\r\n"; - out << ui -> gps_speed -> text(); - out<<"\r\n"; - out << ui -> gps_course -> text(); - out<<"\r\n"; - out << ui -> gps_time -> text(); - out<<"\r\n"; - out << ui -> gps_latlon -> text(); - out<<"\r\n"; - out << ui -> accx -> text(); - out<<"\r\n"; - out << ui -> accy -> text(); - out<<"\r\n"; - out << ui -> accz -> text(); - out<<"\r\n"; - file.flush(); - - file.close(); - + QStringList parts = columnName.split('('); + return parts[0]; } - -void TelemetrySimulator::on_loadTelemetryvalues_clicked() +void TelemetrySimulator::LogPlaybackController::setUiDataValues() { - QString fldr = g.backupDir().trimmed(); - if (fldr.isEmpty()) - fldr = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - - QString idFileNameAndPath = QFileDialog::getOpenFileName(this, tr("Open Telemetry File"), fldr % "/telemetry.tlm", tr(".tlm Files (*.tlm)")); - if (idFileNameAndPath.isEmpty()) - return; - - QFile file(idFileNameAndPath); - - if (!file.open(QIODevice::ReadOnly)){ - QMessageBox::critical(this, CPN_STR_APP_NAME, tr("Unable to open file for reading.\n%1").arg(file.errorString())); - return; - } - - QTextStream in(&file); - - QString n = in.readLine(); - double ns = n.toDouble(); - ui -> rxbt -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> Rssi -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> Swr -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> A1 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> A2 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> A3 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> A4 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> T1 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> T2 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> rpm -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> fuel -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> fuel_qty -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> vspeed -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> valt -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> vfas -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> curr -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell1 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell2 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell3 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell4 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell5 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell6 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell7 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> cell8 -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> aspeed -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> gps_alt -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> gps_speed -> setValue(ns); - - n = in.readLine(); - ns = n.toDouble(); - ui -> gps_course -> setValue(ns); - - n = in.readLine(); - ui -> gps_time -> setText(n); + QStringList columnData = csvRecords[recordIndex].split(','); - n = in.readLine(); - ui -> gps_latlon -> setText(n); + TelemetryProvider *internalProvider = sim->getInternalTelemetryProvider(); + TelemetryProvider *externalProvider = sim->getExternalTelemetryProvider(); - n = in.readLine(); - ns = n.toDouble(); - ui -> accx -> setValue(ns); + QHash * internalSupportedItems = NULL; + if (internalProvider) + internalSupportedItems = internalProvider->getSupportedLogItems(); + QHash * externalSupportedItems = NULL; + if (externalProvider) + externalSupportedItems = externalProvider->getSupportedLogItems(); - n = in.readLine(); - ns = n.toDouble(); - ui -> accy -> setValue(ns); + for (int col = 0; col < columnData.count(); col++) { + QString columnName = columnNames[col]; + QString columnValue = columnData[col]; - n = in.readLine(); - ns = n.toDouble(); - ui -> accz -> setValue(ns); + QString item = itemFromColumnName(columnName); + QString suppliedUnit = unitFromColumnName(columnName); - file.close(); + if (internalSupportedItems && internalSupportedItems->contains(item)) { + QString expectedUnit = internalSupportedItems->value(item); -} - -void TelemetrySimulator::on_GPSpushButton_clicked() -{ - if (ui->GPSpushButton->text() == tr("Run")) { - ui->GPSpushButton->setText(tr("Stop")); - gpsTimer.start(); - } - else - { - ui->GPSpushButton->setText(tr("Run")); - gpsTimer.stop(); - } -} + internalProvider->loadItemFromLog(item, convertItemValue(suppliedUnit, expectedUnit, columnValue)); + } + if (externalSupportedItems && externalSupportedItems->contains(item)) { + QString expectedUnit = externalSupportedItems->value(item); -void TelemetrySimulator::on_gps_course_valueChanged(double arg1) -{ - if (ui->gps_course->value() > 360) { - ui->gps_course->setValue(1); - } - if (ui->gps_course->value() < 1) { - ui->gps_course->setValue(360); + externalProvider->loadItemFromLog(item, convertItemValue(suppliedUnit, expectedUnit, columnValue)); + } } } - diff --git a/companion/src/simulation/telemetrysimu.h b/companion/src/simulation/telemetrysimu.h index 7cb4fa306ad..b47ac648e8b 100644 --- a/companion/src/simulation/telemetrysimu.h +++ b/companion/src/simulation/telemetrysimu.h @@ -19,8 +19,7 @@ * GNU General Public License for more details. */ -#ifndef _TELEMETRYSIMU_H_ -#define _TELEMETRYSIMU_H_ +#pragma once #include #include @@ -28,8 +27,10 @@ #include #include #include +#include #include "simulatorinterface.h" +#include "telemetryprovider.h" static double const SPEEDS[] = { 0.2, 0.4, 0.6, 0.8, 1, 2, 3, 4, 5 }; template t LIMIT(t mi, t x, t ma) { return std::min(std::max(mi, x), ma); } @@ -47,11 +48,9 @@ class TelemetrySimulator : public QWidget explicit TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator); virtual ~TelemetrySimulator(); - public slots: - bool generateSportPacket(uint8_t * packet, uint8_t dataId, uint8_t prim, uint16_t appId, uint32_t data); - signals: - void telemetryDataChanged(const QByteArray data); + void internalTelemetryDataChanged(const quint8 protocol, const QByteArray data); + void externalTelemetryDataChanged(const quint8 protocol, const QByteArray data); protected slots: @@ -61,10 +60,8 @@ class TelemetrySimulator : public QWidget void stopTelemetry(); void onSimulatorStarted(); void onSimulatorStopped(); - void setupDataFields(); void onSimulateToggled(bool isChecked); void onLogTimerEvent(); - void onGpsRunLoop(); void onLoadLogFile(); void onPlay(); void onRewind(); @@ -73,25 +70,32 @@ class TelemetrySimulator : public QWidget void onStop(); void onPositionIndicatorChanged(int value); void onReplayRateChanged(int value); - void refreshSensorRatios(); + void onInternalTelemetrySelectorChanged(int selectedIndex); + void onExternalTelemetrySelectorChanged(int selectedIndex); + void onInternalTelemetryProviderDataChanged(const quint8 protocol, const QByteArray data); + void onExternalTelemetryProviderDataChanged(const quint8 protocol, const QByteArray data); void generateTelemetryFrame(); + TelemetryProvider * newTelemetryProviderFromDropdownChoice(int selectedIndex, QScrollArea * parent, bool isExternal); + TelemetryProvider * getInternalTelemetryProvider(); + TelemetryProvider * getExternalTelemetryProvider(); protected: Ui::TelemetrySimulator * ui; QTimer timer; QTimer logTimer; - QTimer gpsTimer; SimulatorInterface *simulator; bool m_simuStarted; bool m_logReplayEnable; + TelemetryProvider * internalProvider; + TelemetryProvider * externalProvider; // protected classes follow class LogPlaybackController { public: - LogPlaybackController(Ui::TelemetrySimulator * ui); + LogPlaybackController(Ui::TelemetrySimulator * ui, TelemetrySimulator * sim); bool isReady(); void loadLogFile(); void play(); @@ -105,118 +109,21 @@ class TelemetrySimulator : public QWidget bool logFileGpsCordsInDecimalFormat; private: - enum CONVERT_TYPE { - RXBT_V, - RSSI, - RAS, - A1, - A2, - A3, - A4, - T1_DEGC, - T1_DEGF, - T2_DEGC, - T2_DEGF, - RPM, - FUEL, - VSPD_MS, - VSPD_FS, - ALT_FEET, - ALT_METERS, - FASV, - FASC, - CELS_GRE, - ASPD_KTS, - ASPD_KMH, - ASPD_MPH, - GALT_FEET, - GALT_METERS, - GSPD_KNTS, - GSPD_KMH, - GSPD_MPH, - GHDG_DEG, - GDATE, - G_LATLON, - ACCX, - ACCY, - ACCZ, - FUEL_QTY, - }; - - struct DATA_TO_FUNC_XREF { - CONVERT_TYPE functionIndex; - int32_t dataIndex; - }; - - double convertFeetToMeters(QString input); - double convertFahrenheitToCelsius(QString input); QString convertGPSDate(QString input); QString convertGPS(QString input); - void addColumnHash(QString key, CONVERT_TYPE functionIndex); double convertDegMin(QString input); QDateTime parseTransmittterTimestamp(QString row); void calcLogFrequency(); void checkGpsFormat(); - QMap colToFuncMap; // contains all 'known' column headings and how they are to be processed Ui::TelemetrySimulator * ui; + TelemetrySimulator * sim; QStringList csvRecords; // contents of the log file (one string per line); QStringList columnNames; - QList supportedCols; int32_t recordIndex; bool stepping; }; // LogPlaybackController LogPlaybackController *logPlayback; - - class FlvssEmulator - { - public: - static const uint32_t MAXCELLS = 8; - uint32_t setAllCells_GetNextPair(double cellValues[MAXCELLS]); - - private: - void encodeAllCells(); - void splitIntoCells(double totalVolts); - static uint32_t encodeCellPair(uint8_t cellNum, uint8_t firstCellNo, double cell1, double cell2); - double cellFloats[MAXCELLS]; - uint32_t nextCellNum; - uint32_t numCells; - uint32_t cellData1; - uint32_t cellData2; - uint32_t cellData3; - uint32_t cellData4; - }; // FlvssEmulator - - class GPSEmulator - { - public: - GPSEmulator(); - uint32_t getNextPacketData(uint32_t packetType); - void setGPSDateTime(QString dateTime); - void setGPSLatLon(QString latLon); - void setGPSCourse(double course); - void setGPSSpeedKMH(double speed); - void setGPSAltitude(double altitude); - - private: - QDateTime dt; - bool sendLat; - bool sendDate; - double lat; - double lon; - double course; - double speedKNTS; - double altitude; // in meters - uint32_t encodeLatLon(double latLon, bool isLat); - uint32_t encodeDateTime(uint8_t yearOrHour, uint8_t monthOrMinute, uint8_t dayOrSecond, bool isDate); - }; // GPSEmulator -private slots: - void on_saveTelemetryvalues_clicked(); - void on_loadTelemetryvalues_clicked(); - void on_GPSpushButton_clicked(); - void on_gps_course_valueChanged(double arg1); }; // TelemetrySimulator -#endif // _TELEMETRYSIMU_H_ - diff --git a/companion/src/simulation/telemetrysimu.ui b/companion/src/simulation/telemetrysimu.ui index 1719ead2250..39b9739b805 100644 --- a/companion/src/simulation/telemetrysimu.ui +++ b/companion/src/simulation/telemetrysimu.ui @@ -7,7 +7,7 @@ 0 0 715 - 538 + 597 @@ -18,7 +18,7 @@ Telemetry Simulator - + 7 @@ -58,2768 +58,20 @@ - - - - - 0 - 1 - - - - - 16 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - 0 - 0 - 343 - 422 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 4 - - - 0 - - - 3 - - - 5 - - - - - - 0 - 0 - - - - - 8 - - - - dB - - - - - - - - 0 - 0 - - - - - 8 - - - - V / ratio - - - - - - - - 0 - 0 - - - - - 8 - - - - °C - - - - - - - - 0 - 0 - - - - - 8 - - - - 100000 - - - 25 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 6.000000000000000 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - ml - - - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 99.000000000000000 - - - 0.100000000000000 - - - 5.200000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 99.000000000000000 - - - 0.100000000000000 - - - - - - - - - - 0 - 0 - - - - - 8 - - - - A4 - - - - - - - - 0 - 0 - - - - - 8 - - - - Fuel - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - V / ratio - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - RPM - - - - - - - - 0 - 0 - - - - - 8 - - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 5.200000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - RAS - - - - - - - - 0 - 0 - - - - - 8 - - - - A1 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 125.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - Meters - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - A3 - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Tmp1 - - - - - - - - 0 - 0 - - - - - 8 - - - - 30 - - - - - - - - 0 - 0 - - - - - 8 - - - - 65535 - - - 50 - - - - - - - - 0 - 0 - - - - - 8 - - - - RSSI - - - - - - - - 0 - 0 - - - - - 8 - - - - °C - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - VSpd - - - - - - - - 0 - 0 - - - - - 8 - - - - -16777215 - - - 16777215 - - - 26 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - 2147483647 - - - 5000 - - - - - - - - 0 - 0 - - - - - 8 - - - - V / ratio - - - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 999.000000000000000 - - - 0.100000000000000 - - - 5.100000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 999.000000000000000 - - - 0.100000000000000 - - - - - - - - - - 0 - 0 - - - - - 8 - - - - Fuel Qty - - - - - - - - 0 - 0 - - - - - 8 - - - - -16777215 - - - 16777215 - - - 1 - - - 25 - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Alt - - - - - - - - 0 - 0 - - - - - 8 - - - - RxBt - - - - - - - - 0 - 0 - - - - - 8 - - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 5.150000000000000 - - - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 999.000000000000000 - - - 0.100000000000000 - - - 5.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 999.000000000000000 - - - 0.100000000000000 - - - - - - - - - - 0 - 0 - - - - - 8 - - - - m/s - - - - - - - - 0 - 0 - - - - - 8 - - - - RPM - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Tmp2 - - - - - - - - 0 - 0 - - - - - 8 - - - - 75 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - % - - - - - - - - 0 - 0 - - - - - 8 - - - - A2 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - Run - - - - - - - GPS sim - - - - - - - Run/Stop - - - - - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - 0 - 0 - 342 - 422 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 4 - - - 0 - - - 3 - - - 5 - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 0.100000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - G - - - - - - - - 0 - 0 - - - - - 8 - - - - dd-MM-yyyy -hh:mm:ss - - - false - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - -0.100000000000000 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - 1 - - - 0.000000000000000 - - - 5000.000000000000000 - - - 1.000000000000000 - - - 40.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 1125.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Hdg - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - VFAS - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - GPS - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - GAlt - - - - - - - - 0 - 0 - - - - - 8 - - - - Meters - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - GSpd - - - - - - - - 0 - 0 - - - - - 8 - - - - AccZ - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - Volts - - - - - - - Load Telemetry Values - - - - - - - Save Telemetry Values - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - km/h - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - Amps - - - - - - - - 0 - 0 - - - - - 8 - - - - km/h - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Curr - - - - - - - - 0 - 0 - - - - - 8 - - - - Volts - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - ASpd - - - - - - - - 0 - 0 - - - - - 8 - - - - 25.9973,-97.1572 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - Lat,Lon -(dec.deg.) - - - false - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - G - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Date - - - - - - - - 0 - 0 - - - - - 8 - - - - AccY - - - - - - - - 0 - 0 - - - - - 8 - - - - * - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 23.699999999999999 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 0.200000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - -21474836.000000000000000 - - - 21474836.000000000000000 - - - 0.100000000000000 - - - 35.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - AccX - - - - - - - - - - 0 - 0 - - - - - 8 - - - - 0.100000000000000 - - - 3.700000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 3.800000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 3.900000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 4.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 4.100000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 4.200000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 4.200000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - 8.190000000000000 - - - 0.100000000000000 - - - 4.200000000000000 - - - - - - - - - - 0 - 0 - - - - - 8 - - - - 2 - - - 0.000000000000000 - - - 361.000000000000000 - - - 1.000000000000000 - - - 340.000000000000000 - - - - - - - - 0 - 0 - - - - - 8 - - - - <html><head/><body><p><br/></p></body></html> - - - Cels - - - - - - - - 0 - 0 - - - - - 8 - - - - Degrees - - - - - - - - 0 - 0 - - - - - 8 - - - - G - - - - - - - - 0 - 0 - - - - - 8 - - - - 1 - - - -214748364.000000000000000 - - - 214748364.000000000000000 - - - 0.100000000000000 - - - 0.100000000000000 - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - - 0 - 0 - - - - - 45 - 0 - - - - - 45 - 16777215 - - - - - 8 - - - - - - - - Qt::Horizontal - - - - - - - - + + + + + 0 + 0 + + + + Stop sending telemetry data when the Telemetry Simulator window is hidden. + + + Pause simulation when hidden. + @@ -3165,21 +417,227 @@ Timestamp - - - - - 0 - 0 - - - - Stop sending telemetry data when the Telemetry Simulator window is hidden. - - - Pause simulation when hidden. - - + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + Internal Module + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + None + + + + + FrSky S.Port + + + + + CRSF / ELRS + + + + + + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 338 + 428 + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + External Module + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + None + + + + + FrSky S.Port + + + + + CRSF / ELRS + + + + + + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 337 + 428 + + + + + + + + + + + + @@ -3192,62 +650,6 @@ Timestamp play stepForward stop - rxbt_inst - rxbt - rxbt_ratio - rssi_inst - Rssi - swr_inst - Swr - a1_inst - A1 - A1_ratio - a2_inst - A2 - A2_ratio - a3_inst - A3 - a4_inst - A4 - t1_inst - T1 - t2_inst - T2 - rpm_inst - rpm - fuel_inst - fuel - vvspd_inst - vspeed - valt_inst - valt - fasv_inst - vfas - fasc_inst - curr - cells_inst - cell1 - cell2 - cell3 - cell4 - cell5 - cell6 - cell7 - cell8 - aspd_inst - aspeed - gpsa_inst - gps_alt - gpss_inst - gps_speed - gpsc_inst - gps_course - gpst_inst - gps_time - gpsll_inst - gps_latlon - fuel_qty_inst - fuel_qty diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index 3a509c6f20d..44a33c6b766 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -405,12 +405,39 @@ void OpenTxSimulator::setTrainerTimeout(uint16_t ms) trainerSetTimer(ms); } -void OpenTxSimulator::sendTelemetry(const QByteArray data) +void OpenTxSimulator::sendTelemetry(const uint8_t module, const uint8_t protocol, const QByteArray data) { //ETXS_DBG << data; - sportProcessTelemetryPacket(INTERNAL_MODULE, - (uint8_t *)data.constData(), - data.count()); + switch (protocol) { + case SIMU_TELEMETRY_PROTOCOL_FRSKY_SPORT: + sportProcessTelemetryPacket(module, + (uint8_t *)data.constData(), + data.count()); + break; + case SIMU_TELEMETRY_PROTOCOL_FRSKY_HUB: + frskyDProcessPacket(module, + (uint8_t *)data.constData(), + data.count()); + break; + case SIMU_TELEMETRY_PROTOCOL_CROSSFIRE: + processCrossfireTelemetryFrame(module, + (uint8_t *)data.constData(), + data.count()); + break; + default: + // Do nothing + break; + } +} + +void OpenTxSimulator::sendInternalModuleTelemetry(const uint8_t protocol, const QByteArray data) +{ + sendTelemetry(INTERNAL_MODULE, protocol, data); +} + +void OpenTxSimulator::sendExternalModuleTelemetry(const uint8_t protocol, const QByteArray data) +{ + sendTelemetry(EXTERNAL_MODULE, protocol, data); } uint8_t OpenTxSimulator::getSensorInstance(uint16_t id, uint8_t defaultValue) diff --git a/radio/src/targets/simu/opentxsimulator.h b/radio/src/targets/simu/opentxsimulator.h index dca0e5c9f67..350355cfe1f 100644 --- a/radio/src/targets/simu/opentxsimulator.h +++ b/radio/src/targets/simu/opentxsimulator.h @@ -77,7 +77,9 @@ class DLLEXPORT OpenTxSimulator : public SimulatorInterface virtual void touchEvent(int type, int x, int y); virtual void lcdFlushed(); virtual void setTrainerTimeout(uint16_t ms); - virtual void sendTelemetry(const QByteArray data); + virtual void sendTelemetry(const uint8_t module, const uint8_t protocol, const QByteArray data); + virtual void sendInternalModuleTelemetry(const uint8_t protocol, const QByteArray data); + virtual void sendExternalModuleTelemetry(const uint8_t protocol, const QByteArray data); virtual void setLuaStateReloadPermanentScripts(); virtual void addTracebackDevice(QIODevice * device); virtual void removeTracebackDevice(QIODevice * device); diff --git a/radio/util/find_clang.py b/radio/util/find_clang.py index 52954c79ae9..d9cdbb08363 100644 --- a/radio/util/find_clang.py +++ b/radio/util/find_clang.py @@ -69,6 +69,8 @@ def findLibClang(): libSuffix = ".dylib" elif sys.platform.startswith("linux"): knownPaths = [ + "/usr/lib/llvm-14/lib", + "/usr/lib/llvm-11/lib", "/usr/lib/llvm-7/lib", "/usr/lib/llvm-6.0/lib", "/usr/lib/llvm-3.8/lib",