chore(cpn): remove binary file format models and settings support (#5653)

This commit is contained in:
Neil Horne
2024-11-22 10:27:19 +10:00
committed by GitHub
parent d94bc3321c
commit ebb5d39a14
37 changed files with 101 additions and 9548 deletions
-1
View File
@@ -224,7 +224,6 @@ set(companion_NAMES
burnconfigdialog
comparedialog
customizesplashdialog
flasheepromdialog
flashfirmwaredialog
helpers_html
labels
+2 -2
View File
@@ -72,15 +72,15 @@
#define BIN_FILES_FILTER "BIN " % CPN_STR_FILES % " (*.bin);;"
#define DFU_FILES_FILTER "DFU " % CPN_STR_FILES % " (*.dfu);;"
#define EEPE_FILES_FILTER "EEPE " % CPN_STR_FILES % " (*.eepe);;"
#define OTX_FILES_FILTER "OpenTX " % CPN_STR_FILES % " (*.otx);;"
#define ETX_FILES_FILTER "EdgeTX " % CPN_STR_FILES % " (*.etx);;"
#define YML_FILES_FILTER "EdgeTX YAML " % CPN_STR_FILES % " (*.yml);;"
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.etx *.yml *.otx *.eepe *.bin *.hex);;" % ETX_FILES_FILTER % YML_FILES_FILTER % OTX_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.etx *.yml);;" % ETX_FILES_FILTER % YML_FILES_FILTER
#define FLASH_FILES_FILTER "FLASH " % CPN_STR_FILES % " (*.bin *.hex *.dfu);;" % BIN_FILES_FILTER % HEX_FILES_FILTER % DFU_FILES_FILTER
#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM " % CPN_STR_FILES % " (*.bin *.hex);;" % BIN_FILES_FILTER % HEX_FILES_FILTER
#define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE"
#define EEPE_EEPROM_FILE_HEADER "EEPE EEPROM FILE"
#define EEPE_MODEL_FILE_HEADER "EEPE MODEL FILE"
#define SIMU_FILES_FILTER ETX_FILES_FILTER
#define CPN_STR_APP_SETTINGS QCoreApplication::translate("Companion", "Application Settings")
#define CPN_STR_APP_SETTINGS_FILES CPN_STR_APP_SETTINGS % " " % CPN_STR_FILES
-3
View File
@@ -31,9 +31,6 @@ set(${PROJECT_NAME}_NAMES
sourcenumref
telem_data
timerdata
ersky9x/ersky9xeeprom
ersky9x/ersky9xinterface
opentx/opentxeeprom
opentx/opentxinterface
edgetx/edgetxinterface
edgetx/yaml_calibdata
@@ -1,834 +0,0 @@
/*
* 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.
*/
#ifndef _EEPROMIMPORTEXPORT_H_
#define _EEPROMIMPORTEXPORT_H_
#include "customdebug.h"
#include <QtCore>
#include <QBitArray>
#include <utility>
class DataField {
Q_DECLARE_TR_FUNCTIONS(DataField)
public:
explicit DataField(DataField * parent, const char * name=""):
parent(parent),
name(name)
{
}
virtual ~DataField()
= default;
virtual const QString & getName()
{
return name;
}
virtual unsigned int size() = 0; // size in bits
virtual void ExportBits(QBitArray & output) = 0;
virtual void ImportBits(const QBitArray & input) = 0;
static QBitArray bytesToBits(QByteArray bytes)
{
QBitArray bits(bytes.count()*8);
// Convert from QByteArray to QBitArray
for (int i=0; i<bytes.count(); ++i)
for (int b=0; b<8; ++b)
bits.setBit(i*8+b, bytes.at(i)&(1<<b));
return bits;
}
static QByteArray bitsToBytes(QBitArray bits, int offset=0)
{
QByteArray bytes;
bytes.resize((offset+bits.count()+7)/8);
bytes.fill(0);
// Convert from QBitArray to QByteArray
for (int b=0; b<bits.count(); ++b)
bytes[(b+offset)/8] = ( bytes.at((b+offset)/8) | ((bits[b]?1:0)<<((b+offset)%8)));
return bytes;
}
int Export(QByteArray & output)
{
QBitArray result;
ExportBits(result);
output = bitsToBytes(result);
return 0;
}
int Import(const QByteArray & input)
{
QBitArray bits = bytesToBits(input);
if (unsigned(bits.size()) < size()) {
qDebug() << QString("Error importing %1: size too small %2 bits / %3 bits").arg(getName()).arg(bits.size()).arg(size());
return -1;
}
ImportBits(bits);
return 0;
}
virtual int dump(int level=0, int offset=0)
{
QBitArray bits;
ExportBits(bits);
QByteArray bytes = bitsToBytes(bits);
int result = (offset+bits.count()) % 8;
for (int i=0; i<level; i++) printf(" ");
if (bits.count() % 8 == 0)
printf("%s (%dbytes) ", getName().toLatin1().constData(), bytes.count());
else
printf("%s (%dbits) ", getName().toLatin1().constData(), bits.count());
for (int i=0; i<bytes.count(); i++) {
unsigned char c = bytes[i];
if ((i==0 && offset) || (i==bytes.count()-1 && result!=0))
printf("(%02x) ", c);
else
printf("%02x ", c);
}
printf("\n"); fflush(stdout);
return result;
}
protected:
virtual void setError(const QString & error)
{
if (parent) {
parent->setError(error);
}
else {
qWarning() << qPrintable(error);
}
}
DataField * parent;
QString name;
};
//class ProxyField: public DataField {
// public:
// explicit ProxyField(DataField * parent):
// DataField(parent, "Proxy")
// {
// }
//
// virtual DataField * getField() = 0;
//
//};
template<class container, int N>
class BaseUnsignedField: public DataField {
public:
BaseUnsignedField(DataField * parent, container & field):
DataField(parent, "Unsigned"),
field(field),
min(0),
max(std::numeric_limits<container>::max())
{
}
BaseUnsignedField(DataField * parent, container & field, const char * name):
DataField(parent, name),
field(field),
min(0),
max(std::numeric_limits<container>::max())
{
}
BaseUnsignedField(DataField * parent, container & field, container min, container max, const char * name="Unsigned"):
DataField(parent, name),
field(field),
min(min),
max(max)
{
}
BaseUnsignedField() = delete;
void ExportBits(QBitArray & output) override
{
container value = field;
if (value > max) value = max;
if (value < min) value = min;
output.resize(N);
for (int i=0; i<N; i++) {
if (value & ((container)1<<i)) {
output.setBit(i);
}
}
}
void ImportBits(const QBitArray & input) override
{
field = 0;
for (int i=0; i<N; i++) {
if (input[i])
field |= ((container)1<<i);
}
qCDebug(eepromImport) << QString("\timported %1<%2>: 0x%3(%4)").arg(name).arg(N).arg(field, 0, 16).arg(field);
}
unsigned int size() override
{
return N;
}
protected:
container & field;
container min;
container max;
};
template <int N>
class UnsignedField : public BaseUnsignedField<unsigned int, N>
{
public:
UnsignedField(DataField * parent, unsigned int & field):
BaseUnsignedField<unsigned int, N>(parent, field)
{
}
UnsignedField(DataField * parent, unsigned int & field, const char *name):
BaseUnsignedField<unsigned int, N>(parent, field, name)
{
}
UnsignedField(DataField * parent, unsigned int & field, unsigned int min, unsigned int max, const char *name="Unsigned"):
BaseUnsignedField<unsigned int, N>(parent, field, min, max, name)
{
}
};
template<int N>
class BoolField: public DataField {
public:
explicit BoolField(DataField * parent, bool & field):
DataField(parent, "Bool"),
field(field)
{
}
BoolField() = delete;
void ExportBits(QBitArray & output) override
{
output.resize(N);
if (field) {
output.setBit(0);
}
}
void ImportBits(const QBitArray & input) override
{
field = input[0];
qCDebug(eepromImport) << QString("\timported %1<%2>: 0x%3(%4)").arg(name).arg(N).arg(field, 0, 16).arg(field);
}
unsigned int size() override
{
return N;
}
protected:
bool & field;
};
template<int N>
class SignedField: public DataField {
public:
SignedField(DataField * parent, int & field):
DataField(parent, "Signed"),
field(field)
{
}
SignedField(DataField * parent, int & field, const char *name):
DataField(parent, name),
field(field)
{
}
SignedField(DataField * parent, int & field, int min, int max, const char *name="Signed"):
DataField(parent, name),
field(field),
min(min),
max(max)
{
}
void ExportBits(QBitArray & output) override
{
int value = field;
if (value > max) value = max;
if (value < min) value = min;
output.resize(N);
for (int i=0; i<N; i++) {
if (((unsigned int)value) & (1<<i))
output.setBit(i);
}
}
void ImportBits(const QBitArray & input) override
{
unsigned int value = 0;
for (int i=0; i<N; i++) {
if (input[i])
value |= (1<<i);
}
if (input[N-1]) {
for (unsigned int i=N; i<8*sizeof(int); i++) {
value |= (1<<i);
}
}
field = (int)value;
qCDebug(eepromImport) << QString("\timported %1<%2>: 0x%3(%4)").arg(name).arg(N).arg(field, 0, 16).arg(field);
}
unsigned int size() override
{
return N;
}
protected:
int & field;
int min = INT_MIN;
int max = INT_MAX;
};
template<int N>
class SpareBitsField: public UnsignedField<N> {
public:
explicit SpareBitsField(DataField * parent):
UnsignedField<N>(parent, spare, 0, 0, "Spare"),
spare(0)
{
}
protected:
unsigned int spare;
};
template<int N>
class CharField: public DataField {
public:
CharField(DataField * parent, char * field, bool truncate=true, const char * name="Char"):
DataField(parent, name),
field(field),
truncate(truncate)
{
}
void ExportBits(QBitArray & output) override
{
output.resize(N*8);
int b = 0;
int len = truncate ? strlen(field) : N;
for (int i=0; i<N; i++) {
int idx = (i>=len ? 0 : field[i]);
for (int j=0; j<8; j++, b++) {
if (idx & (1<<j))
output.setBit(b);
}
}
}
void ImportBits(const QBitArray & input) override
{
unsigned int b = 0;
for (int i=0; i<N; i++) {
int8_t idx = 0;
for (int j=0; j<8; j++) {
if (input[b++])
idx |= (1<<j);
}
field[i] = idx;
}
qCDebug(eepromImport) << QString("\timported %1<%2>: '%3'").arg(name).arg(N).arg(field);
}
unsigned int size() override
{
return 8 * N;
}
protected:
char * field;
bool truncate;
};
static const char specialCharsTab[] = "_-.,";
static inline int8_t char2zchar(char c)
{
if (c==' ') return 0;
if (c>='A' && c<='Z') return 1+c-'A';
if (c>='a' && c<='z') return -1-c+'a';
if (c>='0' && c<='9') return 27+c-'0';
for (int8_t i=0;;i++) {
char cc = specialCharsTab[i];
if (cc==0) return 0;
if (cc==c) return 37+i;
}
}
#define ZCHAR_MAX 40
static inline char zchar2char(int8_t idx)
{
if (idx == 0) return ' ';
if (idx < 0) {
if (idx > -27) return 'a' - idx - 1;
idx = -idx;
}
if (idx < 27) return 'A' + idx - 1;
if (idx < 37) return '0' + idx - 27;
if (idx <= ZCHAR_MAX) return specialCharsTab[idx-37];
return ' ';
}
template<int N>
class ZCharField: public DataField {
public:
ZCharField(DataField * parent, char * field, const char * name = "ZChar"):
DataField(parent, name),
field(field)
{
}
void ExportBits(QBitArray & output) override
{
output.resize(N*8);
int b = 0;
int len = strlen(field);
for (int i=0; i<N; i++) {
int idx = i>=len ? 0 : char2zchar(field[i]);
for (int j=0; j<8; j++, b++) {
if (idx & (1<<j))
output.setBit(b);
}
}
}
void ImportBits(const QBitArray & input) override
{
unsigned int b = 0;
for (int i=0; i<N; i++) {
int8_t idx = 0;
for (int j=0; j<8; j++) {
if (input[b++])
idx |= (1<<j);
}
field[i] = zchar2char(idx);
}
field[N] = '\0';
for (int i=N-1; i>=0; i--) {
if (field[i] == ' ')
field[i] = '\0';
else
break;
}
qCDebug(eepromImport) << QString("\timported %1<%2>: '%3'").arg(name).arg(N).arg(field);
}
unsigned int size() override
{
return 8*N;
}
protected:
char * field;
};
class StructField: public DataField {
public:
explicit StructField(DataField * parent, const char * name = "Struct"):
DataField(parent, name)
{
}
~StructField() override {
foreach(DataField * field, fields) {
delete field;
}
}
inline void Append(DataField * field) {
//qCDebug(eepromImport) << QString("StructField(%1) appending field: %2").arg(name).arg(field->getName());
fields.append(field);
}
void ExportBits(QBitArray & output) override
{
int offset = 0;
output.resize(size());
foreach(DataField *field, fields) {
QBitArray bits;
field->ExportBits(bits);
for (int i=0; i<bits.size(); i++)
output[offset++] = bits[i];
}
}
void ImportBits(const QBitArray & input) override
{
qCDebug(eepromImport) << QString("\timporting %1[%2]:").arg(name).arg(fields.size());
int offset = 0;
foreach(DataField *field, fields) {
unsigned int size = field->size();
QBitArray bits(size);
for (unsigned int i=0; i<size; i++) {
bits[i] = input[offset++];
}
field->ImportBits(bits);
}
}
unsigned int size() override
{
unsigned int result = 0;
foreach(DataField *field, fields) {
result += field->size();
}
return result;
}
int dump(int level=0, int offset=0) override
{
for (int i=0; i<level; i++) printf(" ");
printf("%s (%d bytes)\n", getName().toLatin1().constData(), size()/8);
foreach(DataField *field, fields) {
offset = field->dump(level+1, offset);
}
return offset;
}
protected:
QList<DataField *> fields;
};
class TransformedField: public DataField {
public:
TransformedField(DataField * parent, DataField & field):
DataField(parent),
field(field)
{
}
~TransformedField() override
= default;
void ExportBits(QBitArray & output) override
{
beforeExport();
field.ExportBits(output);
}
void ImportBits(const QBitArray & input) override
{
qCDebug(eepromImport) << QString("\timporting TransformedField %1:").arg(field.getName());
field.ImportBits(input);
afterImport();
}
const QString & getName() override
{
return field.getName();
}
unsigned int size() override
{
return field.size();
}
virtual void beforeExport() = 0;
virtual void afterImport() = 0;
int dump(int level=0, int offset=0) override
{
beforeExport();
return field.dump(level, offset);
}
protected:
DataField & field;
};
template<class selectorT>
class UnionField: public DataField {
public:
class UnionMember {
public:
virtual ~UnionMember() = default;
virtual bool select(const selectorT& attr) const = 0;
virtual DataField* getField() = 0;
};
class TransformedMember: public UnionMember, public TransformedField {
public:
TransformedMember(DataField* parent, DataField& field):
TransformedField(parent, field)
{
}
virtual DataField* getField()
{
return this;
}
};
UnionField(DataField * parent, selectorT& selectField, const char * name="Union"):
DataField(parent, name), selectField(selectField), maxSize(0)
{
}
~UnionField() override {
foreach(UnionMember *member, members) {
delete member;
}
}
inline void Append(UnionMember * member) {
members.append(member);
if (member->getField()->size() > maxSize)
maxSize = member->getField()->size();
}
void ExportBits(QBitArray & output) override
{
foreach(UnionMember *member, members) {
if (member->select(selectField)) {
member->getField()->ExportBits(output);
break;
}
}
output.resize(maxSize);
}
void ImportBits(const QBitArray & input) override
{
foreach(UnionMember *member, members) {
if (member->select(selectField)) {
member->getField()->ImportBits(input);
break;
}
}
}
unsigned int size() override
{
return maxSize;
}
protected:
selectorT& selectField;
QList<UnionMember *> members;
unsigned int maxSize;
};
class ConversionTable {
public:
bool exportValue(const int before, int &after)
{
after = 0;
for (auto & tuple : exportTable) {
if (before == tuple.a) {
after = tuple.b;
return true;
}
}
return false;
}
bool importValue(const int before, int &after)
{
after = 0;
for (auto & tuple : importTable) {
if (before == tuple.b) {
after = tuple.a;
return true;
}
}
return false;
}
protected:
class ConversionTuple {
public:
ConversionTuple(const int a, const int b):
a(a),
b(b)
{
}
int a;
int b;
};
void addConversion(const int a, const int b)
{
ConversionTuple conversion(a, b);
importTable.push_back(conversion);
exportTable.push_back(conversion);
}
// void addImportConversion(const int a, const int b)
// {
// importTable.push_back(ConversionTuple(a, b));
// }
//
// void addExportConversion(const int a, const int b)
// {
// exportTable.push_back(ConversionTuple(a, b));
// }
std::list<ConversionTuple> importTable;
std::list<ConversionTuple> exportTable;
};
template<class T>
class ConversionField: public TransformedField {
public:
ConversionField(DataField * parent, int & field, ConversionTable *table, const char *name, QString error = ""):
TransformedField(parent, internalField),
internalField(this, _field, name),
field(field),
table(table),
error(std::move(error))
{
}
ConversionField(DataField * parent, unsigned int & field, ConversionTable *table, const char *name, QString error = ""):
TransformedField(parent, internalField),
internalField(this, (unsigned int &)_field, name),
field((int &)field),
table(table),
error(std::move(error))
{
}
ConversionField(DataField * parent, int & field, int (*exportFunc)(int), int (*importFunc)(int)):
TransformedField(parent, internalField),
internalField(this, _field),
field(field),
exportFunc(exportFunc),
importFunc(importFunc)
{
}
ConversionField(DataField * parent, int & field, int shift, int scale=0, int min=INT_MIN, int max=INT_MAX, const char *name = "Signed shifted"):
TransformedField(parent, internalField),
internalField(this, _field, name),
field(field),
shift(shift),
scale(scale),
min(min),
max(max)
{
}
ConversionField(DataField * parent, unsigned int & field, int shift, int scale=0):
TransformedField(parent, internalField),
internalField(this, (unsigned int &)_field),
field((int &)field),
shift(shift),
scale(scale)
{
}
void beforeExport() override
{
_field = scale ? field / scale : field;
if (table) {
if (!table->exportValue(_field, _field)) {
setError(error.isEmpty() ? DataField::tr("Conversion error on field %1").arg(name) : error);
}
return;
}
if (shift) {
if (_field < min)
_field = min + shift;
else if (_field > max)
_field = max + shift;
else
_field += shift;
}
if (exportFunc) {
_field = exportFunc(_field);
}
}
void afterImport() override
{
field = _field;
if (table) {
if (table->importValue(field, field))
return;
}
if (shift) {
field -= shift;
}
if (importFunc) {
field = importFunc(field);
}
if (scale) {
field *= scale;
}
qCDebug(eepromImport) << QString("\timported ConversionField<%1>:").arg(internalField.getName()) << QString(" before: %1, after: %2").arg(_field).arg(field);
}
protected:
T internalField;
int & field;
int _field = 0;
ConversionTable * table = nullptr;
int shift = 0;
int scale = 0;
int min = INT_MIN;
int max = INT_MAX;
int (*exportFunc)(int) = nullptr;
int (*importFunc)(int) = nullptr;
const QString error = "";
};
#endif // _EEPROMIMPORTEXPORT_H_
+11 -86
View File
@@ -20,7 +20,6 @@
*/
#include "eeprominterface.h"
#include "firmwares/opentx/opentxeeprom.h"
#include "firmwareinterface.h"
#include "boardfactories.h"
@@ -30,97 +29,24 @@
float ValToTim(int value)
{
return ((value < -109 ? 129+value : (value < 7 ? (113+value)*5 : (53+value)*10))/10.0);
return ((value < -109 ? 129 + value : (value < 7 ? (113 + value) * 5 : (53 + value) * 10)) / 10.0);
}
int TimToVal(float value)
{
int temp;
if (value>60) {
temp=136+round((value-60));
if (value > 60) {
temp = 136 + round((value - 60));
}
else if (value>2) {
temp=20+round((value-2.0)*2.0);
else if (value > 2) {
temp = 20 + round((value - 2.0) * 2.0);
}
else {
temp=round(value*10.0);
temp = round(value * 10.0);
}
return (temp-129);
return (temp - 129);
}
/*
* EEpromInterface globals
*/
// TODO: No globals
QList<EEPROMInterface *> eepromInterfaces;
void unregisterEEpromInterfaces()
{
foreach(EEPROMInterface * intf, eepromInterfaces) {
// qDebug() << "UnregisterEepromInterfaces(): deleting " << QString::number( reinterpret_cast<uint64_t>(intf), 16 );
delete intf;
}
OpenTxEepromCleanup();
}
#if 0 // TODO: remove if unused, currently commented out in mdiChild.cpp
// TODO: No GUI here, e.g. return string list instead
void EEPROMInterface::showEepromErrors(QWidget *parent, const QString &title, const QString &mainMessage, unsigned long errorsFound)
{
std::bitset<NUM_ERRORS> errors((unsigned long long)errorsFound);
QStringList errorsList;
errorsList << tr("Possible causes for this:");
if (errors.test(UNSUPPORTED_NEWER_VERSION)) { errorsList << tr("- Eeprom is from a newer version of OpenTX"); }
if (errors.test(NOT_OPENTX)) { errorsList << tr("- Eeprom is not from OpenTX"); }
if (errors.test(NOT_ERSKY9X)) { errorsList << tr("- Eeprom is not from ErSky9X"); }
if (errors.test(WRONG_SIZE)) { errorsList << tr("- Eeprom size is invalid"); }
if (errors.test(WRONG_FILE_SYSTEM)) { errorsList << tr("- Eeprom file system is invalid"); }
if (errors.test(UNKNOWN_BOARD)) { errorsList << tr("- Eeprom is from a unknown board"); }
if (errors.test(WRONG_BOARD)) { errorsList << tr("- Eeprom is from the wrong board"); }
if (errors.test(BACKUP_NOT_SUPPORTED)) { errorsList << tr("- Eeprom backup not supported"); }
if (errors.test(UNKNOWN_ERROR)) { errorsList << tr("- Something that couldn't be guessed, sorry"); }
if (errors.test(HAS_WARNINGS)) {
errorsList << tr("Warning:");
if (errors.test(WARNING_WRONG_FIRMWARE)) { errorsList << tr("- Your radio probably uses a wrong firmware,\n eeprom size is 4096 but only the first 2048 are used"); }
}
QMessageBox msgBox(parent);
msgBox.setWindowTitle(title);
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText(mainMessage);
msgBox.setInformativeText(errorsList.join("\n"));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
#endif
// TODO: No GUI here, e.g. return string list instead
QString EEPROMInterface::getEepromWarnings(unsigned long errorsFound)
{
std::bitset<NUM_ERRORS> errors((unsigned long long)errorsFound);
QStringList warningsList;
if (errors.test(WARNING_WRONG_FIRMWARE)) {
warningsList << tr("- Your radio probably uses a wrong firmware,\n eeprom size is 4096 but only the first 2048 are used");
}
if (errors.test(OLD_VERSION)) {
warningsList << tr("- Your eeprom is from an old version of OpenTX, upgrading!\n To keep your original file as a backup, please choose File -> Save As specifying a different name.");
}
return warningsList.join("\n");
}
/*
* Firmware
*/
Firmware::Firmware(Firmware * base, const QString & id, const QString & name, Board::Type board,
const QString & downloadId, const QString & simulatorId, const QString & hwdefnId) :
id(id),
@@ -128,7 +54,6 @@ Firmware::Firmware(Firmware * base, const QString & id, const QString & name, Bo
board(board),
variantBase(0),
base(base),
eepromInterface(nullptr),
downloadId(downloadId),
simulatorId(simulatorId),
hwdefnId(hwdefnId)
@@ -158,10 +83,10 @@ unsigned int Firmware::getVariantNumber()
{
unsigned int result = 0;
const Firmware * base = getFirmwareBase();
QStringList options = id.mid(base->getId().length()+1).split("-", Qt::SkipEmptyParts);
foreach(QString option, options) {
foreach(OptionsGroup group, base->opts) {
foreach(Option opt, group) {
QStringList options = id.mid(base->getId().length() + 1).split("-", Qt::SkipEmptyParts);
foreach (QString option, options) {
foreach (OptionsGroup group, base->opts) {
foreach (Option opt, group) {
if (opt.name == option) {
result += opt.variant;
}
+3 -112
View File
@@ -19,8 +19,7 @@
* GNU General Public License for more details.
*/
#ifndef _EEPROMINTERFACE_H_
#define _EEPROMINTERFACE_H_
#pragma once
#include "boards.h"
#include "macros.h"
@@ -149,7 +148,6 @@ enum Capability {
EnhancedCurves,
HasFasOffset,
HasMahPersistent,
SimulatorVariant,
MavlinkTelemetry,
HasInputDiff,
HasMixerExpo,
@@ -178,98 +176,12 @@ enum Capability {
BacklightLevelMin,
};
class EEPROMInterface
{
Q_DECLARE_TR_FUNCTIONS(EEPROMInterface)
public:
EEPROMInterface(Board::Type board):
board(board)
{
}
virtual ~EEPROMInterface() {}
inline Board::Type getBoard() { return board; }
virtual unsigned long load(RadioData &radioData, const uint8_t * eeprom, int size) = 0;
virtual unsigned long loadBackup(RadioData & radioData, const uint8_t * eeprom, int esize, int index) = 0;
virtual int save(uint8_t * eeprom, const RadioData & radioData, uint8_t version=0, uint32_t variant=0) = 0;
virtual int getSize(const ModelData &) = 0;
virtual int getSize(const GeneralSettings &) = 0;
//static void showEepromErrors(QWidget *parent, const QString &title, const QString &mainMessage, unsigned long errorsFound);
static QString getEepromWarnings(unsigned long errorsFound);
protected:
Board::Type board;
private:
EEPROMInterface();
};
/* EEPROM string conversion function (used only by er9xeeprom and ersky9xeeprom) */
inline void getEEPROMString(char *dst, const char *src, int size)
{
memcpy(dst, src, size);
dst[size] = '\0';
for (int i=size-1; i>=0; i--) {
if (dst[i] == ' ')
dst[i] = '\0';
else
break;
}
}
// (used only by er9xeeprom and ersky9xeeprom)
inline int applyStickMode(int stick, unsigned int mode)
{
if (mode == 0 || mode > 4) {
std::cerr << "Incorrect stick mode" << mode;
return stick;
}
if (stick >= 1 && stick <= 4)
return modn12x3[mode-1][stick-1];
else
return stick;
}
float ValToTim(int value);
int TimToVal(float value);
void registerEEpromInterfaces();
void unregisterEEpromInterfaces();
void registerOpenTxFirmwares();
void unregisterOpenTxFirmwares();
enum EepromLoadErrors {
ALL_OK,
UNKNOWN_ERROR,
UNSUPPORTED_NEWER_VERSION,
WRONG_SIZE,
WRONG_FILE_SYSTEM,
NOT_OPENTX,
NOT_ERSKY9X,
UNKNOWN_BOARD,
WRONG_BOARD,
BACKUP_NOT_SUPPORTED,
HAS_WARNINGS,
OLD_VERSION,
WARNING_WRONG_FIRMWARE,
NUM_ERRORS
};
constexpr char FIRMWARE_ID_PREFIX[] = { "edgetx-" };
class Firmware
@@ -330,16 +242,6 @@ class Firmware
return board;
}
void setEEpromInterface(EEPROMInterface * eeprom)
{
eepromInterface = eeprom;
}
EEPROMInterface * getEEpromInterface()
{
return eepromInterface;
}
QString getName() const
{
return name;
@@ -429,7 +331,6 @@ class Firmware
Board::Type board;
unsigned int variantBase;
Firmware * base;
EEPROMInterface * eepromInterface;
QString downloadId;
QString simulatorId;
QString hwdefnId;
@@ -449,11 +350,6 @@ inline Firmware * getCurrentFirmware()
return Firmware::getCurrentVariant();
}
inline EEPROMInterface * getCurrentEEpromInterface()
{
return Firmware::getCurrentVariant()->getEEpromInterface();
}
inline Board::Type getCurrentBoard()
{
return Firmware::getCurrentVariant()->getBoard();
@@ -461,15 +357,10 @@ inline Board::Type getCurrentBoard()
inline int divRoundClosest(const int n, const int d)
{
return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
return ((n < 0) ^ (d < 0)) ? ((n - d / 2) / d) : ((n + d / 2) / d);
}
inline int calcRESXto100(int x)
{
return divRoundClosest(x*100, 1024);
return divRoundClosest(x * 100, 1024);
}
extern QList<EEPROMInterface *> eepromInterfaces;
#endif // _EEPROMINTERFACE_H_
-482
View File
@@ -1,482 +0,0 @@
/*
* 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 <algorithm>
#include "er9xeeprom.h"
#include "helpers.h"
#include <QObject>
RawSwitch er9xToSwitch(int8_t sw)
{
uint8_t swa = abs(sw);
if (swa == 0)
return RawSwitch(SWITCH_TYPE_NONE);
else if (swa <= 9)
return RawSwitch(SWITCH_TYPE_SWITCH, sw);
else if (swa <= 21)
return RawSwitch(SWITCH_TYPE_VIRTUAL, sw > 0 ? sw-9 : sw+9);
else if (sw == 22)
return RawSwitch(SWITCH_TYPE_ON);
else if (sw == -22)
return RawSwitch(SWITCH_TYPE_OFF);
else if (swa <= 22+9)
return RawSwitch(SWITCH_TYPE_SWITCH, sw > 0 ? sw-22 : sw+22);
else
return RawSwitch(SWITCH_TYPE_VIRTUAL, sw > 0 ? sw-22-9 : sw+22+9);
}
t_Er9xTrainerMix::t_Er9xTrainerMix()
{
memset(this, 0, sizeof(t_Er9xTrainerMix));
}
t_Er9xTrainerMix::operator TrainerMix()
{
TrainerMix c9x;
c9x.src = srcChn;
c9x.swtch = er9xToSwitch(swtch);
c9x.weight = (25 * studWeight) / 8;
c9x.mode = mode;
return c9x;
}
t_Er9xTrainerData::t_Er9xTrainerData()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Er9xTrainerData));
}
t_Er9xTrainerData::operator TrainerData ()
{
TrainerData c9x;
for (int i=0; i<CPN_MAX_STICKS; i++) {
c9x.calib[i] = calib[i];
c9x.mix[i] = mix[i];
}
return c9x;
}
t_Er9xGeneral::t_Er9xGeneral()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Er9xGeneral));
}
Er9xGeneral::operator GeneralSettings ()
{
GeneralSettings result;
result.version = myVers;
for (int i=0; i<CPN_MAX_STICKSnPOTS; i++) {
result.calibMid[i] = calibMid[i];
result.calibSpanNeg[i] = calibSpanNeg[i];
result.calibSpanPos[i] = calibSpanPos[i];
}
result.currModelIndex = currModel;
result.contrast = contrast;
result.vBatWarn = vBatWarn;
result.txVoltageCalibration = txVoltageCalibration;
result.trainer = trainer;
result.view = std::min((uint8_t)4, view);
result.disableThrottleWarning = disableThrottleWarning;
result.disableMemoryWarning = disableMemoryWarning;
switch (beeperVal) {
case 0:
result.beeperMode = GeneralSettings::BEEPER_QUIET;
break;
case 1:
result.beeperMode = GeneralSettings::BEEPER_NOKEYS;
break;
default:
result.beeperMode = GeneralSettings::BEEPER_ALL;
result.beeperLength = beeperVal - 4;
break;
}
result.disableAlarmWarning = disableAlarmWarning;
result.stickMode = std::max((uint8_t)0, std::min(stickMode, (uint8_t)3));
result.inactivityTimer = inactivityTimer + 10;
result.minuteBeep = minuteBeep;
result.preBeep = preBeep;
result.flashBeep = flashBeep;
result.splashMode = disableSplashScreen;
result.backlightMode = 0;
if (lightSw == 22) {
result.backlightMode = 4;
}
else if (lightAutoOff) {
result.backlightMode |= 1;
result.backlightDelay = lightAutoOff;
}
else if (lightOnStickMove) {
result.backlightMode |= 2;
result.backlightDelay = lightOnStickMove;
}
result.templateSetup = templateSetup;
result.PPM_Multiplier = PPM_Multiplier;
getEEPROMString(result.ownerName, ownerName, sizeof(ownerName));
result.speakerPitch = speakerPitch;
result.hapticStrength = hapticStrength - 3;
result.speakerMode = speakerMode;
return result;
}
t_Er9xLimitData::t_Er9xLimitData()
{
memset(this, 0, sizeof(t_Er9xLimitData));
}
t_Er9xLimitData::operator LimitData ()
{
LimitData c9x;
c9x.min = 10*(min-100);
c9x.max = 10*(max+100);
c9x.revert = revert;
c9x.offset = offset;
return c9x;
}
t_Er9xMixData::t_Er9xMixData()
{
memset(this, 0, sizeof(t_Er9xMixData));
}
t_Er9xMixData::operator MixData ()
{
MixData c9x;
c9x.destCh = destCh;
if (abs(weight)>125) {
int gvar;
if (weight>0) {
gvar=weight-125;
} else {
gvar=weight+131;
}
gvar+=10000;
c9x.weight=gvar;
} else {
c9x.weight = weight;
}
if (abs(sOffset)>125) {
int gvar;
if (sOffset>0) {
gvar=sOffset-125;
} else {
gvar=sOffset+131;
}
gvar+=10000;
c9x.sOffset=gvar;
} else {
c9x.sOffset=sOffset;
}
c9x.swtch = er9xToSwitch(swtch);
if (srcRaw == 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_NONE);
}
else if (srcRaw <= 7) {
c9x.srcRaw = RawSource(SOURCE_TYPE_INPUT, srcRaw-1);
}
else if (srcRaw == 8) {
c9x.srcRaw = RawSource(SOURCE_TYPE_MAX);
}
else if (srcRaw == 9) {
if (swtch < 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, swtch < -3 ? -swtch-3 : -swtch);
c9x.weight = -weight;
}
else if (swtch > 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, swtch > 3 ? swtch-3 : swtch);
}
else {
c9x.srcRaw = RawSource(SOURCE_TYPE_MAX);
}
if (mltpx != MLTPX_REP)
c9x.swtch = RawSwitch(SWITCH_TYPE_NONE);
}
else if (srcRaw <= 12) {
c9x.srcRaw = RawSource(SOURCE_TYPE_CYC, srcRaw-10);
}
else if (srcRaw <= 20) {
c9x.srcRaw = RawSource(SOURCE_TYPE_PPM, srcRaw-13);
}
else if (srcRaw <=37) {
c9x.srcRaw = RawSource(SOURCE_TYPE_CH, srcRaw-21);
}
else {
c9x.srcRaw = RawSource(SOURCE_TYPE_GVAR, srcRaw-38);
}
if (differential==1) {
c9x.curve.type = CurveReference::CURVE_REF_DIFF;
c9x.curve.value = differential;
}
else if (curve > 6) {
c9x.curve.type = CurveReference::CURVE_REF_CUSTOM;
c9x.curve.value = curve - 6;
}
else if (curve > 0) {
c9x.curve.type = CurveReference::CURVE_REF_FUNC;
c9x.curve.value = curve;
}
// c9x.lateOffset=lateOffset;
c9x.delayUp = delayUp;
c9x.delayDown = delayDown;
c9x.speedUp = speedUp;
c9x.speedDown = speedDown;
c9x.carryTrim = carryTrim;
c9x.mltpx = (MltpxValue)mltpx;
c9x.mixWarn = mixWarn;
// c9x.enableFmTrim=enableFmTrim;
return c9x;
}
RawSource er9xToSource(int8_t value)
{
if (value == 0) {
return RawSource(SOURCE_TYPE_NONE);
}
else if (value <= 7) {
return RawSource(SOURCE_TYPE_INPUT, value - 1);
}
else if (value == 8) {
return RawSource(SOURCE_TYPE_MAX);
}
else if (value == 9) {
return RawSource(SOURCE_TYPE_MAX);
}
else if (value <= 12) {
return RawSource(SOURCE_TYPE_CYC, value-10);
}
else if (value <= 20) {
return RawSource(SOURCE_TYPE_PPM, value-13);
}
else if (value <= 36) {
return RawSource(SOURCE_TYPE_CH, value-21);
}
else {
return RawSource(SOURCE_TYPE_TELEMETRY, value-36);
}
}
Er9xLogicalSwitchData::operator LogicalSwitchData ()
{
LogicalSwitchData c9x;
c9x.func = func;
c9x.val1 = v1;
c9x.val2 = v2;
if ((c9x.func >= LS_FN_VPOS && c9x.func <= LS_FN_ANEG) || c9x.func >= LS_FN_EQUAL) {
c9x.val1 = er9xToSource(v1).toValue();
}
if (c9x.func >= LS_FN_EQUAL) {
c9x.val2 = er9xToSource(v2).toValue();
}
if (c9x.func >= LS_FN_AND && c9x.func <= LS_FN_XOR) {
c9x.val1 = er9xToSwitch(v1).toValue();
c9x.val2 = er9xToSwitch(v2).toValue();
}
return c9x;
}
t_Er9xSafetySwData::t_Er9xSafetySwData()
{
memset(this, 0, sizeof(t_Er9xSafetySwData));
}
t_Er9xFrSkyChannelData::t_Er9xFrSkyChannelData()
{
memset(this, 0, sizeof(t_Er9xFrSkyChannelData));
}
t_Er9xFrSkyChannelData::operator FrSkyChannelData ()
{
FrSkyChannelData c9x;
c9x.ratio = ratio;
c9x.alarms[0].value = alarms_value[0];
c9x.alarms[0].level = alarms_level & 3;
c9x.alarms[0].greater = alarms_greater & 1;
c9x.alarms[1].value = alarms_value[1];
c9x.alarms[1].level = (alarms_level >> 2) & 3;
c9x.alarms[1].greater = (alarms_greater >> 1) & 1;
if (type==2) {
c9x.type = 0;
c9x.multiplier=1;
} else {
c9x.type = type;
}
return c9x;
}
t_Er9xFrSkyData::t_Er9xFrSkyData()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Er9xFrSkyData));
}
t_Er9xFrSkyData::operator FrSkyData ()
{
FrSkyData c9x;
c9x.channels[0] = channels[0];
c9x.channels[1] = channels[1];
return c9x;
}
RawSwitch getEr9xTimerMode(int mode)
{
/*
if (mode <= -33)
return TimerMode(TMRMODE_FIRST_NEG_SWITCH+(mode+33));
else if (mode <= -1)
return TimerMode(TMRMODE_FIRST_NEG_SWITCH+(mode+1));
else if (mode < 16)
return TimerMode(mode);
else if (mode < 16+21)
return TimerMode(TMRMODE_FIRST_SWITCH+(mode-16));
else
return TimerMode(TMRMODE_FIRST_SWITCH+(mode-16-21));
*/
return RawSwitch();
}
t_Er9xModelData::operator ModelData ()
{
ModelData c9x;
c9x.used = true;
getEEPROMString(c9x.name, name, sizeof(name));
c9x.timers[0].mode = getEr9xTimerMode(tmrMode);
c9x.timers[0].val = tmrVal;
switch(protocol) {
case 1:
c9x.moduleData[0].protocol = PULSES_PXX_DJT;
break;
case 2:
c9x.moduleData[0].protocol = PULSES_DSM2;
break;
case 3:
c9x.moduleData[0].protocol = PULSES_PPM16;
break;
default:
c9x.moduleData[0].protocol = PULSES_PPM;
break;
}
c9x.moduleData[0].ppm.frameLength=ppmFrameLength;
c9x.moduleData[0].channelsCount = 8 + 2 * ppmNCH;
c9x.thrTrim = thrTrim;
c9x.trimInc = trimInc-2;
c9x.moduleData[0].ppm.delay = 300 + 50 * ppmDelay;
c9x.customFn[0].func = FuncInstantTrim;
if (trimSw) {
c9x.customFn[0].swtch = er9xToSwitch(trimSw);
}
c9x.beepANACenter = beepANACenter;
c9x.moduleData[0].ppm.pulsePol = pulsePol;
c9x.extendedLimits = extendedLimits;
c9x.swashRingData.elevatorWeight = swashInvertELE ? -100 : 100;
c9x.swashRingData.aileronWeight = swashInvertAIL ? -100 : 100;
c9x.swashRingData.collectiveWeight = swashInvertCOL ? -100 : 100;
c9x.swashRingData.type = swashType;
c9x.swashRingData.collectiveSource = er9xToSource(swashCollectiveSource);
c9x.swashRingData.value = swashRingValue;
for (int i=0; i<ER9X_MAX_MIXERS; i++) {
Er9xMixData mix = mixData[i];
c9x.mixData[i] = mix;
}
for (int i=0; i<ER9X_NUM_CHNOUT; i++)
c9x.limitData[i] = limitData[i];
// expoData
int e = 0;
for (int ch = 0; ch < 4 && e < CPN_MAX_EXPOS; ch++) {
for (int dr = 0, pos = 0; dr < 3 && e < CPN_MAX_EXPOS; dr++, pos++) {
if ((dr == 0 && !expoData[ch].drSw1) || (dr == 1 && !expoData[ch].drSw2))
dr = 2;
if (dr == 2 && !expoData[ch].expo[0][0][0] && !expoData[ch].expo[0][0][1] && !expoData[ch].expo[0][1][0] && !expoData[ch].expo[0][1][1])
break;
if (expoData[ch].drSw1 && !expoData[ch].drSw2) {
c9x.expoData[e].swtch = er9xToSwitch(dr == 0 ? expoData[ch].drSw1 : 0);
pos = dr == 0 ? 1 : 0;
}
else {
c9x.expoData[e].swtch = er9xToSwitch(dr == 0 ? -expoData[ch].drSw1 : (dr == 1 ? -expoData[ch].drSw2 : 0));
}
c9x.expoData[e].chn = ch;
if (expoData[ch].expo[pos][0][0]) {
c9x.expoData[e].curve.type = CurveReference::CURVE_REF_EXPO;
c9x.expoData[e].curve.value = expoData[ch].expo[pos][0][0];
}
c9x.expoData[e].weight = 100 + expoData[ch].expo[pos][1][0];
if (expoData[ch].expo[pos][0][0] == expoData[ch].expo[pos][0][1] && expoData[ch].expo[pos][1][0] == expoData[ch].expo[pos][1][1]) {
c9x.expoData[e++].mode = 3;
}
else {
c9x.expoData[e].mode = 2;
if (e < CPN_MAX_EXPOS - 1) {
c9x.expoData[e + 1].swtch = c9x.expoData[e].swtch;
c9x.expoData[++e].chn = ch;
c9x.expoData[e].mode = 1;
if (expoData[ch].expo[pos][0][1]) {
c9x.expoData[e].curve.type = CurveReference::CURVE_REF_EXPO;
c9x.expoData[e].curve.value = expoData[ch].expo[pos][0][1];
}
c9x.expoData[e++].weight = 100 + expoData[ch].expo[pos][1][1];
}
}
}
}
for (int i=0; i<CPN_MAX_STICKS; i++)
c9x.flightModeData[0].trim[i] = trim[i];
for (int i=0; i<ER9X_MAX_CURVE5; i++) {
c9x.curves[i].count = 5;
for (int j = 0; j < 5; j++) {
c9x.curves[i].points[j].x = -100 + 50 * i;
c9x.curves[i].points[j].y = curves5[i][j];
}
}
for (int i=0; i<ER9X_MAX_CURVE9; i++) {
c9x.curves[ER9X_MAX_CURVE5 + i].count = 9;
for (int j = 0; j < 9; j++) {
c9x.curves[ER9X_MAX_CURVE5 + i].points[j].x = -100 + 25 * i;
c9x.curves[ER9X_MAX_CURVE5 + i].points[j].y = curves9[i][j];
}
}
for (int i=0; i<ER9X_NUM_CSW; i++)
c9x.logicalSw[i] = logicalSw[i];
// for (int i=0; i<ER9X_NUM_CHNOUT; i++)
// c9x.safetySw[i] = safetySw[i];
c9x.frsky = frsky;
c9x.frsky.usrProto=FrSkyUsrProto;
return c9x;
}
-266
View File
@@ -1,266 +0,0 @@
/*
* 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.
*/
#ifndef _ER9XEEPROM_H_
#define _ER9XEEPROM_H_
#include <inttypes.h>
#include "eeprominterface.h"
//eeprom data
#define ER9X_MAX_MIXERS 32
#define ER9X_MAX_CURVE5 8
#define ER9X_MAX_CURVE9 8
#define ER9X_MAX_CSFUNC 13
#define MDVERS 10
#define ER9X_NUM_CHNOUT 16 //number of real outputchannels CH1-CH8
#define ER9X_NUM_CSW 12 //number of custom switches
#define CPN_MAX_STICKSnPOTS 7 //number of sticks and pots
#define ER9X_MAX_GVARS 7
#define ER9X_MAX_MODES 4
PACK(typedef struct t_Er9xTrainerMix {
uint8_t srcChn:3; //0-7 = ch1-8
int8_t swtch:5;
int8_t studWeight:6;
uint8_t mode:2; // off, add-mode, subst-mode
operator TrainerMix();
t_Er9xTrainerMix();
}) Er9xTrainerMix; //
PACK(typedef struct t_Er9xTrainerData {
int16_t calib[4];
Er9xTrainerMix mix[4];
operator TrainerData();
t_Er9xTrainerData();
}) Er9xTrainerData;
PACK(typedef struct t_Er9xGeneral {
uint8_t myVers;
int16_t calibMid[CPN_MAX_STICKSnPOTS];
int16_t calibSpanNeg[CPN_MAX_STICKSnPOTS];
int16_t calibSpanPos[CPN_MAX_STICKSnPOTS];
uint16_t chkSum;
uint8_t currModel; //0..15
uint8_t contrast;
uint8_t vBatWarn;
int8_t txVoltageCalibration;
int8_t lightSw;
Er9xTrainerData trainer;
uint8_t view;
uint8_t disableThrottleWarning:1;
uint8_t disableSwitchWarning:1;
uint8_t disableMemoryWarning:1;
uint8_t beeperVal:3;
uint8_t reserveWarning:1;
uint8_t disableAlarmWarning:1;
uint8_t stickMode;
int8_t inactivityTimer;
uint8_t throttleReversed:1;
uint8_t minuteBeep:1;
uint8_t preBeep:1;
uint8_t flashBeep:1;
uint8_t disableSplashScreen:1;
uint8_t disablePotScroll:1;
uint8_t disableBG:1;
uint8_t frskyinternalalarm:1;
uint8_t spare_filter ; // No longer needed, left for eepe compatibility for now
uint8_t lightAutoOff;
uint8_t templateSetup; //RETA order according to chout_ar array
int8_t PPM_Multiplier;
uint8_t unused1;
uint8_t unused2:4;
uint8_t hideNameOnSplash:1;
uint8_t enablePpmsim:1;
uint8_t blightinv:1;
uint8_t stickScroll:1;
uint8_t speakerPitch;
uint8_t hapticStrength;
uint8_t speakerMode;
uint8_t lightOnStickMove;
char ownerName[10];
uint8_t switchWarningStates;
uint8_t volume ;
uint8_t res[3];
uint8_t crosstrim:1;
uint8_t spare1:7;
operator GeneralSettings();
t_Er9xGeneral();
}) Er9xGeneral;
PACK(typedef struct t_Er9xExpoData {
int8_t expo[3][2][2];
int8_t drSw1;
int8_t drSw2;
t_Er9xExpoData() { memset(this, 0, sizeof(t_Er9xExpoData)); }
}) Er9xExpoData;
PACK(typedef struct t_Er9xLimitData {
int8_t min;
int8_t max;
bool revert;
int16_t offset;
operator LimitData();
t_Er9xLimitData();
}) Er9xLimitData;
#define MLTPX_ADD 0
#define MLTPX_MUL 1
#define MLTPX_REP 2
PACK(typedef struct t_Er9xMixData {
uint8_t destCh; // 1..CPN_MAX_CHNOUT
uint8_t srcRaw; //
int8_t weight;
int8_t swtch;
uint8_t curve; //0=symmetrisch 1=no neg 2=no pos
uint8_t delayUp:4;
uint8_t delayDown:4;
uint8_t speedUp:4; // Servogeschwindigkeit aus Tabelle (10ms Cycle)
uint8_t speedDown:4; // 0 nichts
uint8_t carryTrim:1;
uint8_t mltpx:2; // multiplex method 0=+ 1=* 2=replace
uint8_t lateOffset:1; // Add offset later
uint8_t mixWarn:2; // mixer warning
uint8_t enableFmTrim:1;
uint8_t differential:1;
int8_t sOffset;
int8_t res;
operator MixData();
t_Er9xMixData();
}) Er9xMixData;
PACK(typedef struct t_Er9xLogicalSwitchData { // Custom Switches data
int8_t v1; //input
int8_t v2; //offset
uint8_t func:4;
uint8_t andsw:4;
operator LogicalSwitchData();
t_Er9xLogicalSwitchData() { memset(this, 0, sizeof(t_Er9xLogicalSwitchData)); }
}) Er9xLogicalSwitchData;
PACK(typedef struct t_Er9xSafetySwData { // Custom Switches data
int8_t swtch;
int8_t val;
t_Er9xSafetySwData();
}) Er9xSafetySwData;
PACK(typedef struct t_Er9xFrSkyChannelData {
uint8_t ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_value[2]; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_level:4;
uint8_t alarms_greater:2; // 0=LT(<), 1=GT(>)
uint8_t type:2; // future use: 0=volts, ...
operator FrSkyChannelData();
t_Er9xFrSkyChannelData();
}) Er9xFrSkyChannelData;
PACK(typedef struct t_Er9xFrSkyData {
Er9xFrSkyChannelData channels[2];
operator FrSkyData();
t_Er9xFrSkyData();
}) Er9xFrSkyData;
PACK(typedef struct t_gvar {
int8_t gvar ;
uint8_t gvsource ;
// int8_t gvswitch ;
}) Er9xGvarData ;
PACK(typedef struct t_FlightModeData {
// Trim store as -1001 to -1, trim value-501, 0-5 use trim of phase 0-5
int16_t trim[4]; // -500..500 => trim value, 501 => use trim of phase 0, 502, 503, 504 => use trim of modes 1|2|3|4 instead
int8_t swtch; // Try 0-5 use trim of phase 0-5, 1000-2000, trim + 1500 ???
uint8_t fadeIn:4;
uint8_t fadeOut:4;
}) Er9xFlightModeData;
PACK(typedef struct t_Er9xModelData {
char name[10]; // 10 must be first for eeLoadModelName
uint8_t modelVoice ; // Index to model name voice (260+value)
int8_t tmrMode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw
uint8_t tmrDir:1; //0=>Count Down, 1=>Count Up
uint8_t traineron:1; // 0 disable trainer, 1 allow trainer
uint8_t t2throttle:1 ; // Start timer2 using throttle
uint8_t FrSkyUsrProto:1 ; // Protocol in FrSky User Data, 0=FrSky Hub, 1=WS HowHigh
uint8_t FrSkyGpsAlt:1 ; // Use Gps Altitude as main altitude reading
uint8_t FrSkyImperial:1 ; // Convert FrSky values to imperial units
uint8_t FrSkyAltAlarm:2;
uint16_t tmrVal;
uint8_t protocol;
int8_t ppmNCH;
uint8_t thrTrim:1; // Enable Throttle Trim
uint8_t xnumBlades:2; // RPM scaling
uint8_t spare10:1;
uint8_t thrExpo:1; // Enable Throttle Expo
uint8_t ppmStart:3 ; // Start channel for PPM
int8_t trimInc; // Trim Increments
int8_t ppmDelay;
int8_t trimSw;
uint8_t beepANACenter; // 1<<0->A1.. 1<<6->A7
uint8_t pulsePol:1;
uint8_t extendedLimits:1;
uint8_t swashInvertELE:1;
uint8_t swashInvertAIL:1;
uint8_t swashInvertCOL:1;
uint8_t swashType:3;
uint8_t swashCollectiveSource;
uint8_t swashRingValue;
int8_t ppmFrameLength;
Er9xMixData mixData[ER9X_MAX_MIXERS];
Er9xLimitData limitData[ER9X_NUM_CHNOUT];
Er9xExpoData expoData[4];
int8_t trim[4];
int8_t curves5[ER9X_MAX_CURVE5][5];
int8_t curves9[ER9X_MAX_CURVE9][9];
Er9xLogicalSwitchData logicalSw[ER9X_NUM_CSW];
uint8_t frSkyVoltThreshold ;
int8_t tmrModeB;
uint8_t numVoice;
Er9xSafetySwData safetySw[ER9X_NUM_CHNOUT];
Er9xFrSkyData frsky;
uint8_t numBlades ;
uint8_t unused1[8] ;
uint8_t CustomDisplayIndex[6] ;
Er9xGvarData gvars[ER9X_MAX_GVARS] ;
Er9xFlightModeData flightModeData[ER9X_MAX_MODES] ;
operator ModelData();
t_Er9xModelData() { memset(reinterpret_cast<void *>(this), 0, sizeof(t_Er9xModelData)); }
}) Er9xModelData;
#endif // _ER9XEEPROM_H_
@@ -1,342 +0,0 @@
/*
* 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 "er9xinterface.h"
#include "er9xeeprom.h"
#include "rlefile.h"
// #include "appdata.h"
// #include <iostream>
#include <bitset>
#define FILE_TYP_GENERAL 1
#define FILE_TYP_MODEL 2
#define FILE_GENERAL 0
#define FILE_MODEL(n) (1+n)
Er9xInterface::Er9xInterface():
EEPROMInterface(Board::BOARD_9X_M64),
efile(new RleFile())
{
}
Er9xInterface::~Er9xInterface()
{
delete efile;
}
const char * Er9xInterface::getName()
{
return "Er9x";
}
inline void applyStickModeToModel(Er9xModelData & model, unsigned int mode)
{
for (int i=0; i<2; i++) {
int stick = applyStickMode(i+1, mode) - 1;
{
int tmp = model.trim[i];
model.trim[i] = model.trim[stick];
model.trim[stick] = tmp;
}
{
Er9xExpoData tmp = model.expoData[i];
model.expoData[i] = model.expoData[stick];
model.expoData[stick] = tmp;
}
}
for (int i=0; i<ER9X_MAX_MIXERS; i++)
model.mixData[i].srcRaw = applyStickMode(model.mixData[i].srcRaw, mode);
for (int i=0; i<ER9X_NUM_CSW; i++) {
switch (LogicalSwitchData(model.logicalSw[i].func).getFunctionFamily()) {
case LS_FAMILY_VCOMP:
model.logicalSw[i].v2 = applyStickMode(model.logicalSw[i].v2, mode);
// no break
case LS_FAMILY_VOFS:
model.logicalSw[i].v1 = applyStickMode(model.logicalSw[i].v1, mode);
break;
default:
break;
}
}
model.swashCollectiveSource = applyStickMode(model.swashCollectiveSource, mode);
}
#if 0
unsigned long Er9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
{
QDebug dbg = qDebug();
dbg.setAutoInsertSpaces(false);
dbg << "trying er9x xml import... ";
std::bitset<NUM_ERRORS> errors;
Er9xGeneral er9xGeneral;
memset(&er9xGeneral,0,sizeof(er9xGeneral));
if (!loadRadioSettingsDataXML(&doc, &er9xGeneral)) {
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
else {
radioData.generalSettings=er9xGeneral;
dbg << "version " << (unsigned int)er9xGeneral.myVers << " ";
}
for (int i=0; i<getCapability(Models); i++) {
Er9xModelData er9xModel;
memset(&er9xModel,0,sizeof(er9xModel));
if(loadModelDataXML(&doc, &er9xModel, i)) {
applyStickModeToModel(er9xModel, radioData.generalSettings.stickMode+1);
radioData.models[i] = er9xModel;
}
}
dbg << "ok";
errors.set(ALL_OK);
return errors.to_ulong();
}
#endif
unsigned long Er9xInterface::load(RadioData &radioData, const uint8_t *eeprom, int size)
{
QDebug dbg = qDebug();
dbg.setAutoInsertSpaces(false);
dbg << "trying er9x import... ";
std::bitset<NUM_ERRORS> errors;
if (size != Boards::getEEpromSize(Board::BOARD_9X_M64)) {
dbg << "wrong size";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
if (!efile->EeFsOpen((uint8_t *)eeprom, size, Board::BOARD_9X_M64)) {
dbg << "wrong file system";
errors.set(WRONG_FILE_SYSTEM);
return errors.to_ulong();
}
efile->openRd(FILE_GENERAL);
Er9xGeneral er9xGeneral;
if (efile->readRlc1((uint8_t*)&er9xGeneral, 1) != 1) {
dbg << "no";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
dbg << "version " << (unsigned int)er9xGeneral.myVers << " ";
switch(er9xGeneral.myVers) {
case 3:
dbg << "(old gruvin9x) ";
case 4:
// case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
break;
default:
dbg << "not er9x";
errors.set(NOT_ER9X);
return errors.to_ulong();
}
efile->openRd(FILE_GENERAL);
if (!efile->readRlc1((uint8_t*)&er9xGeneral, sizeof(Er9xGeneral))) {
dbg << "ko";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
radioData.generalSettings = er9xGeneral;
for (int i=0; i<getCapability(Models); i++) {
Er9xModelData er9xModel;
efile->openRd(FILE_MODEL(i));
if (!efile->readRlc1((uint8_t*)&er9xModel, sizeof(Er9xModelData))) {
radioData.models[i].clear();
}
else {
applyStickModeToModel(er9xModel, radioData.generalSettings.stickMode+1);
radioData.models[i] = er9xModel;
}
}
dbg << "ok";
errors.set(ALL_OK);
return errors.to_ulong();
}
unsigned long Er9xInterface::loadBackup(RadioData &radioData, const uint8_t *eeprom, int esize, int index)
{
std::bitset<NUM_ERRORS> errors;
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
int Er9xInterface::getSize(const ModelData &model)
{
return 0;
}
int Er9xInterface::getSize(const GeneralSettings &settings)
{
return 0;
}
bool Er9xInterface::isAvailable(PulsesProtocol prot, int port)
{
switch (prot) {
case PULSES_PPM:
case PULSES_DSM2:
case PULSES_PXX_DJT:
case PULSES_PPM16:
return 1;
default:
return 0;
}
}
void Er9xInterface::appendTextElement(QDomDocument * qdoc, QDomElement * pe, QString name, QString value)
{
QDomElement e = qdoc->createElement(name);
QDomText t = qdoc->createTextNode(name);
t.setNodeValue(value);
e.appendChild(t);
pe->appendChild(e);
}
void Er9xInterface::appendNumberElement(QDomDocument * qdoc, QDomElement * pe,QString name, int value, bool forceZeroWrite)
{
if(value || forceZeroWrite) {
QDomElement e = qdoc->createElement(name);
QDomText t = qdoc->createTextNode(name);
t.setNodeValue(QString("%1").arg(value));
e.appendChild(t);
pe->appendChild(e);
}
}
void Er9xInterface::appendCDATAElement(QDomDocument * qdoc, QDomElement * pe,QString name, const char * data, int size)
{
QDomElement e = qdoc->createElement(name);
QDomCDATASection t = qdoc->createCDATASection(name);
t.setData(QByteArray(data, size).toBase64());
e.appendChild(t);
pe->appendChild(e);
}
QDomElement Er9xInterface::getGeneralDataXML(QDomDocument * qdoc, Er9xGeneral * tgen)
{
QDomElement gd = qdoc->createElement("GENERAL_DATA");
appendNumberElement(qdoc, &gd, "Version", tgen->myVers, true); // have to write value here
appendTextElement(qdoc, &gd, "Owner", QString::fromLatin1(tgen->ownerName,sizeof(tgen->ownerName)).trimmed());
appendCDATAElement(qdoc, &gd, "Data", (const char *)tgen,sizeof(Er9xGeneral));
return gd;
}
QDomElement Er9xInterface::getModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod, int modelNum, int mdver)
{
QDomElement md = qdoc->createElement("MODEL_DATA");
md.setAttribute("number", modelNum);
appendNumberElement(qdoc, &md, "Version", mdver, true); // have to write value here
appendTextElement(qdoc, &md, "Name", QString::fromLatin1(tmod->name,sizeof(tmod->name)).trimmed());
appendCDATAElement(qdoc, &md, "Data", (const char *)tmod,sizeof(Er9xModelData));
return md;
}
bool Er9xInterface::loadRadioSettingsDataXML(QDomDocument * qdoc, Er9xGeneral * tgen)
{
// look for "GENERAL_DATA" tag
QDomElement gde = qdoc->elementsByTagName("GENERAL_DATA").at(0).toElement();
if(gde.isNull()) // couldn't find
return false;
// load cdata into tgen
QDomNode n = gde.elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
QString ds = n.toCDATASection().data();
QByteArray ba = QByteArray::fromBase64(ds.toLatin1());
const char * data = ba.data();
memcpy(tgen, data, sizeof(Er9xGeneral));
break;
}
n = n.nextSibling();
}
// check version?
return true;
}
bool Er9xInterface::loadModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod, int modelNum)
{
// look for MODEL_DATA with modelNum attribute.
// if modelNum = -1 then just pick the first one
QDomNodeList ndl = qdoc->elementsByTagName("MODEL_DATA");
// cycle through nodes to find correct model number
QDomNode k = ndl.at(0);
if(modelNum>=0) {
while(!k.isNull()) {
int a = k.toElement().attribute("number").toInt();
if(a==modelNum)
break;
k = k.nextSibling();
}
}
if (k.isNull()) // couldn't find
return false;
// load cdata into tgen
QDomNode n = k.toElement().elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
QString ds = n.toCDATASection().data();
QByteArray ba = QByteArray::fromBase64(ds.toLatin1());
const char * data = ba.data();
memcpy(tmod, data, sizeof(Er9xModelData));
break;
}
n = n.nextSibling();
}
// check version?
return true;
}
int Er9xInterface::getCapability(Capability capability)
{
switch (capability) {
case Models:
return 16;
default:
return 0;
}
}
QString Er9xInterface::getCapabilityStr(Capability capability)
{
return QString();
}
@@ -1,83 +0,0 @@
/*
* 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.
*/
#ifndef _ER9XINTERFACE_H_
#define _ER9XINTERFACE_H_
#include <QtXml>
#include "eeprominterface.h"
#include "er9xeeprom.h"
class RleFile;
class Er9xInterface : public EEPROMInterface
{
public:
Er9xInterface();
virtual ~Er9xInterface();
virtual const char * getName();
virtual unsigned long load(RadioData &, const uint8_t * eeprom, int size);
virtual unsigned long loadBackup(RadioData &, const uint8_t * eeprom, int esize, int index);
virtual unsigned long loadxml(RadioData &radioData, QDomDocument &doc);
virtual int save(uint8_t * eeprom, const RadioData & radioData, uint8_t version=0, uint32_t variant=0)
{
return 0;
}
virtual int getSize(const ModelData &);
virtual int getSize(const GeneralSettings &settings);
virtual bool isAvailable(PulsesProtocol proto, int port=0);
virtual int getCapability(Capability capability);
virtual QString getCapabilityStr(Capability);
protected:
RleFile * efile;
private:
void appendTextElement(QDomDocument * qdoc, QDomElement * pe, QString name, QString value);
void appendNumberElement(QDomDocument * qdoc, QDomElement * pe,QString name, int value, bool forceZeroWrite = false);
void appendCDATAElement(QDomDocument * qdoc, QDomElement * pe,QString name, const char * data, int size);
QDomElement getGeneralDataXML(QDomDocument * qdoc, Er9xGeneral * tgen); //parse out data to XML format
QDomElement getModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod, int modelNum, int mdver); //parse out data to XML format
bool loadRadioSettingsDataXML(QDomDocument * qdoc, Er9xGeneral * tgen); // get data from XML
bool loadModelDataXML(QDomDocument * qdoc, Er9xModelData * tmod, int modelNum = -1); // get data from XML
};
#endif // _ER9XINTERFACE_H_
@@ -1,731 +0,0 @@
/*
* 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 <algorithm>
#include "helpers.h"
#include "ersky9xeeprom.h"
#include <QObject>
extern RawSwitch er9xToSwitch(int8_t sw);
RawSwitch getErSky9xTimerMode(int mode)
{
if (mode<4)
return RawSwitch(SWITCH_TYPE_TIMER_MODE, mode);
else
return RawSwitch();
}
RawSwitch ersky9xToSwitch(int8_t sw)
{
uint8_t swa = abs(sw);
if (swa == 0)
return RawSwitch(SWITCH_TYPE_NONE);
else if (swa <= 9)
return RawSwitch(SWITCH_TYPE_SWITCH, sw);
else if (swa <= 33)
return RawSwitch(SWITCH_TYPE_VIRTUAL, sw > 0 ? sw-9 : sw+9);
else if (sw == 34)
return RawSwitch(SWITCH_TYPE_ON);
else if (sw == -34)
return RawSwitch(SWITCH_TYPE_OFF);
else if (swa <= 34+9)
return RawSwitch(SWITCH_TYPE_SWITCH, sw > 0 ? sw-34 : sw+34);
else
return RawSwitch(SWITCH_TYPE_VIRTUAL, sw > 0 ? sw-34-9 : sw+34+9);
}
t_Ersky9xTrainerMix::t_Ersky9xTrainerMix()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xTrainerMix));
}
t_Ersky9xTrainerMix::operator TrainerMix()
{
TrainerMix c9x;
c9x.src = srcChn;
c9x.swtch = er9xToSwitch(swtch);
c9x.weight = (25 * studWeight) / 8;
c9x.mode = mode;
return c9x;
}
t_Ersky9xTrainerData::t_Ersky9xTrainerData()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xTrainerData));
}
t_Ersky9xTrainerData::operator TrainerData ()
{
TrainerData c9x;
for (int i=0; i<CPN_MAX_STICKS; i++) {
c9x.calib[i] = calib[i];
c9x.mix[i] = mix[i];
}
return c9x;
}
t_Ersky9xGeneral::t_Ersky9xGeneral()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xGeneral));
}
Ersky9xGeneral::operator GeneralSettings ()
{
GeneralSettings result;
result.version = myVers;
for (int i=0; i<CPN_MAX_STICKSnPOTS; i++) {
result.calibMid[i] = calibMid[i];
result.calibSpanNeg[i] = calibSpanNeg[i];
result.calibSpanPos[i] = calibSpanPos[i];
}
result.currModelIndex = currModel;
result.contrast = contrast;
result.vBatWarn = vBatWarn;
result.txVoltageCalibration = txVoltageCalibration;
result.backlightMode = 0;
if (lightSw == 22) {
result.backlightMode = 4;
}
else if (lightAutoOff) {
result.backlightMode |= 1;
result.backlightDelay = lightAutoOff;
}
else if (lightOnStickMove) {
result.backlightMode |= 2;
result.backlightDelay = lightOnStickMove;
}
result.trainer = trainer;
result.view = std::min((uint8_t)4, view);
result.disableThrottleWarning = disableThrottleWarning;
result.disableMemoryWarning = disableMemoryWarning;
switch (beeperVal) {
case 0:
result.beeperMode = GeneralSettings::BEEPER_QUIET;
break;
case 1:
result.beeperMode = GeneralSettings::BEEPER_NOKEYS;
break;
default:
result.beeperMode = GeneralSettings::BEEPER_ALL;
result.beeperLength = beeperVal - 4;
break;
}
result.disableAlarmWarning = disableAlarmWarning;
result.stickMode = std::max((uint8_t)0, std::min(stickMode, (uint8_t)3));
result.inactivityTimer = inactivityTimer + 10;
result.minuteBeep = minuteBeep;
result.preBeep = preBeep;
result.alarmsFlash = alarmsFlash;
result.splashMode = disableSplashScreen;
result.templateSetup = templateSetup;
result.PPM_Multiplier = PPM_Multiplier;
getEEPROMString(result.ownerName, ownerName, sizeof(ownerName));
result.optrexDisplay = optrexDisplay;
result.speakerPitch = speakerPitch;
result.hapticStrength = hapticStrength - 3;
result.speakerMode = speakerMode;
result.speakerVolume = volume;
result.backlightBright = bright ;
result.txCurrentCalibration = current_calib;
return result;
}
t_Ersky9xLimitData::t_Ersky9xLimitData()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xLimitData));
}
t_Ersky9xLimitData::operator LimitData ()
{
LimitData c9x;
c9x.min = 10*(min-100);
c9x.max = 10*(max+100);
c9x.revert = revert;
c9x.offset = offset;
return c9x;
}
t_Ersky9xMixData_v10::t_Ersky9xMixData_v10()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xMixData_v10));
}
t_Ersky9xMixData_v10::operator MixData ()
{
MixData c9x;
c9x.destCh = destCh;
c9x.weight = weight;
c9x.swtch = er9xToSwitch(swtch);
if (srcRaw == 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_NONE);
}
else if (srcRaw <= 7) {
c9x.srcRaw = RawSource(SOURCE_TYPE_INPUT, srcRaw-1);
}
else if (srcRaw == 8) {
c9x.srcRaw = RawSource(SOURCE_TYPE_MAX);
}
else if (srcRaw == 9) {
if (swtch < 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, swtch < -3 ? -swtch-3 : -swtch);
c9x.weight = -weight;
}
else if (swtch > 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, swtch > 3 ? swtch-3 : swtch);
}
else {
c9x.srcRaw = RawSource(SOURCE_TYPE_MAX);
}
if (mltpx != MLTPX_REP)
c9x.swtch = RawSwitch(SWITCH_TYPE_NONE);
}
else if (srcRaw == 37) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, 0);
}
else if (srcRaw <= 12) {
c9x.srcRaw = RawSource(SOURCE_TYPE_CYC, srcRaw-10);
}
else if (srcRaw <= 20) {
c9x.srcRaw = RawSource(SOURCE_TYPE_PPM, srcRaw-13);
}
else {
c9x.srcRaw = RawSource(SOURCE_TYPE_CH, srcRaw-21);
}
if (curve > 6) {
c9x.curve.type = CurveReference::CURVE_REF_CUSTOM;
c9x.curve.value = curve - 6;
}
else if (curve > 0) {
c9x.curve.type = CurveReference::CURVE_REF_FUNC;
c9x.curve.value = curve;
}
c9x.delayUp = delayUp;
c9x.delayDown = delayDown;
c9x.speedUp = speedUp;
c9x.speedDown = speedDown;
c9x.carryTrim = carryTrim;
c9x.mltpx = (MltpxValue)mltpx;
c9x.mixWarn = mixWarn;
// c9x.enableFmTrim=enableFmTrim;
c9x.sOffset = sOffset;
return c9x;
}
t_Ersky9xMixData_v11::t_Ersky9xMixData_v11()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xMixData_v11));
}
t_Ersky9xMixData_v11::operator MixData ()
{
MixData c9x;
c9x.destCh = destCh;
c9x.weight = weight;
c9x.swtch = ersky9xToSwitch(swtch);
if (srcRaw == 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_NONE);
}
else if (srcRaw <= 7) {
c9x.srcRaw = RawSource(SOURCE_TYPE_INPUT, srcRaw-1);
}
else if (srcRaw == 8) {
c9x.srcRaw = RawSource(SOURCE_TYPE_MAX);
}
else if (srcRaw == 9) {
if (swtch < 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, swtch < -3 ? -swtch-3 : -swtch);
c9x.weight = -weight;
}
else if (swtch > 0) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, swtch > 3 ? swtch-3 : swtch);
}
else {
c9x.srcRaw = RawSource(SOURCE_TYPE_MAX);
}
if (mltpx != MLTPX_REP)
c9x.swtch = RawSwitch(SWITCH_TYPE_NONE);
}
else if (srcRaw == 45) {
c9x.srcRaw = RawSource(SOURCE_TYPE_SWITCH, 0);
}
else if (srcRaw > 45) {
c9x.srcRaw = RawSource(SOURCE_TYPE_GVAR, srcRaw-46);
}
else if (srcRaw <= 12) {
c9x.srcRaw = RawSource(SOURCE_TYPE_CYC, srcRaw-10);
}
else if (srcRaw <= 20) {
c9x.srcRaw = RawSource(SOURCE_TYPE_PPM, srcRaw-13);
}
else {
c9x.srcRaw = RawSource(SOURCE_TYPE_CH, srcRaw-21);
}
if (curve > 6) {
c9x.curve.type = CurveReference::CURVE_REF_CUSTOM;
c9x.curve.value = curve - 6;
}
else if (curve > 0) {
c9x.curve.type = CurveReference::CURVE_REF_FUNC;
c9x.curve.value = curve;
}
c9x.delayUp = delayUp;
c9x.delayDown = delayDown;
c9x.speedUp = speedUp;
c9x.speedDown = speedDown;
c9x.carryTrim = carryTrim;
c9x.mltpx = (MltpxValue)mltpx;
c9x.mixWarn = mixWarn;
// c9x.enableFmTrim=enableFmTrim;
c9x.sOffset = sOffset;
return c9x;
}
RawSource ersky9xToSource_v10(int8_t value)
{
if (value == 0) {
return RawSource(SOURCE_TYPE_NONE);
}
else if (value <= 7) {
return RawSource(SOURCE_TYPE_INPUT, value - 1);
}
else if (value == 8) {
return RawSource(SOURCE_TYPE_MAX);
}
else if (value == 9) {
return RawSource(SOURCE_TYPE_MAX);
}
else if (value <= 12) {
return RawSource(SOURCE_TYPE_CYC, value-10);
}
else if (value <= 20) {
return RawSource(SOURCE_TYPE_PPM, value-13);
}
else if (value <= 36) {
return RawSource(SOURCE_TYPE_CH, value-21);
}
else {
return RawSource(SOURCE_TYPE_TELEMETRY, value-36);
}
}
RawSource ersky9xToSource_v11(int8_t value)
{
if (value == 0) {
return RawSource(SOURCE_TYPE_NONE);
}
else if (value <= 7) {
return RawSource(SOURCE_TYPE_INPUT, value - 1);
}
else if (value == 8) {
return RawSource(SOURCE_TYPE_MAX);
}
else if (value == 9) {
return RawSource(SOURCE_TYPE_MAX);
}
else if (value <= 12) {
return RawSource(SOURCE_TYPE_CYC, value-10);
}
else if (value <= 20) {
return RawSource(SOURCE_TYPE_PPM, value-13);
}
else if (value <= 36) {
return RawSource(SOURCE_TYPE_CH, value-21);
}
else {
return RawSource(SOURCE_TYPE_TELEMETRY, value-36);
}
}
Ersky9xLogicalSwitchData_v10::operator LogicalSwitchData ()
{
LogicalSwitchData c9x;
c9x.func = func;
c9x.val1 = v1;
c9x.val2 = v2;
if ((c9x.func >= LS_FN_VPOS && c9x.func <= LS_FN_ANEG) || c9x.func >= LS_FN_EQUAL) {
c9x.val1 = ersky9xToSource_v10(v1).toValue();
}
if (c9x.func >= LS_FN_EQUAL) {
c9x.val2 = ersky9xToSource_v10(v2).toValue();
}
if (c9x.func >= LS_FN_AND && c9x.func <= LS_FN_XOR) {
c9x.val1 = er9xToSwitch(v1).toValue();
c9x.val2 = er9xToSwitch(v2).toValue();
}
return c9x;
}
Ersky9xLogicalSwitchData_v11::operator LogicalSwitchData ()
{
LogicalSwitchData c9x;
c9x.func = func;
c9x.val1 = v1;
c9x.val2 = v2;
if ((c9x.func >= LS_FN_VPOS && c9x.func <= LS_FN_ANEG) || c9x.func >= LS_FN_EQUAL) {
c9x.val1 = ersky9xToSource_v11(v1).toValue();
}
if (c9x.func >= LS_FN_EQUAL) {
c9x.val2 = ersky9xToSource_v11(v2).toValue();
}
if (c9x.func >= LS_FN_AND && c9x.func <= LS_FN_XOR) {
c9x.val1 = er9xToSwitch(v1).toValue();
c9x.val2 = er9xToSwitch(v2).toValue();
}
return c9x;
}
t_Ersky9xSafetySwData_v10::t_Ersky9xSafetySwData_v10()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xSafetySwData_v10));
}
t_Ersky9xSafetySwData_v11::t_Ersky9xSafetySwData_v11()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xSafetySwData_v11));
}
t_Ersky9xFrSkyChannelData_v10::t_Ersky9xFrSkyChannelData_v10()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xFrSkyChannelData_v10));
}
t_Ersky9xFrSkyChannelData_v10::operator FrSkyChannelData ()
{
FrSkyChannelData c9x;
c9x.ratio = ratio;
c9x.alarms[0].value = alarms_value[0];
c9x.alarms[0].level = alarms_level & 3;
c9x.alarms[0].greater = alarms_greater & 1;
c9x.alarms[1].value = alarms_value[1];
c9x.alarms[1].level = (alarms_level >> 2) & 3;
c9x.alarms[1].greater = (alarms_greater >> 1) & 1;
if (type==2) {
c9x.type = 0;
c9x.multiplier=1;
} else {
c9x.type = type;
}
return c9x;
}
t_Ersky9xFrSkyChannelData_v11::t_Ersky9xFrSkyChannelData_v11()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xFrSkyChannelData_v11));
}
t_Ersky9xFrSkyChannelData_v11::operator FrSkyChannelData ()
{
FrSkyChannelData c9x;
c9x.ratio = ratio;
c9x.alarms[0].value = alarms_value[0];
c9x.alarms[0].level = alarms_level & 3;
c9x.alarms[0].greater = alarms_greater & 1;
c9x.alarms[1].value = alarms_value[1];
c9x.alarms[1].level = (alarms_level >> 2) & 3;
c9x.alarms[1].greater = (alarms_greater >> 1) & 1;
if (type==2) {
c9x.type = 0;
c9x.multiplier=1;
} else {
c9x.type = type;
}
return c9x;
}
t_Ersky9xFrSkyData_v10::t_Ersky9xFrSkyData_v10()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xFrSkyData_v10));
}
t_Ersky9xFrSkyData_v10::operator FrSkyData ()
{
FrSkyData c9x;
c9x.channels[0] = channels[0];
c9x.channels[1] = channels[1];
return c9x;
}
t_Ersky9xFrSkyData_v11::t_Ersky9xFrSkyData_v11()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xFrSkyData_v11));
}
t_Ersky9xFrSkyData_v11::operator FrSkyData ()
{
FrSkyData c9x;
c9x.channels[0] = channels[0];
c9x.channels[1] = channels[1];
return c9x;
}
t_Ersky9xModelData_v10::operator ModelData ()
{
ModelData c9x;
c9x.used = true;
getEEPROMString(c9x.name, name, sizeof(name));
for (int i=0; i<2; i++) {
c9x.timers[i].mode = timer[i].tmrModeA < 4 ? timer[i].tmrModeA : 0;
c9x.timers[i].val = timer[i].tmrVal;
}
switch(protocol) {
case 1:
c9x.moduleData[0].protocol = PULSES_PXX_DJT;
break;
case 2:
c9x.moduleData[0].protocol = PULSES_DSM2;
break;
case 3:
c9x.moduleData[0].protocol = PULSES_PPM16;
break;
default:
c9x.moduleData[0].protocol = PULSES_PPM;
break;
}
c9x.moduleData[0].ppm.frameLength=ppmFrameLength;
c9x.moduleData[0].channelsCount = 8 + 2 * ppmNCH;
c9x.thrTrim = thrTrim;
c9x.trimInc = trimInc-2;
c9x.moduleData[0].ppm.delay = 300 + 50 * ppmDelay;
c9x.customFn[0].func = FuncInstantTrim;
if (trimSw) {
c9x.customFn[0].swtch = er9xToSwitch(trimSw);
}
c9x.beepANACenter = beepANACenter;
c9x.moduleData[0].ppm.pulsePol = pulsePol;
c9x.extendedLimits = extendedLimits;
c9x.swashRingData.elevatorWeight = swashInvertELE ? -100 : 100;
c9x.swashRingData.aileronWeight = swashInvertAIL ? -100 : 100;
c9x.swashRingData.collectiveWeight = swashInvertCOL ? -100 : 100;
c9x.swashRingData.type = swashType;
c9x.swashRingData.collectiveSource = ersky9xToSource_v10(swashCollectiveSource);
c9x.swashRingData.value = swashRingValue;
for (int i=0; i<ERSKY9X_MAX_MIXERS_V10; i++)
c9x.mixData[i] = mixData[i];
for (int i=0; i<ERSKY9X_NUM_CHNOUT_V10; i++)
c9x.limitData[i] = limitData[i];
// expoData
int e = 0;
for (int ch = 0; ch < 4 && e < CPN_MAX_EXPOS; ch++) {
for (int dr = 0, pos = 0; dr < 3 && e < CPN_MAX_EXPOS; dr++, pos++) {
if ((dr == 0 && !expoData[ch].drSw1) || (dr == 1 && !expoData[ch].drSw2))
dr = 2;
if (dr == 2 && !expoData[ch].expo[0][0][0] && !expoData[ch].expo[0][0][1] && !expoData[ch].expo[0][1][0] && !expoData[ch].expo[0][1][1])
break;
if (expoData[ch].drSw1 && !expoData[ch].drSw2) {
c9x.expoData[e].swtch = er9xToSwitch(dr == 0 ? expoData[ch].drSw1 : 0);
pos = dr == 0 ? 1 : 0;
}
else {
c9x.expoData[e].swtch = er9xToSwitch(dr == 0 ? -expoData[ch].drSw1 : (dr == 1 ? -expoData[ch].drSw2 : 0));
}
c9x.expoData[e].chn = ch;
if (expoData[ch].expo[pos][0][0]) {
c9x.expoData[e].curve.type = CurveReference::CURVE_REF_EXPO;
c9x.expoData[e].curve.value = expoData[ch].expo[pos][0][0];
}
c9x.expoData[e].weight = 100 + expoData[ch].expo[pos][1][0];
if (expoData[ch].expo[pos][0][0] == expoData[ch].expo[pos][0][1] && expoData[ch].expo[pos][1][0] == expoData[ch].expo[pos][1][1]) {
c9x.expoData[e++].mode = 3;
}
else {
c9x.expoData[e].mode = 2;
if (e < CPN_MAX_EXPOS - 1) {
c9x.expoData[e + 1].swtch = c9x.expoData[e].swtch;
c9x.expoData[++e].chn = ch;
c9x.expoData[e].mode = 1;
if (expoData[ch].expo[pos][0][1]) {
c9x.expoData[e].curve.type = CurveReference::CURVE_REF_EXPO;
c9x.expoData[e].curve.value = expoData[ch].expo[pos][0][1];
}
c9x.expoData[e++].weight = 100 + expoData[ch].expo[pos][1][1];
}
}
}
}
for (int i=0; i<CPN_MAX_STICKS; i++)
c9x.flightModeData[0].trim[i] = trim[i];
for (int i=0; i<ERSKY9X_MAX_CURVE5; i++) {
c9x.curves[i].count = 5;
for (int j = 0; j < 5; j++) {
c9x.curves[i].points[j].x = -100 + 50 * i;
c9x.curves[i].points[j].y = curves5[i][j];
}
}
for (int i=0; i<ERSKY9X_MAX_CURVE9; i++) {
c9x.curves[ERSKY9X_MAX_CURVE5 + i].count = 9;
for (int j = 0; j < 9; j++) {
c9x.curves[ERSKY9X_MAX_CURVE5 + i].points[j].x = -100 + 25 * i;
c9x.curves[ERSKY9X_MAX_CURVE5 + i].points[j].y = curves9[i][j];
}
}
for (int i=0; i<ERSKY9X_NUM_CSW_V10; i++)
c9x.logicalSw[i] = logicalSw[i];
// for (int i=0; i<ERSKY9X_NUM_CHNOUT_V10; i++)
// c9x.safetySw[i] = safetySw[i];
c9x.frsky = frsky;
c9x.frsky.usrProto=FrSkyUsrProto;
return c9x;
}
t_Ersky9xModelData_v11::operator ModelData ()
{
ModelData c9x;
c9x.used = true;
getEEPROMString(c9x.name, name, sizeof(name));
for (int i=0; i<2; i++) {
c9x.timers[i].mode = timer[i].tmrModeA < 4 ? timer[i].tmrModeA : 0;
c9x.timers[i].val = timer[i].tmrVal;
}
switch(protocol) {
case 1:
c9x.moduleData[0].protocol = PULSES_PXX_DJT;
break;
case 2:
c9x.moduleData[0].protocol = PULSES_DSM2;
break;
case 3:
c9x.moduleData[0].protocol = PULSES_PPM16;
break;
default:
c9x.moduleData[0].protocol = PULSES_PPM;
break;
}
c9x.moduleData[0].ppm.frameLength=ppmFrameLength;
c9x.moduleData[0].channelsCount = 8 + 2 * ppmNCH;
c9x.thrTrim = thrTrim;
c9x.trimInc = trimInc-2;
c9x.moduleData[0].ppm.delay = 300 + 50 * ppmDelay;
c9x.customFn[0].func = FuncInstantTrim;
if (trimSw) {
c9x.customFn[0].swtch = er9xToSwitch(trimSw);
}
c9x.beepANACenter = beepANACenter;
c9x.moduleData[0].ppm.pulsePol = pulsePol;
c9x.extendedLimits = extendedLimits;
c9x.swashRingData.elevatorWeight = swashInvertELE ? -100 : 100;
c9x.swashRingData.aileronWeight = swashInvertAIL ? -100 : 100;
c9x.swashRingData.collectiveWeight = swashInvertCOL ? -100 : 100;
c9x.swashRingData.type = swashType;
c9x.swashRingData.collectiveSource = ersky9xToSource_v11(swashCollectiveSource);
c9x.swashRingData.value = swashRingValue;
for (int i=0; i<ERSKY9X_MAX_MIXERS_V11; i++)
c9x.mixData[i] = mixData[i];
for (int i=0; i<ERSKY9X_NUM_CHNOUT_V11; i++)
c9x.limitData[i] = limitData[i];
// expoData
int e = 0;
for (int ch = 0; ch < 4 && e < CPN_MAX_EXPOS; ch++) {
for (int dr = 0, pos = 0; dr < 3 && e < CPN_MAX_EXPOS; dr++, pos++) {
if ((dr == 0 && !expoData[ch].drSw1) || (dr == 1 && !expoData[ch].drSw2))
dr = 2;
if (dr == 2 && !expoData[ch].expo[0][0][0] && !expoData[ch].expo[0][0][1] && !expoData[ch].expo[0][1][0] && !expoData[ch].expo[0][1][1])
break;
if (expoData[ch].drSw1 && !expoData[ch].drSw2) {
c9x.expoData[e].swtch = er9xToSwitch(dr == 0 ? expoData[ch].drSw1 : 0);
pos = dr == 0 ? 1 : 0;
}
else {
c9x.expoData[e].swtch = er9xToSwitch(dr == 0 ? -expoData[ch].drSw1 : (dr == 1 ? -expoData[ch].drSw2 : 0));
}
c9x.expoData[e].chn = ch;
if (expoData[ch].expo[pos][0][0]) {
c9x.expoData[e].curve.type = CurveReference::CURVE_REF_EXPO;
c9x.expoData[e].curve.value = expoData[ch].expo[pos][0][0];
}
c9x.expoData[e].weight = 100 + expoData[ch].expo[pos][1][0];
if (expoData[ch].expo[pos][0][0] == expoData[ch].expo[pos][0][1] && expoData[ch].expo[pos][1][0] == expoData[ch].expo[pos][1][1]) {
c9x.expoData[e++].mode = 3;
}
else {
c9x.expoData[e].mode = 2;
if (e < CPN_MAX_EXPOS - 1) {
c9x.expoData[e + 1].swtch = c9x.expoData[e].swtch;
c9x.expoData[++e].chn = ch;
c9x.expoData[e].mode = 1;
if (expoData[ch].expo[pos][0][1]) {
c9x.expoData[e].curve.type = CurveReference::CURVE_REF_EXPO;
c9x.expoData[e].curve.value = expoData[ch].expo[pos][0][1];
}
c9x.expoData[e++].weight = 100 + expoData[ch].expo[pos][1][1];
}
}
}
}
for (int i=0; i<CPN_MAX_STICKS; i++)
c9x.flightModeData[0].trim[i] = trim[i];
for (int i=0; i<ERSKY9X_MAX_CURVE5; i++) {
c9x.curves[i].count = 5;
for (int j = 0; j < 5; j++) {
c9x.curves[i].points[j].x = -100 + 50 * i;
c9x.curves[i].points[j].y = curves5[i][j];
}
}
for (int i=0; i<ERSKY9X_MAX_CURVE9; i++) {
c9x.curves[ERSKY9X_MAX_CURVE5 + i].count = 9;
for (int j = 0; j < 9; j++) {
c9x.curves[ERSKY9X_MAX_CURVE5 + i].points[j].x = -100 + 25 * i;
c9x.curves[ERSKY9X_MAX_CURVE5 + i].points[j].y = curves9[i][j];
}
}
for (int i=0; i<ERSKY9X_NUM_CSW_V11; i++)
c9x.logicalSw[i] = logicalSw[i];
c9x.frsky = frsky;
c9x.frsky.usrProto=FrSkyUsrProto;
for (int i=0; i<5;i++) {
c9x.flightModeData[0].gvars[i]=gvars[i].gvar;
}
return c9x;
}
@@ -1,437 +0,0 @@
/*
* 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.
*/
#ifndef _ERSKY9XEEPROM_H_
#define _ERSKY9XEEPROM_H_
#include <inttypes.h>
#include "eeprominterface.h"
extern RawSwitch getEr9xTimerMode(int mode);
//eeprom data
#define ERSKY9X_MAX_MIXERS_V10 32
#define ERSKY9X_MAX_MIXERS_V11 48
#define ERSKY9X_MAX_CURVE5 8
#define ERSKY9X_MAX_CURVE9 8
#define ERSKY9X_MAX_CSFUNC 13
#define ERSKY9X_MDVERS10 10
#define ERSKY9X_MDVERS11 11
#define ERSKY9X_GENERAL_OWNER_NAME_LEN 10
#define ERSKY9X_NUM_CHNOUT_V10 16 //number of real outputchannels CH1-CH8
#define ERSKY9X_NUM_CHNOUT_V11 24 //number of real outputchannels CH1-CH8
#define ERSKY9X_NUM_CSW_V10 12 //number of custom switches
#define ERSKY9X_NUM_CSW_V11 24
#define ERSKY9X_NUM_FSW 16
#define ERSKY9X_NUM_VOICE 8
#define ERSKY9X_MAX_GVARS 7
#define CPN_MAX_STICKSnPOTS 7 //number of sticks and pots
PACK(typedef struct t_Ersky9xTrainerMix {
uint8_t srcChn:3; //0-7 = ch1-8
int8_t swtch:5;
int8_t studWeight:6;
uint8_t mode:2; // off, add-mode, subst-mode
operator TrainerMix();
t_Ersky9xTrainerMix();
}) Ersky9xTrainerMix; //
PACK(typedef struct t_Ersky9xTrainerData {
int16_t calib[4];
Ersky9xTrainerMix mix[4];
operator TrainerData();
t_Ersky9xTrainerData();
}) Ersky9xTrainerData;
PACK(typedef struct t_Ersky9xGeneral {
uint8_t myVers;
int16_t calibMid[CPN_MAX_STICKSnPOTS];
int16_t calibSpanNeg[CPN_MAX_STICKSnPOTS];
int16_t calibSpanPos[CPN_MAX_STICKSnPOTS];
uint16_t chkSum;
uint8_t currModel; //0..15
uint8_t contrast;
uint8_t vBatWarn;
int8_t txVoltageCalibration;
int8_t lightSw;
Ersky9xTrainerData trainer;
uint8_t view;
uint8_t disableThrottleWarning:1;
uint8_t disableSwitchWarning:1;
uint8_t disableMemoryWarning:1;
uint8_t beeperVal:3;
uint8_t reserveWarning:1;
uint8_t disableAlarmWarning:1;
uint8_t stickMode;
int8_t inactivityTimer;
uint8_t throttleReversed:1;
uint8_t minuteBeep:1;
uint8_t preBeep:1;
uint8_t alarmsFlash:1;
uint8_t disableSplashScreen:1;
uint8_t disablePotScroll:1;
uint8_t disableBG:1;
uint8_t res1:1;
uint8_t filterInput;
uint8_t lightAutoOff;
uint8_t templateSetup; //RETA order according to chout_ar array
int8_t PPM_Multiplier;
uint8_t FRSkyYellow:4;
uint8_t FRSkyOrange:4;
uint8_t FRSkyRed:4; //mike please check these are correct
uint8_t hideNameOnSplash:1;
uint8_t optrexDisplay:1;
uint8_t spare:2;
uint8_t speakerPitch;
uint8_t hapticStrength;
uint8_t speakerMode;
uint8_t lightOnStickMove;
char ownerName[10];
uint8_t switchWarningStates;
uint8_t volume ;
uint8_t bright ; // backlight
uint8_t stickGain;
uint8_t mAh_alarm ;
uint16_t mAh_used ;
uint16_t run_time ;
int8_t current_calib ;
uint8_t bt_baudrate ;
uint8_t rotaryDivisor ;
operator GeneralSettings();
t_Ersky9xGeneral();
}) Ersky9xGeneral;
PACK(typedef struct t_Ersky9xExpoData {
int8_t expo[3][2][2];
int8_t drSw1;
int8_t drSw2;
t_Ersky9xExpoData() { memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xExpoData)); }
}) Ersky9xExpoData;
PACK(typedef struct t_Ersky9xLimitData {
int8_t min;
int8_t max;
bool revert;
int16_t offset;
operator LimitData();
t_Ersky9xLimitData() ;
}) Ersky9xLimitData;
#define MLTPX_ADD 0
#define MLTPX_MUL 1
#define MLTPX_REP 2
PACK(typedef struct t_Ersky9xMixData_v10 {
uint8_t destCh; // 1..CPN_MAX_CHNOUT
uint8_t srcRaw; //
int8_t weight;
int8_t swtch;
uint8_t curve; //0=symmetrisch 1=no neg 2=no pos
uint8_t delayUp:4;
uint8_t delayDown:4;
uint8_t speedUp:4; // Servogeschwindigkeit aus Tabelle (10ms Cycle)
uint8_t speedDown:4; // 0 nichts
uint8_t carryTrim:1;
uint8_t mltpx:3; // multiplex method 0=+ 1=* 2=replace
uint8_t mixWarn:2; // mixer warning
uint8_t enableFmTrim:1;
uint8_t mixres:1;
int8_t sOffset;
int8_t res;
operator MixData();
t_Ersky9xMixData_v10();
}) Ersky9xMixData_v10;
PACK(typedef struct t_Ersky9xMixData_v11 {
uint8_t destCh; // 1..CPN_MAX_CHNOUT
uint8_t srcRaw; //
int8_t weight;
int8_t swtch;
uint8_t curve; //0=symmetrisch 1=no neg 2=no pos
uint8_t delayUp;
uint8_t delayDown;
uint8_t speedUp; // Servogeschwindigkeit aus Tabelle (10ms Cycle)
uint8_t speedDown; // 0 nichts
uint8_t carryTrim:1;
uint8_t mltpx:3; // multiplex method 0=+ 1=* 2=replace
uint8_t mixWarn:2; // mixer warning
uint8_t enableFmTrim:1;
uint8_t mixres:1;
int8_t sOffset;
uint8_t res[4];
operator MixData();
t_Ersky9xMixData_v11();
}) Ersky9xMixData_v11;
PACK(typedef struct t_Ersky9xLogicalSwitchData_v10 { // Custom Switches data
int8_t v1; //input
int8_t v2; //offset
uint8_t func;
operator LogicalSwitchData();
t_Ersky9xLogicalSwitchData_v10() { memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xLogicalSwitchData_v10)); }
}) Ersky9xLogicalSwitchData_v10;
PACK(typedef struct t_Ersky9xLogicalSwitchData_v11 { // Custom Switches data
int8_t v1; //input
int8_t v2; //offset
uint8_t func;
uint8_t andsw;
uint8_t res ;
operator LogicalSwitchData();
t_Ersky9xLogicalSwitchData_v11() { memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xLogicalSwitchData_v11)); }
}) Ersky9xLogicalSwitchData_v11;
PACK(typedef struct t_Ersky9xSafetySwData_v10 { // Custom Switches data
int8_t swtch;
int8_t val;
t_Ersky9xSafetySwData_v10();
}) Ersky9xSafetySwData_v10;
PACK(typedef struct t_Ersky9xSafetySwData_v11 { // Custom Switches data
union opt {
struct ss {
int8_t swtch ;
uint8_t mode ;
int8_t val ;
uint8_t res ;
} ss ;
struct vs {
uint8_t vswtch ;
uint8_t vmode ; // ON, OFF, BOTH, 15Secs, 30Secs, 60Secs, Varibl
uint8_t vval;
uint8_t vres ;
} vs ;
} opt ;
t_Ersky9xSafetySwData_v11();
}) Ersky9xSafetySwData_v11;
PACK(typedef struct t_Ersky9xgvar {
int8_t gvar ;
uint8_t gvsource ;
// int8_t gvswitch ;
}) Ersky9xGvarData ;
PACK(typedef struct t_Ersky9xFlightModeData {
int16_t trim[4]; // -500..500 => trim value, 501 => use trim of phase 0, 502, 503, 504 => use trim of phases 1|2|3|4 instead
int8_t swtch; // swtch of phase[0] is not used
char name[6];
uint8_t fadeIn:4;
uint8_t fadeOut:4;
uint16_t spare ; // Future expansion
}) Ersky9xFlightModeData;
PACK(typedef struct t_Ersky9xCustomFunctionData { // Function Switches data
int8_t swtch; //input
uint8_t func;
char param[6];
uint8_t delay;
uint8_t spare;
}) Ersky9xCustomFunctionData;
PACK(typedef struct t_Ersky9xFrSkyChannelData_v10 {
uint8_t ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_value[2]; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_level:4;
uint8_t alarms_greater:2; // 0=LT(<), 1=GT(>)
uint8_t type:2; // future use: 0=volts, ...
operator FrSkyChannelData();
t_Ersky9xFrSkyChannelData_v10();
}) Ersky9xFrSkyChannelData_v10;
PACK(typedef struct t_Ersky9xFrSkyData_v10 {
Ersky9xFrSkyChannelData_v10 channels[2];
operator FrSkyData();
t_Ersky9xFrSkyData_v10();
}) Ersky9xFrSkyData_v10;
PACK(typedef struct t_Ersky9xFrSkyChannelData_v11 {
uint8_t ratio ; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t offset ; //
uint8_t gain ; //
uint8_t alarms_value[2] ; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_level ;
uint8_t alarms_greater ; // 0=LT(<), 1=GT(>)
uint8_t type ; // 0=volts, 1=raw, 2=volts*2, 3=Amps
operator FrSkyChannelData();
t_Ersky9xFrSkyChannelData_v11();
}) Ersky9xFrSkyChannelData_v11;
PACK(typedef struct t_Ersky9xFrSkyData_v11 {
Ersky9xFrSkyChannelData_v11 channels[2];
operator FrSkyData();
t_Ersky9xFrSkyData_v11();
}) Ersky9xFrSkyData_v11;
PACK(typedef struct t_Ersky9xFrSkyalarms_v11 {
uint8_t frskyAlarmType ;
uint8_t frskyAlarmLimit ;
uint8_t frskyAlarmSound ;
}) Ersky9xFrSkyAlData_v11;
PACK(typedef struct t_Ersky9xFrSkyAlarmData_v11 {
Ersky9xFrSkyAlData_v11 alarmData[8] ;
}) Ersky9xFrSkyAlarmData_v11;
PACK(typedef struct t_Ersky9xTimerMode_v10 {
uint8_t tmrModeA:7 ; // timer trigger source -> off, abs, stk, stk%, cx%
uint8_t tmrDir:1 ; // Timer direction
int8_t tmrModeB ; // timer trigger source -> !sw, none, sw, m_sw
uint16_t tmrVal ;
}) Ersky9xTimerMode_v10;
PACK(typedef struct t_Ersky9xTimerMode_v11 {
uint8_t tmrModeA:7 ; // timer trigger source -> off, abs, stk, stk%, cx%
uint8_t tmrDir:1 ; // Timer direction
int8_t tmrModeB ; // timer trigger source -> !sw, none, sw, m_sw
uint16_t tmrVal ;
}) Ersky9xTimerMode_v11;
PACK(typedef struct te_Ersky9xswVoice {
uint8_t swtch ;
uint8_t mode ; // ON, OFF, BOTH, 15Secs, 30Secs, 60Secs
uint8_t val ;
uint8_t vres ;
}) Ersky9xvoiceSwData ;
PACK(typedef struct t_Ersky9xModelData_v10 {
char name[10]; // 10 must be first for eeLoadModelName
uint8_t reserved_spare; //used to be MDVERS - now depreciated
int8_t spare21; // was timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw
// uint8_t sparex:1; // was tmrDir, now use tmrVal>0 => count down
uint8_t sparex:1; // was tmrDir, now use tmrVal>0 => count down
// uint8_t tmrDir:1; // was tmrDir, now use tmrVal>0 => count down
uint8_t traineron:1; // 0 disable trainer, 1 allow trainer
uint8_t spare22:1 ; // Start timer2 using throttle
uint8_t FrSkyUsrProto:1 ; // Protocol in FrSky User Data, 0=FrSky Hub, 1=WS HowHigh
uint8_t FrSkyGpsAlt:1 ; // Use Gps Altitude as main altitude reading
uint8_t FrSkyImperial:1 ; // Convert FrSky values to imperial units
uint8_t FrSkyAltAlarm:2;
uint16_t spare_u16 ; // Was timerval
uint8_t protocol;
int8_t ppmNCH;
int8_t thrTrim:4; // Enable Throttle Trim
int8_t thrExpo:4; // Enable Throttle Expo
int8_t trimInc; // Trim Increments
int8_t ppmDelay;
int8_t trimSw;
uint8_t beepANACenter; // 1<<0->A1.. 1<<6->A7
uint8_t pulsePol:1;
uint8_t extendedLimits:1;
uint8_t swashInvertELE:1;
uint8_t swashInvertAIL:1;
uint8_t swashInvertCOL:1;
uint8_t swashType:3;
uint8_t swashCollectiveSource;
uint8_t swashRingValue;
int8_t ppmFrameLength; //0=22.5 (10msec-30msec) 0.5msec increments
Ersky9xMixData_v10 mixData[ERSKY9X_MAX_MIXERS_V10];
Ersky9xLimitData limitData[ERSKY9X_NUM_CHNOUT_V10];
Ersky9xExpoData expoData[4];
int8_t trim[4];
int8_t curves5[ERSKY9X_MAX_CURVE5][5];
int8_t curves9[ERSKY9X_MAX_CURVE9][9];
Ersky9xLogicalSwitchData_v10 logicalSw[ERSKY9X_NUM_CSW_V10];
uint8_t frSkyVoltThreshold ;
uint8_t res3[2];
Ersky9xSafetySwData_v10 safetySw[ERSKY9X_NUM_CHNOUT_V10];
Ersky9xFrSkyData_v10 frsky;
Ersky9xTimerMode_v10 timer[2] ;
operator ModelData();
t_Ersky9xModelData_v10() { memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xModelData_v10)); }
}) Ersky9xModelData_v10;
PACK(typedef struct t_Ersky9xModelData_v11 {
char name[10]; // 10 must be first for eeLoadModelName
uint8_t modelVoice ; // Index to model name voice (261+value)
uint8_t RxNum ; // was timer trigger source, now RxNum for model match
uint8_t sparex:1; // was tmrDir, now use tmrVal>0 => count down
uint8_t traineron:1; // 0 disable trainer, 1 allow trainer
uint8_t spare22:1 ; // Start timer2 using throttle
uint8_t FrSkyUsrProto:1 ; // Protocol in FrSky User Data, 0=FrSky Hub, 1=WS HowHigh
uint8_t FrSkyGpsAlt:1 ; // Use Gps Altitude as main altitude reading
uint8_t FrSkyImperial:1 ; // Convert FrSky values to imperial units
uint8_t FrSkyAltAlarm:2;
uint8_t version ;
uint8_t protocol;
int8_t ppmNCH;
uint8_t thrTrim:1; // Enable Throttle Trim
uint8_t numBlades:2; // RPM scaling
uint8_t spare10:1;
uint8_t thrExpo:1; // Enable Throttle Expo
uint8_t spare11:3;
int8_t trimInc; // Trim Increments
int8_t ppmDelay;
int8_t trimSw;
uint8_t beepANACenter; // 1<<0->A1.. 1<<6->A7
uint8_t pulsePol:1;
uint8_t extendedLimits:1;
uint8_t swashInvertELE:1;
uint8_t swashInvertAIL:1;
uint8_t swashInvertCOL:1;
uint8_t swashType:3;
uint8_t swashCollectiveSource;
uint8_t swashRingValue;
int8_t ppmFrameLength; //0=22.5 (10msec-30msec) 0.5msec increments
Ersky9xMixData_v11 mixData[ERSKY9X_MAX_MIXERS_V11];
Ersky9xLimitData limitData[ERSKY9X_NUM_CHNOUT_V11];
Ersky9xExpoData expoData[4];
int8_t trim[4];
int8_t curves5[ERSKY9X_MAX_CURVE5][5];
int8_t curves9[ERSKY9X_MAX_CURVE9][9];
int8_t curvexy[18];
Ersky9xLogicalSwitchData_v11 logicalSw[ERSKY9X_NUM_CSW_V11];
uint8_t frSkyVoltThreshold ;
uint8_t bt_telemetry;
uint8_t numVoice; // 0-16, rest are Safety switches
Ersky9xSafetySwData_v11 safetySw[ERSKY9X_NUM_CHNOUT_V11];
Ersky9xvoiceSwData voiceSwitches[ERSKY9X_NUM_VOICE] ;
Ersky9xFrSkyData_v11 frsky;
Ersky9xTimerMode_v11 timer[2] ;
Ersky9xFrSkyAlarmData_v11 frskyAlarms ;
// Add 6 bytes for custom telemetry screen
uint8_t customDisplayIndex[6] ;
Ersky9xCustomFunctionData customFn[ERSKY9X_NUM_FSW];
Ersky9xFlightModeData flightModeData[6] ;
Ersky9xGvarData gvars[ERSKY9X_MAX_GVARS] ;
operator ModelData();
t_Ersky9xModelData_v11() { memset(reinterpret_cast<void *>(this), 0, sizeof(t_Ersky9xModelData_v11)); }
}) Ersky9xModelData_v11;
#endif // _ERSKY9XEEPROM_H_
@@ -1,395 +0,0 @@
/*
* 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 "ersky9xinterface.h"
#include "ersky9xeeprom.h"
#include "rlefile.h"
#include <iostream>
#include <bitset>
#define FILE_TYP_GENERAL 1
#define FILE_TYP_MODEL 2
/// fileId of general file
#define FILE_GENERAL 0
/// convert model number 0..MAX_MODELS-1 int fileId
#define FILE_MODEL(n) (1+n)
#define FILE_TMP (1+16)
Ersky9xInterface::Ersky9xInterface():
EEPROMInterface(Board::BOARD_SKY9X),
efile(new RleFile())
{
}
Ersky9xInterface::~Ersky9xInterface()
{
delete efile;
}
const char * Ersky9xInterface::getName()
{
return "Ersky9x";
}
inline void applyStickModeToModel(Ersky9xModelData_v10 & model, unsigned int mode)
{
for (int i=0; i<2; i++) {
int stick = applyStickMode(i+1, mode) - 1;
{
int tmp = model.trim[i];
model.trim[i] = model.trim[stick];
model.trim[stick] = tmp;
}
{
Ersky9xExpoData tmp = model.expoData[i];
model.expoData[i] = model.expoData[stick];
model.expoData[stick] = tmp;
}
}
for (int i=0; i<ERSKY9X_MAX_MIXERS_V10; i++)
model.mixData[i].srcRaw = applyStickMode(model.mixData[i].srcRaw, mode);
for (int i=0; i<ERSKY9X_NUM_CSW_V10; i++) {
switch (LogicalSwitchData(model.logicalSw[i].func).getFunctionFamily()) {
case LS_FAMILY_VCOMP:
model.logicalSw[i].v2 = applyStickMode(model.logicalSw[i].v2, mode);
// no break
case LS_FAMILY_VOFS:
model.logicalSw[i].v1 = applyStickMode(model.logicalSw[i].v1, mode);
break;
default:
break;
}
}
model.swashCollectiveSource = applyStickMode(model.swashCollectiveSource, mode);
}
inline void applyStickModeToModel(Ersky9xModelData_v11 & model, unsigned int mode)
{
for (int i=0; i<2; i++) {
int stick = applyStickMode(i+1, mode) - 1;
{
int tmp = model.trim[i];
model.trim[i] = model.trim[stick];
model.trim[stick] = tmp;
}
{
Ersky9xExpoData tmp = model.expoData[i];
model.expoData[i] = model.expoData[stick];
model.expoData[stick] = tmp;
}
}
for (int i=0; i<ERSKY9X_MAX_MIXERS_V11; i++)
model.mixData[i].srcRaw = applyStickMode(model.mixData[i].srcRaw, mode);
for (int i=0; i<ERSKY9X_NUM_CSW_V11; i++) {
switch (LogicalSwitchData(model.logicalSw[i].func).getFunctionFamily()) {
case LS_FAMILY_VCOMP:
model.logicalSw[i].v2 = applyStickMode(model.logicalSw[i].v2, mode);
// no break
case LS_FAMILY_VOFS:
model.logicalSw[i].v1 = applyStickMode(model.logicalSw[i].v1, mode);
break;
default:
break;
}
}
model.swashCollectiveSource = applyStickMode(model.swashCollectiveSource, mode);
}
#if 0
unsigned long Ersky9xInterface::loadxml(RadioData &radioData, QDomDocument &doc)
{
QDebug dbg = qDebug();
dbg.setAutoInsertSpaces(false);
dbg << "trying ersky9x xml import... ";
std::bitset<NUM_ERRORS> errors;
Ersky9xGeneral ersky9xGeneral;
memset(&ersky9xGeneral,0,sizeof(ersky9xGeneral));
if(!loadRadioSettingsDataXML(&doc, &ersky9xGeneral)) {
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
else {
radioData.generalSettings=ersky9xGeneral;
dbg << "version " << (unsigned int)ersky9xGeneral.myVers << " ";
}
for(int i=0; i<getCapability(Models); i++) {
if (ersky9xGeneral.myVers == 10) {
if (!loadModelDataXML<Ersky9xModelData_v10>(&doc, &radioData.models[i], i, radioData.generalSettings.stickMode+1)) {
dbg << "ko";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
}
else {
if (!loadModelDataXML<Ersky9xModelData_v11>(&doc, &radioData.models[i], i, radioData.generalSettings.stickMode+1)) {
dbg << "ko";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
}
}
dbg << "ok";
errors.set(ALL_OK);
return errors.to_ulong();
}
#endif
unsigned long Ersky9xInterface::load(RadioData &radioData, const uint8_t *eeprom, int size)
{
QDebug dbg = qDebug();
dbg.setAutoInsertSpaces(false);
dbg << "trying ersky9x import... ";
std::bitset<NUM_ERRORS> errors;
if (size != Boards::getEEpromSize(Board::BOARD_SKY9X)) {
dbg << "wrong size";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
if (!efile->EeFsOpen((uint8_t *)eeprom, size, Board::BOARD_SKY9X)) {
dbg << "wrong file system";
errors.set(WRONG_FILE_SYSTEM);
return errors.to_ulong();
}
efile->openRd(FILE_GENERAL);
Ersky9xGeneral ersky9xGeneral;
if (efile->readRlc2((uint8_t*)&ersky9xGeneral, 1) != 1) {
dbg << "no";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
dbg << "version " << (unsigned int)ersky9xGeneral.myVers << " ";
switch(ersky9xGeneral.myVers) {
case 10:
break;
case 11:
break;
default:
dbg << "not ersky9x";
errors.set(NOT_ERSKY9X);
return errors.to_ulong();
}
efile->openRd(FILE_GENERAL);
if (!efile->readRlc2((uint8_t*)&ersky9xGeneral, sizeof(Ersky9xGeneral))) {
dbg << "ko";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
radioData.generalSettings = ersky9xGeneral;
for (int i=0; i<getCapability(Models); i++) {
uint8_t buffer[4096];
uint size;
memset(buffer,0,sizeof(buffer));
efile->openRd(FILE_MODEL(i));
// if (!efile->readRlc2((uint8_t*)&ersky9xModel, sizeof(Ersky9xModelData))) {
size = efile->readRlc2(buffer, 4096);
if (!size) {
radioData.models[i].clear();
}
else {
if (size<720) {
Ersky9xModelData_v10 ersky9xModel;
memcpy(&ersky9xModel, buffer, sizeof(ersky9xModel));
applyStickModeToModel(ersky9xModel, radioData.generalSettings.stickMode+1);
radioData.models[i] = ersky9xModel;
} else {
Ersky9xModelData_v11 ersky9xModel;
memcpy(&ersky9xModel, buffer, sizeof(ersky9xModel));
applyStickModeToModel(ersky9xModel, radioData.generalSettings.stickMode+1);
radioData.models[i] = ersky9xModel;
}
}
}
dbg << "ok";
errors.set(ALL_OK);
return errors.to_ulong();
}
unsigned long Ersky9xInterface::loadBackup(RadioData &radioData, const uint8_t *eeprom, int esize, int index)
{
std::bitset<NUM_ERRORS> errors;
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
int Ersky9xInterface::getSize(const ModelData & model)
{
return 0;
}
int Ersky9xInterface::getSize(const GeneralSettings & settings)
{
return 0;
}
bool Ersky9xInterface::isAvailable(PulsesProtocol prot, int port)
{
switch (prot) {
case PULSES_PPM:
case PULSES_DSM2:
case PULSES_PXX_DJT:
case PULSES_PPM16:
return 1;
default:
return 0;
}
}
void Ersky9xInterface::appendTextElement(QDomDocument * qdoc, QDomElement * pe, QString name, QString value)
{
QDomElement e = qdoc->createElement(name);
QDomText t = qdoc->createTextNode(name);
t.setNodeValue(value);
e.appendChild(t);
pe->appendChild(e);
}
void Ersky9xInterface::appendNumberElement(QDomDocument * qdoc, QDomElement * pe,QString name, int value, bool forceZeroWrite)
{
if(value || forceZeroWrite) {
QDomElement e = qdoc->createElement(name);
QDomText t = qdoc->createTextNode(name);
t.setNodeValue(QString("%1").arg(value));
e.appendChild(t);
pe->appendChild(e);
}
}
void Ersky9xInterface::appendCDATAElement(QDomDocument * qdoc, QDomElement * pe,QString name, const char * data, int size)
{
QDomElement e = qdoc->createElement(name);
QDomCDATASection t = qdoc->createCDATASection(name);
t.setData(QByteArray(data, size).toBase64());
e.appendChild(t);
pe->appendChild(e);
}
QDomElement Ersky9xInterface::getGeneralDataXML(QDomDocument * qdoc, Ersky9xGeneral * tgen)
{
QDomElement gd = qdoc->createElement("GENERAL_DATA");
appendNumberElement(qdoc, &gd, "Version", tgen->myVers, true); // have to write value here
appendTextElement(qdoc, &gd, "Owner", QString::fromLatin1(tgen->ownerName,sizeof(tgen->ownerName)).trimmed());
appendCDATAElement(qdoc, &gd, "Data", (const char *)tgen,sizeof(Ersky9xGeneral));
return gd;
}
QDomElement Ersky9xInterface::getModelDataXML(QDomDocument * qdoc, Ersky9xModelData_v11 * tmod, int modelNum, int mdver)
{
QDomElement md = qdoc->createElement("MODEL_DATA");
md.setAttribute("number", modelNum);
appendNumberElement(qdoc, &md, "Version", mdver, true); // have to write value here
appendTextElement(qdoc, &md, "Name", QString::fromLatin1(tmod->name,sizeof(tmod->name)).trimmed());
appendCDATAElement(qdoc, &md, "Data", (const char *)tmod,sizeof(Ersky9xModelData_v11));
return md;
}
bool Ersky9xInterface::loadRadioSettingsDataXML(QDomDocument * qdoc, Ersky9xGeneral * tgen)
{
// look for "GENERAL_DATA" tag
QDomElement gde = qdoc->elementsByTagName("GENERAL_DATA").at(0).toElement();
if(gde.isNull()) // couldn't find
return false;
// load cdata into tgen
QDomNode n = gde.elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
QString ds = n.toCDATASection().data();
QByteArray ba = QByteArray::fromBase64(ds.toLatin1());
const char * data = ba.data();
memcpy(tgen, data, std::min((unsigned int)ba.size(), (unsigned int)sizeof(Ersky9xGeneral)));
break;
}
n = n.nextSibling();
}
// check version?
return true;
}
template <class T>
bool Ersky9xInterface::loadModelDataXML(QDomDocument * qdoc, ModelData *model, int modelNum, int stickMode)
{
T ersky9xModel;
memset(&ersky9xModel, 0, sizeof(ersky9xModel));
// look for MODEL_DATA with modelNum attribute.
//if modelNum = -1 then just pick the first one
QDomNodeList ndl = qdoc->elementsByTagName("MODEL_DATA");
// cycle through nodes to find correct model number
QDomNode k = ndl.at(0);
if (modelNum>=0) {
while(!k.isNull()) {
int a = k.toElement().attribute("number").toInt();
if(a==modelNum)
break;
k = k.nextSibling();
}
}
if (k.isNull()) // couldn't find
return false;
// load cdata into tgen
QDomNode n = k.toElement().elementsByTagName("Data").at(0).firstChild();// get all children in Data
while (!n.isNull()) {
if (n.isCDATASection()) {
QString ds = n.toCDATASection().data();
QByteArray ba = QByteArray::fromBase64(ds.toLatin1());
const char * data = ba.data();
memcpy(&ersky9xModel, data, std::min(ba.size(), (int)sizeof(ersky9xModel)));
break;
}
n = n.nextSibling();
}
applyStickModeToModel(ersky9xModel, stickMode);
*model = ersky9xModel;
return true;
}
int Ersky9xInterface::getCapability(Capability capability)
{
switch (capability) {
case Models:
return 20;
default:
return 0;
}
}
QString Ersky9xInterface::getCapabilityStr(Capability capability)
{
return QString();
}
@@ -1,83 +0,0 @@
/*
* 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.
*/
#ifndef _ERSKY9XINTERFACE_H_
#define _ERSKY9XINTERFACE_H_
#include <QtXml>
#include "eeprominterface.h"
#include "ersky9xeeprom.h"
class RleFile;
class Ersky9xInterface : public EEPROMInterface
{
public:
Ersky9xInterface();
virtual ~Ersky9xInterface();
virtual const char * getName();
virtual unsigned long load(RadioData &, const uint8_t * eeprom, int size);
virtual unsigned long loadBackup(RadioData &, const uint8_t * eeprom, int esize, int index);
virtual int save(uint8_t * eeprom, const RadioData & radioData, uint8_t version=0, uint32_t variant=0)
{
return 0;
}
virtual int getSize(const ModelData &);
virtual int getSize(const GeneralSettings & settings);
virtual bool isAvailable(PulsesProtocol proto, int port=0);
virtual int getCapability(Capability capability);
virtual QString getCapabilityStr(Capability);
protected:
RleFile * efile;
private:
void appendTextElement(QDomDocument * qdoc, QDomElement * pe, QString name, QString value);
void appendNumberElement(QDomDocument * qdoc, QDomElement * pe,QString name, int value, bool forceZeroWrite = false);
void appendCDATAElement(QDomDocument * qdoc, QDomElement * pe,QString name, const char * data, int size);
QDomElement getGeneralDataXML(QDomDocument * qdoc, Ersky9xGeneral * tgen); //parse out data to XML format
QDomElement getModelDataXML(QDomDocument * qdoc, Ersky9xModelData_v11 * tmod, int modelNum, int mdver); //parse out data to XML format
bool loadRadioSettingsDataXML(QDomDocument * qdoc, Ersky9xGeneral * tgen); // get data from XML
template <class T>
bool loadModelDataXML(QDomDocument * qdoc, ModelData * model, int modelNum, int stickMode); // get data from XML
};
#endif // _ERSKY9XINTERFACE_H_
File diff suppressed because it is too large Load Diff
@@ -1,163 +0,0 @@
/*
* 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.
*/
#ifndef _OPENTXEEPROM_H_
#define _OPENTXEEPROM_H_
#include <inttypes.h>
#include "eeprominterface.h"
#include "eepromimportexport.h"
#include <qbytearray.h>
#define GVARS_VARIANT 0x0001
#define FRSKY_VARIANT 0x0002
#define M128_VARIANT 0x8000
#define TARANIS_X9E_VARIANT 0x8000
#define TARANIS_X7_VARIANT 0x4000
#define TARANIS_XLITE_VARIANT 0x2000
#define TARANIS_XLITES_VARIANT 0x1000
#define TARANIS_X9LITE_VARIANT 0x0800
#define TARANIS_X9LITES_VARIANT 0x0801
#define JUMPER_T12_VARIANT 0x4001
#define RADIOMASTER_TX12_VARIANT 0x4002
#define JUMPER_TLITE_VARIANT 0x4003
#define RADIOMASTER_T8_VARIANT 0x4004
#define JUMPER_TPRO_VARIANT 0x4005
#define RADIOMASTER_ZORRO_VARIANT 0x4006
#define BETAFPV_LR3PRO_VARIANT 0x4007
#define RADIOMASTER_TX12_MK2_VARIANT 0x4008
#define IFLIGHT_COMMANDO8_VARIANT 0x4009
class OpenTxGeneralData: public TransformedField {
public:
OpenTxGeneralData(GeneralSettings & generalData, Board::Type board, unsigned int version, unsigned int variant=0);
virtual const QString & getName() { return internalField.getName(); }
QStringList errors()
{
return _errors;
}
protected:
virtual void beforeExport();
virtual void afterImport();
virtual void setError(const QString & error)
{
qWarning() << error;
_errors << error;
}
StructField internalField;
GeneralSettings & generalData;
Board::Type board;
unsigned int version;
int inputsCount;
unsigned int chkSum;
QStringList _errors;
};
class ProtocolsConversionTable: public ConversionTable
{
public:
ProtocolsConversionTable(Board::Type board, int version)
{
int val = 0;
addConversion(PULSES_OFF, val++);
addConversion(PULSES_PPM, val++);
addConversion(PULSES_PXX_XJT_X16, val);
addConversion(PULSES_PXX_XJT_D8, val);
addConversion(PULSES_PXX_XJT_LR12, val++);
if (version >= 219) {
addConversion(PULSES_ACCESS_ISRM, val);
addConversion(PULSES_ACCST_ISRM_D16, val++);
}
addConversion(PULSES_LP45, val);
addConversion(PULSES_DSM2, val);
addConversion(PULSES_DSMX, val++);
addConversion(PULSES_CROSSFIRE, val++);
addConversion(PULSES_MULTIMODULE, val++);
addConversion(PULSES_PXX_R9M, val++);
addConversion(PULSES_ACCESS_R9M, val++);
addConversion(PULSES_PXX_R9M_LITE, val++);
addConversion(PULSES_ACCESS_R9M_LITE, val++);
addConversion(PULSES_GHOST, val++);
addConversion(PULSES_ACCESS_R9M_LITE_PRO, val++);
addConversion(PULSES_SBUS, val++);
addConversion(PULSES_XJT_LITE_X16, val);
addConversion(PULSES_XJT_LITE_D8, val);
addConversion(PULSES_XJT_LITE_LR12, val++);
}
};
class ChannelsConversionTable: public ConversionTable
{
public:
ChannelsConversionTable()
{
for (int i=4; i<=16; i+= 2)
addConversion(i, -4+i/2);
}
};
class OpenTxModelData: public TransformedField {
public:
OpenTxModelData(ModelData & modelData, Board::Type board, unsigned int version, unsigned int variant);
virtual const QString & getName() { return internalField.getName(); }
QStringList errors()
{
return _errors;
}
protected:
virtual void beforeExport();
virtual void afterImport();
virtual void setError(const QString & error)
{
qWarning() << error;
_errors << error;
}
StructField internalField;
ModelData & modelData;
Board::Type board;
unsigned int version;
unsigned int variant;
char name[256];
int subprotocols[CPN_MAX_MODULES+1/*trainer*/];
ChannelsConversionTable channelsConversionTable;
QStringList _errors;
};
void OpenTxEepromCleanup(void);
#endif // _OPENTXEEPROM_H_
@@ -20,8 +20,6 @@
*/
#include "opentxinterface.h"
#include "opentxeeprom.h"
#include "rlefile.h"
#include "appdata.h"
#include "constants.h"
@@ -42,463 +40,6 @@
using namespace Board;
QList<OpenTxEepromInterface *> opentxEEpromInterfaces;
OpenTxEepromInterface::OpenTxEepromInterface(OpenTxFirmware * firmware):
EEPROMInterface(firmware->getBoard()),
efile(new RleFile()),
firmware(firmware)
{
}
OpenTxEepromInterface::~OpenTxEepromInterface()
{
delete efile;
}
const char * OpenTxEepromInterface::getName()
{
switch (board) {
case BOARD_JUMPER_T12:
return "EdgeTX for Jumper T12";
case BOARD_JUMPER_TLITE:
return "EdgeTX for Jumper T-Lite";
case BOARD_JUMPER_TLITE_F4:
return "EdgeTX for Jumper T-Lite (F4 MCU)";
case BOARD_JUMPER_TPRO:
return "EdgeTX for Jumper T-Pro";
case BOARD_JUMPER_TPROV2:
return "EdgeTX for Jumper T-Pro V2";
case BOARD_JUMPER_TPROS:
return "EdgeTX for Jumper T-Pro S";
case BOARD_JUMPER_BUMBLEBEE:
return "EdgeTX for Jumper Bumblebee";
case BOARD_JUMPER_T12MAX:
return "EdgeTX for Jumper T12 MAX";
case BOARD_JUMPER_T14:
return "EdgeTX for Jumper T14";
case BOARD_JUMPER_T15:
return "EdgeTX for Jumper T15";
case BOARD_JUMPER_T16:
return "EdgeTX for Jumper T16";
case BOARD_JUMPER_T18:
return "EdgeTX for Jumper T18";
case BOARD_JUMPER_T20:
return "EdgeTX for Jumper T20";
case BOARD_JUMPER_T20V2:
return "EdgeTX for Jumper T20 V2";
case BOARD_RADIOMASTER_TX16S:
return "EdgeTX for Radiomaster TX16S";
case BOARD_RADIOMASTER_TX12:
return "EdgeTX for Radiomaster TX12";
case BOARD_RADIOMASTER_TX12_MK2:
return "EdgeTX for Radiomaster TX12 Mark II";
case BOARD_RADIOMASTER_ZORRO:
return "EdgeTX for Radiomaster Zorro";
case BOARD_RADIOMASTER_BOXER:
return "EdgeTX for Radiomaster Boxer";
case BOARD_RADIOMASTER_POCKET:
return "EdgeTX for Radiomaster Pocket";
case BOARD_RADIOMASTER_T8:
return "EdgeTX for Radiomaster T8";
case BOARD_TARANIS_X9D:
return "EdgeTX for FrSky Taranis X9D";
case BOARD_TARANIS_X9DP:
return "EdgeTX for FrSky Taranis X9D+";
case BOARD_TARANIS_X9DP_2019:
return "EdgeTX for FrSky Taranis X9D+ 2019";
case BOARD_TARANIS_X9E:
return "EdgeTX for FrSky Taranis X9E";
case BOARD_TARANIS_X7:
return "EdgeTX for FrSky Taranis X7";
case BOARD_TARANIS_X7_ACCESS:
return "EdgeTX for FrSky Taranis X7 Access";
case BOARD_TARANIS_X9LITES:
return "EdgeTX for FrSky Taranis X9-Lite S";
case BOARD_TARANIS_X9LITE:
return "EdgeTX for FrSky Taranis X9-Lite";
case BOARD_TARANIS_XLITE:
return "EdgeTX for FrSky Taranis X-Lite";
case BOARD_TARANIS_XLITES:
return "EdgeTX for FrSky Taranis X-Lite S/Pro";
case BOARD_SKY9X:
return "EdgeTX for Sky9x board / 9X";
case BOARD_9XRPRO:
return "EdgeTX for 9XR-PRO";
case BOARD_AR9X:
return "EdgeTX for ar9x board / 9X";
case BOARD_HORUS_X12S:
return "EdgeTX for FrSky Horus";
case BOARD_X10:
return "EdgeTX for FrSky X10";
case BOARD_X10_EXPRESS:
return "EdgeTX for FrSky X10 Express";
case BOARD_FLYSKY_NV14:
return "EdgeTX for FlySky NV14";
case BOARD_FLYSKY_EL18:
return "EdgeTX for FlySky EL18";
case BOARD_FLYSKY_PL18:
return "EdgeTX for FlySky PL18";
case BOARD_BETAFPV_LR3PRO:
return "EdgeTx for BETAFPV LR3PRO";
case BOARD_IFLIGHT_COMMANDO8:
return "EdgeTX for iFlight Commando 8";
case BOARD_FATFISH_F16:
return "EdgeTX for Fatfish F16";
case BOARD_HELLORADIOSKY_V16:
return "EdgeTX for HelloRadioSky V16";
default:
return "Board is unknown to EdgeTX";
}
}
bool OpenTxEepromInterface::loadRadioSettingsFromRLE(GeneralSettings & settings, RleFile * rleFile, uint8_t version)
{
// Companion loads the current profile firmware and associated board hardware definition ONLY!!!!
// For each OpenTxEepromInterface create a firmware variant to force the associated base firmware board hardware definition to be loaded.
// Note: The base firmware instance does not do load its hardware definition to save unnecessary processing.
// To create the variant take the base id and append the language from the current radio profile.
Firmware *fw = Firmware::getFirmwareForId(firmware->getId().append("-%1").arg(g.currentProfile().fwType().split('-').last()));
if (!fw) {
qWarning() << " error unable to create firmware variant";
return false;
}
QByteArray data(sizeof(settings), 0); // GeneralSettings should be always bigger than the EEPROM struct
OpenTxGeneralData open9xSettings(settings, board, version);
efile->openRd(FILE_GENERAL);
int size = rleFile->readRlc2((uint8_t *)data.data(), data.size());
if (size) {
open9xSettings.Import(data);
return checkVariant(settings.version, settings.variant);
}
else {
qWarning() << " error when loading general settings";
return false;
}
}
bool OpenTxEepromInterface::loadModelFromRLE(ModelData & model, RleFile * rleFile, unsigned int index, uint8_t version, uint32_t variant)
{
QByteArray data(sizeof(model), 0); // ModelData should be always bigger than the EEPROM struct
rleFile->openRd(FILE_MODEL(index));
int size = rleFile->readRlc2((uint8_t *)data.data(), data.size());
if (size) {
if (loadFromByteArray<ModelData, OpenTxModelData>(model, data, version, variant)) {
model.used = true;
}
}
else {
model.clear();
}
return true;
}
template <class T, class M>
bool OpenTxEepromInterface::saveToByteArray(const T & src, QByteArray & data, uint8_t version)
{
if (version == 0) {
version = getLastDataVersion(getBoard());
}
QByteArray raw;
T srcCopy(src); // work on a copy of radio data, because Export() will modify it!
M manager(srcCopy, board, version, 0);
// manager.dump();
manager.Export(raw);
data.resize(8);
*((uint32_t*)&data.data()[0]) = Boards::getFourCC(board);
data[4] = version;
data[5] = 'M';
*((uint16_t*)&data.data()[6]) = raw.size();
data.append(raw);
return true;
}
template <class T, class M>
bool OpenTxEepromInterface::loadFromByteArray(T & dest, const QByteArray & data, uint8_t version, uint32_t variant)
{
// Companion loads the current profile firmware and associated board hardware definition ONLY!!!!
// For each OpenTxEepromInterface create a firmware variant to force the associated base firmware board hardware definition to be loaded.
// Note: The base firmware instance does not do load its hardware definition to save unnecessary processing.
// To create the variant take the base id and append the language from the current radio profile.
Firmware *fw = Firmware::getFirmwareForId(firmware->getId().append("-%1").arg(g.currentProfile().fwType().split('-').last()));
if (!fw) {
qWarning() << " error unable to create firmware variant";
return false;
}
M manager(dest, board, version, variant);
if (manager.Import(data) != 0) {
return false;
}
// manager.dump(); // Dumps the structure so that it's easy to check with firmware datastructs.h
return true;
}
template <class T, class M>
bool OpenTxEepromInterface::loadFromByteArray(T & dest, const QByteArray & data)
{
uint32_t fourcc = *((uint32_t*)&data.data()[0]);
if (Boards::getFourCC(board) != fourcc) {
if (IS_FAMILY_HORUS_OR_T16(board) && fourcc == 0x3178396F) {
qDebug() << QString("%1: Deprecated fourcc used %2 vs %3").arg(getName()).arg(fourcc).arg(Boards::getFourCC(board));
}
else {
qDebug() << QString("%1: Wrong fourcc %2 vs %3").arg(getName()).arg(fourcc).arg(Boards::getFourCC(board));
return false;
}
}
if (Boards::getFourCC(board) == Boards::getFourCC(getCurrentBoard()) &&
board != getCurrentFirmware()->getBoard()) {
qDebug() << QString("%1: Fourcc not unique and does not match profile board: %2").arg(getName()).arg(Boards::getBoardName(getCurrentBoard()));
return false;
}
qDebug() << QString("%1: OK").arg(getName());
uint8_t version = data[4];
QByteArray raw = data.right(data.size() - 8);
return loadFromByteArray<T, M>(dest, raw, version);
}
unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t * eeprom, int size)
{
QDebug dbg = qDebug();
dbg.setAutoInsertSpaces(false);
dbg << "trying " << getName() << " import...";
std::bitset<NUM_ERRORS> errors;
if (size != Boards::getEEpromSize(board)) {
if (size == 4096) {
int notnull = false;
for (int i = 2048; i < 4096; i++) {
if (eeprom[i] != 255) {
notnull = true;
}
}
if (notnull) {
dbg << " wrong size (" << size << ")";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
else {
errors.set(HAS_WARNINGS);
errors.set(WARNING_WRONG_FIRMWARE);
size = 2048;
}
}
else {
dbg << " wrong size (" << size << "/" << Boards::getEEpromSize(board) << ")";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
}
if (!efile->EeFsOpen((uint8_t *) eeprom, size, board)) {
dbg << " wrong file system";
errors.set(WRONG_FILE_SYSTEM);
return errors.to_ulong();
}
efile->openRd(FILE_GENERAL);
uint8_t version;
if (efile->readRlc2(&version, 1) != 1) {
dbg << " no";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
dbg << " version " << (unsigned int) version;
EepromLoadErrors version_error = checkVersion(version);
if (version_error == OLD_VERSION) {
errors.set(version_error);
errors.set(HAS_WARNINGS);
}
else if (version_error == NOT_OPENTX) {
dbg << " not OpenTX";
errors.set(version_error);
return errors.to_ulong();
}
if (!loadRadioSettingsFromRLE(radioData.generalSettings, efile, version)) {
dbg << " ko";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
dbg << " variant " << radioData.generalSettings.variant;
if (getCurrentFirmware()->getCapability(Models) == 0) {
radioData.models.resize(firmware->getCapability(Models));
}
for (int i = 0; i < firmware->getCapability(Models); i++) {
if (i < (int)radioData.models.size() && !loadModelFromRLE(radioData.models[i], efile, i, version, radioData.generalSettings.variant)) {
dbg << " ko";
errors.set(UNKNOWN_ERROR);
if (getCurrentFirmware()->getCapability(Models) == 0) {
radioData.models.resize(i);
}
return errors.to_ulong();
}
}
dbg << " ok";
errors.set(ALL_OK);
return errors.to_ulong();
}
uint8_t OpenTxEepromInterface::getLastDataVersion(Board::Type board)
{
return 220; // Note: this is the last bin format before the switch to YAML introduced in Companion version 2.6
}
void OpenTxEepromInterface::showErrors(const QString & title, const QStringList & errors)
{
QString msg;
if (errors.empty()) {
msg = tr("Unknown error");
}
else {
int noErrorsToDisplay = std::min((int)errors.size(), 10);
for (int n = 0; n < noErrorsToDisplay; n++) {
msg += " -" + errors.at(n) + "\n";
}
if (noErrorsToDisplay < errors.size()) {
msg += tr(" ... plus %1 errors").arg(errors.size() - noErrorsToDisplay) + "\n" + msg;
}
}
QMessageBox::warning(NULL,
CPN_STR_TTL_ERROR,
title + "\n" + msg);
}
int OpenTxEepromInterface::save(uint8_t * eeprom, const RadioData & radioData, uint8_t version, uint32_t variant)
{
// TODO QMessageBox::warning should not be called here
if (version == 0) {
version = getLastDataVersion(board);
}
int size = Boards::getEEpromSize(board);
efile->EeFsCreate(eeprom, size, board, version);
if (IS_TARANIS_X9E(board)) {
variant |= TARANIS_X9E_VARIANT;
}
else if (IS_TARANIS_X9LITES(board)) {
variant |= TARANIS_X9LITES_VARIANT;
}
else if (IS_TARANIS_X9LITE(board)) {
variant |= TARANIS_X9LITE_VARIANT;
}
else if (IS_TARANIS_X7_ACCESS(board)) {
variant |= TARANIS_X7_VARIANT;
}
else if (IS_TARANIS_X7(board)) {
variant |= TARANIS_X7_VARIANT;
}
else if (IS_TARANIS_XLITES(board)) {
variant |= TARANIS_XLITES_VARIANT;
}
else if (IS_TARANIS_XLITE(board)) {
variant |= TARANIS_XLITE_VARIANT;
}
else if (IS_JUMPER_T12(board)) {
variant |= JUMPER_T12_VARIANT;
}
else if (IS_JUMPER_TLITE(board)) {
variant |= JUMPER_TLITE_VARIANT;
}
else if (IS_JUMPER_TPRO(board)) {
variant |= JUMPER_TPRO_VARIANT;
}
else if (IS_RADIOMASTER_TX12(board)) {
variant |= RADIOMASTER_TX12_VARIANT;
}
else if (IS_RADIOMASTER_TX12_MK2(board)) {
variant |= RADIOMASTER_TX12_MK2_VARIANT;
}
else if (IS_RADIOMASTER_ZORRO(board)) {
variant |= RADIOMASTER_ZORRO_VARIANT;
}
else if (IS_RADIOMASTER_T8(board)) {
variant |= RADIOMASTER_T8_VARIANT;
}
OpenTxGeneralData generator((GeneralSettings &)radioData.generalSettings, board, version, variant);
// generator.dump();
QByteArray data;
generator.Export(data);
int sz = efile->writeRlc2(FILE_GENERAL, FILE_TYP_GENERAL, (const uint8_t *)data.constData(), data.size());
if (sz == 0 || generator.errors().count() > 0) {
showErrors(tr("Cannot write radio settings"), generator.errors());
return 0;
}
for (int i = 0; i < getCurrentFirmware()->getCapability(Models); i++) {
if (i < (int)radioData.models.size() && !radioData.models[i].isEmpty()) {
OpenTxModelData generator((ModelData &)radioData.models[i], board, version, variant);
// generator.dump();
QByteArray data;
generator.Export(data);
int sz = efile->writeRlc2(FILE_MODEL(i), FILE_TYP_MODEL, (const uint8_t *)data.constData(), data.size());
if (sz == 0 || generator.errors().count() > 0) {
showErrors(tr("Cannot write model %1").arg(radioData.models[i].name), generator.errors());
return 0;
}
}
}
return size;
}
int OpenTxEepromInterface::getSize(const ModelData &model)
{
if (IS_SKY9X(board))
return 0;
if (model.isEmpty())
return 0;
QByteArray tmp(Boards::getEEpromSize(Board::BOARD_UNKNOWN), 0);
efile->EeFsCreate((uint8_t *) tmp.data(), Boards::getEEpromSize(Board::BOARD_UNKNOWN), board, 255/*version max*/);
ModelData srcCopy(model); // work on a copy of model data, because Export() may modify it!
OpenTxModelData open9xModel(srcCopy, board, 255/*version max*/, getCurrentFirmware()->getVariantNumber());
QByteArray eeprom;
open9xModel.Export(eeprom);
int sz = efile->writeRlc2(0, FILE_TYP_MODEL, (const uint8_t *) eeprom.constData(), eeprom.size());
if (sz != eeprom.size()) {
return -1;
}
return efile->size(0);
}
int OpenTxEepromInterface::getSize(const GeneralSettings &settings)
{
if (IS_SKY9X(board))
return 0;
QByteArray tmp(Boards::getEEpromSize(Board::BOARD_UNKNOWN), 0);
efile->EeFsCreate((uint8_t *) tmp.data(), Boards::getEEpromSize(Board::BOARD_UNKNOWN), board, 255);
GeneralSettings srcCopy(settings); // work on a copy of settings data, because Export() may modify it!
OpenTxGeneralData open9xGeneral(srcCopy, board, 255, getCurrentFirmware()->getVariantNumber());
// open9xGeneral.dump();
QByteArray eeprom;
open9xGeneral.Export(eeprom);
int sz = efile->writeRlc2(0, FILE_TYP_GENERAL, (const uint8_t *) eeprom.constData(), eeprom.size());
if (sz != eeprom.size()) {
return -1;
}
return efile->size(0);
}
Firmware * OpenTxFirmware::getFirmwareVariant(const QString &id)
{
if (id == getId()) {
@@ -736,33 +277,6 @@ int OpenTxFirmware::getCapability(::Capability capability)
return true;
case HasMahPersistent:
return true;
case SimulatorVariant:
if (IS_TARANIS_X9E(board))
return TARANIS_X9E_VARIANT;
else if (IS_TARANIS_X9LITES(board))
return TARANIS_X9LITES_VARIANT;
else if (IS_TARANIS_X9LITE(board))
return TARANIS_X9LITE_VARIANT;
else if (IS_TARANIS_X7(board))
return TARANIS_X7_VARIANT;
else if (IS_TARANIS_XLITES(board))
return TARANIS_XLITES_VARIANT;
else if (IS_TARANIS_XLITE(board))
return TARANIS_XLITE_VARIANT;
else if (IS_BETAFPV_LR3PRO(board))
return BETAFPV_LR3PRO_VARIANT;
else if (IS_IFLIGHT_COMMANDO8(board))
return IFLIGHT_COMMANDO8_VARIANT;
else if (IS_JUMPER_T12(board))
return JUMPER_T12_VARIANT;
else if (IS_JUMPER_TLITE(board))
return JUMPER_TLITE_VARIANT;
else if (IS_RADIOMASTER_TX12(board))
return RADIOMASTER_TX12_VARIANT;
else if (IS_RADIOMASTER_T8(board))
return RADIOMASTER_T8_VARIANT;
else
return 0;
case MavlinkTelemetry:
return id.contains("mavlink") ? 1 : 0;
case SportTelemetry:
@@ -873,273 +387,6 @@ size_t getSizeA(T (&)[SIZE])
return SIZE;
}
EepromLoadErrors OpenTxEepromInterface::checkVersion(unsigned int version)
{
switch (version) {
case 201:
// first version
case 202:
// channel order is now always RUD - ELE - THR - AIL
// changes in timers
// ppmFrameLength added
// thrTraceSrc added
case 203:
// mixers changed (for the trims use for change the offset of a mix)
// telemetry offset raised to -127 +127
// function switches now have a param on 4 bits
case 204:
// telemetry changes (bars)
case 205:
// mixer changes (differential, negative curves)...
// case 206:
case 207:
// V4: Rotary Encoders position in FlightModes
case 208:
// Trim value in 16bits
// FrSky A1/A2 offset on 12bits
// ARM: More Mixers / Expos / CSW / FSW / CHNOUT
case 209:
// Add TrmR, TrmE, TrmT, TrmA as Mix sources
// Trims are now OFF / ON / Rud / Ele / Thr / Ail
case 210:
// Add names in Mixes / Expos
// Add a new telemetry screen
// Add support for Play Track <filename>
case 211:
// Curves big change
case 212:
// Big changes in mixers / limitse
case 213:
// GVARS / Variants introduction
case 214:
// Massive EEPROM change!
case 215:
// M128 revert because too much RAM used!
case 216:
// A lot of things (first github release)
case 217:
// 3 logical switches removed on M128 / gruvin9x boards
return OLD_VERSION;
case 218:
// ACCESS
// switches add for X7/X10/X12S
// 60 sensors for X12
return OLD_VERSION;
case 219:
// 60 (Horus / X9) / 40 (others) telemetry sensors instead of 32
// ALL: ReceiverData array added
// ALL: registrationId added
// ALL: failsafeChannels moved from ModuleData to ModelData
// ALL: ModuleData / TrainerModuleData modified
// PCBX9 : 6 chars for expos / mixes names instead of 8
// on X7: 2 additional switches
// on X9D / X9D+: 1 additional switch
// on xlite : 2 more storage switches
// on X10: 2 additional pots => 12 multipos switches
return OLD_VERSION;
case 220:
// Refer radio/src/storage/conversions/conversions_219_220.cpp
// ALL: TimerData split mode into separate mode and switch and change modes
// ALL: TimerData countdownStart invert value
// ALL: convert more fields from zchar to normal string
// COLORLCD: change CustomScreenData and TopBarPersistentData
return OLD_VERSION;
case 221:
// ALL: YAML storage
// X10PCB: add pots EXT3 and EXT4 (conversion required as shifts slider settings in multiple fields)
// RM TX16S: add Flysky gimbals firmware build option and repurpose EXT3 and EXT4 if selected
// ??: add source TX GPS
break;
default:
return NOT_OPENTX;
}
return ALL_OK;
}
bool OpenTxEepromInterface::checkVariant(unsigned int version, unsigned int variant)
{
bool variantError = false;
if (IS_TARANIS_X9E(board)) {
if (variant != TARANIS_X9E_VARIANT) {
variantError = true;
}
}
else if (IS_TARANIS_X7(board)) {
if (variant != TARANIS_X7_VARIANT) {
variantError = true;
}
}
else if (IS_TARANIS_XLITES(board)) {
if (variant != TARANIS_XLITES_VARIANT) {
variantError = true;
}
}
else if (IS_TARANIS_XLITE(board)) {
if (variant != TARANIS_XLITE_VARIANT) {
variantError = true;
}
}
else if (IS_TARANIS_X9LITES(board)) {
if (variant != TARANIS_X9LITES_VARIANT) {
variantError = true;
}
}
else if (IS_TARANIS_X9LITE(board)) {
if (variant != TARANIS_X9LITE_VARIANT) {
variantError = true;
}
}
else if (IS_JUMPER_T12(board)) {
if (variant != JUMPER_T12_VARIANT) {
variantError = true;
}
}
else if (IS_JUMPER_TLITE(board)) {
if (variant != JUMPER_TLITE_VARIANT) {
variantError = true;
}
}
else if (IS_JUMPER_TPRO(board)) {
if (variant != JUMPER_TPRO_VARIANT) {
variantError = true;
}
}
else if (IS_RADIOMASTER_TX12(board)) {
if (variant != RADIOMASTER_TX12_VARIANT) {
variantError = true;
}
}
else if (IS_RADIOMASTER_TX12_MK2(board)) {
if (variant != RADIOMASTER_TX12_MK2_VARIANT) {
variantError = true;
}
}
else if (IS_RADIOMASTER_ZORRO(board)) {
if (variant != RADIOMASTER_ZORRO_VARIANT) {
variantError = true;
}
}
else if (IS_RADIOMASTER_T8(board)) {
if (variant != RADIOMASTER_T8_VARIANT) {
variantError = true;
}
}
else if (IS_TARANIS(board)) {
if (variant != 0) {
variantError = true;
}
}
if (variantError) {
qWarning() << " wrong variant (" << variant << ")";
return false;
}
return true;
}
bool
OpenTxEepromInterface::loadModelFromBackup(ModelData & model, const uint8_t * data, unsigned int size, uint8_t version, uint32_t variant)
{
QByteArray backupData((char *) data, size);
QByteArray modelData;
if (IS_SKY9X(board)) {
modelData = QByteArray((char *) data, size);
}
else {
importRlc(modelData, backupData);
}
if (modelData.size()) {
if (loadFromByteArray<ModelData, OpenTxModelData>(model, modelData, version, variant)) {
model.used = true;
return true;
}
}
model.clear();
return false;
}
unsigned long OpenTxEepromInterface::loadBackup(RadioData &radioData, const uint8_t * eeprom, int esize, int index)
{
std::bitset<NUM_ERRORS> errors;
QDebug dbg = qDebug();
dbg.setAutoInsertSpaces(false);
dbg << "trying " << getName() << " backup import...";
if (esize < 8 || memcmp(eeprom, "o9x", 3) != 0) {
dbg << " no\n";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
Type backupBoard = (Type) -1;
switch (eeprom[3]) {
case 0x33:
backupBoard = BOARD_TARANIS_X9D;
break;
case 0x32:
backupBoard = BOARD_SKY9X;
break;
default:
dbg << " unknown board";
errors.set(UNKNOWN_BOARD);
return errors.to_ulong();
}
if (backupBoard != board) {
dbg << " not right board";
errors.set(WRONG_BOARD);
return errors.to_ulong();
}
uint8_t version = eeprom[4];
uint8_t bcktype = eeprom[5];
uint16_t size = ((uint16_t) eeprom[7] << 8) + eeprom[6];
uint16_t variant = ((uint16_t) eeprom[9] << 8) + eeprom[8];
dbg << " version " << (unsigned int) version << " ";
EepromLoadErrors version_error = checkVersion(version);
if (version_error == OLD_VERSION) {
errors.set(version_error);
errors.set(HAS_WARNINGS);
}
else if (version_error == NOT_OPENTX) {
dbg << " not open9x";
errors.set(version_error);
return errors.to_ulong();
}
if (size > esize - 8) {
dbg << " wrong size";
errors.set(WRONG_SIZE);
return errors.to_ulong();
}
if (bcktype == 'M') {
if (!loadModelFromBackup(radioData.models[index], &eeprom[8], size, version, variant)) {
dbg << " ko";
errors.set(UNKNOWN_ERROR);
return errors.to_ulong();
}
}
else {
dbg << " backup type not supported";
errors.set(BACKUP_NOT_SUPPORTED);
return errors.to_ulong();
}
dbg << " ok";
errors.set(ALL_OK);
return errors.to_ulong();
}
QString OpenTxFirmware::getReleaseNotesUrl()
{
return QString("%1/downloads").arg(EDGETX_HOME_PAGE_URL);
@@ -1150,10 +397,6 @@ QString OpenTxFirmware::getReleaseNotesUrl()
void registerOpenTxFirmware(OpenTxFirmware * firmware, bool deprecated = false)
{
OpenTxEepromInterface * eepromInterface = new OpenTxEepromInterface(firmware);
firmware->setEEpromInterface(eepromInterface);
opentxEEpromInterfaces.push_back(eepromInterface);
eepromInterfaces.push_back(eepromInterface);
if (!deprecated)
Firmware::addRegisteredFirmware(firmware);
}
@@ -1215,24 +458,6 @@ void addOpenTxTaranisOptions(OpenTxFirmware * firmware)
addOpenTxFontOptions(firmware);
}
void addOpenTxArm9xOptions(OpenTxFirmware * firmware, bool dblkeys = true)
{
addOpenTxCommonOptions(firmware);
firmware->addOption("heli", Firmware::tr("Enable HELI menu and cyclic mix support"));
firmware->addOption("gvars", Firmware::tr("Global variables"), GVARS_VARIANT);
firmware->addOption("potscroll", Firmware::tr("Pots use in menus navigation"));
firmware->addOption("autosource", Firmware::tr("In model setup menus automatically set source by moving the control"));
firmware->addOption("autoswitch", Firmware::tr("In model setup menus automatically set switch by moving the control"));
firmware->addOption("nographics", Firmware::tr("No graphical check boxes and sliders"));
firmware->addOption("battgraph", Firmware::tr("Battery graph"));
firmware->addOption("nobold", Firmware::tr("Don't use bold font for highlighting active items"));
//firmware->addOption("bluetooth", Firmware::tr("Bluetooth interface"));
if (dblkeys)
firmware->addOption("dblkeys", Firmware::tr("Enable resetting values by pressing up and down at the same time"));
addOpenTxFontOptions(firmware);
addOpenTxRfOptions(firmware, FLEX);
}
// TODO: refactoring OpenTxFirmware constructor - need to check if used elsewhere and how
#define FIRMWAREID(flavour) std::string(FIRMWARE_ID_PREFIX).append(flavour).c_str()
@@ -1585,48 +810,4 @@ void unregisterOpenTxFirmwares()
foreach (Firmware * f, Firmware::getRegisteredFirmwares()) {
delete f;
}
unregisterEEpromInterfaces();
}
template <class T, class M>
OpenTxEepromInterface * loadFromByteArray(T & dest, const QByteArray & data)
{
foreach(OpenTxEepromInterface * eepromInterface, opentxEEpromInterfaces) {
if (eepromInterface->loadFromByteArray<T, M>(dest, data)) {
return eepromInterface;
}
}
return NULL;
}
template <class T, class M>
bool saveToByteArray(const T & dest, QByteArray & data)
{
Board::Type board = getCurrentBoard();
foreach(OpenTxEepromInterface * eepromInterface, opentxEEpromInterfaces) {
if (eepromInterface->getBoard() == board) {
return eepromInterface->saveToByteArray<T, M>(dest, data);
}
}
return false;
}
OpenTxEepromInterface * loadModelFromByteArray(ModelData & model, const QByteArray & data)
{
return loadFromByteArray<ModelData, OpenTxModelData>(model, data);
}
OpenTxEepromInterface * loadRadioSettingsFromByteArray(GeneralSettings & settings, const QByteArray & data)
{
return loadFromByteArray<GeneralSettings, OpenTxGeneralData>(settings, data);
}
bool writeModelToByteArray(const ModelData & model, QByteArray & data)
{
return saveToByteArray<ModelData, OpenTxModelData>(model, data);
}
bool writeRadioSettingsToByteArray(const GeneralSettings & settings, QByteArray & data)
{
return saveToByteArray<GeneralSettings, OpenTxGeneralData>(settings, data);
}
@@ -19,70 +19,12 @@
* GNU General Public License for more details.
*/
#ifndef _OPENTXINTERFACE_H_
#define _OPENTXINTERFACE_H_
#pragma once
#include "eeprominterface.h"
#include <QtCore>
class RleFile;
class OpenTxFirmware;
class OpenTxEepromInterface : public EEPROMInterface
{
Q_DECLARE_TR_FUNCTIONS(OpenTxEepromInterface)
public:
OpenTxEepromInterface(OpenTxFirmware * firmware);
virtual ~OpenTxEepromInterface();
virtual unsigned long load(RadioData &, const uint8_t * eeprom, int size);
bool loadModelFromBackup(ModelData & model, const uint8_t * data, unsigned int size, uint8_t version, uint32_t variant);
virtual unsigned long loadBackup(RadioData &, const uint8_t * eeprom, int esize, int index);
virtual int save(uint8_t * eeprom, const RadioData & radioData, uint8_t version=0, uint32_t variant=0);
virtual int getSize(const ModelData &);
virtual int getSize(const GeneralSettings &);
protected:
const char * getName();
EepromLoadErrors checkVersion(unsigned int version);
bool checkVariant(unsigned int version, unsigned int variant);
template <class T, class M>
bool loadFromByteArray(T & dest, const QByteArray & data, uint8_t version, uint32_t variant=0);
public:
template <class T, class M>
bool loadFromByteArray(T & dest, const QByteArray & data);
template <class T, class M>
bool saveToByteArray(const T & src, QByteArray & data, uint8_t version=0);
bool loadRadioSettingsFromRLE(GeneralSettings & settings, RleFile * rleFile, uint8_t version);
bool loadModelFromRLE(ModelData & model, RleFile * rleFile, unsigned int index, uint8_t version, uint32_t variant);
void showErrors(const QString & title, const QStringList & errors);
uint8_t getLastDataVersion(Board::Type board);
RleFile * efile;
OpenTxFirmware * firmware;
};
class OpenTxFirmware: public Firmware
{
Q_DECLARE_TR_FUNCTIONS(OpenTxFirmware)
@@ -92,7 +34,6 @@ class OpenTxFirmware: public Firmware
Firmware(parent, id, parent->getName(), parent->getBoard(),
parent->getDownloadId(), parent->getSimulatorId(), parent->getHwDefnId())
{
setEEpromInterface(parent->getEEpromInterface());
}
OpenTxFirmware(const QString & id, const QString & name, const Board::Type board,
@@ -157,13 +98,3 @@ class OpenTxFirmware: public Firmware
void registerOpenTxFirmwares();
void unregisterOpenTxFirmwares();
extern QList<OpenTxEepromInterface *> opentxEEpromInterfaces;
OpenTxEepromInterface * loadModelFromByteArray(ModelData & model, const QByteArray & data);
OpenTxEepromInterface * loadRadioSettingsFromByteArray(GeneralSettings & settings, const QByteArray & data);
bool writeModelToByteArray(const ModelData & model, QByteArray & data);
bool writeRadioSettingsToByteArray(const GeneralSettings & settings, QByteArray & data);
#endif // _OPENTXINTERFACE_H_
-316
View File
@@ -1,316 +0,0 @@
/*
* 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 "flasheepromdialog.h"
#include "ui_flasheepromdialog.h"
#include "eeprominterface.h"
#include "helpers.h"
#include "storage.h"
#include "appdata.h"
#include "progressdialog.h"
#include "radiointerface.h"
#include "splashlibrarydialog.h"
FlashEEpromDialog::FlashEEpromDialog(QWidget *parent, const QString &filename):
QDialog(parent),
ui(new Ui::FlashEEpromDialog),
eepromFilename(filename)
{
ui->setupUi(this);
ui->profileLabel->setText(tr("Current profile: %1").arg(g.profile[g.id()].name()));
if (!filename.isEmpty()) {
ui->eepromFilename->hide();
ui->eepromLoad->hide();
}
QString backupPath = g.profile[g.id()].pBackupDir();
if (backupPath.isEmpty()) {
backupPath = g.backupDir();
ui->backupBeforeWrite->setChecked(g.enableBackup() || g.profile[g.id()].penableBackup());
}
if (backupPath.isEmpty() || !QDir(backupPath).exists()) {
ui->backupBeforeWrite->setEnabled(false);
}
}
FlashEEpromDialog::~FlashEEpromDialog()
{
delete ui;
}
void FlashEEpromDialog::showEvent(QShowEvent *)
{
updateUI();
}
void FlashEEpromDialog::updateUI()
{
// ui->burnButton->setEnabled(true);
ui->eepromFilename->setText(eepromFilename);
if (!eepromFilename.isEmpty() && getEEpromVersion(eepromFilename) >= 0) {
ui->profileLabel->show();
ui->patchCalibration->show();
ui->patchHardwareSettings->show();
QString name = g.profile[g.id()].name();
QString calib = g.profile[g.id()].stickPotCalib();
QString trainercalib = g.profile[g.id()].trainerCalib();
QString DisplaySet = g.profile[g.id()].display();
QString BeeperSet = g.profile[g.id()].beeper();
QString HapticSet = g.profile[g.id()].haptic();
QString SpeakerSet = g.profile[g.id()].speaker();
if (!name.isEmpty()) {
ui->profileLabel->show();
ui->patchCalibration->show();
ui->patchHardwareSettings->show();
// TODO I hardcode the number of pots here, should be dependant on the board?
if (!((calib.length()==(CPN_MAX_STICKS+3)*12) && (trainercalib.length()==16))) {
ui->patchCalibration->setDisabled(true);
}
if (!((DisplaySet.length()==6) && (BeeperSet.length()==4) && (HapticSet.length()==6) && (SpeakerSet.length()==6))) {
ui->patchHardwareSettings->setDisabled(true);
}
}
else {
ui->profileLabel->hide();
}
ui->burnButton->setEnabled(true);
}
else {
ui->profileLabel->hide();
ui->patchCalibration->hide();
ui->patchHardwareSettings->hide();
}
QTimer::singleShot(0, this, SLOT(shrink()));
}
void FlashEEpromDialog::on_eepromLoad_clicked()
{
QString filename = QFileDialog::getOpenFileName(this, tr("Choose Radio Backup file"), g.eepromDir(), EXTERNAL_EEPROM_FILES_FILTER);
if (!filename.isEmpty()) {
eepromFilename = filename;
updateUI();
}
}
int FlashEEpromDialog::getEEpromVersion(const QString & filename)
{
int result = -1;
QSharedPointer<RadioData> radioData = QSharedPointer<RadioData>(new RadioData());
Storage storage(filename);
if (storage.load(*radioData)) {
result = radioData->generalSettings.version;
}
else {
QMessageBox::warning(this, CPN_STR_TTL_ERROR, storage.error());
}
return result;
}
bool FlashEEpromDialog::patchCalibration(RadioData * radioData)
{
QString calib = g.profile[g.id()].stickPotCalib();
QString trainercalib = g.profile[g.id()].trainerCalib();
int potsnum = getBoardCapability(getCurrentBoard(), Board::Pots);
int8_t txVoltageCalibration = (int8_t) g.profile[g.id()].txVoltageCalibration();
int8_t txCurrentCalibration = (int8_t) g.profile[g.id()].txCurrentCalibration();
int8_t PPM_Multiplier = (int8_t) g.profile[g.id()].ppmMultiplier();
if ((calib.length()==(CPN_MAX_STICKS+potsnum)*12) && (trainercalib.length()==16)) {
QString Byte;
int16_t byte16;
bool ok;
for (int i=0; i<CPN_MAX_STICKS+potsnum; i++) {
Byte=calib.mid(i*12,4);
byte16=(int16_t)Byte.toInt(&ok,16);
if (ok)
radioData->generalSettings.calibMid[i]=byte16;
Byte=calib.mid(4+i*12,4);
byte16=(int16_t)Byte.toInt(&ok,16);
if (ok)
radioData->generalSettings.calibSpanNeg[i]=byte16;
Byte=calib.mid(8+i*12,4);
byte16=(int16_t)Byte.toInt(&ok,16);
if (ok)
radioData->generalSettings.calibSpanPos[i]=byte16;
}
for (int i=0; i<4; i++) {
Byte = trainercalib.mid(i*4,4);
byte16 = (int16_t)Byte.toInt(&ok,16);
if (ok) {
radioData->generalSettings.trainer.calib[i] = byte16;
}
}
radioData->generalSettings.txCurrentCalibration = txCurrentCalibration;
radioData->generalSettings.txVoltageCalibration = txVoltageCalibration;
radioData->generalSettings.PPM_Multiplier = PPM_Multiplier;
return true;
}
else {
QMessageBox::critical(this, CPN_STR_TTL_WARNING, tr("Wrong radio calibration data in profile, Settings not patched"));
return false;
}
}
bool FlashEEpromDialog::patchHardwareSettings(RadioData * radioData)
{
QString DisplaySet = g.profile[g.id()].display();
QString BeeperSet = g.profile[g.id()].beeper();
QString HapticSet = g.profile[g.id()].haptic();
QString SpeakerSet = g.profile[g.id()].speaker();
uint8_t GSStickMode=(uint8_t) g.profile[g.id()].gsStickMode();
uint8_t vBatWarn=(uint8_t) g.profile[g.id()].vBatWarn();
if ((DisplaySet.length()==6) && (BeeperSet.length()==4) && (HapticSet.length()==6) && (SpeakerSet.length()==6)) {
radioData->generalSettings.vBatWarn = vBatWarn;
radioData->generalSettings.stickMode = GSStickMode;
uint8_t byte8u;
int8_t byte8;
bool ok;
byte8=(int8_t)DisplaySet.mid(0,2).toInt(&ok,16);
if (ok) radioData->generalSettings.optrexDisplay=(byte8==1 ? true : false);
byte8u=(uint8_t)DisplaySet.mid(2,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.contrast=byte8u;
byte8u=(uint8_t)DisplaySet.mid(4,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.backlightBright=byte8u;
byte8u=(uint8_t)BeeperSet.mid(0,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.beeperMode=(GeneralSettings::BeeperMode)byte8u;
byte8=(int8_t)BeeperSet.mid(2,2).toInt(&ok,16);
if (ok) radioData->generalSettings.beeperLength=byte8;
byte8u=(uint8_t)HapticSet.mid(0,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.hapticMode=(GeneralSettings::BeeperMode)byte8u;
byte8u=(uint8_t)HapticSet.mid(2,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.hapticStrength=byte8u;
byte8=(int8_t)HapticSet.mid(4,2).toInt(&ok,16);
if (ok) radioData->generalSettings.hapticLength=byte8;
byte8u=(uint8_t)SpeakerSet.mid(0,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.speakerMode=byte8u;
byte8u=(uint8_t)SpeakerSet.mid(2,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.speakerPitch=byte8u;
byte8u=(uint8_t)SpeakerSet.mid(4,2).toUInt(&ok,16);
if (ok) radioData->generalSettings.speakerVolume=byte8u;
return true;
}
else {
QMessageBox::critical(this, CPN_STR_TTL_WARNING, tr("Wrong radio setting data in profile, Settings not patched"));
return false;
}
}
void FlashEEpromDialog::on_burnButton_clicked()
{
QString filename = eepromFilename;
bool patch = false;
if (ui->patchCalibration->isChecked() || ui->patchHardwareSettings->isChecked()) {
QSharedPointer<RadioData> radioData = QSharedPointer<RadioData>(new RadioData());
Storage storage(eepromFilename);
if (storage.load(*radioData)) {
if (ui->patchCalibration->isChecked())
patch |= patchCalibration(radioData.data());
if (ui->patchHardwareSettings->isChecked())
patch |= patchHardwareSettings(radioData.data());
}
if (patch) {
filename = generateProcessUniqueTempFileName("patched.bin");
QFile file(filename);
uint8_t *eeprom = (uint8_t*)malloc(Boards::getEEpromSize(getCurrentBoard()));
int eeprom_size = getCurrentEEpromInterface()->save(eeprom, *radioData, 0, getCurrentFirmware()->getVariantNumber());
if (eeprom_size == 0) {
return;
}
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::warning(this, CPN_STR_TTL_ERROR, tr("Cannot write file %1:\n%2.").arg(filename).arg(file.errorString()));
return;
}
QTextStream outputStream(&file);
long result = file.write((char*)eeprom, eeprom_size);
if (result != eeprom_size) {
QMessageBox::warning(this, CPN_STR_TTL_ERROR, tr("Error writing file %1:\n%2.").arg(filename).arg(file.errorString()));
return;
}
}
}
close();
ProgressDialog progressDialog(this, tr("Write Models and Settings to Radio"), CompanionIcon("write_eeprom.png"));
// backup previous EEPROM if requested
QString backupFilename;
QString backupPath;
if (ui->backupBeforeWrite->isChecked()) {
backupPath = g.profile[g.id()].pBackupDir();
if (backupPath.isEmpty()) {
backupPath=g.backupDir();
}
backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin";
}
else if (ui->checkFirmwareCompatibility->isChecked()) {
backupFilename = generateProcessUniqueTempFileName("eeprom.bin");
}
if (!backupFilename.isEmpty()) {
if (!readSettings(backupFilename, progressDialog.progress())) {
return;
}
}
// check EEPROM compatibility if requested
if (ui->checkFirmwareCompatibility->isChecked()) {
int eepromVersion = getEEpromVersion(filename);
QString firmwareFilename = generateProcessUniqueTempFileName("flash.bin");
if (!readFirmware(firmwareFilename, progressDialog.progress()))
return;
QString compatEEprom = generateProcessUniqueTempFileName("compat.bin");
if (convertEEprom(filename, compatEEprom, firmwareFilename)) {
int compatVersion = getEEpromVersion(compatEEprom);
if ((compatVersion / 100) != (eepromVersion / 100)) {
QMessageBox::warning(this, CPN_STR_TTL_WARNING, tr("The radio firmware belongs to another product family, check file and preferences!"));
return;
}
else if (compatVersion < eepromVersion) {
QMessageBox::warning(this, CPN_STR_TTL_WARNING, tr("The radio firmware is outdated, please upgrade!"));
return;
}
filename = compatEEprom;
}
else if (QMessageBox::question(this, CPN_STR_TTL_ERROR, tr("Cannot check Models and Settings compatibility! Continue anyway?"), QMessageBox::Yes|QMessageBox::No) == QMessageBox::No) {
return;
}
qunlink(firmwareFilename);
}
// and write...
bool result = writeSettings(filename, progressDialog.progress());
if (!result && !progressDialog.isEmpty()) {
progressDialog.exec();
}
}
void FlashEEpromDialog::on_cancelButton_clicked()
{
close();
}
void FlashEEpromDialog::shrink()
{
resize(0, 0);
}
-62
View File
@@ -1,62 +0,0 @@
/*
* 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.
*/
#ifndef _FLASHEEPROMDIALOG_H_
#define _FLASHEEPROMDIALOG_H_
#include <constants.h>
#include <QtWidgets>
namespace Ui
{
class FlashEEpromDialog;
}
class RadioData;
class FlashEEpromDialog : public QDialog
{
Q_OBJECT
public:
explicit FlashEEpromDialog(QWidget *parent, const QString &fileName="");
~FlashEEpromDialog();
private slots:
void on_eepromLoad_clicked();
void on_burnButton_clicked();
void on_cancelButton_clicked();
void shrink();
protected:
void updateUI();
bool isValidEEprom(const QString &filename);
int getEEpromVersion(const QString &filename);
bool patchCalibration(RadioData * radioData);
bool patchHardwareSettings(RadioData * radioData);
virtual void showEvent(QShowEvent *);
private:
Ui::FlashEEpromDialog *ui;
QString eepromFilename;
};
#endif // _FLASHEEPROMDIALOG_H_
-198
View File
@@ -1,198 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FlashEEpromDialog</class>
<widget class="QDialog" name="FlashEEpromDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>399</width>
<height>201</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Write Models and Settings to Radio</string>
</property>
<property name="windowIcon">
<iconset resource="companion.qrc">
<normaloff>:/themes/classic/16/write_eeprom.png</normaloff>:/themes/classic/16/write_eeprom.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="filanameLayout">
<item>
<widget class="QLineEdit" name="eepromFilename">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="eepromLoad">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Load...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="profileLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Current Profile</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkFirmwareCompatibility">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Allows Companion to write to older version of the firmware</string>
</property>
<property name="text">
<string>Check Firmware compatibility</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="backupBeforeWrite">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves a dated copy of your eeprom to the backup folder you specified in the Companion settings before writing the current model to the radio.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Backup before Write</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="patchCalibration">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Modify calibration parameters using settings from current profile&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Patch calibration setting from profile</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="patchHardwareSettings">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Modify HW parameters using settings from current profile&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Patch HW settings from profile</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonsLayout">
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="burnButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Write to TX</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>burnButton</tabstop>
<tabstop>eepromFilename</tabstop>
<tabstop>eepromLoad</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources>
<include location="companion.qrc"/>
</resources>
<connections/>
</ui>
+26 -25
View File
@@ -267,7 +267,7 @@ void FlashFirmwareDialog::shrink()
void FlashFirmwareDialog::startFlash(const QString &filename)
{
bool backup = g.backupOnFlash();
// bool backup = g.backupOnFlash();
close();
@@ -297,34 +297,35 @@ void FlashFirmwareDialog::startFlash(const QString &filename)
if (checkPassed) {
// backup if requested
bool result = true;
QString backupFilename;
QString backupPath;
if (backup) {
backupPath = g.profile[g.id()].pBackupDir();
if (backupPath.isEmpty()) {
backupPath=g.backupDir();
}
backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin";
result = readSettings(backupFilename, progressDialog.progress());
sleep(2);
}
// bool result = true;
// QString backupFilename;
// QString backupPath;
// if (backup) {
// backupPath = g.profile[g.id()].pBackupDir();
// if (backupPath.isEmpty()) {
// backupPath=g.backupDir();
// }
// backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin";
// result = readSettings(backupFilename, progressDialog.progress());
// sleep(2);
// }
// flash
result = (result && writeFirmware(filename, progressDialog.progress()));
//result = (result && writeFirmware(filename, progressDialog.progress()));
writeFirmware(filename, progressDialog.progress());
// restore if backup requested
if (backup && result) {
sleep(2);
QString restoreFilename = generateProcessUniqueTempFileName("restore.bin");
if (!convertEEprom(backupFilename, restoreFilename, filename)) {
QMessageBox::warning(this, tr("Conversion failed"), tr("Cannot convert Models and Settings for use with this firmware, original data will be used"));
restoreFilename = backupFilename;
}
if (!writeSettings(restoreFilename, progressDialog.progress())) {
QMessageBox::warning(this, tr("Restore failed"), tr("Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1").arg(backupFilename));
}
}
// if (backup && result) {
// sleep(2);
// QString restoreFilename = generateProcessUniqueTempFileName("restore.bin");
// if (!convertEEprom(backupFilename, restoreFilename, filename)) {
// QMessageBox::warning(this, tr("Conversion failed"), tr("Cannot convert Models and Settings for use with this firmware, original data will be used"));
// restoreFilename = backupFilename;
// }
// if (!writeSettings(restoreFilename, progressDialog.progress())) {
// QMessageBox::warning(this, tr("Restore failed"), tr("Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1").arg(backupFilename));
// }
// }
progressDialog.progress()->setInfo(tr("Flashing done"));
}
+2 -9
View File
@@ -29,7 +29,6 @@
#include "printdialog.h"
#include "version.h"
#include "customizesplashdialog.h"
#include "flasheepromdialog.h"
#include "flashfirmwaredialog.h"
#include "hexinterface.h"
#include "warnings.h"
@@ -125,7 +124,7 @@ MainWindow::MainWindow():
if (!str.isEmpty()) {
int fileType = getStorageType(str);
if (fileType==STORAGE_TYPE_EEPE || fileType==STORAGE_TYPE_EEPM || fileType==STORAGE_TYPE_BIN || fileType==STORAGE_TYPE_ETX) {
if (fileType == STORAGE_TYPE_ETX) {
MdiChild * child = createMdiChild();
if (child->loadFile(str)) {
if (!(printing && model >= 0 && (getCurrentFirmware()->getCapability(Models) == 0 || model<getCurrentFirmware()->getCapability(Models)) && !printfilename.isEmpty())) {
@@ -582,13 +581,7 @@ bool MainWindow::readSettingsFromRadio(const QString & filename)
void MainWindow::writeBackup()
{
if (IS_FAMILY_HORUS_OR_T16(getCurrentBoard())) {
QMessageBox::information(this, CPN_STR_APP_NAME, tr("This function is not yet implemented"));
return;
// TODO implementation
}
FlashEEpromDialog *cd = new FlashEEpromDialog(this);
cd->exec();
QMessageBox::information(this, CPN_STR_APP_NAME, tr("This function is not yet implemented"));
}
void MainWindow::writeFlash(QString fileToFlash)
+12 -28
View File
@@ -25,7 +25,6 @@
#include "generaledit/generaledit.h"
#include "burnconfigdialog.h"
#include "printdialog.h"
#include "flasheepromdialog.h"
#include "helpers.h"
#include "appdata.h"
#include "wizarddialog.h"
@@ -1500,36 +1499,21 @@ void MdiChild::writeSettings(StatusDialog * status) // write to Tx
return;
}
Board::Type board = getCurrentBoard();
QString radioPath = findMassstoragePath("RADIO", true);
qDebug() << "Searching for SD card, found" << radioPath;
if (radioPath.isEmpty()) {
qDebug() << "Radio SD card not found";
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Unable to find radio SD card!"));
return;
}
if (Boards::getCapability(board, Board::HasSDCard)) {
QString radioPath = findMassstoragePath("RADIO", true);
qDebug() << "Searching for SD card, found" << radioPath;
if (radioPath.isEmpty()) {
qDebug() << "Radio SD card not found";
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Unable to find radio SD card!"));
return;
}
if (saveFile(radioPath, false)) {
status->hide();
QMessageBox::information(this, CPN_STR_TTL_INFO, tr("Models and settings written to radio"));
}
else {
qDebug() << "MdiChild::writeEeprom(): saveFile error";
status->hide();
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Error writing models and settings to radio!"));
}
if (saveFile(radioPath, false)) {
status->hide();
QMessageBox::information(this, CPN_STR_TTL_INFO, tr("Models and settings written to radio"));
}
else {
QString tempFile = generateProcessUniqueTempFileName("temp.bin");
saveFile(tempFile, false);
if (!QFileInfo(tempFile).exists()) {
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Cannot write temporary file!"));
return;
}
FlashEEpromDialog * cd = new FlashEEpromDialog(this, tempFile);
cd->exec();
status->hide();
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Error writing models and settings to radio!"));
}
}
-5
View File
@@ -154,11 +154,6 @@ QString ModelPrinter::printBoolean(const bool val, const int typ)
}
}
QString ModelPrinter::printEEpromSize()
{
return QString("%1 ").arg(getCurrentEEpromInterface()->getSize(model)) + tr("bytes");
}
QString ModelPrinter::printChannelName(int idx)
{
QString str = RawSource(SOURCE_TYPE_CH, idx + 1).toString(&model, &generalSettings);
-1
View File
@@ -41,7 +41,6 @@ class ModelPrinter: public QObject
virtual ~ModelPrinter();
QString printBoolean(const bool val, const int typ);
QString printEEpromSize();
QString printTrimIncrementMode();
QString printThrottle();
QString printFlightModeSwitch(const RawSwitch & swtch);
+1 -5
View File
@@ -81,11 +81,7 @@ QStringList getSambaArgs(const QString & tcl)
QStringList getReadEEpromCmd(const QString & filename)
{
QStringList result;
EEPROMInterface *eepromInterface = getCurrentEEpromInterface();
if (IS_STM32(eepromInterface->getBoard())) {
// impossible
}
else {
if (!IS_STM32(getCurrentBoard())) {
result = getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename + "\" 0x0 0x80000 0\n");
}
return result;
@@ -304,7 +304,7 @@ void SimulatorStartupDialog::onRadioTypeChanged(int index)
void SimulatorStartupDialog::onDataFileSelect(bool)
{
QString filter = EEPROM_FILES_FILTER % tr("All files (*.*)");
QString filter = SIMU_FILES_FILTER;
QString file = QFileDialog::getSaveFileName(this, tr("Select a data file"), ui->dataFile->text(),
filter, NULL, QFileDialog::DontConfirmOverwrite);
if (!file.isEmpty()) {
+16 -13
View File
@@ -316,8 +316,10 @@ bool SimulatorWidget::setStartupData(const QByteArray & dataSource, bool fromFil
}
// Assume a byte array of radio data was passed, load it.
else if (!dataSource.isEmpty()) {
ret = firmware->getEEpromInterface()->load(simuData, (uint8_t *)dataSource.constData(), Boards::getEEpromSize(m_board));
startupData = dataSource; // save the data for start()
//ret = firmware->getEEpromInterface()->load(simuData, (uint8_t *)dataSource.constData(), Boards::getEEpromSize(m_board));
//startupData = dataSource; // save the data for start()
error = tr("Unable to handle byte array.");
ret = 0;
}
// we're :-(
else {
@@ -352,11 +354,11 @@ bool SimulatorWidget::setRadioData(RadioData * radioData)
if (ret) {
if (!hasSdCard) {
startupData.fill(0, Boards::getEEpromSize(m_board));
if (firmware->getEEpromInterface()->save(
(uint8_t *)startupData.data(), *radioData, 0,
firmware->getCapability(SimulatorVariant)) <= 0) {
//if (firmware->getEEpromInterface()->save(
// (uint8_t *)startupData.data(), *radioData, 0,
// firmware->getCapability(SimulatorVariant)) <= 0) {
ret = false;
}
//}
} else {
ret = saveRadioData(radioData, radioDataPath);
}
@@ -452,13 +454,14 @@ bool SimulatorWidget::saveTempData()
fh.close();
}
if (!firmware->getEEpromInterface()->load(radioData, (uint8_t *)startupData.constData(), Boards::getEEpromSize(m_board))) {
error = tr("Error saving data: could not get data from simulator interface.");
}
else {
radioData.fixModelFilenames();
ret = true;
}
qDebug() << "ERROR: WE DO NOT WANT TO BE HERE";
// if (!firmware->getEEpromInterface()->load(radioData, (uint8_t *)startupData.constData(), Boards::getEEpromSize(m_board))) {
// error = tr("Error saving data: could not get data from simulator interface.");
// }
// else {
// radioData.fixModelFilenames();
// ret = true;
// }
}
}
else {
-3
View File
@@ -4,17 +4,14 @@ project(storage)
set(${PROJECT_NAME}_NAMES
hexinterface
mountlist
rlefile
appdata
firmwareinterface
storage
bineeprom
eepe
hexeeprom
labeled
sdcard
etx
otx
yaml
crc
minizinterface
+16 -165
View File
@@ -25,159 +25,20 @@
#include <regex>
bool LabelsStorageFormat::load(RadioData & radioData)
{
StorageType st = getStorageType(filename);
if (st == STORAGE_TYPE_UNKNOWN) {
st = probeFormat();
}
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML
|| st == STORAGE_TYPE_UNKNOWN) {
return loadYaml(radioData);
} else {
return loadBin(radioData);
}
}
bool LabelsStorageFormat::write(const RadioData & radioData)
{
StorageType st = getStorageType(filename);
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML ||
st == STORAGE_TYPE_UNKNOWN) {
return writeYaml(radioData);
}
return false;
}
StorageType LabelsStorageFormat::probeFormat()
{
if (QFile(filename + "/RADIO/radio.yml").exists()) // converted
return getStorageType("radio.yml");
else if (QFile(filename + "/RADIO/radio.bin").exists()) // unconverted
return getStorageType("radio.bin");
else
return getStorageType(filename);
}
bool LabelsStorageFormat::loadBin(RadioData & radioData)
bool LabelsStorageFormat::load(RadioData & radioData)
{
QByteArray radioSettingsBuffer;
if (!loadFile(radioSettingsBuffer, "RADIO/radio.bin")) {
setError(tr("Cannot extract RADIO/radio.bin"));
return false;
}
StorageType st = getStorageType(filename);
if (st == STORAGE_TYPE_UNKNOWN)
st = probeFormat();
OpenTxEepromInterface * loadInterface = loadRadioSettingsFromByteArray(radioData.generalSettings, radioSettingsBuffer);
if (!loadInterface) {
return false;
}
board = loadInterface->getBoard();
radioData.generalSettings.convertLegacyConfiguration(board);
QByteArray modelsListBuffer;
if (!loadFile(modelsListBuffer, "RADIO/models.txt")) {
setError(tr("Cannot extract RADIO/models.txt"));
return false;
}
QList<QByteArray> lines = modelsListBuffer.split('\n');
int modelIndex = 0;
foreach (const QByteArray & lineArray, lines) {
QString line = QString(lineArray).trimmed();
if (line.isEmpty()) continue;
// qDebug() << "parsing line" << line;
if (line.startsWith('[') && line.endsWith(']')) {
continue;
}
// determine if we have a model number
QStringList parts = line.split(QRegExp("\\s+"), Qt::SkipEmptyParts);
if (parts.size() == 2) {
// parse model number
int modelNumber = parts[0].toInt();
if (modelNumber > 0 && modelNumber > modelIndex && modelNumber < getCurrentFirmware()->getCapability(Models)) {
modelIndex = modelNumber;
qDebug() << "advanced model number to" << modelIndex;
}
else {
if (modelNumber != modelIndex) qDebug() << "Invalid model number" << parts[0];
}
parts.removeFirst();
}
if (parts.size() == 1) {
// parse model file name and load
QString fileName = parts[0];
qDebug() << "Loading model from file" << fileName << "into slot" << modelIndex;
QByteArray modelBuffer;
if (!loadFile(modelBuffer, QString("MODELS/%1").arg(fileName))) {
setError(tr("Cannot extract %1").arg(fileName));
return false;
}
if ((int)radioData.models.size() <= modelIndex) {
radioData.models.resize(modelIndex + 1);
}
if (!loadModelFromByteArray(radioData.models[modelIndex], modelBuffer)) {
setError(tr("Error loading models"));
return false;
}
strncpy(radioData.models[modelIndex].filename, qPrintable(fileName), sizeof(radioData.models[modelIndex].filename));
if (IS_FAMILY_HORUS_OR_T16(board) && !strcmp(radioData.generalSettings.currModelFilename, qPrintable(fileName))) {
radioData.generalSettings.currModelIndex = modelIndex;
qDebug() << "currModelIndex =" << modelIndex;
}
radioData.models[modelIndex].used = true;
modelIndex++;
continue;
}
// invalid line
// TODO add to parsing report
qDebug() << "Invalid line" <<line;
continue;
}
// Add a Favorites label
if(getCurrentFirmware()->getCapability(HasModelLabels)) {
RadioData::LabelData ld = { tr("Favorites"), false };
radioData.labels.append(ld);
}
return true;
}
bool LabelsStorageFormat::writeBin(const RadioData & radioData)
{
QByteArray radioSettingsData; // radio.bin
size_t numModels = radioData.models.size();
size_t numCategories = radioData.labels.size();
std::vector<std::vector<QString>> sortedModels(numCategories);
writeRadioSettingsToByteArray(radioData.generalSettings, radioSettingsData);
if (!writeFile(radioSettingsData, "RADIO/radio.bin")) {
return false;
}
for (size_t m=0; m<numModels; m++) {
const ModelData & model = radioData.models[m];
if (model.isEmpty()) continue;
QString modelFilename = QString("MODELS/%1").arg(model.filename);
QByteArray modelData;
writeModelToByteArray(model, modelData);
if (!writeFile(modelData, modelFilename)) {
return false;
}
}
return true;
}
bool LabelsStorageFormat::loadYaml(RadioData & radioData)
{
if (getStorageType(filename) == STORAGE_TYPE_UNKNOWN && probeFormat() == STORAGE_TYPE_ETX) {
if (!QFile(filename + "/" + "RADIO/radio.yml").exists())
qDebug() << tr("Cannot find %1/RADIO/radio.yml").arg(filename);
@@ -238,11 +99,11 @@ bool LabelsStorageFormat::loadYaml(RadioData & radioData)
// Scan for all models
std::list<std::string> filelist;
if (!getFileList(filelist)) {
if (!getFileList(filelist))
return false;
}
const std::regex yml_regex("MODELS/(model([0-9]+)\\.yml)");
for(const auto& f : filelist) {
std::smatch match;
if (std::regex_match(f, match, yml_regex)) {
@@ -302,10 +163,8 @@ bool LabelsStorageFormat::loadYaml(RadioData & radioData)
model.modelIndex = modelIdx;
strncpy(model.filename, mc.filename.c_str(), sizeof(model.filename)-1);
if (hasLabels && !strncmp(radioData.generalSettings.currModelFilename,
model.filename, sizeof(model.filename))) {
if (hasLabels && !strncmp(radioData.generalSettings.currModelFilename, model.filename, sizeof(model.filename)))
radioData.generalSettings.currModelIndex = modelIdx;
}
model.used = true;
modelIdx++;
@@ -325,16 +184,14 @@ bool LabelsStorageFormat::loadYaml(RadioData & radioData)
return true;
}
bool LabelsStorageFormat::writeYaml(const RadioData & radioData)
bool LabelsStorageFormat::write(const RadioData & radioData)
{
QByteArray radioSettingsBuffer;
if (!writeRadioSettingsToYaml(radioData.generalSettings, radioSettingsBuffer)) {
if (!writeRadioSettingsToYaml(radioData.generalSettings, radioSettingsBuffer))
return false;
}
if (!writeFile(radioSettingsBuffer, "RADIO/radio.yml")) {
if (!writeFile(radioSettingsBuffer, "RADIO/radio.yml"))
return false;
}
bool hasLabels = getCurrentFirmware()->getCapability(HasModelLabels);
@@ -347,7 +204,7 @@ bool LabelsStorageFormat::writeYaml(const RadioData & radioData)
// Delete all old modelxx.yml from radio MODELS folder before writing new modelxx.yml files
const std::regex yml_regex("MODELS/(model([0-9s]+)\\.yml)");
for(const auto& f : filelist) {
for (const auto& f : filelist) {
std::smatch match;
if (std::regex_match(f, match, yml_regex)) {
if (match.size() == 3) {
@@ -368,28 +225,22 @@ bool LabelsStorageFormat::writeYaml(const RadioData & radioData)
QString modelFilename;
if (hasLabels) {
std::string ymlFilename = patchFilenameToYaml(model.filename);
modelFilename =
QString("MODELS/%1").arg(QString::fromStdString(ymlFilename));
modelFiles.push_back(
{ymlFilename, std::string(model.name)});
modelFilename = QString("MODELS/%1").arg(QString::fromStdString(ymlFilename));
modelFiles.push_back({ymlFilename, std::string(model.name)});
} else {
modelFilename = QString("MODELS/model%1.yml")
.arg(model.modelIndex, 2, 10, QLatin1Char('0'));
modelFilename = QString("MODELS/model%1.yml").arg(model.modelIndex, 2, 10, QLatin1Char('0'));
}
QByteArray modelData;
writeModelToYaml(model, modelData);
if (!writeFile(modelData, modelFilename)) {
if (!writeFile(modelData, modelFilename))
return false;
}
}
if (hasLabels) {
QByteArray labelsListBuffer;
if (!writeLabelsListToYaml(radioData, labelsListBuffer)
|| !writeFile(labelsListBuffer, "MODELS/labels.yml")) {
if (!writeLabelsListToYaml(radioData, labelsListBuffer) || !writeFile(labelsListBuffer, "MODELS/labels.yml"))
return false;
}
}
return true;
-5
View File
@@ -47,10 +47,5 @@ class LabelsStorageFormat : public StorageFormat
virtual bool getFileList(std::list<std::string>& filelist) = 0;
virtual bool deleteFile(const QString & fileName) = 0;
virtual bool loadBin(RadioData & radioData);
virtual bool writeBin(const RadioData & radioData);
virtual bool loadYaml(RadioData & radioData);
virtual bool writeYaml(const RadioData & radioData);
StorageType probeFormat();
};
-116
View File
@@ -1,116 +0,0 @@
/*
* 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 "otx.h"
#include <QFile>
#define MZ_ALLOCATION_SIZE (32*1024)
bool OtxFormat::load(RadioData & radioData)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
setError(tr("Error opening file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
QByteArray archiveContents = file.readAll();
qDebug() << "File" << filename << "read, size:" << archiveContents.size();
// open zip file
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_reader_init_mem(&zip_archive, archiveContents.data(), archiveContents.size(), 0)) {
qDebug() << tr("Error opening OpenTX archive %1").arg(filename);
return false;
}
bool result = LabelsStorageFormat::load(radioData);
mz_zip_reader_end(&zip_archive);
return result;
}
bool OtxFormat::write(const RadioData & radioData)
{
qDebug() << "Saving to archive" << filename;
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_writer_init_heap(&zip_archive, 0, MZ_ALLOCATION_SIZE)) {
setError(tr("Error initializing OpenTX archive writer"));
return false;
}
bool result = LabelsStorageFormat::write(radioData);
if (result) {
// finalize archive and get contents
char * archiveContents;
size_t archiveSize;
if (mz_zip_writer_finalize_heap_archive(&zip_archive, (void **)&archiveContents, &archiveSize)) {
qDebug() << "Archive size" << archiveSize;
// write contents to file
QFile file(filename);
if (file.open(QIODevice::WriteOnly)) {
qint64 len = file.write(archiveContents, archiveSize);
if (len != (qint64)archiveSize) {
setError(tr("Error writing file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(tr("Error creating OpenTX file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(tr("Error creating OpenTX archive"));
result = false;
}
}
mz_zip_writer_end(&zip_archive);
return result;
}
bool OtxFormat::loadFile(QByteArray & filedata, const QString & filename)
{
size_t size;
void * data = mz_zip_reader_extract_file_to_heap(&zip_archive, qPrintable(filename), &size, 0);
if (!data) {
return false;
}
qDebug() << QString("Extracted file %1, size=%2").arg(filename).arg(size);
filedata.clear();
filedata.append((char *)data, size);
mz_free(data);
return true;
}
bool OtxFormat::writeFile(const QByteArray & filedata, const QString & filename)
{
if (!mz_zip_writer_add_mem(&zip_archive, filename.toStdString().c_str(), filedata.data(), filedata.size(), MZ_DEFAULT_LEVEL)) {
setError(tr("Error adding %1 to OpenTX archive").arg(filename));
return false;
}
return true;
}
-49
View File
@@ -1,49 +0,0 @@
/*
* 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 "labeled.h"
#include <QtCore>
class OtxFormat : public LabelsStorageFormat
{
Q_DECLARE_TR_FUNCTIONS(OtxFormat)
public:
OtxFormat(const QString & filename):
LabelsStorageFormat(filename)
{
}
virtual QString name() { return "otx"; }
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName);
virtual bool writeFile(const QByteArray & fileData, const QString & fileName);
virtual bool getFileList(std::list<std::string>& filelist) { return false; }
virtual bool deleteFile(const QString & fileName) { return false; }
mz_zip_archive zip_archive;
};
+1 -4
View File
@@ -19,8 +19,7 @@
* GNU General Public License for more details.
*/
#ifndef _SDCARD_H2_
#define _SDCARD_H2_
#pragma once
#include "labeled.h"
@@ -56,5 +55,3 @@ class SdcardStorageFactory : public DefaultStorageFactory<SdcardFormat>
virtual bool probe(const QString & name);
};
#endif // _SDCARD_H2_
+7 -99
View File
@@ -19,32 +19,17 @@
* GNU General Public License for more details.
*/
#include "bineeprom.h"
#include "eepe.h"
#include "otx.h"
#include "etx.h"
#include "sdcard.h"
#include "yaml.h"
#include "firmwareinterface.h"
#include "eeprominterface.h"
#include "sdcard.h"
#include <QFileInfo>
StorageType getStorageType(const QString & filename)
{
QString suffix = QFileInfo(filename).suffix().toUpper();
if (suffix == "HEX")
return STORAGE_TYPE_HEX;
else if (suffix == "BIN")
return STORAGE_TYPE_BIN;
else if (suffix == "EEPM")
return STORAGE_TYPE_EEPM;
else if (suffix == "EEPE")
return STORAGE_TYPE_EEPE;
else if (suffix == "XML")
return STORAGE_TYPE_XML;
else if (suffix == "OTX")
return STORAGE_TYPE_OTX;
else if (suffix == "ETX")
if (suffix == "ETX")
return STORAGE_TYPE_ETX;
else if (suffix == "YML")
return STORAGE_TYPE_YML;
@@ -64,10 +49,6 @@ void registerStorageFactory(StorageFactory * factory)
void registerStorageFactories()
{
registerStorageFactory(new DefaultStorageFactory<BinEepromFormat>("bin"));
registerStorageFactory(new DefaultStorageFactory<EepeFormat>("eepe"));
registerStorageFactory(new DefaultStorageFactory<HexEepromFormat>("hex"));
registerStorageFactory(new DefaultStorageFactory<OtxFormat>("otx"));
registerStorageFactory(new DefaultStorageFactory<EtxFormat>("etx"));
registerStorageFactory(new DefaultStorageFactory<YamlFormat>("yml"));
registerStorageFactory(new SdcardStorageFactory());
@@ -88,7 +69,7 @@ bool Storage::load(RadioData & radioData)
}
bool ret = false;
foreach(StorageFactory * factory, registeredStorageFactories) {
foreach (StorageFactory * factory, registeredStorageFactories) {
if (factory->probe(filename)) {
StorageFormat * format = factory->instance(filename);
if (format->load(radioData)) {
@@ -111,7 +92,7 @@ bool Storage::load(RadioData & radioData)
bool Storage::write(const RadioData & radioData)
{
bool ret = false;
foreach(StorageFactory * factory, registeredStorageFactories) {
foreach (StorageFactory * factory, registeredStorageFactories) {
if (factory->probe(filename)) {
StorageFormat * format = factory->instance(filename);
ret = format->write(radioData);
@@ -125,7 +106,7 @@ bool Storage::write(const RadioData & radioData)
bool Storage::writeModel(const RadioData & radioData, const int modelIndex)
{
bool ret = false;
foreach(StorageFactory * factory, registeredStorageFactories) {
foreach (StorageFactory * factory, registeredStorageFactories) {
if (factory->probe(filename)) {
StorageFormat * format = factory->instance(filename);
ret = format->writeModel(radioData, modelIndex);
@@ -135,76 +116,3 @@ bool Storage::writeModel(const RadioData & radioData, const int modelIndex)
}
return ret;
}
bool convertEEprom(const QString & sourceEEprom, const QString & destinationEEprom, const QString & firmwareFilename)
{
FirmwareInterface firmware(firmwareFilename);
if (!firmware.isValid())
return false;
uint8_t version = firmware.getEEpromVersion();
unsigned int variant = firmware.getEEpromVariant();
QSharedPointer<RadioData> radioData = QSharedPointer<RadioData>(new RadioData());
Storage storage(sourceEEprom);
if (!storage.load(*radioData))
return false;
QByteArray eeprom(Boards::getEEpromSize(Board::BOARD_UNKNOWN), 0);
int size = getCurrentEEpromInterface()->save((uint8_t *)eeprom.data(), *radioData, version, variant);
if (size == 0) {
return false;
}
QFile destinationFile(destinationEEprom);
if (!destinationFile.open(QIODevice::WriteOnly)) {
return false;
}
int result = destinationFile.write(eeprom.constData(), size);
destinationFile.close();
return (result == size);
}
#if 0
unsigned long LoadBackup(RadioData & radioData, uint8_t * eeprom, int size, int index)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface *eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->loadBackup(radioData, eeprom, size, index));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
unsigned long LoadEepromXml(RadioData & radioData, QDomDocument & doc)
{
std::bitset<NUM_ERRORS> errors;
foreach(EEPROMInterface *eepromInterface, eepromInterfaces) {
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->loadxml(radioData, doc));
if (result.test(ALL_OK)) {
return result.to_ulong();
}
else {
errors |= result;
}
}
if (errors.none()) {
errors.set(UNKNOWN_ERROR);
}
return errors.to_ulong();
}
#endif
+2 -18
View File
@@ -19,8 +19,7 @@
* GNU General Public License for more details.
*/
#ifndef _STORAGE_H_
#define _STORAGE_H_
#pragma once
#include "radiodata.h"
@@ -31,13 +30,8 @@
enum StorageType
{
STORAGE_TYPE_UNKNOWN,
STORAGE_TYPE_BIN,
STORAGE_TYPE_HEX,
STORAGE_TYPE_EEPE,
STORAGE_TYPE_EEPM,
STORAGE_TYPE_XML,
STORAGE_TYPE_HEX, // needed for FirmwareInterface
STORAGE_TYPE_SDCARD,
STORAGE_TYPE_OTX,
STORAGE_TYPE_ETX,
STORAGE_TYPE_YML
};
@@ -164,13 +158,3 @@ class Storage : public StorageFormat
void registerStorageFactories();
void unregisterStorageFactories();
#if 0
unsigned long LoadBackup(RadioData &radioData, uint8_t *eeprom, int esize, int index);
unsigned long LoadEeprom(RadioData &radioData, const uint8_t *eeprom, int size);
unsigned long LoadEepromXml(RadioData &radioData, QDomDocument &doc);
#endif
bool convertEEprom(const QString & sourceEEprom, const QString & destinationEEprom, const QString & firmware);
#endif // _STORAGE_H_