feat(cpn): support more than just FrSky S.Port telemetry simulation (#5410)
This commit is contained in:
@@ -10,7 +10,10 @@ set(${PROJECT_NAME}_NAMES
|
||||
simulatormainwindow
|
||||
simulatorstartupdialog
|
||||
simulatorwidget
|
||||
simulatedgps
|
||||
telemetrysimu
|
||||
telemetryprovidercrossfire
|
||||
telemetryproviderfrsky
|
||||
trainersimu
|
||||
widgets/lcdwidget
|
||||
widgets/radiowidget
|
||||
|
||||
@@ -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 <QtMath>
|
||||
#include <QDebug>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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 <QTimer>
|
||||
#include <QDateTime>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <QHash>
|
||||
|
||||
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<QString, QString> * 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;
|
||||
};
|
||||
@@ -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 <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "appdata.h"
|
||||
#include "telemetryprovidercrossfire.h"
|
||||
#include "ui_telemetryprovidercrossfire.h"
|
||||
#include "telem_data.h"
|
||||
|
||||
template<class t> 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<double>::of(&QDoubleSpinBox::valueChanged), &gps, &SimulatedGPS::setCourseDegrees);
|
||||
connect(ui->input_gps, &QLineEdit::textChanged, &gps, &SimulatedGPS::setLatLon);
|
||||
connect(ui->input_gspd, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &gps, &SimulatedGPS::setSpeedKMH);
|
||||
connect(ui->input_alt, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &gps, &SimulatedGPS::setAltitude);
|
||||
connect(ui->input_sats, QOverload<int>::of(&QSpinBox::valueChanged), &gps, &SimulatedGPS::setSatelliteCount);
|
||||
|
||||
connect(&gps, &SimulatedGPS::positionChanged, ui->input_gps, &QLineEdit::setText);
|
||||
connect(&gps, &SimulatedGPS::courseDegreesChanged, ui->input_hdg, QOverload<double>::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<QString, QString> * 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();
|
||||
}
|
||||
@@ -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 <QWidget>
|
||||
#include <QDateTime>
|
||||
|
||||
#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<QString, QString> * 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<QString, QString> supportedLogItems;
|
||||
|
||||
private slots:
|
||||
void on_button_gpsRunStop_clicked();
|
||||
void on_button_saveTelemetryValues_clicked();
|
||||
void on_button_loadTelemetryValues_clicked();
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "appdata.h"
|
||||
#include "telemetryproviderfrsky.h"
|
||||
#include "ui_telemetryproviderfrsky.h"
|
||||
#include "telem_data.h"
|
||||
#include "radio/src/telemetry/frsky_defs.h"
|
||||
|
||||
template<class t> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<int32_t>(-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<int32_t>(-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<int32_t>(-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<int32_t>(-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<uint32_t>(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<uint32_t>(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<int32_t>(-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<int32_t>(-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<uint32_t>(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<uint32_t>(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<uint32_t>(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<int32_t>(-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<int32_t>(-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<int32_t>(-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<uint32_t>(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<FRSKY_SPORT_PACKET_SIZE-1; i++) {
|
||||
crc += packet[i]; //0-1FF
|
||||
crc += crc >> 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<QString, QString> * 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <QWidget>
|
||||
#include <QDateTime>
|
||||
|
||||
#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<QString, QString> * 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<QString, QString> 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);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _TELEMETRYSIMU_H_
|
||||
#define _TELEMETRYSIMU_H_
|
||||
#pragma once
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QWidget>
|
||||
@@ -28,8 +27,10 @@
|
||||
#include <QDateTime>
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QFileDialog>
|
||||
#include <QScrollArea>
|
||||
|
||||
#include "simulatorinterface.h"
|
||||
#include "telemetryprovider.h"
|
||||
|
||||
static double const SPEEDS[] = { 0.2, 0.4, 0.6, 0.8, 1, 2, 3, 4, 5 };
|
||||
template<class t> 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<QString, CONVERT_TYPE> 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<DATA_TO_FUNC_XREF> 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_
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user