diff --git a/companion/src/eeprominterface.cpp b/companion/src/eeprominterface.cpp index b8270f629..e8d47a139 100644 --- a/companion/src/eeprominterface.cpp +++ b/companion/src/eeprominterface.cpp @@ -423,7 +423,7 @@ QString RotaryEncoderString(int index) return CHECK_IN_ARRAY(rotary, index); } -QString RawSource::toString(const ModelData * model) +QString RawSource::toString(const ModelData * model) const { static const QString trims[] = { QObject::tr("TrmR"), QObject::tr("TrmE"), QObject::tr("TrmT"), QObject::tr("TrmA") @@ -1201,11 +1201,31 @@ RawSourceRange FrSkyChannelData::getRange() const return result; } +void FrSkyScreenData::clear() +{ + memset(this, 0, sizeof(FrSkyScreenData)); + if (!IS_ARM(GetCurrentFirmware()->getBoard())) { + type = TELEMETRY_SCREEN_NUMBERS; + } +} + void FrSkyData::clear() { - memset(this, 0, sizeof(FrSkyData)); + usrProto = 0; + voltsSource = 0; + altitudeSource = 0; + currentSource = 0; + varioMin = 0; + varioCenterMin = 0; // if increment in 0.2m/s = 3.0m/s max + varioCenterMax = 0; + varioMax = 0; + mAhPersistent = 0; + storedMah = 0; + fasOffset = 0; rssiAlarms[0].clear(2, 45); rssiAlarms[1].clear(3, 42); + for (int i=0; i<4; i++) + screens[i].clear(); varioSource = 2/*VARIO*/; blades = 2; } diff --git a/companion/src/eeprominterface.h b/companion/src/eeprominterface.h index ce9d13c67..c20edeeda 100644 --- a/companion/src/eeprominterface.h +++ b/companion/src/eeprominterface.h @@ -316,7 +316,7 @@ class RawSource { { } - RawSource(int value): + explicit RawSource(int value): type(RawSourceType(abs(value)/65536)), index(value >= 0 ? abs(value)%65536 : -(abs(value)%65536)) { @@ -333,7 +333,7 @@ class RawSource { return index >= 0 ? (type * 65536 + index) : -(type * 65536 - index); } - QString toString(const ModelData * model = NULL); + QString toString(const ModelData * model = NULL) const; RawSourceRange getRange(const ModelData * model, const GeneralSettings & settings, unsigned int flags=0) const; @@ -692,13 +692,13 @@ class FrSkyChannelData { }; struct FrSkyBarData { - unsigned int source; - unsigned int barMin; // minimum for bar display - unsigned int barMax; // ditto for max display (would usually = ratio) + RawSource source; + unsigned int barMin; // minimum for bar display + unsigned int barMax; // ditto for max display (would usually = ratio) }; struct FrSkyLineData { - unsigned int source[3]; + RawSource source[3]; }; struct TelemetryScriptData { @@ -716,7 +716,7 @@ class FrSkyScreenData { public: FrSkyScreenData() { clear(); } - typedef union { + typedef struct { FrSkyBarData bars[4]; FrSkyLineData lines[4]; TelemetryScriptData script; @@ -725,7 +725,7 @@ class FrSkyScreenData { unsigned int type; FrSkyScreenBody body; - void clear() { memset(this, 0, sizeof(FrSkyScreenData)); } + void clear(); }; enum TelemetryVarioSources { @@ -1252,7 +1252,6 @@ enum Capability { Haptic, HasBeeper, ModelTrainerEnable, - Timer2ThrTrig, HasExpoNames, HasNoExpo, HasMixerNames, @@ -1265,7 +1264,6 @@ enum Capability { OptrexDisplay, PPMExtCtrl, PPMFrameLength, - DSM2Indexes, Telemetry, TelemetryUnits, TelemetryBars, @@ -1301,6 +1299,8 @@ enum Capability { HasDisplayText, VirtualInputs, TrainerInputs, + RtcTime, + SportTelemetry, LuaScripts, LuaInputsPerScript, LuaOutputsPerScript, diff --git a/companion/src/firmwares/opentx/open9xGruvin9xeeprom.cpp b/companion/src/firmwares/opentx/open9xGruvin9xeeprom.cpp index 63df8e9d1..77ce0ee70 100644 --- a/companion/src/firmwares/opentx/open9xGruvin9xeeprom.cpp +++ b/companion/src/firmwares/opentx/open9xGruvin9xeeprom.cpp @@ -502,13 +502,6 @@ t_Open9xGruvin9xModelData_v207::operator ModelData () c9x.moduleData[0].ppmFrameLength = ppmFrameLength; c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } - return c9x; } @@ -588,16 +581,9 @@ t_Open9xGruvin9xModelData_v208::operator ModelData () c9x.moduleData[0].ppmFrameLength = ppmFrameLength; c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } for (int i=0; i<16; i++) { c9x.limitData[i].ppmCenter = servoCenter[i]; } - return c9x; } @@ -678,16 +664,9 @@ t_Open9xGruvin9xModelData_v209::operator ModelData () c9x.moduleData[0].ppmFrameLength = ppmFrameLength; c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } for (int i=0; i<16; i++) { c9x.limitData[i].ppmCenter = servoCenter[i]; } - return c9x; } diff --git a/companion/src/firmwares/opentx/open9xSky9xeeprom.cpp b/companion/src/firmwares/opentx/open9xSky9xeeprom.cpp index 9f0ab590c..4122bd573 100644 --- a/companion/src/firmwares/opentx/open9xSky9xeeprom.cpp +++ b/companion/src/firmwares/opentx/open9xSky9xeeprom.cpp @@ -683,21 +683,8 @@ t_Open9xArmFrSkyData_v210::operator FrSkyData () c9x.blades = blades+2; c9x.currentSource=currentSource; c9x.screens[0].type = 1; - for (int i=0; i<4; i++) { - c9x.screens[0].body.bars[i].source = bars[i].source; - c9x.screens[0].body.bars[i].barMin = bars[i].barMin; - c9x.screens[0].body.bars[i].barMax = bars[i].barMax; - } c9x.rssiAlarms[0] = rssiAlarms[0].get(0); c9x.rssiAlarms[1] = rssiAlarms[1].get(1); - - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - int i=line*2+col; - c9x.screens[1].body.lines[line].source[col] = lines[i]; - } - } - return c9x; } @@ -711,21 +698,8 @@ t_Open9xArmFrSkyData_v211::operator FrSkyData () c9x.blades = blades+2; c9x.currentSource=currentSource; c9x.screens[0].type = 1; - for (int i=0; i<4; i++) { - c9x.screens[0].body.bars[i].source = bars[i].source; - c9x.screens[0].body.bars[i].barMin = bars[i].barMin; - c9x.screens[0].body.bars[i].barMax = bars[i].barMax; - } c9x.rssiAlarms[0] = rssiAlarms[0].get(0); c9x.rssiAlarms[1] = rssiAlarms[1].get(1); - - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - int i=line*2+col; - c9x.screens[1].body.lines[line].source[col] = lines[i]; - } - } - c9x.varioSource = varioSource; c9x.varioCenterMax = varioSpeedUpMin; c9x.varioCenterMin = varioSpeedDownMin; @@ -809,12 +783,6 @@ t_Open9xArmModelData_v208::operator ModelData () c9x.moduleData[0].ppmFrameLength = ppmFrameLength; c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } for (int i=0; i<16; i++) { c9x.limitData[i].ppmCenter = servoCenter[i]; } @@ -899,12 +867,6 @@ t_Open9xArmModelData_v209::operator ModelData () c9x.moduleData[0].ppmFrameLength = ppmFrameLength; c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } for (int i=0; i<16; i++) { c9x.limitData[i].ppmCenter = servoCenter[i]; } diff --git a/companion/src/firmwares/opentx/open9xStockeeprom.cpp b/companion/src/firmwares/opentx/open9xStockeeprom.cpp index 040d6ab07..bc510dc0a 100644 --- a/companion/src/firmwares/opentx/open9xStockeeprom.cpp +++ b/companion/src/firmwares/opentx/open9xStockeeprom.cpp @@ -24,8 +24,8 @@ int8_t char2idx(char c) 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; + if (cc==0) return 0; + if (cc==c) return 37+i; } } @@ -828,7 +828,7 @@ t_Open9xFrSkyData_v203::operator FrSkyData () t_Open9xFrSkyBarData_v204::operator FrSkyBarData () { FrSkyBarData c9x; - c9x.source = source; + c9x.source = RawSource(SOURCE_TYPE_TELEMETRY, source); c9x.barMin = barMin; c9x.barMax = barMax; return c9x; @@ -899,12 +899,6 @@ t_Open9xFrSkyData_v210::operator FrSkyData () } c9x.screens[lines_screen_index].type = 0; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.screens[lines_screen_index].body.lines[line].source[col] = (col==0 ? (lines[line] & 0x0f) : ((lines[line] & 0xf0) / 16)); - c9x.screens[lines_screen_index].body.lines[line].source[col] += (((linesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } c9x.rssiAlarms[0] = rssiAlarms[0].get(0); c9x.rssiAlarms[1] = rssiAlarms[1].get(1); @@ -1289,13 +1283,6 @@ t_Open9xModelData_v205::operator ModelData () c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0]. modelId = modelId; c9x.frsky.screens[1].type = 0; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } - return c9x; } @@ -1379,16 +1366,9 @@ t_Open9xModelData_v208::operator ModelData () c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; c9x.frsky.screens[1].type = 0; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } for (int i=0; i<16; i++) { c9x.limitData[i].ppmCenter = servoCenter[i]; } - return c9x; } @@ -1473,16 +1453,9 @@ t_Open9xModelData_v209::operator ModelData () c9x.thrTraceSrc = thrTraceSrc; c9x.moduleData[0].modelId = modelId; c9x.frsky.screens[1].type = 0; - for (int line=0; line<4; line++) { - for (int col=0; col<2; col++) { - c9x.frsky.screens[1].body.lines[line].source[col] = (col==0 ? (frskyLines[line] & 0x0f) : ((frskyLines[line] & 0xf0) / 16)); - c9x.frsky.screens[1].body.lines[line].source[col] += (((frskyLinesXtra >> (4*line+2*col)) & 0x03) * 16); - } - } for (int i=0; i<16; i++) { c9x.limitData[i].ppmCenter = servoCenter[i]; } - return c9x; } diff --git a/companion/src/firmwares/opentx/opentxeeprom.cpp b/companion/src/firmwares/opentx/opentxeeprom.cpp index 5d9cc8bd3..d9f88b355 100644 --- a/companion/src/firmwares/opentx/opentxeeprom.cpp +++ b/companion/src/firmwares/opentx/opentxeeprom.cpp @@ -277,8 +277,9 @@ class SourcesConversionTable: public ConversionTable { addConversion(RawSource(SOURCE_TYPE_SPECIAL, 2), val++); // Timer1 addConversion(RawSource(SOURCE_TYPE_SPECIAL, 3), val++); // Timer2 addConversion(RawSource(SOURCE_TYPE_SPECIAL, 4), val++); // Timer3 - for (int i=0; i > { { } + virtual ~SwitchField() + { + } + virtual void beforeExport() { _switch = sw.toValue(); @@ -517,20 +522,38 @@ class TelemetrySourcesConversionTable: public ConversionTable { template class TelemetrySourceField: public ConversionField< UnsignedField > { public: - TelemetrySourceField(unsigned int & source, BoardEnum board, unsigned int version): - ConversionField< UnsignedField >(source, &conversionTable, "Telemetry source"), + TelemetrySourceField(RawSource & source, BoardEnum board, unsigned int version): + ConversionField< UnsignedField >(_source, &conversionTable, "Telemetry source"), conversionTable(board, version), source(source), board(board), - version(version) + version(version), + _source(0) { } + virtual ~TelemetrySourceField() + { + } + + virtual void beforeExport() + { + _source = (source.type == SOURCE_TYPE_TELEMETRY ? source.index+1 : 0); + ConversionField< UnsignedField >::beforeExport(); + } + + virtual void afterImport() + { + ConversionField< UnsignedField >::afterImport(); + source = (_source == 0 ? RawSource(0) : RawSource(SOURCE_TYPE_TELEMETRY, _source-1)); + } + protected: TelemetrySourcesConversionTable conversionTable; - unsigned int & source; + RawSource & source; BoardEnum board; unsigned int version; + unsigned int _source; }; template @@ -538,14 +561,19 @@ class SourceField: public ConversionField< UnsignedField > { public: SourceField(RawSource & source, BoardEnum board, unsigned int version, unsigned int variant, unsigned long flags=0): ConversionField< UnsignedField >(_source, SourcesConversionTable::getInstance(board, version, variant, flags), - "Source", "Source "+ source.toString()+" cannot be exported on this board!"), + "Source", QObject::tr("Source %1 cannot be exported on this board!").arg(source.toString())), source(source), _source(0) { } + virtual ~SourceField() + { + } + virtual void beforeExport() { + if (source.type == SOURCE_TYPE_TELEMETRY) qDebug() << source.type << source.index; _source = source.toValue(); ConversionField< UnsignedField >::beforeExport(); } @@ -2203,6 +2231,7 @@ class AvrCustomFunctionField: public TransformedField { functionsConversionTable(board, version), sourcesConversionTable(SourcesConversionTable::getInstance(board, version, variant, version >= 216 ? 0 : FLAG_NONONE)), _param(0), + _mode(0), _union_param(0), _active(0) { @@ -2380,7 +2409,7 @@ class AvrCustomFunctionField: public TransformedField { class FrskyScreenField: public DataField { public: - FrskyScreenField(FrSkyScreenData & screen, BoardEnum board, unsigned int version): + FrskyScreenField(FrSkyScreenData & screen, BoardEnum board, unsigned int version, unsigned int variant): DataField("Frsky Screen"), screen(screen), board(board), @@ -2389,9 +2418,9 @@ class FrskyScreenField: public DataField { for (int i=0; i<4; i++) { if (IS_ARM(board) && version >= 217) { if (IS_TARANIS(board)) - bars.Append(new TelemetrySourceField<16>(screen.body.bars[i].source, board, version)); + bars.Append(new SourceField<16>(screen.body.bars[i].source, board, version, variant)); else - bars.Append(new TelemetrySourceField<8>(screen.body.bars[i].source, board, version)); + bars.Append(new SourceField<8>(screen.body.bars[i].source, board, version, variant)); bars.Append(new UnsignedField<16>(screen.body.bars[i].barMin)); bars.Append(new UnsignedField<16>(screen.body.bars[i].barMax)); } @@ -2406,7 +2435,9 @@ class FrskyScreenField: public DataField { for (int i=0; i<4; i++) { for (int j=0; j= 217) - numbers.Append(new TelemetrySourceField<16>(screen.body.lines[i].source[j], board, version)); + numbers.Append(new SourceField<16>(screen.body.lines[i].source[j], board, version, variant)); + else if (IS_ARM(board) && version >= 217) + numbers.Append(new SourceField<8>(screen.body.lines[i].source[j], board, version, variant)); else numbers.Append(new TelemetrySourceField<8>(screen.body.lines[i].source[j], board, version)); } @@ -2419,12 +2450,12 @@ class FrskyScreenField: public DataField { numbers.Append(new SpareBitsField<4*8>()); } - if (version >= 217) { - if (IS_TARANIS(board)) - none.Append(new SpareBitsField<24*8>()); - else - none.Append(new SpareBitsField<20*8>()); - } + if (IS_TARANIS(board)) + none.Append(new SpareBitsField<24*8>()); + else if (IS_ARM(board)) + none.Append(new SpareBitsField<20*8>()); + else + none.Append(new SpareBitsField<12*8>()); if (IS_TARANIS(board) && version >= 217) { script.Append(new CharField<8>(screen.body.script.filename)); @@ -2532,6 +2563,20 @@ class TelemetryVoltsSourceConversionTable: public ConversionTable } }; +class ScreenTypesConversionTable: public ConversionTable +{ + public: + ScreenTypesConversionTable(BoardEnum board, unsigned int version) + { + int val = 0; + if (IS_ARM(board)) { + addConversion(TELEMETRY_SCREEN_NONE, val++); + } + addConversion(TELEMETRY_SCREEN_NUMBERS, val++); + addConversion(TELEMETRY_SCREEN_BARS, val++); + } +}; + class TelemetryCurrentSourceConversionTable: public ConversionTable { public: @@ -2551,9 +2596,10 @@ class TelemetryCurrentSourceConversionTable: public ConversionTable class FrskyField: public StructField { public: - FrskyField(FrSkyData & frsky, BoardEnum board, unsigned int version): + FrskyField(FrSkyData & frsky, BoardEnum board, unsigned int version, unsigned int variant): StructField("FrSky"), telemetryVarioSourceConversionTable(board, version), + screenTypesConversionTable(board, version), telemetryVoltsSourceConversionTable(board, version), telemetryCurrentSourceConversionTable(board, version) { @@ -2596,7 +2642,7 @@ class FrskyField: public StructField { Append(new UnsignedField<2>(frsky.screens[i].type)); } for (int i=0; i<4; i++) { - Append(new FrskyScreenField(frsky.screens[i], board, version)); + Append(new FrskyScreenField(frsky.screens[i], board, version, variant)); } } else { @@ -2605,7 +2651,7 @@ class FrskyField: public StructField { Append(new UnsignedField<1>(frsky.screens[2].type)); Append(new SpareBitsField<5>()); for (int i=0; i<3; i++) { - Append(new FrskyScreenField(frsky.screens[i], board, version)); + Append(new FrskyScreenField(frsky.screens[i], board, version, variant)); } } @@ -2639,8 +2685,9 @@ class FrskyField: public StructField { } Append(new UnsignedField<2>(frsky.usrProto, "USR Proto")); Append(new ConversionField< UnsignedField<2> >((unsigned int &)frsky.blades, -2)); - Append(new UnsignedField<1>(frsky.screens[0].type)); - Append(new UnsignedField<1>(frsky.screens[1].type)); + for (int i=0; i<2; i++) { + Append(new ConversionField< UnsignedField<1> >(frsky.screens[i].type, &screenTypesConversionTable, "Screen Type")); + } Append(new ConversionField< UnsignedField<2> >(frsky.voltsSource, &telemetryVoltsSourceConversionTable, "Volts Source")); Append(new SignedField<4>(frsky.varioMin, "Vario Min")); Append(new SignedField<4>(frsky.varioMax)); @@ -2649,7 +2696,7 @@ class FrskyField: public StructField { Append(new ConversionField< SignedField<6> >(frsky.rssiAlarms[i].value, -45+i*3, 0, 0, 100, "RSSI value")); } for (int i=0; i<2; i++) { - Append(new FrskyScreenField(frsky.screens[i], board, version)); + Append(new FrskyScreenField(frsky.screens[i], board, version, variant)); } Append(new UnsignedField<3>(frsky.varioSource)); Append(new SignedField<5>(frsky.varioCenterMin)); @@ -2664,6 +2711,7 @@ class FrskyField: public StructField { protected: RSSIConversionTable rssiConversionTable[2]; TelemetryVarioSourceConversionTable telemetryVarioSourceConversionTable; + ScreenTypesConversionTable screenTypesConversionTable; TelemetryVoltsSourceConversionTable telemetryVoltsSourceConversionTable; TelemetryCurrentSourceConversionTable telemetryCurrentSourceConversionTable; }; @@ -2940,7 +2988,7 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, BoardEnum board, unsigne } if ((board != BOARD_STOCK && (board != BOARD_M128 || version < 215)) || (variant & FRSKY_VARIANT)) { - internalField.Append(new FrskyField(modelData.frsky, board, version)); + internalField.Append(new FrskyField(modelData.frsky, board, version, variant)); } else if ((board == BOARD_STOCK || board == BOARD_M128) && (variant & MAVLINK_VARIANT)) { internalField.Append(new MavlinkField(modelData.mavlink, board, version)); @@ -2955,7 +3003,7 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, BoardEnum board, unsigne if (IS_TARANIS(board)) { modulesCount = 3; if (version >= 217) { - internalField.Append(new ConversionField< SignedField<3> >(modelData.moduleData[1].protocol, &protocolsConversionTable, "Protocol", ::QObject::tr("OpenTX doesn't accept this radio protocol"))); + internalField.Append(new SpareBitsField<3>()); internalField.Append(new UnsignedField<3>(modelData.trainerMode)); internalField.Append(new UnsignedField<2>(modelData.potsWarningMode)); } @@ -2967,8 +3015,7 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, BoardEnum board, unsigne else if (IS_ARM(board)) { if (version >= 217) { modulesCount = 3; - internalField.Append(new ConversionField< SignedField<3> >(modelData.moduleData[0].protocol, &protocolsConversionTable, "Protocol", ::QObject::tr("OpenTX doesn't accept this radio protocol"))); - internalField.Append(new SpareBitsField<3>()); + internalField.Append(new SpareBitsField<6>()); internalField.Append(new UnsignedField<2>(modelData.potsWarningMode)); } else if (version >= 216) { @@ -2979,7 +3026,13 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, BoardEnum board, unsigne if (IS_ARM(board) && version >= 215) { for (int module=0; module(subprotocols[module])); + if (version >= 217) { + internalField.Append(new ConversionField< SignedField<4> >(modelData.moduleData[module].protocol, &protocolsConversionTable, "Protocol", ::QObject::tr("OpenTX doesn't accept this radio protocol"))); + internalField.Append(new SignedField<4>(subprotocols[module])); + } + else { + internalField.Append(new SignedField<8>(subprotocols[module])); + } internalField.Append(new UnsignedField<8>(modelData.moduleData[module].channelsStart)); internalField.Append(new ConversionField< SignedField<8> >(modelData.moduleData[module].channelsCount, -8)); if (version >= 217) @@ -2989,18 +3042,17 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, BoardEnum board, unsigne for (int i=0; i<32; i++) { internalField.Append(new SignedField<16>(modelData.moduleData[module].failsafeChannels[i])); } - internalField.Append(new ConversionField< SignedField<8> >(modelData.moduleData[module].ppmDelay, exportPpmDelay, importPpmDelay)); - internalField.Append(new SignedField<8>(modelData.moduleData[module].ppmFrameLength)); - if (IS_9XRPRO(board)) { + if (version >= 217) { + internalField.Append(new ConversionField< SignedField<6> >(modelData.moduleData[module].ppmDelay, exportPpmDelay, importPpmDelay)); internalField.Append(new BoolField<1>(modelData.moduleData[module].ppmPulsePol)); internalField.Append(new BoolField<1>(modelData.moduleData[module].ppmOutputType)); - internalField.Append(new SpareBitsField<6>()); + internalField.Append(new SignedField<8>(modelData.moduleData[module].ppmFrameLength)); } else { - internalField.Append(new BoolField<1>(modelData.moduleData[module].ppmPulsePol)); - internalField.Append(new SpareBitsField<7>()); + internalField.Append(new ConversionField< SignedField<8> >(modelData.moduleData[module].ppmDelay, exportPpmDelay, importPpmDelay)); + internalField.Append(new SignedField<8>(modelData.moduleData[module].ppmFrameLength)); + internalField.Append(new BoolField<8>(modelData.moduleData[module].ppmPulsePol)); } - } } diff --git a/companion/src/firmwares/opentx/opentxinterface.cpp b/companion/src/firmwares/opentx/opentxinterface.cpp index 4c9bc50e3..241177273 100644 --- a/companion/src/firmwares/opentx/opentxinterface.cpp +++ b/companion/src/firmwares/opentx/opentxinterface.cpp @@ -662,8 +662,6 @@ int OpenTxFirmware::getCapability(const Capability capability) return 500; case Simulation: return 1; - case DSM2Indexes: - return 1; case NumCurves: return (IS_TARANIS(board) ? 32 : (IS_ARM(board) ? 16 : 8)); case HasMixerNames: @@ -737,8 +735,10 @@ int OpenTxFirmware::getCapability(const Capability capability) return IS_TARANIS(board) ? 32 : 0; case TrainerInputs: return IS_ARM(board) ? 16 : 8; + case RtcTime: + return IS_ARM(board) || IS_2560(board) ? 1 : 0; case LuaScripts: - return IS_TARANIS(board) ? 7 : 0; + return IS_TARANIS(board) && id.contains("lua") ? 7 : 0; case LuaInputsPerScript: return IS_TARANIS(board) ? 10 : 0; case LuaOutputsPerScript: @@ -763,6 +763,8 @@ int OpenTxFirmware::getCapability(const Capability capability) return 0; case MavlinkTelemetry: return id.contains("mavlink") ? 1 : 0; + case SportTelemetry: + return IS_ARM(board) ? 1 : 0; case HasInputDiff: case HasMixerExpo: return (IS_TARANIS(board) ? true : false); @@ -1078,13 +1080,25 @@ void addOpenTxCommonOptions(OpenTxFirmware * firmware) firmware->addOptions(fai_options); } +void addOpenTxLcdOptions(OpenTxFirmware * firmware) +{ + Option lcd_options[] = { + { "ST7565P", QObject::tr("ST7565P LCD or compatible") }, + { "ST7565R", QObject::tr("ST7565R LCD or compatible") }, + { "ERC12864FSF", QObject::tr("ERC12864FSF LCD") }, + { "ST7920", QObject::tr("ST7920 LCD") }, + { NULL } + }; + firmware->addOptions(lcd_options); +} + void registerOpenTxFirmwares() { OpenTxFirmware * openTx; Option ext_options[] = { { "frsky", QObject::tr("Support for frsky telemetry mod"), FRSKY_VARIANT }, { "telemetrez", QObject::tr("Support for telemetry easy board"), FRSKY_VARIANT }, { "jeti", QObject::tr("Support for jeti telemetry mod"), 0 }, { "ardupilot", QObject::tr("Support for receiving ardupilot data"), 0 }, { "nmea", QObject::tr("Support for receiving NMEA data"), 0 }, { "mavlink", QObject::tr("Support for MAVLINK devices"), MAVLINK_VARIANT }, { NULL } }; - Option nav_options[] = { { "rotenc", QObject::tr("Rotary Encoder use in menus navigation") }, { "potscroll", QObject::tr("Pots use in menus navigation") }, { NULL } }; Option extr_options[] = { { "frsky", QObject::tr("Support for frsky telemetry mod"), FRSKY_VARIANT }, { "jeti", QObject::tr("Support for jeti telemetry mod"), 0 }, { "ardupilot", QObject::tr("Support for receiving ardupilot data"), 0 }, { "nmea", QObject::tr("Support for receiving NMEA data"), 0 }, { "mavlink", QObject::tr("Support for MAVLINK devices"), MAVLINK_VARIANT }, { NULL } }; + Option nav_options[] = { { "rotenc", QObject::tr("Rotary Encoder use in menus navigation") }, { "potscroll", QObject::tr("Pots use in menus navigation") }, { NULL } }; Option dsm2_options[] = { { "DSM2", QObject::tr("Support for DSM2 modules"), 0 }, { "DSM2PPM", QObject::tr("Support for DSM2 modules using ppm instead of true serial"), 0 }, { NULL } }; /* 9x board */ @@ -1241,16 +1255,16 @@ void registerOpenTxFirmwares() /* MEGA2560 board */ openTx = new OpenTxFirmware("opentx-mega2560", QObject::tr("OpenTX for MEGA2560 board"), BOARD_MEGA2560); - openTx->addOption("ST7565R", QObject::tr("ST7565R LCD or compatible")); - openTx->addOption("heli", QObject::tr("Enable heli menu and cyclic mix support")); + addOpenTxLcdOptions(openTx); + openTx->addOptions(ext_options); + openTx->addOption("PXX", QObject::tr("Support of FrSky PXX protocol")); + openTx->addOptions(dsm2_options); openTx->addOption("heli", QObject::tr("Enable heli menu and cyclic mix support")); openTx->addOption("templates", QObject::tr("Enable TEMPLATES menu")); openTx->addOption("nofp", QObject::tr("No flight modes")); openTx->addOption("nocurves", QObject::tr("Disable curves menus")); openTx->addOption("sdcard", QObject::tr("Support for SD memory card")); openTx->addOption("voice", QObject::tr("Used if you have voice module")); - openTx->addOption("PXX", QObject::tr("Support of FrSky PXX protocol")); - openTx->addOptions(dsm2_options); openTx->addOption("ppmca", QObject::tr("PPM center adjustment in limits")); openTx->addOption("gvars", QObject::tr("Global variables"), GVARS_VARIANT); openTx->addOption("symlimits", QObject::tr("Symetrical Limits")); diff --git a/companion/src/flashfirmwaredialog.ui b/companion/src/flashfirmwaredialog.ui index 01d74b78c..3ff1ea558 100755 --- a/companion/src/flashfirmwaredialog.ui +++ b/companion/src/flashfirmwaredialog.ui @@ -241,7 +241,7 @@ Backup and restore Models and Settings - false + true diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index 91a9f1f7a..a5d00d0a3 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -534,7 +534,7 @@ void populateSourceCB(QComboBox *b, const RawSource & source, const GeneralSetti b->clear(); - if (flags & POPULATE_SOURCES) { + if (flags & POPULATE_NONE) { item = RawSource(SOURCE_TYPE_NONE); b->addItem(item.toString(model), item.toValue()); if (item == source) b->setCurrentIndex(b->count()-1); @@ -627,8 +627,8 @@ void populateSourceCB(QComboBox *b, const RawSource & source, const GeneralSetti } } - if (IS_ARM(GetCurrentFirmware()->getBoard())) { - if ((flags & POPULATE_TELEMETRY) || (flags & POPULATE_TELEMETRYEXT)) { + if (flags & POPULATE_TELEMETRY) { + if (IS_ARM(GetCurrentFirmware()->getBoard())) { for (int i=0; i<5; ++i) { item = RawSource(SOURCE_TYPE_SPECIAL, i); b->addItem(item.toString(model), item.toValue()); @@ -639,30 +639,26 @@ void populateSourceCB(QComboBox *b, const RawSource & source, const GeneralSetti for (int j=0; j<3; ++j) { item = RawSource(SOURCE_TYPE_TELEMETRY, 3*i+j); b->addItem(item.toString(model), item.toValue()); + // qDebug() << item.toString(model) << source.toString(model); if (item == source) b->setCurrentIndex(b->count()-1); } } } } - } - else if (flags & POPULATE_TELEMETRYEXT) { - for (int i=0; iaddItem(item.toString(model), item.toValue()); - if (item == source) b->setCurrentIndex(b->count()-1); - } - } - else if (flags & POPULATE_TELEMETRY) { - for (int i=0; iaddItem(item.toString(model), item.toValue()); - if (item == source) b->setCurrentIndex(b->count()-1); + else { + for (int i=0; i<(flags & POPULATE_TELEMETRYEXT ? TELEMETRY_SOURCES_STATUS_COUNT : TELEMETRY_SOURCES_COUNT); i++) { + if (i==TELEMETRY_SOURCE_RSSI_TX && IS_TARANIS(board)) + continue; + if (i==TELEMETRY_SOURCE_TX_TIME && !GetCurrentFirmware()->getCapability(RtcTime)) + continue; + if (i==TELEMETRY_SOURCE_SWR && !GetCurrentFirmware()->getCapability(SportTelemetry)) + continue; + if (i==TELEMETRY_SOURCE_TIMER3 && !IS_ARM(board)) + continue; + item = RawSource(SOURCE_TYPE_TELEMETRY, i); + b->addItem(item.toString(model), item.toValue()); + if (item == source) b->setCurrentIndex(b->count()-1); + } } } diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 5824e2235..aec01b20f 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -101,14 +101,15 @@ void populateGvarUseCB(QComboBox *b, unsigned int phase); QString getProtocolStr(const int proto); QString getPhasesStr(unsigned int phases, ModelData * model); -#define POPULATE_SOURCES (1<<0) -#define POPULATE_TRIMS (1<<1) -#define POPULATE_SWITCHES (1<<2) -#define POPULATE_GVARS (1<<3) -#define POPULATE_TELEMETRY (1<<4) -#define POPULATE_TELEMETRYEXT (1<<5) -#define POPULATE_VIRTUAL_INPUTS (1<<6) -#define POPULATE_SCRIPT_OUTPUTS (1<<7) +#define POPULATE_NONE (1<<0) +#define POPULATE_SOURCES (1<<1) +#define POPULATE_TRIMS (1<<2) +#define POPULATE_SWITCHES (1<<3) +#define POPULATE_GVARS (1<<4) +#define POPULATE_TELEMETRY (1<<5) +#define POPULATE_TELEMETRYEXT (1<<6) +#define POPULATE_VIRTUAL_INPUTS (1<<7) +#define POPULATE_SCRIPT_OUTPUTS (1<<8) #define GVARS_VARIANT 0x0001 #define FRSKY_VARIANT 0x0002 diff --git a/companion/src/logsdialog.cpp b/companion/src/logsdialog.cpp index dccd4708f..e6be94bfd 100644 --- a/companion/src/logsdialog.cpp +++ b/companion/src/logsdialog.cpp @@ -46,19 +46,35 @@ logsDialog::logsDialog(QWidget *parent) : ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectPlottables); - ui->customPlot->xAxis->setLabel(tr("Time (hh:mm:ss)")); - ui->customPlot->xAxis->setTickLabelType(QCPAxis::ltDateTime); - ui->customPlot->xAxis->setDateTimeFormat("hh:mm:ss"); + axisRect = ui->customPlot->axisRect(); + axisRect->axis(QCPAxis::atBottom)->setLabel(tr("Time (hh:mm:ss)")); + axisRect->axis(QCPAxis::atBottom)->setTickLabelType(QCPAxis::ltDateTime); + axisRect->axis(QCPAxis::atBottom)->setDateTimeFormat("hh:mm:ss"); QDateTime now = QDateTime::currentDateTime(); - ui->customPlot->xAxis->setRange(now.addSecs(-60*60*2).toTime_t(), + axisRect->axis(QCPAxis::atBottom)->setRange(now.addSecs(-60*60*2).toTime_t(), now.toTime_t()); - ui->customPlot->yAxis->setTickLabels(false); + axisRect->axis(QCPAxis::atLeft)->setTickLabels(false); + + axisRect->addAxis(QCPAxis::atLeft); + axisRect->addAxis(QCPAxis::atRight); + axisRect->axis(QCPAxis::atLeft, 1)->setVisible(false); + axisRect->axis(QCPAxis::atRight, 1)->setVisible(false); QFont legendFont = font(); legendFont.setPointSize(10); ui->customPlot->legend->setFont(legendFont); ui->customPlot->legend->setSelectedFont(legendFont); + axisRect->insetLayout()->setInsetAlignment(0, Qt::AlignTop | Qt::AlignLeft); + + rightLegend = new QCPLegend; + axisRect->insetLayout()->addElement(rightLegend, Qt::AlignTop | Qt::AlignRight); + rightLegend->setLayer("legend"); + rightLegend->setFont(legendFont); + rightLegend->setSelectedFont(legendFont); + rightLegend->setVisible(false); + + ui->customPlot->setAutoAddPlottableToLegend(false); QString Path=g.gePath(); if (Path.isEmpty() || !QFile(Path).exists()) { @@ -72,7 +88,7 @@ logsDialog::logsDialog(QWidget *parent) : connect(ui->customPlot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel())); // make left axes transfer its range to right axes: - connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(setRangeyAxis2(QCPRange))); + connect(axisRect->axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), this, SLOT(yAxisChangeRanges(QCPRange))); // connect some interaction slots: connect(ui->customPlot, SIGNAL(titleDoubleClick(QMouseEvent*, QCPPlotTitle*)), this, SLOT(titleDoubleClick(QMouseEvent*, QCPPlotTitle*))); @@ -151,24 +167,43 @@ void logsDialog::selectionChanged() if (plotLock) return; // handle bottom axis and tick labels as one selectable object: - if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || - ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels)) + if (axisRect->axis(QCPAxis::atBottom)->selectedParts().testFlag(QCPAxis::spAxis) || + axisRect->axis(QCPAxis::atBottom)->selectedParts().testFlag(QCPAxis::spTickLabels)) { - ui->customPlot->xAxis->setSelectedParts(QCPAxis::spAxis | + axisRect->axis(QCPAxis::atBottom)->setSelectedParts(QCPAxis::spAxis | QCPAxis::spTickLabels); } // make left and right axes be selected synchronously, // and handle axis and tick labels as one selectable object: - if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis) || - ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spTickLabels) || - ui->customPlot->yAxis2->selectedParts().testFlag(QCPAxis::spAxis) || - ui->customPlot->yAxis2->selectedParts().testFlag(QCPAxis::spTickLabels)) - { - ui->customPlot->yAxis->setSelectedParts(QCPAxis::spAxis | + if (axisRect->axis(QCPAxis::atLeft)->selectedParts().testFlag(QCPAxis::spAxis) || + axisRect->axis(QCPAxis::atLeft)->selectedParts().testFlag(QCPAxis::spTickLabels) || + ( + axisRect->axis(QCPAxis::atRight)->visible() && + (axisRect->axis(QCPAxis::atRight)->selectedParts().testFlag(QCPAxis::spAxis) || + axisRect->axis(QCPAxis::atRight)->selectedParts().testFlag(QCPAxis::spTickLabels)) + ) || ( + axisRect->axis(QCPAxis::atLeft, 1)->visible() && + (axisRect->axis(QCPAxis::atLeft, 1)->selectedParts().testFlag(QCPAxis::spAxis) || + axisRect->axis(QCPAxis::atLeft, 1)->selectedParts().testFlag(QCPAxis::spTickLabels)) + ) || ( + axisRect->axis(QCPAxis::atRight)->visible() && + (axisRect->axis(QCPAxis::atRight, 1)->selectedParts().testFlag(QCPAxis::spAxis) || + axisRect->axis(QCPAxis::atRight, 1)->selectedParts().testFlag(QCPAxis::spTickLabels)) + ) + ) { + axisRect->axis(QCPAxis::atLeft)->setSelectedParts(QCPAxis::spAxis | QCPAxis::spTickLabels); - if (hasyAxis2) { - ui->customPlot->yAxis2->setSelectedParts(QCPAxis::spAxis | + if (axisRect->axis(QCPAxis::atRight)->visible()) { + axisRect->axis(QCPAxis::atRight)->setSelectedParts(QCPAxis::spAxis | QCPAxis::spTickLabels); + if (axisRect->axis(QCPAxis::atLeft, 1)->visible()) { + axisRect->axis(QCPAxis::atLeft, 1)->setSelectedParts(QCPAxis::spAxis | + QCPAxis::spTickLabels); + if (axisRect->axis(QCPAxis::atRight, 1)->visible()) { + axisRect->axis(QCPAxis::atRight, 1)->setSelectedParts(QCPAxis::spAxis | + QCPAxis::spTickLabels); + } + } } } @@ -177,6 +212,7 @@ void logsDialog::selectionChanged() { QCPGraph *graph = ui->customPlot->graph(i); QCPPlottableLegendItem *item = ui->customPlot->legend->itemWithPlottable(graph); + if (item == NULL) item = rightLegend->itemWithPlottable(graph); if (item->selected() || graph->selected()) { item->setSelected(true); @@ -339,11 +375,11 @@ void logsDialog::on_mapsButton_clicked() outputStream << "\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n"; geFile.close(); QStringList parameters; -#ifdef __APPLE__ + #ifdef __APPLE__ parameters << "-a"; parameters << gePath; gePath = "/usr/bin/open"; -#endif + #endif parameters << geFilename; QProcess *process = new QProcess(this); process->start(gePath, parameters); @@ -351,18 +387,17 @@ void logsDialog::on_mapsButton_clicked() ui->logTable->setDisabled(false); } - void logsDialog::mousePress() { // if an axis is selected, only allow the direction of that axis to be dragged // if no axis is selected, both directions may be dragged - if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis)) - ui->customPlot->axisRect()->setRangeDrag(ui->customPlot->xAxis->orientation()); - else if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis)) - ui->customPlot->axisRect()->setRangeDrag(ui->customPlot->yAxis->orientation()); + if (axisRect->axis(QCPAxis::atBottom)->selectedParts().testFlag(QCPAxis::spAxis)) + axisRect->setRangeDrag(axisRect->axis(QCPAxis::atBottom)->orientation()); + else if (axisRect->axis(QCPAxis::atLeft)->selectedParts().testFlag(QCPAxis::spAxis)) + axisRect->setRangeDrag(axisRect->axis(QCPAxis::atLeft)->orientation()); else - ui->customPlot->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical); + axisRect->setRangeDrag(Qt::Horizontal | Qt::Vertical); } void logsDialog::mouseWheel() @@ -377,18 +412,9 @@ void logsDialog::mouseWheel() orientation|=Qt::Vertical; } if (orientation) { - ui->customPlot->axisRect()->setRangeZoom((Qt::Orientation)orientation); + axisRect->setRangeZoom((Qt::Orientation)orientation); } else { - ui->customPlot->axisRect()->setRangeZoom(Qt::Horizontal|Qt::Vertical); - } -} - -void logsDialog::removeSelectedGraph() -{ - if (ui->customPlot->selectedGraphs().size() > 0) - { - ui->customPlot->removeGraph(ui->customPlot->selectedGraphs().first()); - ui->customPlot->replot(); + axisRect->setRangeZoom(Qt::Horizontal|Qt::Vertical); } } @@ -396,8 +422,17 @@ void logsDialog::removeAllGraphs() { ui->customPlot->clearGraphs(); ui->customPlot->legend->setVisible(false); - ui->customPlot->yAxis2->setVisible(false); - ui->customPlot->yAxis->setTickLabels(false); + rightLegend->clearItems(); + rightLegend->setVisible(false); + axisRect->axis(QCPAxis::atRight)->setSelectedParts(QCPAxis::spNone); + axisRect->axis(QCPAxis::atRight)->setVisible(false); + axisRect->axis(QCPAxis::atLeft)->setSelectedParts(QCPAxis::spNone); + axisRect->axis(QCPAxis::atLeft)->setTickLabels(false); + axisRect->axis(QCPAxis::atLeft, 1)->setVisible(false); + axisRect->axis(QCPAxis::atLeft, 1)->setSelectedParts(QCPAxis::spNone); + axisRect->axis(QCPAxis::atRight, 1)->setVisible(false); + axisRect->axis(QCPAxis::atRight, 1)->setSelectedParts(QCPAxis::spNone); + axisRect->axis(QCPAxis::atBottom)->setSelectedParts(QCPAxis::spNone); ui->customPlot->replot(); } @@ -525,7 +560,6 @@ bool logsDialog::cvsFileParse() return true; } - void logsDialog::on_sessions_CB_currentIndexChanged(int index) { if (plotLock) return; @@ -588,9 +622,9 @@ void logsDialog::plotLogs() coords plotCoords; int plotColumn = plot->row() + 2; // Date and Time first - plotCoords.min_y = 999999; - plotCoords.max_y = -999999; - plotCoords.secondRange = false; + plotCoords.min_y = INVALID_MIN; + plotCoords.max_y = INVALID_MAX; + plotCoords.yaxis = firstLeft; plotCoords.name = plot->text(); for (int row = 0; row < rowCount; row++) { @@ -612,11 +646,8 @@ void logsDialog::plotLogs() y = logValue->text().toDouble(); plotCoords.y.push_back(y); - if (plotCoords.min_y > y) { - plotCoords.min_y = y; - } else if (plotCoords.max_y < y) { - plotCoords.max_y = y; - } + if (plotCoords.min_y > y) plotCoords.min_y = y; + if (plotCoords.max_y < y) plotCoords.max_y = y; if (time_str.contains('.')) { time = QDateTime::fromString(time_str, "yyyy-MM-dd HH:mm:ss.zzz") @@ -628,133 +659,219 @@ void logsDialog::plotLogs() } plotCoords.x.push_back(time); - if (plots.min_x > time) { - plots.min_x = time; - } else if (plots.max_x < time) { - plots.max_x = time; - } + if (plots.min_x > time) plots.min_x = time; + if (plots.max_x < time) plots.max_x = time; } + + double range_inc = (plotCoords.max_y - plotCoords.min_y) / 100; + if (range_inc == 0) range_inc = 1; + plotCoords.max_y += range_inc; + plotCoords.min_y -= range_inc; + plots.coords.append(plotCoords); } - plots.rangeOneMin = plots.coords.at(0).min_y; - plots.rangeOneMax = plots.coords.at(0).max_y; - plots.twoRanges = false; + yAxesRanges[firstLeft].min = plots.coords.at(0).min_y; + yAxesRanges[firstLeft].max = plots.coords.at(0).max_y; + for (int i = firstRight; i < AXES_LIMIT; i++) { + yAxesRanges[i].min = INVALID_MIN; + yAxesRanges[i].max = INVALID_MAX; + } plots.tooManyRanges = false; for (int i = 1; i < plots.coords.size(); i++) { - if (plots.tooManyRanges) { - if (plots.coords.at(i).min_y < plots.rangeOneMin) { - plots.rangeOneMin = plots.coords.at(i).min_y; - } - if (plots.coords.at(i).max_y > plots.rangeOneMax) { - plots.rangeOneMax = plots.coords.at(i).max_y; - } - - continue; - } - - double actualRange = plots.rangeOneMax - plots.rangeOneMin; + double actualRange = yAxesRanges[firstLeft].max - yAxesRanges[firstLeft].min; double thisRange = plots.coords.at(i).max_y - plots.coords.at(i).min_y; - if (thisRange > actualRange * 1.3 || thisRange * 1.3 < actualRange || - plots.coords.at(i).min_y > plots.rangeOneMax || - plots.coords.at(i).max_y < plots.rangeOneMin) { - plots.coords[i].secondRange = true; - if (plots.twoRanges) { - actualRange = plots.rangeTwoMax - plots.rangeTwoMin; - if (thisRange > actualRange * 1.3 || thisRange * 1.3 < actualRange || - plots.coords.at(i).min_y > plots.rangeTwoMax || - plots.coords.at(i).max_y < plots.rangeTwoMin) { + while (yAxesRanges[plots.coords.at(i).yaxis].max != INVALID_MAX && + (thisRange > actualRange * 1.3 || thisRange * 1.3 < actualRange || + plots.coords.at(i).min_y > yAxesRanges[plots.coords.at(i).yaxis].max || + plots.coords.at(i).max_y < yAxesRanges[plots.coords.at(i).yaxis].min) + ) { + + switch (plots.coords[i].yaxis) { + case firstLeft: + plots.coords[i].yaxis = firstRight; + break; + case firstRight: + plots.coords[i].yaxis = secondLeft; + break; + case secondLeft: + plots.coords[i].yaxis = secondRight; + break; + case secondRight: plots.tooManyRanges = true; - plots.twoRanges = false; - if (plots.rangeTwoMin < plots.rangeOneMin) { - plots.rangeOneMin = plots.rangeTwoMin; - } - if (plots.rangeTwoMax > plots.rangeOneMax) { - plots.rangeOneMax = plots.rangeTwoMax; - } - if (plots.coords.at(i).min_y < plots.rangeOneMin) { - plots.rangeOneMin = plots.coords.at(i).min_y; - } - if (plots.coords.at(i).max_y > plots.rangeOneMax) { - plots.rangeOneMax = plots.coords.at(i).max_y; - } - } else { - if (plots.coords.at(i).min_y < plots.rangeTwoMin) { - plots.rangeTwoMin = plots.coords.at(i).min_y; - } - if (plots.coords.at(i).max_y > plots.rangeTwoMax) { - plots.rangeTwoMax = plots.coords.at(i).max_y; - } - } - } else { - plots.twoRanges = true; - plots.rangeTwoMax = plots.coords.at(i).max_y; - plots.rangeTwoMin = plots.coords.at(i).min_y; + break; + default: + break; } + if (plots.tooManyRanges) break; + + actualRange = yAxesRanges[plots.coords.at(i).yaxis].max + - yAxesRanges[plots.coords.at(i).yaxis].min; + } + + if (plots.tooManyRanges) { + break; } else { - if (plots.coords.at(i).min_y < plots.rangeOneMin) { - plots.rangeOneMin = plots.coords.at(i).min_y; + if (plots.coords.at(i).min_y < yAxesRanges[plots.coords.at(i).yaxis].min) { + yAxesRanges[plots.coords.at(i).yaxis].min = plots.coords.at(i).min_y; } - if (plots.coords.at(i).max_y > plots.rangeOneMax) { - plots.rangeOneMax = plots.coords.at(i).max_y; + if (plots.coords.at(i).max_y > yAxesRanges[plots.coords.at(i).yaxis].max) { + yAxesRanges[plots.coords.at(i).yaxis].max = plots.coords.at(i).max_y; } } } - if (plots.twoRanges) { - rangeRatio = (plots.rangeTwoMax - plots.rangeTwoMin) / - (plots.rangeOneMax - plots.rangeOneMin); - rangeyAxisMin = plots.rangeOneMin; - rangeyAxisMax = plots.rangeOneMax; - rangeyAxis2Min = plots.rangeTwoMin; - rangeyAxis2Max = plots.rangeTwoMax; + if (plots.tooManyRanges) { + yAxesRanges[firstLeft].max = 101; + yAxesRanges[firstLeft].min = -1; + yAxesRanges[firstRight].max = INVALID_MAX; + yAxesRanges[firstRight].min = INVALID_MIN; + yAxesRanges[secondLeft].max = INVALID_MAX; + yAxesRanges[secondLeft].min = INVALID_MIN; + yAxesRanges[secondRight].max = INVALID_MAX; + yAxesRanges[secondRight].min = INVALID_MIN; - hasyAxis2 = true; + for (int i = 0; i < plots.coords.size(); i++) { + plots.coords[i].yaxis = firstLeft; + + double factor = 100 / (plots.coords.at(i).max_y - plots.coords.at(i).min_y); + for (int j = 0; j < plots.coords.at(i).y.count(); j++) { + plots.coords[i].y[j] = factor * (plots.coords.at(i).y.at(j) - plots.coords.at(i).min_y); + } + } } else { - hasyAxis2 = false; + for (int i = firstRight; i < AXES_LIMIT; i++) { + if (yAxesRanges[i].max == INVALID_MAX) break; + + yAxesRatios[i] = (yAxesRanges[i].max - yAxesRanges[i].min) / + (yAxesRanges[firstLeft].max - yAxesRanges[firstLeft].min); + } } removeAllGraphs(); - ui->customPlot->xAxis->setRange(plots.min_x, plots.max_x); + axisRect->axis(QCPAxis::atBottom)->setRange(plots.min_x, plots.max_x); - ui->customPlot->yAxis->setRange(plots.rangeOneMin, plots.rangeOneMax); - ui->customPlot->yAxis->setTickLabels(true); + axisRect->axis(QCPAxis::atLeft)->setRange(yAxesRanges[firstLeft].min, + yAxesRanges[firstLeft].max); - if (plots.twoRanges) { - ui->customPlot->yAxis2->setRange(plots.rangeTwoMin, plots.rangeTwoMax); - ui->customPlot->yAxis2->setVisible(true); + if (plots.tooManyRanges) { + axisRect->axis(QCPAxis::atLeft)->setTickLabels(false); + } else { + axisRect->axis(QCPAxis::atLeft)->setTickLabels(true); + } + + if (yAxesRanges[firstRight].max != INVALID_MAX) { + axisRect->axis(QCPAxis::atRight)->setRange(yAxesRanges[firstRight].min, + yAxesRanges[firstRight].max); + axisRect->axis(QCPAxis::atRight)->setVisible(true); + + rightLegend->setVisible(true); + + if (yAxesRanges[secondLeft].max != INVALID_MAX) { + axisRect->axis(QCPAxis::atLeft, 1)->setVisible(true); + axisRect->axis(QCPAxis::atLeft, 1)->setRange(yAxesRanges[secondLeft].min, + yAxesRanges[secondLeft].max); + + if (yAxesRanges[secondRight].max != INVALID_MAX) { + axisRect->axis(QCPAxis::atRight, 1)->setVisible(true); + axisRect->axis(QCPAxis::atRight, 1)->setRange(yAxesRanges[secondRight].min, + yAxesRanges[secondRight].max); + } + } } for (int i = 0; i < plots.coords.size(); i++) { - if (plots.coords.at(i).secondRange && plots.twoRanges) { - ui->customPlot->addGraph(ui->customPlot->xAxis, ui->customPlot->yAxis2); - } else { - ui->customPlot->addGraph(); + switch (plots.coords[i].yaxis) { + case firstLeft: + ui->customPlot->addGraph(); + if (yAxesRanges[secondLeft].max != INVALID_MAX) { + ui->customPlot->graph(i)->setName(plots.coords.at(i).name + tr(" (L1)")); + } else { + ui->customPlot->graph(i)->setName(plots.coords.at(i).name); + } + ui->customPlot->legend->addItem( + new QCPPlottableLegendItem(ui->customPlot->legend, ui->customPlot->graph(i))); + break; + case firstRight: + ui->customPlot->addGraph(axisRect->axis(QCPAxis::atBottom), + axisRect->axis(QCPAxis::atRight)); + if (yAxesRanges[secondRight].max != INVALID_MAX) { + ui->customPlot->graph(i)->setName(plots.coords.at(i).name + tr(" (R1)")); + } else { + ui->customPlot->graph(i)->setName(plots.coords.at(i).name); + } + rightLegend->addItem( + new QCPPlottableLegendItem(rightLegend, ui->customPlot->graph(i))); + break; + case secondLeft: + ui->customPlot->addGraph(axisRect->axis(QCPAxis::atBottom), + axisRect->axis(QCPAxis::atLeft, 1)); + ui->customPlot->graph(i)->setName(plots.coords.at(i).name + tr(" (L2)")); + ui->customPlot->legend->addItem( + new QCPPlottableLegendItem(ui->customPlot->legend, ui->customPlot->graph(i))); + break; + case secondRight: + ui->customPlot->addGraph(axisRect->axis(QCPAxis::atBottom), + axisRect->axis(QCPAxis::atRight, 1)); + ui->customPlot->graph(i)->setName(plots.coords.at(i).name + tr(" (R2)")); + rightLegend->addItem( + new QCPPlottableLegendItem(rightLegend, ui->customPlot->graph(i))); + break; + default: + break; } + ui->customPlot->graph(i)->setData(plots.coords.at(i).x, plots.coords.at(i).y); pen.setColor(colors.at(i % colors.size())); ui->customPlot->graph(i)->setPen(pen); - ui->customPlot->graph(i)->setName(plots.coords.at(i).name); } ui->customPlot->legend->setVisible(true); ui->customPlot->replot(); } -void logsDialog::setRangeyAxis2(QCPRange range) +void logsDialog::yAxisChangeRanges(QCPRange range) { - if (hasyAxis2) { - double lowerChange = (range.lower - rangeyAxisMin) * rangeRatio; - double upperChange = (range.upper - rangeyAxisMax) * rangeRatio; - rangeyAxisMin = range.lower; - rangeyAxisMax = range.upper; - rangeyAxis2Min += lowerChange; - rangeyAxis2Max += upperChange; + if (axisRect->axis(QCPAxis::atRight)->visible()) { + double lowerChange = (range.lower - yAxesRanges[firstLeft].min) * + yAxesRatios[firstRight]; + double upperChange = (range.upper - yAxesRanges[firstLeft].max) * + yAxesRatios[firstRight]; - ui->customPlot->yAxis2->setRange(rangeyAxis2Min, rangeyAxis2Max); + yAxesRanges[firstRight].min += lowerChange; + yAxesRanges[firstRight].max += upperChange; + axisRect->axis(QCPAxis::atRight)->setRange(yAxesRanges[firstRight].min, + yAxesRanges[firstRight].max); + + if (axisRect->axisCount(QCPAxis::atLeft) == 2) { + lowerChange = (range.lower - yAxesRanges[firstLeft].min) * + yAxesRatios[secondLeft]; + upperChange = (range.upper - yAxesRanges[firstLeft].max) * + yAxesRatios[secondLeft]; + + yAxesRanges[secondLeft].min += lowerChange; + yAxesRanges[secondLeft].max += upperChange; + axisRect->axis(QCPAxis::atLeft, 1)->setRange(yAxesRanges[secondLeft].min, + yAxesRanges[secondLeft].max); + + if (axisRect->axisCount(QCPAxis::atRight) == 2) { + lowerChange = (range.lower - yAxesRanges[firstLeft].min) * + yAxesRatios[secondRight]; + upperChange = (range.upper - yAxesRanges[firstLeft].max) * + yAxesRatios[secondRight]; + + yAxesRanges[secondRight].min += lowerChange; + yAxesRanges[secondRight].max += upperChange; + axisRect->axis(QCPAxis::atRight, 1)->setRange(yAxesRanges[secondRight].min, + yAxesRanges[secondRight].max); + } + } + + yAxesRanges[firstLeft].min = range.lower; + yAxesRanges[firstLeft].max = range.upper; } } diff --git a/companion/src/logsdialog.h b/companion/src/logsdialog.h index 368210665..062b8736d 100644 --- a/companion/src/logsdialog.h +++ b/companion/src/logsdialog.h @@ -5,24 +5,35 @@ #include #include "qcustomplot/qcustomplot.h" +#define INVALID_MIN 999999 +#define INVALID_MAX -999999 + +enum yaxes_t { + firstLeft = 0, + firstRight, + secondLeft, + secondRight, + AXES_LIMIT // = 4 +}; + struct coords { QVector x, y; double min_y; double max_y; - bool secondRange; + yaxes_t yaxis; QString name; }; +struct minMax { + double min; + double max; +}; + struct plotsCollection { QVarLengthArray coords; double min_x; double max_x; - bool twoRanges; bool tooManyRanges; - double rangeOneMin; - double rangeOneMax; - double rangeTwoMin; - double rangeTwoMax; }; namespace Ui { @@ -44,17 +55,18 @@ private slots: void selectionChanged(); void mousePress(); void mouseWheel(); - void removeSelectedGraph(); void removeAllGraphs(); void plotLogs(); void on_fileOpen_BT_clicked(); void on_sessions_CB_currentIndexChanged(int index); void on_mapsButton_clicked(); - void setRangeyAxis2(QCPRange range); + void yAxisChangeRanges(QCPRange range); private: QList csvlog; Ui::logsDialog *ui; + QCPAxisRect *axisRect; + QCPLegend *rightLegend; bool cvsFileParse(); bool plotLock; QString logFilename; @@ -62,12 +74,8 @@ private: QVarLengthArray colors; QPen pen; - bool hasyAxis2; - double rangeRatio; - double rangeyAxisMin; - double rangeyAxisMax; - double rangeyAxis2Min; - double rangeyAxis2Max; + double yAxesRatios[AXES_LIMIT]; + minMax yAxesRanges[AXES_LIMIT]; }; #endif // LOGSDIALOG_H diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 0e02072a2..a7b6c08b3 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -715,15 +715,15 @@ void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsi b->setCurrentIndex(value); } else if (function==FuncVolume) { - populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_TRIMS); + populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_NONE|POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_TRIMS); } else if (function==FuncPlayValue) { - populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_SWITCHES|POPULATE_GVARS|POPULATE_TRIMS|POPULATE_TELEMETRYEXT); + populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_NONE|POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_SWITCHES|POPULATE_GVARS|POPULATE_TRIMS|POPULATE_TELEMETRY|POPULATE_TELEMETRYEXT); } else if (function>=FuncAdjustGV1 && function<=FuncAdjustGVLast) { switch (adjustmode) { case 1: - populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_TRIMS|POPULATE_SWITCHES); + populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_NONE|POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_TRIMS|POPULATE_SWITCHES); break; case 2: populateSourceCB(b, RawSource(value), generalSettings, model, POPULATE_GVARS); diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 54f7fb61f..d64c0887c 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -60,7 +60,7 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G if (firmware->getCapability(VirtualInputs)) { ui->inputName->setMaxLength(4); - populateSourceCB(ui->sourceCB, ed->srcRaw, generalSettings, &model, POPULATE_SOURCES | POPULATE_SWITCHES | POPULATE_TRIMS | POPULATE_TELEMETRY); + populateSourceCB(ui->sourceCB, ed->srcRaw, generalSettings, &model, POPULATE_NONE | POPULATE_SOURCES | POPULATE_SWITCHES | POPULATE_TRIMS | POPULATE_TELEMETRY); ui->sourceCB->removeItem(0); } else { diff --git a/companion/src/modeledit/heli.cpp b/companion/src/modeledit/heli.cpp index 391c1c328..7de479a11 100644 --- a/companion/src/modeledit/heli.cpp +++ b/companion/src/modeledit/heli.cpp @@ -70,11 +70,11 @@ void HeliPanel::edited() { if (!lock) { model->swashRingData.type = ui->swashType->currentIndex(); - model->swashRingData.collectiveSource = ui->swashCollectiveSource->itemData(ui->swashCollectiveSource->currentIndex()).toInt(); + model->swashRingData.collectiveSource = RawSource(ui->swashCollectiveSource->itemData(ui->swashCollectiveSource->currentIndex()).toInt()); model->swashRingData.value = ui->swashRingVal->value(); if (firmware->getCapability(VirtualInputs)) { - model->swashRingData.elevatorSource = ui->swashElevatorSource->itemData(ui->swashElevatorSource->currentIndex()).toInt(); - model->swashRingData.aileronSource = ui->swashAileronSource->itemData(ui->swashAileronSource->currentIndex()).toInt(); + model->swashRingData.elevatorSource = RawSource(ui->swashElevatorSource->itemData(ui->swashElevatorSource->currentIndex()).toInt()); + model->swashRingData.aileronSource = RawSource(ui->swashAileronSource->itemData(ui->swashAileronSource->currentIndex()).toInt()); model->swashRingData.elevatorWeight = ui->swashElevatorWeight->value(); model->swashRingData.aileronWeight = ui->swashAileronWeight->value(); model->swashRingData.collectiveWeight = ui->swashCollectiveWeight->value(); diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index dcb138072..60dfbeb64 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -301,7 +301,7 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) { case LS_FAMILY_VOFS: mask |= SOURCE1_VISIBLE; - populateSourceCB(cswitchSource1[i], source, generalSettings, model, POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); + populateSourceCB(cswitchSource1[i], source, generalSettings, model, POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); cswitchOffset[i]->setDecimals(range.decimals); cswitchOffset[i]->setSingleStep(range.step); if (source.isTimeBased()) { @@ -346,8 +346,8 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) break; case LS_FAMILY_VCOMP: mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE; - populateSourceCB(cswitchSource1[i], RawSource(model->logicalSw[i].val1), generalSettings, model, POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); - populateSourceCB(cswitchSource2[i], RawSource(model->logicalSw[i].val2), generalSettings, model, POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); + populateSourceCB(cswitchSource1[i], RawSource(model->logicalSw[i].val1), generalSettings, model, POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); + populateSourceCB(cswitchSource2[i], RawSource(model->logicalSw[i].val2), generalSettings, model, POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); break; case LS_FAMILY_TIMER: mask |= VALUE1_VISIBLE | VALUE2_VISIBLE; diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 12a754673..b4191d9ca 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -19,7 +19,7 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData *mixdata, G this->setWindowTitle(tr("DEST -> CH%1").arg(md->destCh)); - populateSourceCB(ui->sourceCB, md->srcRaw, generalSettings, &model, POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_SWITCHES | POPULATE_TRIMS); + populateSourceCB(ui->sourceCB, md->srcRaw, generalSettings, &model, POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_SWITCHES | POPULATE_TRIMS); ui->sourceCB->removeItem(0); int limit = firmware->getCapability(OffsetWeight); diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index ab66467f1..b891ad5da 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -330,31 +330,24 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, disableMouseScrolling(); + if (IS_ARM(firmware->getBoard())) + ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE); + ui->screenType->addItem(tr("Numbers"), TELEMETRY_SCREEN_NUMBERS); + ui->screenType->addItem(tr("Bars"), TELEMETRY_SCREEN_BARS); + if (IS_TARANIS(firmware->getBoard())) + ui->screenType->addItem(tr("Script"), TELEMETRY_SCREEN_SCRIPT); + ui->screenType->setField(screen.type, this); + update(); } -void TelemetryCustomScreen::populateTelemetrySourceCB(QComboBox *b, unsigned int value, bool last, int hubproto) +void TelemetryCustomScreen::populateTelemetrySourceCB(QComboBox * b, RawSource & source, bool last) { - b->clear(); - if (IS_ARM(firmware->getBoard())) { - // TODO + populateSourceCB(b, source, generalSettings, model, POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0)); } else { - b->addItem(RawSource(SOURCE_TYPE_NONE, 0).toString(model)); - - for (unsigned int i = 0; i < (last ? TELEMETRY_SOURCES_STATUS_COUNT : TELEMETRY_SOURCES_DISPLAY_COUNT); i++) { - b->addItem(RawSource(SOURCE_TYPE_TELEMETRY, i).toString(model)); - if (!firmware->isTelemetrySourceAvailable(i)) { - // disable item - QModelIndex index = b->model()->index(i+1, 0); - QVariant v(0); - b->model()->setData(index, v, Qt::UserRole - 1); - } - } - - b->setCurrentIndex(value); - b->setMaxVisibleItems(10); + populateSourceCB(b, source, generalSettings, model, POPULATE_NONE | POPULATE_TELEMETRY | (last ? POPULATE_TELEMETRYEXT : 0)); } } @@ -367,18 +360,17 @@ void TelemetryCustomScreen::update() { lock = true; - ui->screenType->setCurrentIndex(screen.type); ui->screenNums->setVisible(screen.type == TELEMETRY_SCREEN_NUMBERS); ui->screenBars->setVisible(screen.type == TELEMETRY_SCREEN_BARS); for (int l=0; l<4; l++) { for (int c=0; cgetCapability(TelemetryCustomScreensFieldsPerLine); c++) { - populateTelemetrySourceCB(fieldsCB[l][c], screen.body.lines[l].source[c], l==3, model->frsky.usrProto); + populateTelemetrySourceCB(fieldsCB[l][c], screen.body.lines[l].source[c], l==3); } } for (int l=0; l<4; l++) { - populateTelemetrySourceCB(barsCB[l], screen.body.bars[l].source, false, model->frsky.usrProto); + populateTelemetrySourceCB(barsCB[l], screen.body.bars[l].source); } if (screen.type == TELEMETRY_SCREEN_BARS) { @@ -394,10 +386,8 @@ void TelemetryCustomScreen::updateBar(int line) { lock = true; - int index = screen.body.bars[line].source; - barsCB[line]->setCurrentIndex(index); - if (index) { - RawSource source = RawSource(SOURCE_TYPE_TELEMETRY, index-1); + RawSource source = screen.body.bars[line].source; + if (source.type != SOURCE_TYPE_NONE) { RawSourceRange range = source.getRange(model, generalSettings, RANGE_SINGLE_PRECISION); int max = round((range.max - range.min) / range.step); if (int(255-screen.body.bars[line].barMax) > max) @@ -427,7 +417,6 @@ void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index) { if (!lock) { memset(&screen.body, 0, sizeof(screen.body)); - screen.type = index; update(); emit modified(); } @@ -437,20 +426,20 @@ void TelemetryCustomScreen::customFieldChanged(int value) { if (!lock) { int index = sender()->property("index").toInt(); - screen.body.lines[index/256].source[index%256] = value; + screen.body.lines[index/256].source[index%256] = RawSource(((QComboBox *)sender())->itemData(value).toInt()); emit modified(); } } -void TelemetryCustomScreen::barSourceChanged(int index) +void TelemetryCustomScreen::barSourceChanged(int value) { if (!lock) { QComboBox * cb = qobject_cast(sender()); - int line = cb->property("index").toInt(); - screen.body.bars[line].source = index; - screen.body.bars[line].barMin = 0; - screen.body.bars[line].barMax = 0; - updateBar(line); + int index = cb->property("index").toInt(); + screen.body.bars[index].source = RawSource(((QComboBox *)sender())->itemData(value).toInt()); + screen.body.bars[index].barMin = 0; + screen.body.bars[index].barMax = 0; + updateBar(index); emit modified(); } } @@ -745,6 +734,9 @@ void TelemetryPanel::update() for (int i=0; iupdate(); } + for (int i=0; igetCapability(TelemetryCustomScreens); i++) { + telemetryCustomScreens[i]->update(); + } } } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index c7f993e3f..f30a1cd21 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -66,7 +66,7 @@ class TelemetryCustomScreen: public ModelPanel void barMaxChanged(double value); protected: - void populateTelemetrySourceCB(QComboBox *b, unsigned int value, bool last, int hubproto); + void populateTelemetrySourceCB(QComboBox * b, RawSource & source, bool last=false); private: void updateBar(int line); diff --git a/companion/src/modeledit/telemetry_customscreen.ui b/companion/src/modeledit/telemetry_customscreen.ui index 5f92139d6..ea5498e03 100644 --- a/companion/src/modeledit/telemetry_customscreen.ui +++ b/companion/src/modeledit/telemetry_customscreen.ui @@ -2,6 +2,14 @@ TelemetryCustomScreen + + + 0 + 0 + 283 + 86 + + @@ -28,7 +36,7 @@ - + 0 @@ -41,26 +49,6 @@ 0 - - - None - - - - - Numbers - - - - - Bars - - - - - Script - - @@ -131,6 +119,13 @@ + + + AutoComboBox + QComboBox +
autocombobox.h
+
+
diff --git a/companion/src/process_flash.cpp b/companion/src/process_flash.cpp index 95d38e766..a57c3c02c 100755 --- a/companion/src/process_flash.cpp +++ b/companion/src/process_flash.cpp @@ -197,7 +197,8 @@ void FlashProcess::analyseStandardError(const QString &text) currStderrLine = currStderrLine.mid(nlPos+1); } - if (text.contains("-E-") && !text.contains("-E- No receive file name")) { + if ((text.contains("-E-") && !text.contains("-E- No receive file name")) || + text.contains("No DFU capable USB device found")) { hasErrors = true; } } @@ -218,38 +219,39 @@ void FlashProcess::onReadyReadStandardError() void FlashProcess::errorWizard() { - QString output; // TODO = ui->plainTextEdit->toPlainText(); + QString output = progress->getText(); + if (output.contains("avrdude: Expected signature for")) { // wrong signature - int pos=output.indexOf("avrdude: Device signature = "); - bool fwexist=false; - QString DeviceStr="Unknown"; - QString FwStr=""; + int pos = output.indexOf("avrdude: Device signature = "); + bool fwexist = false; + QString DeviceStr = tr("unknown"); + QString FwStr = ""; - if (pos>0) { - QString DeviceId=output.mid(pos+28,8); + if (pos > 0) { + QString DeviceId = output.mid(pos+28, 8); if (DeviceId=="0x1e9602") { - DeviceStr="Atmega 64"; - FwStr="\n"+tr("ie: OpenTX for 9X board or OpenTX for 9XR board"); - fwexist=true; + DeviceStr = "Atmega 64"; + FwStr="\n" + tr("ie: OpenTX for 9X board or OpenTX for 9XR board"); + fwexist = true; } else if (DeviceId=="0x1e9702") { - DeviceStr="Atmega 128"; - FwStr="\n"+tr("ie: OpenTX for M128 / 9X board or OpenTX for 9XR board with M128 chip"); - fwexist=true; + DeviceStr = "Atmega 128"; + FwStr="\n" + tr("ie: OpenTX for M128 / 9X board or OpenTX for 9XR board with M128 chip"); + fwexist = true; } else if (DeviceId=="0x1e9703") { - DeviceStr="Atmega 1280"; + DeviceStr = "Atmega 1280"; } else if (DeviceId=="0x1e9704") { - DeviceStr="Atmega 1281"; + DeviceStr = "Atmega 1281"; } else if (DeviceId=="0x1e9801") { - DeviceStr="Atmega 2560"; - FwStr="\n"+tr("ie: OpenTX for Gruvin9X board"); + DeviceStr = "Atmega 2560"; + FwStr="\n" + tr("ie: OpenTX for Gruvin9X board"); fwexist = true; } else if (DeviceId=="0x1e9802") { - DeviceStr="Atmega 2561"; + DeviceStr = "Atmega 2561"; } } if (fwexist==false) { @@ -260,6 +262,13 @@ void FlashProcess::errorWizard() QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio uses a %1 CPU!!!\n\nPlease select an appropriate firmware type to program it.").arg(DeviceStr)+FwStr+tr("\nYou are currently using:\n %1").arg(firmware->getName())); } } + else if (output.contains("No DFU capable USB device found")){ +#if defined WIN32 || !defined __GNUC__ + QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio does not seem connected to USB or the driver is not installed!!!\n\nPlease use ZADIG to properly install the driver.")); +#else + QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio does not seem connected to USB or the driver is not initialized!!!.")); +#endif + } } void FlashProcess::onFinished(int code=0) @@ -270,7 +279,7 @@ void FlashProcess::onFinished(int code=0) } if (code) { progress->setInfo(tr("Flashing done (exit code = %1)").arg(code)); - if (cmd.toLower().contains("avrdude")) { + if (cmd.toLower().contains("avrdude") || cmd.toLower().contains("dfu")) { errorWizard(); } } @@ -288,6 +297,6 @@ void FlashProcess::onFinished(int code=0) void FlashProcess::addReadFuses() { progress->addSeparator(); - progress->addText(tr("FUSES: Low=%1 High=%2 Ext=%3").arg(lfuse,2,16,QChar('0')).arg(hfuse,2,16,QChar('0')).arg(efuse,2,16,QChar('0'))); + progress->addText(tr("FUSES: Low=%1 High=%2 Ext=%3").arg(lfuse, 2, 16, QChar('0')).arg(hfuse, 2, 16, QChar('0')).arg(efuse, 2, 16, QChar('0'))); progress->addSeparator(); } diff --git a/companion/src/progresswidget.cpp b/companion/src/progresswidget.cpp index 10484a69a..9c19aa01e 100755 --- a/companion/src/progresswidget.cpp +++ b/companion/src/progresswidget.cpp @@ -78,6 +78,11 @@ void ProgressWidget::addText(const QString &text) } } +QString ProgressWidget::getText() +{ + return ui->textEdit->toPlainText(); +} + void ProgressWidget::setProgressColor(const QColor &color) { ui->progressBar->setStyleSheet(QString("QProgressBar {text-align: center;} QProgressBar::chunk { background-color: %1; text-align:center;}:").arg(color.name())); diff --git a/companion/src/progresswidget.h b/companion/src/progresswidget.h index 392511f4e..ba1007963 100755 --- a/companion/src/progresswidget.h +++ b/companion/src/progresswidget.h @@ -16,6 +16,7 @@ class ProgressWidget : public QWidget ~ProgressWidget(); void lock(bool lock); void addText(const QString &text); + QString getText(); void setInfo(const QString &text); void setMaximum(int value); int maximum(); diff --git a/companion/src/qcustomplot/changelog.txt b/companion/src/qcustomplot/changelog.txt index 1afe0d9d4..7acf22a6a 100644 --- a/companion/src/qcustomplot/changelog.txt +++ b/companion/src/qcustomplot/changelog.txt @@ -1,3 +1,24 @@ +#### Version 1.3.1 released on 25.04.15 #### + + Bugfixes: + - Fixed bug that prevented automatic axis rescaling when some graphs/curves had only NaN data points + - Improved QCPItemBracket selection boundaries, especially bsCurly and bsCalligraphic + - Fixed bug of axis rect and colorscale background shifted downward by one logical pixel (visible in scaled png and pdf export) + - Replot upon mouse release is now only performed if a selection change has actually happened (improves responsivity on particularly complex plots) + - Fixed bug that allowed scatter-only graphs to be selected by clicking the non-existent line between scatters + - Fixed crash when trying to select a scatter-only QCPGraph whose only points in the visible key range are at identical key coordinates and vertically off-screen, with adaptive sampling enabled + - Fixed pdf export of QCPColorMap with enabled interpolation (didn't appear interpolated in pdf) + - Reduced QCPColorMap jitter of internal cell boundaries for small sized maps when viewed with high zoom, by applying oversampling factors dependant on map size + - Fixed bug of QCPColorMap::fill() not causing the buffered internal image map to be updated, and thus the change didn't become visible immediately + - Axis labels with size set in pixels (setPixelSize) instead of points now correctly calculate the exponent's font size if beautifully typeset powers are enabled + - Fixed QCPColorMap appearing at the wrong position for logarithmic axes and color map spanning larger ranges + + Other: + - Pdf export used to embed entire QCPColorMaps, potentially leading to large files. Now only the visible portion of the map is embedded in the pdf + - Many documentation fixes and extensions, style modernization + - Reduced documentation file size (and thus full package size) by automatically reducing image palettes during package build + - Fixed MSVC warning message (at warning level 4) due to temporary QLists in some foreach statements + #### Version 1.3.0 released on 27.12.14 #### Added features: diff --git a/companion/src/qcustomplot/qcustomplot.cpp b/companion/src/qcustomplot/qcustomplot.cpp index 42dd132bb..d7e548526 100644 --- a/companion/src/qcustomplot/qcustomplot.cpp +++ b/companion/src/qcustomplot/qcustomplot.cpp @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** Copyright (C) 2011-2015 Emanuel Eichhammer ** ** ** ** This program is free software: you can redistribute it and/or modify ** ** it under the terms of the GNU General Public License as published by ** @@ -19,8 +19,8 @@ **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** -** Date: 27.12.14 ** -** Version: 1.3.0 ** +** Date: 25.04.15 ** +** Version: 1.3.1 ** ****************************************************************************/ #include "qcustomplot.h" @@ -263,20 +263,11 @@ void QCPPainter::makeNonCosmetic() \section QCPScatterStyle-defining Specifying a scatter style You can set all these configurations either by calling the respective functions on an instance: - \code - QCPScatterStyle myScatter; - myScatter.setShape(QCPScatterStyle::ssCircle); - myScatter.setPen(Qt::blue); - myScatter.setBrush(Qt::white); - myScatter.setSize(5); - customPlot->graph(0)->setScatterStyle(myScatter); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1 Or you can use one of the various constructors that take different parameter combinations, making it easy to specify a scatter style in a single call, like so: - \code - customPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, Qt::white, 5)); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2 \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable @@ -287,9 +278,7 @@ void QCPPainter::makeNonCosmetic() color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes it very convenient to set up typical scatter settings: - \code - customPlot->graph(0)->setScatterStyle(QCPScatterStyle::ssPlus); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly @@ -1034,10 +1023,10 @@ bool QCPLayerable::realVisibility() const distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the object is not selectable, -1.0 is returned, too. - If the item is represented not by single lines but by an area like QCPItemRect or QCPItemText, a - click inside the area returns a constant value greater zero (typically the selectionTolerance of - the parent QCustomPlot multiplied by 0.99). If the click lies outside the area, this function - returns -1.0. + If the object is represented not by single lines but by an area like a \ref QCPItemText or the + bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In + these cases this function thus returns a constant value greater zero but still below the parent + plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99). Providing a constant value for area objects allows selecting line objects even when they are obscured by such area objects, by clicking close to the lines (i.e. closer than @@ -1531,14 +1520,9 @@ bool QCPRange::validRange(const QCPRange &range) \section QCPMarginGroup-example Example First create a margin group: - \code - QCPMarginGroup *group = new QCPMarginGroup(customPlot); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1 Then set this group on the layout element sides: - \code - customPlot->axisRect(0)->setMarginGroup(QCP::msLeft|QCP::msRight, group); - customPlot->axisRect(1)->setMarginGroup(QCP::msLeft|QCP::msRight, group); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2 Here, we've used the first two axis rects of the plot and synchronized their left margins with each other and their right margins with each other. */ @@ -1945,7 +1929,8 @@ void QCPLayoutElement::update(UpdatePhase phase) { // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group: QMargins newMargins = mMargins; - foreach (QCP::MarginSide side, QList() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom) + QList allMarginSides = QList() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom; + foreach (QCP::MarginSide side, allMarginSides) { if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically { @@ -2202,7 +2187,9 @@ void QCPLayout::simplify() invalid or points to an empty cell, returns false. This function internally uses \ref takeAt to remove the element from the layout and then deletes - the returned element. + the returned element. Note that some layouts don't remove the respective cell right away but leave an + empty cell after successful removal of the layout element. To collapse empty cells, use \ref + simplify. \see remove, takeAt */ @@ -2221,7 +2208,9 @@ bool QCPLayout::removeAt(int index) layout, returns false. This function internally uses \ref takeAt to remove the element from the layout and then deletes - the element. + the element. Note that some layouts don't remove the respective cell right away but leave an + empty cell after successful removal of the layout element. To collapse empty cells, use \ref + simplify. \see removeAt, take */ @@ -2236,7 +2225,8 @@ bool QCPLayout::remove(QCPLayoutElement *element) } /*! - Removes and deletes all layout elements in this layout. + Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure + all empty cells are collapsed. \see remove, removeAt */ @@ -3387,8 +3377,8 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) respective arrow point inward. Note that due to the overloaded QCPLineEnding constructor, you may directly specify a - QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. \code - myItemLine->setHead(QCPLineEnding::esSpikeArrow) \endcode + QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. + \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead */ /*! @@ -3959,6 +3949,15 @@ void QCPGrid::drawSubGridLines(QCPPainter *painter) const This signal is emitted when the range of this axis has changed. You can connect it to the \ref setRange slot of another axis to communicate the new range to the other axis, in order for it to be synchronized. + + You may also manipulate/correct the range with \ref setRange in a slot connected to this signal. + This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper + range shouldn't go beyond certain values. For example, the following slot would limit the x axis + to only positive ranges: + \code + if (newRange.lower < 0) + plot->xAxis->setRange(0, newRange.size()); + \endcode */ /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange) @@ -4075,6 +4074,7 @@ QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) : QCPAxis::~QCPAxis() { delete mAxisPainter; + delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order } /* No documentation as it is a property getter */ @@ -6125,7 +6125,6 @@ void QCPAxisPainterPrivate::draw(QCPPainter *painter) case QCPAxis::atRight: xCor = 1; break; default: break; } - int margin = 0; // draw baseline: QLineF baseLine; @@ -6391,58 +6390,51 @@ void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, } if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled { - if (!mLabelCache.contains(text)) // no cached label exists, create it + CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache + if (!cachedLabel) // no cached label existed, create it { - CachedLabel *newCachedLabel = new CachedLabel; + cachedLabel = new CachedLabel; TickLabelData labelData = getTickLabelData(painter->font(), text); - newCachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft(); - newCachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); - newCachedLabel->pixmap.fill(Qt::transparent); - QCPPainter cachePainter(&newCachedLabel->pixmap); + cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft(); + cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()); + cachedLabel->pixmap.fill(Qt::transparent); + QCPPainter cachePainter(&cachedLabel->pixmap); cachePainter.setPen(painter->pen()); drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData); - mLabelCache.insert(text, newCachedLabel, 1); } - // draw cached label: - const CachedLabel *cachedLabel = mLabelCache.object(text); // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; if (tickLabelSide == QCPAxis::lsOutside) { if (QCPAxis::orientation(type) == Qt::Horizontal) - { - if (labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || - labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left()) - return; - } else - { - if (labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() >viewportRect.bottom() || - labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top()) - return; - } + labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left(); + else + labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top(); } - painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap); - finalSize = cachedLabel->pixmap.size(); + if (!labelClippedByBorder) + { + painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap); + finalSize = cachedLabel->pixmap.size(); + } + mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created } else // label caching disabled, draw text directly on surface: { TickLabelData labelData = getTickLabelData(painter->font(), text); QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData); // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels): + bool labelClippedByBorder = false; if (tickLabelSide == QCPAxis::lsOutside) { if (QCPAxis::orientation(type) == Qt::Horizontal) - { - if (finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || - finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left()) - return; - } else - { - if (finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || - finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top()) - return; - } + labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left(); + else + labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top(); + } + if (!labelClippedByBorder) + { + drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData); + finalSize = labelData.rotatedTotalBounds.size(); } - drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData); - finalSize = labelData.rotatedTotalBounds.size(); } // expand passed tickLabelsSize if current tick label is larger: @@ -6514,7 +6506,7 @@ QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(con // calculate text bounding rects and do string preparation for beautiful decimal powers: result.baseFont = font; - if (result.baseFont.pointSizeF() > 0) // On some rare systems, this sometimes is initialized with -1 (Qt bug?), so we check here before possibly setting a negative value in the next line + if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding if (useBeautifulPowers) { @@ -6533,7 +6525,10 @@ QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(con result.expPart.remove(0, 1); // prepare smaller font for exponent: result.expFont = font; - result.expFont.setPointSize(result.expFont.pointSize()*0.75); + if (result.expFont.pointSize() > 0) + result.expFont.setPointSize(result.expFont.pointSize()*0.75); + else + result.expFont.setPixelSize(result.expFont.pixelSize()*0.75); // calculate bounding rects of base part, exponent part and total one: result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart); result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart); @@ -6772,6 +6767,9 @@ void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation of this plottable inside \a rect, next to the plottable name. + + The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't + appear outside the legend icon border. */ /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0 @@ -6868,7 +6866,7 @@ void QCPAbstractPlottable::setName(const QString &name) } /*! - Sets whether fills of this plottable is drawn antialiased or not. + Sets whether fills of this plottable are drawn antialiased or not. Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref QCustomPlot::setNotAntialiasedElements. @@ -6905,8 +6903,7 @@ void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled) The pen is used to draw basic lines that make up the plottable representation in the plot. - For example, the \ref QCPGraph subclass draws its graph lines and scatter points - with this pen. + For example, the \ref QCPGraph subclass draws its graph lines with this pen. \see setBrush */ @@ -8212,31 +8209,17 @@ void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint) \section items-using Using items First you instantiate the item you want to use and add it to the plot: - \code - QCPItemLine *line = new QCPItemLine(customPlot); - customPlot->addItem(line); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1 by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just set the plot coordinates where the line should start/end: - \code - line->start->setCoords(-0.1, 0.8); - line->end->setCoords(1.1, 0.2); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2 If we don't want the line to be positioned in plot coordinates but a different coordinate system, e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this: - \code - line->start->setType(QCPItemPosition::ptAbsolute); - line->end->setType(QCPItemPosition::ptAbsolute); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3 Then we can set the coordinates, this time in pixels: - \code - line->start->setCoords(100, 200); - line->end->setCoords(450, 320); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4 and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect: - \code - line->setClipToAxisRect(false); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5 For more advanced plots, it is even possible to set different types and parent anchors per X/Y coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref @@ -8584,9 +8567,7 @@ double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, For example, if your item consists of four rects, call this function four times, once for each rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned - values which were greater or equal to zero. (Because this function may return -1.0 when \a pos - doesn't hit \a rect at all). If all calls returned -1.0, return -1.0, too, because your item - wasn't hit. + values. \see distSqrToLine */ @@ -10467,6 +10448,8 @@ bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width Q_UNUSED(noCosmeticPen) Q_UNUSED(width) Q_UNUSED(height) + Q_UNUSED(pdfCreator) + Q_UNUSED(pdfTitle) qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created."; #else int newWidth, newHeight; @@ -10821,9 +10804,11 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) clickedLayerable->selectEvent(event, additive, details, &selChanged); selectionStateChanged |= selChanged; } - doReplot = true; if (selectionStateChanged) + { + doReplot = true; emit selectionChangedByUser(); + } } // emit specialized object click signals: @@ -11097,7 +11082,7 @@ QPixmap QCustomPlot::toPixmap(int width, int height, double scale) painter.setMode(QCPPainter::pmNonCosmetic); painter.scale(scale, scale); } - if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) + if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill painter.fillRect(mViewport, mBackgroundBrush); draw(&painter); setViewport(oldViewport); @@ -11141,9 +11126,7 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) QRect oldViewport = viewport(); setViewport(QRect(0, 0, newWidth, newHeight)); painter->setMode(QCPPainter::pmNoCaching); - // warning: the following is different in toPixmap, because a solid background color is applied there via QPixmap::fill - // here, we need to do this via QPainter::fillRect. - if (mBackgroundBrush.style() != Qt::NoBrush) + if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here painter->fillRect(mViewport, mBackgroundBrush); draw(painter); setViewport(oldViewport); @@ -11173,9 +11156,7 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref GradientPreset to all the \a setGradient methods, e.g.: - \code - colorMap->setGradient(QCPColorGradient::gpHot); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the color gradient shall be applied periodically (wrapping around) to data values that lie outside @@ -12311,7 +12292,7 @@ double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag to enable the range dragging interaction. - \see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag + \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag */ void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) { @@ -12423,10 +12404,10 @@ void QCPAxisRect::drawBackground(QCPPainter *painter) scaledSize.scale(mRect.size(), mBackgroundScaledMode); if (mScaledBackgroundPixmap.size() != scaledSize) mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation); - painter->drawPixmap(mRect.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect()); + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect()); } else { - painter->drawPixmap(mRect.topLeft(), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height())); + painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height())); } } } @@ -13498,10 +13479,7 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) and \ref setTextColor. A plot title can be added as follows: - \code - customPlot->plotLayout()->insertRow(0); // inserts an empty row above the default axis rect - customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Your Plot Title")); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the @@ -13760,27 +13738,17 @@ QColor QCPPlotTitle::mainTextColor() const To have finer control over the number display and axis behaviour, you can directly access the \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if you want to change the number of automatically generated ticks, call - \code - colorScale->axis()->setAutoTickCount(3); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount Placing a color scale next to the main axis rect works like with any other layout element: - \code - QCPColorScale *colorScale = new QCPColorScale(customPlot); - customPlot->plotLayout()->addElement(0, 1, colorScale); - colorScale->setLabel("Some Label Text"); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation In this case we have placed it to the right of the default axis rect, so it wasn't necessary to call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color scale can be set with \ref setLabel. For optimum appearance (like in the image above), it may be desirable to line up the axis rect and the borders of the color scale. Use a \ref QCPMarginGroup to achieve this: - \code - QCPMarginGroup *group = new QCPMarginGroup(customPlot); - colorScale->setMarginGroup(QCP::msTop|QCP::msBottom, group); - customPlot->axisRect()->setMarginGroup(QCP::msTop|QCP::msBottom, group); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup Color scales are initialized with a non-zero minimum top and bottom margin (\ref setMinimumMargins), because vertical color scales are most common and the minimum top/bottom @@ -13920,7 +13888,8 @@ void QCPColorScale::setType(QCPAxis::AxisType type) disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); } - foreach (QCPAxis::AxisType atype, QList() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop) + QList allAxisTypes = QList() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop; + foreach (QCPAxis::AxisType atype, allAxisTypes) { mAxisRect.data()->axis(atype)->setTicks(atype == mType); mAxisRect.data()->axis(atype)->setTickLabels(atype== mType); @@ -14254,7 +14223,8 @@ QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parent { setParentLayerable(parentColorScale); setMinimumMargins(QMargins(0, 0, 0, 0)); - foreach (QCPAxis::AxisType type, QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight) + QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) { axis(type)->setVisible(true); axis(type)->grid()->setVisible(false); @@ -14275,7 +14245,7 @@ QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parent // make layer transfers of color scale transfer to axis rect and axes // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect: connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*))); - foreach (QCPAxis::AxisType type, QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight) + foreach (QCPAxis::AxisType type, allAxisTypes) connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*))); } @@ -14296,7 +14266,7 @@ void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter) mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight); } - painter->drawImage(rect(), mGradientImage.mirrored(mirrorHorz, mirrorVert)); + painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert)); QCPAxisRect::draw(painter); } @@ -14350,7 +14320,8 @@ void QCPColorScaleAxisRectPrivate::updateGradientImage() void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts) { // axis bases of four axes shall always (de-)selected synchronously: - foreach (QCPAxis::AxisType type, QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight) + QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) { if (QCPAxis *senderAxis = qobject_cast(sender())) if (senderAxis->axisType() == type) @@ -14374,7 +14345,8 @@ void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts) { // synchronize axis base selectability: - foreach (QCPAxis::AxisType type, QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight) + QList allAxisTypes = QList() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight; + foreach (QCPAxis::AxisType type, allAxisTypes) { if (QCPAxis *senderAxis = qobject_cast(sender())) if (senderAxis->axisType() == type) @@ -14458,6 +14430,10 @@ QCPData::QCPData(double key, double value) : data point per unique key coordinate. In other words, the graph can't have \a loops. If you do want to plot non-single-valued curves, rather use the QCPCurve plottable. + Gaps in the graph line can be created by adding data points with NaN as value + (qQNaN() or std::numeric_limits::quiet_NaN()) in between the two data points that shall be + separated. + \section appearance Changing the appearance The appearance of the graph is mainly determined by the line style, scatter style, brush and pen @@ -14473,7 +14449,7 @@ QCPData::QCPData(double key, double value) : between this graph and another one, call \ref setChannelFillGraph with the other graph as parameter. - \see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph + \see QCustomPlot::addGraph, QCustomPlot::graph */ /* start of documentation of inline functions */ @@ -15601,24 +15577,32 @@ void QCPGraph::drawLinePlot(QCPPainter *painter, QVector *lineData) con // if drawing solid line and not in PDF, use much faster line drawing instead of polyline: if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) && painter->pen().style() == Qt::SolidLine && - !painter->modes().testFlag(QCPPainter::pmVectorized)&& + !painter->modes().testFlag(QCPPainter::pmVectorized) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) { - int i = 1; - int lineDataSize = lineData->size(); + int i = 0; + bool lastIsNan = false; + const int lineDataSize = lineData->size(); + while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect while (i < lineDataSize) { if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line - painter->drawLine(lineData->at(i-1), lineData->at(i)); - else - ++i; + { + if (!lastIsNan) + painter->drawLine(lineData->at(i-1), lineData->at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; ++i; } } else { int segmentStart = 0; int i = 0; - int lineDataSize = lineData->size(); + const int lineDataSize = lineData->size(); while (i < lineDataSize) { if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line @@ -15629,7 +15613,7 @@ void QCPGraph::drawLinePlot(QCPPainter *painter, QVector *lineData) con ++i; } // draw last segment: - painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); // lineDataSize, because we do want to include the last point + painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); } } } @@ -16399,71 +16383,66 @@ int QCPGraph::findIndexAboveY(const QVector *data, double y) const \ref selectTest. If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape - is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns - 500. + is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0. */ double QCPGraph::pointDistance(const QPointF &pixelPoint) const { if (mData->isEmpty()) - { - qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data"; - return 500; - } - if (mData->size() == 1) - { - QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value); - return QVector2D(dataPoint-pixelPoint).length(); - } - + return -1.0; if (mLineStyle == lsNone && mScatterStyle.isNone()) - return 500; + return -1.0; // calculate minimum distances to graph representation: if (mLineStyle == lsNone) { // no line displayed, only calculate distance to scatter points: - QVector *scatterData = new QVector; - getScatterPlotData(scatterData); - double minDistSqr = std::numeric_limits::max(); - QPointF ptA; - QPointF ptB = coordsToPixels(scatterData->at(0).key, scatterData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels - for (int i=1; isize(); ++i) + QVector scatterData; + getScatterPlotData(&scatterData); + if (scatterData.size() > 0) { - ptA = ptB; - ptB = coordsToPixels(scatterData->at(i).key, scatterData->at(i).value); - double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint); - if (currentDistSqr < minDistSqr) - minDistSqr = currentDistSqr; - } - delete scatterData; - return qSqrt(minDistSqr); + double minDistSqr = std::numeric_limits::max(); + for (int i=0; i *lineData = new QVector; - getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here - double minDistSqr = std::numeric_limits::max(); - if (mLineStyle == lsImpulse) + // line displayed, calculate distance to line segments: + QVector lineData; + getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here + if (lineData.size() > 1) // at least one line segment, compare distance to line segments { - // impulse plot differs from other line styles in that the lineData points are only pairwise connected: - for (int i=0; isize()-1; i+=2) // iterate pairs + double minDistSqr = std::numeric_limits::max(); + if (mLineStyle == lsImpulse) { - double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint); - if (currentDistSqr < minDistSqr) - minDistSqr = currentDistSqr; + // impulse plot differs from other line styles in that the lineData points are only pairwise connected: + for (int i=0; i 0) // only single data point, calculate distance to that point { - // all other line plots (line and step) connect points directly: - for (int i=0; isize()-1; ++i) - { - double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint); - if (currentDistSqr < minDistSqr) - minDistSqr = currentDistSqr; - } - } - delete lineData; - return qSqrt(minDistSqr); + return QVector2D(lineData.at(0)-pixelPoint).length(); + } else // no data available in view to calculate distance to + return -1.0; } } @@ -16525,18 +16504,21 @@ QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool i QCPDataMap::const_iterator it = mData->constBegin(); while (it != mData->constEnd()) { - current = it.value().key; - currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); - currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); - if (current-currentErrorMinus < range.lower || !haveLower) + if (!qIsNaN(it.value().value)) { - range.lower = current-currentErrorMinus; - haveLower = true; - } - if (current+currentErrorPlus > range.upper || !haveUpper) - { - range.upper = current+currentErrorPlus; - haveUpper = true; + current = it.value().key; + currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); + if (current-currentErrorMinus < range.lower || !haveLower) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if (current+currentErrorPlus > range.upper || !haveUpper) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } } ++it; } @@ -16545,31 +16527,34 @@ QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool i QCPDataMap::const_iterator it = mData->constBegin(); while (it != mData->constEnd()) { - current = it.value().key; - currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); - currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); - if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) + if (!qIsNaN(it.value().value)) { - range.lower = current-currentErrorMinus; - haveLower = true; - } - if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) - { - range.upper = current+currentErrorPlus; - haveUpper = true; - } - if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. - { - if ((current < range.lower || !haveLower) && current < 0) + current = it.value().key; + currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) { - range.lower = current; + range.lower = current-currentErrorMinus; haveLower = true; } - if ((current > range.upper || !haveUpper) && current < 0) + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) { - range.upper = current; + range.upper = current+currentErrorPlus; haveUpper = true; } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. + { + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } } ++it; } @@ -16578,31 +16563,34 @@ QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool i QCPDataMap::const_iterator it = mData->constBegin(); while (it != mData->constEnd()) { - current = it.value().key; - currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); - currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); - if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) + if (!qIsNaN(it.value().value)) { - range.lower = current-currentErrorMinus; - haveLower = true; - } - if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) - { - range.upper = current+currentErrorPlus; - haveUpper = true; - } - if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. - { - if ((current < range.lower || !haveLower) && current > 0) + current = it.value().key; + currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) { - range.lower = current; + range.lower = current-currentErrorMinus; haveLower = true; } - if ((current > range.upper || !haveUpper) && current > 0) + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) { - range.upper = current; + range.upper = current+currentErrorPlus; haveUpper = true; } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. + { + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } } ++it; } @@ -16632,17 +16620,20 @@ QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool while (it != mData->constEnd()) { current = it.value().value; - currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); - currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); - if (current-currentErrorMinus < range.lower || !haveLower) + if (!qIsNaN(current)) { - range.lower = current-currentErrorMinus; - haveLower = true; - } - if (current+currentErrorPlus > range.upper || !haveUpper) - { - range.upper = current+currentErrorPlus; - haveUpper = true; + currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); + if (current-currentErrorMinus < range.lower || !haveLower) + { + range.lower = current-currentErrorMinus; + haveLower = true; + } + if (current+currentErrorPlus > range.upper || !haveUpper) + { + range.upper = current+currentErrorPlus; + haveUpper = true; + } } ++it; } @@ -16652,30 +16643,33 @@ QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool while (it != mData->constEnd()) { current = it.value().value; - currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); - currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); - if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) + if (!qIsNaN(current)) { - range.lower = current-currentErrorMinus; - haveLower = true; - } - if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) - { - range.upper = current+currentErrorPlus; - haveUpper = true; - } - if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. - { - if ((current < range.lower || !haveLower) && current < 0) + currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0) { - range.lower = current; + range.lower = current-currentErrorMinus; haveLower = true; } - if ((current > range.upper || !haveUpper) && current < 0) + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0) { - range.upper = current; + range.upper = current+currentErrorPlus; haveUpper = true; } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point. + { + if ((current < range.lower || !haveLower) && current < 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current < 0) + { + range.upper = current; + haveUpper = true; + } + } } ++it; } @@ -16685,30 +16679,33 @@ QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool while (it != mData->constEnd()) { current = it.value().value; - currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); - currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); - if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) + if (!qIsNaN(current)) { - range.lower = current-currentErrorMinus; - haveLower = true; - } - if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) - { - range.upper = current+currentErrorPlus; - haveUpper = true; - } - if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. - { - if ((current < range.lower || !haveLower) && current > 0) + currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0); + currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0); + if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0) { - range.lower = current; + range.lower = current-currentErrorMinus; haveLower = true; } - if ((current > range.upper || !haveUpper) && current > 0) + if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0) { - range.upper = current; + range.upper = current+currentErrorPlus; haveUpper = true; } + if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point. + { + if ((current < range.lower || !haveLower) && current > 0) + { + range.lower = current; + haveLower = true; + } + if ((current > range.upper || !haveUpper) && current > 0) + { + range.upper = current; + haveUpper = true; + } + } } ++it; } @@ -16773,6 +16770,10 @@ QCPCurveData::QCPCurveData(double t, double key, double value) : To plot data, assign it with the \ref setData or \ref addData functions. + Gaps in the curve can be created by adding data points with NaN as key and value + (qQNaN() or std::numeric_limits::quiet_NaN()) in between the two data points that shall be + separated. + \section appearance Changing the appearance The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush). @@ -16781,16 +16782,10 @@ QCPCurveData::QCPCurveData(double t, double key, double value) : Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.) - Usually, you first create an instance: - \code - QCPCurve *newCurve = new QCPCurve(customPlot->xAxis, customPlot->yAxis);\endcode - add it to the customPlot with QCustomPlot::addPlottable: - \code - customPlot->addPlottable(newCurve);\endcode + Usually, you first create an instance and add it to the customPlot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1 and then modify the properties of the newly created plottable, e.g.: - \code - newCurve->setName("Fermat's Spiral"); - newCurve->setData(tData, xData, yData);\endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2 */ /*! @@ -17103,21 +17098,29 @@ void QCPCurve::draw(QCPPainter *painter) !painter->modes().testFlag(QCPPainter::pmVectorized) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) { - int i = 1; - int lineDataSize = lineData->size(); + int i = 0; + bool lastIsNan = false; + const int lineDataSize = lineData->size(); + while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN + ++i; + ++i; // because drawing works in 1 point retrospect while (i < lineDataSize) { if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line - painter->drawLine(lineData->at(i-1), lineData->at(i)); - else - ++i; + { + if (!lastIsNan) + painter->drawLine(lineData->at(i-1), lineData->at(i)); + else + lastIsNan = false; + } else + lastIsNan = true; ++i; } } else { int segmentStart = 0; int i = 0; - int lineDataSize = lineData->size(); + const int lineDataSize = lineData->size(); while (i < lineDataSize) { if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line @@ -17128,7 +17131,7 @@ void QCPCurve::draw(QCPPainter *painter) ++i; } // draw last segment: - painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); // lineDataSize, because we do want to include the last point + painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart); } } @@ -17937,17 +17940,20 @@ QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const while (it != mData->constEnd()) { current = it.value().key; - if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + if (!qIsNaN(current) && !qIsNaN(it.value().value)) { - if (current < range.lower || !haveLower) + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) { - range.lower = current; - haveLower = true; - } - if (current > range.upper || !haveUpper) - { - range.upper = current; - haveUpper = true; + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } } } ++it; @@ -17970,17 +17976,20 @@ QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) cons while (it != mData->constEnd()) { current = it.value().value; - if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) + if (!qIsNaN(current) && !qIsNaN(it.value().key)) { - if (current < range.lower || !haveLower) + if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0)) { - range.lower = current; - haveLower = true; - } - if (current > range.upper || !haveUpper) - { - range.upper = current; - haveUpper = true; + if (current < range.lower || !haveLower) + { + range.lower = current; + haveLower = true; + } + if (current > range.upper || !haveUpper) + { + range.upper = current; + haveUpper = true; + } } } ++it; @@ -18009,11 +18018,7 @@ QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) cons To add a QCPBars plottable to the group, create a new group and then add the respective bars intances: - \code - QCPBarsGroup *group = new QCPBarsGroup(customPlot); - group->append(bars1); - group->append(bars2); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation Alternatively to appending to the group like shown above, you can also set the group on the QCPBars plottable via \ref QCPBars::setBarsGroup. @@ -18026,6 +18031,11 @@ QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) cons QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable. To clear the entire group, call \ref clear, or simply delete the group. + + \section qcpbarsgroup-example Example + + The image above is generated with the following code: + \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example */ /* start of documentation of inline functions */ @@ -18397,15 +18407,11 @@ QCPBarData::QCPBarData(double key, double value) : (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.) Usually, you first create an instance: - \code - QCPBars *newBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);\endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1 add it to the customPlot with QCustomPlot::addPlottable: - \code - customPlot->addPlottable(newBars);\endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2 and then modify the properties of the newly created plottable, e.g.: - \code - newBars->setName("Country population"); - newBars->setData(xData, yData);\endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3 */ /* start of documentation of inline functions */ @@ -19151,21 +19157,11 @@ QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const \section usage Usage - Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable - (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies - (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.) - - Usually, you first create an instance: - \code - QCPStatisticalBox *newBox = new QCPStatisticalBox(customPlot->xAxis, customPlot->yAxis);\endcode - add it to the customPlot with QCustomPlot::addPlottable: - \code - customPlot->addPlottable(newBox);\endcode + Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable. + Usually, you first create an instance and add it to the customPlot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1 and then modify the properties of the newly created plottable, e.g.: - \code - newBox->setName("Measurement Series 1"); - newBox->setData(1, 3, 4, 5, 7); - newBox->setOutliers(QVector() << 0.5 << 0.64 << 7.2 << 7.42);\endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2 */ /*! @@ -19812,6 +19808,12 @@ void QCPColorMapData::setValueRange(const QCPRange &valueRange) /*! Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a z. + + \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or + value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, + you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to + determine the cell index. Rather directly access the cell index with \ref + QCPColorMapData::setCell. \see setCell, setRange */ @@ -19905,6 +19907,7 @@ void QCPColorMapData::fill(double z) for (int i=0; ixAxis, customPlot->yAxis);\endcode - add it to the customPlot with QCustomPlot::addPlottable: - \code - customPlot->addPlottable(colorMap);\endcode + Usually, you first create an instance and add it to the customPlot: + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1 and then modify the properties of the newly created color map, e.g.: - \code - colorMap->data()->setSize(50, 50); - colorMap->data()->setRange(QCPRange(0, 2), QCPRange(0, 2)); - for (int x=0; x<50; ++x) - for (int y=0; y<50; ++y) - colorMap->data()->setCell(x, y, qCos(x/10.0)+qSin(y/10.0)); - colorMap->setGradient(QCPColorGradient::gpPolar); - colorMap->rescaleDataRange(true); - customPlot->rescaleAxes(); - customPlot->replot(); - \endcode + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2 \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes, @@ -20174,6 +20173,7 @@ void QCPColorMap::setGradient(const QCPColorGradient &gradient) void QCPColorMap::setInterpolate(bool enabled) { mInterpolate = enabled; + mMapImageInvalidated = true; // because oversampling factors might need to change } /*! @@ -20321,29 +20321,49 @@ double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image has been invalidated for a different reason (e.g. a change of the data range with \ref setDataRange). + + If the map cell count is low, the image created will be oversampled in order to avoid a + QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images + without smooth transform enabled. Accordingly, oversampling isn't performed if \ref + setInterpolate is true. */ void QCPColorMap::updateMapImage() { QCPAxis *keyAxis = mKeyAxis.data(); if (!keyAxis) return; - - // resize mMapImage to correct dimensions, according to key/value axes orientation: - if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.size().width() != mMapData->keySize() || mMapImage.size().height() != mMapData->valueSize())) - mMapImage = QImage(QSize(mMapData->keySize(), mMapData->valueSize()), QImage::Format_RGB32); - else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.size().width() != mMapData->valueSize() || mMapImage.size().height() != mMapData->keySize())) - mMapImage = QImage(QSize(mMapData->valueSize(), mMapData->keySize()), QImage::Format_RGB32); + if (mMapData->isEmpty()) return; const int keySize = mMapData->keySize(); const int valueSize = mMapData->valueSize(); - const double *rawData = mMapData->mData; + int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on + // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation: + if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor)) + mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32); + else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor)) + mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32); + + QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + // resize undersampled map image to actual key/value cell sizes: + if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize)) + mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32); + else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize)) + mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32); + localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image + } else if (!mUndersampledMapImage.isNull()) + mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it + + const double *rawData = mMapData->mData; if (keyAxis->orientation() == Qt::Horizontal) { const int lineCount = valueSize; const int rowCount = keySize; for (int line=0; line(mMapImage.scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + QRgb* pixels = reinterpret_cast(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic); } } else // keyAxis->orientation() == Qt::Vertical @@ -20352,11 +20372,18 @@ void QCPColorMap::updateMapImage() const int rowCount = valueSize; for (int line=0; line(mMapImage.scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) + QRgb* pixels = reinterpret_cast(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system) mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic); } } + if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1) + { + if (keyAxis->orientation() == Qt::Horizontal) + mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + else + mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation); + } mMapData->mDataModified = false; mMapImageInvalidated = false; } @@ -20371,30 +20398,63 @@ void QCPColorMap::draw(QCPPainter *painter) if (mMapData->mDataModified || mMapImageInvalidated) updateMapImage(); - double halfSampleKey = 0; - double halfSampleValue = 0; - if (mMapData->keySize() > 1) - halfSampleKey = 0.5*mMapData->keyRange().size()/(double)(mMapData->keySize()-1); - if (mMapData->valueSize() > 1) - halfSampleValue = 0.5*mMapData->valueRange().size()/(double)(mMapData->valueSize()-1); - QRectF imageRect(coordsToPixels(mMapData->keyRange().lower-halfSampleKey, mMapData->valueRange().lower-halfSampleValue), - coordsToPixels(mMapData->keyRange().upper+halfSampleKey, mMapData->valueRange().upper+halfSampleValue)); - imageRect = imageRect.normalized(); + // use buffer if painting vectorized (PDF): + bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized); + QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized + QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in + QPixmap mapBuffer; + double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps + if (useBuffer) + { + mapBufferTarget = painter->clipRegion().boundingRect(); + mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize()); + mapBuffer.fill(Qt::transparent); + localPainter = new QCPPainter(&mapBuffer); + localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio); + localPainter->translate(-mapBufferTarget.topLeft()); + } + + QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), + coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(); + // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary): + double halfCellWidth = 0; // in pixels + double halfCellHeight = 0; // in pixels + if (keyAxis()->orientation() == Qt::Horizontal) + { + if (mMapData->keySize() > 1) + halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1); + if (mMapData->valueSize() > 1) + halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1); + } else // keyAxis orientation is Qt::Vertical + { + if (mMapData->keySize() > 1) + halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1); + if (mMapData->valueSize() > 1) + halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1); + } + imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight); bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed(); bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed(); - bool smoothBackup = painter->renderHints().testFlag(QPainter::SmoothPixmapTransform); - painter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate); + bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform); + localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate); QRegion clipBackup; if (mTightBoundary) { - clipBackup = painter->clipRegion(); - painter->setClipRect(QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), - coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(), Qt::IntersectClip); + clipBackup = localPainter->clipRegion(); + QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower), + coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized(); + localPainter->setClipRect(tightClipRect, Qt::IntersectClip); } - painter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY)); + localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY)); if (mTightBoundary) - painter->setClipRegion(clipBackup); - painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup); + localPainter->setClipRegion(clipBackup); + localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup); + + if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter + { + delete localPainter; + painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer); + } } /* inherits documentation from base class */ @@ -23352,7 +23412,27 @@ double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVari lengthVec = lengthVec.normalized()*mLength; QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec; - return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos)); + switch (mStyle) + { + case QCPItemBracket::bsSquare: + case QCPItemBracket::bsRound: + { + double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos); + double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos); + double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos); + return qSqrt(qMin(qMin(a, b), c)); + } + case QCPItemBracket::bsCurly: + case QCPItemBracket::bsCalligraphic: + { + double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos); + double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos); + double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos); + double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos); + return qSqrt(qMin(qMin(a, b), qMin(c, d))); + } + } + return -1; } /* inherits documentation from base class */ diff --git a/companion/src/qcustomplot/qcustomplot.h b/companion/src/qcustomplot/qcustomplot.h index 6f41f38b0..b33923743 100644 --- a/companion/src/qcustomplot/qcustomplot.h +++ b/companion/src/qcustomplot/qcustomplot.h @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** Copyright (C) 2011-2015 Emanuel Eichhammer ** ** ** ** This program is free software: you can redistribute it and/or modify ** ** it under the terms of the GNU General Public License as published by ** @@ -19,8 +19,8 @@ **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** -** Date: 27.12.14 ** -** Version: 1.3.0 ** +** Date: 25.04.15 ** +** Version: 1.3.1 ** ****************************************************************************/ #ifndef QCUSTOMPLOT_H @@ -3099,7 +3099,7 @@ protected: bool mTightBoundary; QPointer mColorScale; // non-property members: - QImage mMapImage; + QImage mMapImage, mUndersampledMapImage; QPixmap mLegendIcon; bool mMapImageInvalidated; diff --git a/companion/src/radiointerface.cpp b/companion/src/radiointerface.cpp index 4089d9fa7..741cd054b 100755 --- a/companion/src/radiointerface.cpp +++ b/companion/src/radiointerface.cpp @@ -40,6 +40,8 @@ QStringList getAvrdudeArgs(const QString &cmd, const QString &filename) args << "-c" << programmer << "-p"; if (GetEepromInterface()->getBoard() == BOARD_GRUVIN9X) args << "m2560"; + else if (GetEepromInterface()->getBoard() == BOARD_MEGA2560) + args << "m2560"; else if (GetEepromInterface()->getBoard() == BOARD_M128) args << "m128"; else diff --git a/companion/src/simulation/simulatordialog-9x.ui b/companion/src/simulation/simulatordialog-9x.ui index 80e26556c..73bbb2586 100644 --- a/companion/src/simulation/simulatordialog-9x.ui +++ b/companion/src/simulation/simulatordialog-9x.ui @@ -1020,7 +1020,7 @@ QPushButton:checked { - + 0 @@ -1054,7 +1054,7 @@ QPushButton:checked { - + 0 % @@ -1077,7 +1077,7 @@ QPushButton:checked { - + 0 @@ -1114,7 +1114,7 @@ QPushButton:checked { - + 0 % @@ -1137,7 +1137,7 @@ QPushButton:checked { - + 0 @@ -1171,7 +1171,7 @@ QPushButton:checked { - + 0 % diff --git a/companion/src/simulation/simulatordialog.cpp b/companion/src/simulation/simulatordialog.cpp index 939d65242..1d0babb87 100644 --- a/companion/src/simulation/simulatordialog.cpp +++ b/companion/src/simulation/simulatordialog.cpp @@ -118,7 +118,8 @@ SimulatorDialog9X::SimulatorDialog9X(QWidget * parent, SimulatorInterface *simul ui->trimVL_U->setText(QString::fromUtf8(upArrow)); ui->trimVL_D->setText(QString::fromUtf8(downArrow)); for (int i=0; isetProperty("index", i); + connect(pots[i], SIGNAL(valueChanged(int)), this, SLOT(dialChanged(int))); } connect(ui->cursor, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int))); connect(ui->menu, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int))); @@ -241,9 +242,10 @@ void SimulatorDialog::mouseReleaseEvent(QMouseEvent *event) } } -void SimulatorDialog::dialChanged(int index) +void SimulatorDialog9X::dialChanged(int value) { - potValues[index]->setText(QString("%1 %").arg((pots[index]->value()*100)/1024)); + int index = sender()->property("index").toInt(); + potValues[index]->setText(QString("%1 %").arg((value*100)/1024)); } void SimulatorDialog::wheelEvent (QWheelEvent *event) diff --git a/companion/src/simulation/simulatordialog.h b/companion/src/simulation/simulatordialog.h index 4111bdbe0..23c829732 100644 --- a/companion/src/simulation/simulatordialog.h +++ b/companion/src/simulation/simulatordialog.h @@ -131,7 +131,6 @@ class SimulatorDialog : public QDialog private slots: void onButtonPressed(int value); - void dialChanged(int index); void on_FixRightY_clicked(bool checked); void on_FixRightX_clicked(bool checked); void on_FixLeftY_clicked(bool checked); @@ -173,6 +172,9 @@ class SimulatorDialog9X: public SimulatorDialog void saveSwitches(void); void restoreSwitches(void); + protected slots: + void dialChanged(int index); + private: Ui::SimulatorDialog9X * ui; static uint32_t switchstatus; diff --git a/companion/src/translations/companion_fr.ts b/companion/src/translations/companion_fr.ts index 1391de27a..aacdb427f 100644 --- a/companion/src/translations/companion_fr.ts +++ b/companion/src/translations/companion_fr.ts @@ -7254,12 +7254,12 @@ p, li { white-space: pre-wrap; } EleTrim Down - TrmE Bas + TrmP Bas EleTrim Up - TrmE Haut + TrmP Haut diff --git a/doc/Mega2560/MEGA2560.sch b/doc/Mega2560/MEGA2560.sch index b81c8e34b..e6f5a233a 100644 --- a/doc/Mega2560/MEGA2560.sch +++ b/doc/Mega2560/MEGA2560.sch @@ -36,7 +36,7 @@ $Descr A4 11693 8268 encoding utf-8 Sheet 1 1 Title "OpenTX board based on Arduino MEGA2560" -Date "14 feb 2015" +Date "20 may 2015" Rev "0" Comp "Copyright 2015 F. Aguerre" Comment1 "" @@ -196,10 +196,10 @@ F 3 "" H 8800 6900 60 0001 C CNN 1 0 0 -1 $EndComp $Comp -L R R6 +L R R7 U 1 1 54997DEC P 8800 6550 -F 0 "R6" V 8880 6550 50 0000 C CNN +F 0 "R7" V 8880 6550 50 0000 C CNN F 1 "100K" V 8800 6550 50 0000 C CNN F 2 "" H 8800 6550 60 0001 C CNN F 3 "" H 8800 6550 60 0001 C CNN @@ -207,11 +207,11 @@ F 3 "" H 8800 6550 60 0001 C CNN -1 0 0 1 $EndComp $Comp -L R R6 +L R R5 U 1 1 54997CFE P 7800 6000 -F 0 "R6" V 7880 6000 50 0000 C CNN -F 1 "100K" V 7800 6000 50 0000 C CNN +F 0 "R5" V 7880 6000 50 0000 C CNN +F 1 "6.8K" V 7800 6000 50 0000 C CNN F 2 "" H 7800 6000 60 0001 C CNN F 3 "" H 7800 6000 60 0001 C CNN 1 7800 6000 @@ -310,11 +310,11 @@ F 3 "" H 9850 4600 60 0001 C CNN 0 1 -1 0 $EndComp $Comp -L R R6 +L R R5 U 1 1 5499427F P 10100 4850 -F 0 "R6" V 10180 4850 50 0000 C CNN -F 1 "100K" V 10100 4850 50 0000 C CNN +F 0 "R5" V 10180 4850 50 0000 C CNN +F 1 "6.8K" V 10100 4850 50 0000 C CNN F 2 "" H 10100 4850 60 0001 C CNN F 3 "" H 10100 4850 60 0001 C CNN 1 10100 4850 @@ -342,42 +342,42 @@ F 3 "" H 9100 4600 60 0001 C CNN 1 9100 4600 1 0 0 -1 $EndComp -Text Label 6450 3850 2 42 ~ 0 +Text Label 6650 3900 2 42 ~ 0 U_batt $Comp L R R3 U 1 1 54994034 -P 6050 4100 -F 0 "R3" V 6130 4100 50 0000 C CNN -F 1 "2.7K" V 6050 4100 50 0000 C CNN -F 2 "" H 6050 4100 60 0001 C CNN -F 3 "" H 6050 4100 60 0001 C CNN - 1 6050 4100 +P 6250 4150 +F 0 "R3" V 6330 4150 50 0000 C CNN +F 1 "2.7K" V 6250 4150 50 0000 C CNN +F 2 "" H 6250 4150 60 0001 C CNN +F 3 "" H 6250 4150 60 0001 C CNN + 1 6250 4150 -1 0 0 -1 $EndComp $Comp L R R4 U 1 1 54994033 -P 5800 3850 -F 0 "R4" V 5880 3850 50 0000 C CNN -F 1 "5.1K" V 5800 3850 50 0000 C CNN -F 2 "" H 5800 3850 60 0001 C CNN -F 3 "" H 5800 3850 60 0001 C CNN - 1 5800 3850 +P 6000 3900 +F 0 "R4" V 6080 3900 50 0000 C CNN +F 1 "5.1K" V 6000 3900 50 0000 C CNN +F 2 "" H 6000 3900 60 0001 C CNN +F 3 "" H 6000 3900 60 0001 C CNN + 1 6000 3900 0 1 -1 0 $EndComp $Comp L GND #PWR? U 1 1 54994032 -P 6050 4450 -F 0 "#PWR?" H 6050 4450 30 0001 C CNN -F 1 "GND" H 6050 4380 30 0001 C CNN -F 2 "" H 6050 4450 60 0001 C CNN -F 3 "" H 6050 4450 60 0001 C CNN - 1 6050 4450 +P 6250 4500 +F 0 "#PWR?" H 6250 4500 30 0001 C CNN +F 1 "GND" H 6250 4430 30 0001 C CNN +F 2 "" H 6250 4500 60 0001 C CNN +F 3 "" H 6250 4500 60 0001 C CNN + 1 6250 4500 1 0 0 -1 $EndComp -Text Notes 5400 3500 0 60 ~ 0 +Text Notes 5600 3550 0 60 ~ 0 Battery voltage reading Text Notes 7250 1950 0 60 ~ 0 Rotary encoders (x2) @@ -461,11 +461,11 @@ F 3 "" H 5250 1000 60 0001 C CNN 1 0 0 -1 $EndComp $Comp -L R R3 +L R R2 U 1 1 54993A62 P 5250 2200 -F 0 "R3" V 5330 2200 50 0000 C CNN -F 1 "2.7K" V 5250 2200 50 0000 C CNN +F 0 "R2" V 5330 2200 50 0000 C CNN +F 1 "2.2K" V 5250 2200 50 0000 C CNN F 2 "" H 5250 2200 60 0001 C CNN F 3 "" H 5250 2200 60 0001 C CNN 1 5250 2200 @@ -646,10 +646,10 @@ $EndComp Text Label 9250 1000 0 42 ~ 0 M2560_Ouput $Comp -L R R5 +L R R6 U 1 1 54992F2E P 10250 1250 -F 0 "R5" V 10330 1250 50 0000 C CNN +F 0 "R6" V 10330 1250 50 0000 C CNN F 1 "10K" V 10250 1250 50 0000 C CNN F 2 "" H 10250 1250 60 0001 C CNN F 3 "" H 10250 1250 60 0001 C CNN @@ -837,10 +837,10 @@ F 3 "" H 2350 2100 60 0001 C CNN 1 0 0 -1 $EndComp $Comp -L R R5 +L R R6 U 1 1 549891B4 P 2150 1700 -F 0 "R5" V 2230 1700 50 0000 C CNN +F 0 "R6" V 2230 1700 50 0000 C CNN F 1 "10K" V 2150 1700 50 0000 C CNN F 2 "" H 2150 1700 60 0001 C CNN F 3 "" H 2150 1700 60 0001 C CNN @@ -872,10 +872,10 @@ F 3 "" H 1400 2300 60 0001 C CNN 1 0 0 -1 $EndComp $Comp -L R R5 +L R R6 U 1 1 549891AC P 2050 2800 -F 0 "R5" V 2130 2800 50 0000 C CNN +F 0 "R6" V 2130 2800 50 0000 C CNN F 1 "10K" V 2050 2800 50 0000 C CNN F 2 "" H 2050 2800 60 0001 C CNN F 3 "" H 2050 2800 60 0001 C CNN @@ -998,10 +998,10 @@ F 3 "" H 9100 5400 60 0001 C CNN 0 1 -1 0 $EndComp $Comp -L R R6 +L R R7 U 1 1 549881D8 P 8850 5600 -F 0 "R6" V 8930 5600 50 0000 C CNN +F 0 "R7" V 8930 5600 50 0000 C CNN F 1 "100K" V 8850 5600 50 0000 C CNN F 2 "" H 8850 5600 60 0001 C CNN F 3 "" H 8850 5600 60 0001 C CNN @@ -40954,12 +40954,12 @@ $EndComp $Comp L +BATT U? U 1 1 549E8260 -P 5450 3750 -F 0 "U?" H 5450 3700 60 0001 C CNN -F 1 "+BATT" H 5450 3850 50 0000 C BNN -F 2 "" H 5450 3750 60 0001 C CNN -F 3 "" H 5450 3750 60 0001 C CNN - 1 5450 3750 +P 5650 3800 +F 0 "U?" H 5650 3750 60 0001 C CNN +F 1 "+BATT" H 5650 3900 50 0000 C BNN +F 2 "" H 5650 3800 60 0001 C CNN +F 3 "" H 5650 3800 60 0001 C CNN + 1 5650 3800 1 0 0 -1 $EndComp $Comp @@ -41022,24 +41022,24 @@ F 3 "" H 10450 4200 60 0001 C CNN 1 0 0 -1 $EndComp $Comp -L R R? +L R R6 U 1 1 549E8FFF P 10450 3850 -F 0 "R?" V 10530 3850 50 0000 C CNN +F 0 "R6" V 10530 3850 50 0000 C CNN F 1 "10K" V 10450 3850 50 0000 C CNN F 2 "" H 10450 3850 60 0001 C CNN F 3 "" H 10450 3850 60 0001 C CNN 1 10450 3850 -1 0 0 -1 $EndComp -Text Label 6500 7150 2 42 ~ 0 -OpenTX I/O -Text GLabel 5250 7150 2 42 Output ~ 0 +Text Label 6550 7450 2 42 ~ 0 +OpenTX output +Text GLabel 5250 7450 2 42 Output ~ 0 port pin (initial state) -Text Notes 5500 7050 0 60 ~ 0 -Caption +Text Notes 5450 7300 0 60 ~ 0 +Pinout caption : Text Label 5550 2550 2 42 ~ 0 -Gain = 1 + R3/R6 +Gain = 1 + R2/R6 $Bitmap Pos 4600 4650 Scale 1.000000 @@ -41200,7 +41200,7 @@ Wire Wire Line Wire Wire Line 2400 1700 2500 1700 Wire Wire Line - 6050 4350 6050 4450 + 6250 4400 6250 4500 Wire Wire Line 9600 2250 9450 2250 Wire Wire Line @@ -41604,7 +41604,7 @@ Wire Wire Line Wire Wire Line 10750 3800 10750 4200 Wire Wire Line - 6450 3850 6050 3850 + 6650 3900 6250 3900 Wire Wire Line 1900 1450 1900 1700 Wire Wire Line @@ -41642,9 +41642,9 @@ Wire Wire Line Wire Wire Line 4400 1350 4850 1350 Wire Wire Line - 5450 3750 5450 3850 + 5650 3800 5650 3900 Wire Wire Line - 5450 3850 5550 3850 + 5650 3900 5750 3900 Wire Wire Line 7500 4600 8600 4600 Connection ~ 8300 4600 @@ -41664,9 +41664,9 @@ Wire Wire Line Wire Wire Line 10450 4100 10450 4200 Wire Wire Line - 6050 7150 6500 7150 + 6050 7450 6550 7450 Wire Wire Line - 5100 7150 5250 7150 + 5100 7450 5250 7450 Wire Wire Line 4700 4650 4700 4300 Wire Wire Line @@ -41738,4 +41738,16 @@ F 3 "" H 1750 7050 60 0000 C CNN 1 1750 7050 -1 0 0 1 $EndComp +Text Notes 4850 6550 0 42 ~ 0 +LCD supported : ST7565P, ST7565R, ERC12864FSF, ST7920\nProtocol = 6800, 8 bits parallel mode\n\nLCD pinout labels (may differ, depending of brand) :\nRD = Read or Enable on ST7920\nWR = WRite or Write(0)/Read(1) on ST7920\nA0 = R/S (register select) or D/I (data/instruction select)\nRST = RES (reset)\nCS = Chip Select (not used on ST7920) +Text Label 6550 7550 2 42 ~ 0 +OpenTX input +Text GLabel 5250 7550 2 42 Input ~ 0 +port pin (initial state) +Wire Wire Line + 6050 7550 6550 7550 +Wire Wire Line + 5100 7550 5250 7550 +Text Notes 2300 2950 0 60 ~ 0 +NOTA : all features are optional !\nM2560 can properly work without switch deboucing,\nrotary encoder or PPM / power management.\nMinimum requirement is just LCD and navigation keys.\nSome switches could be usefull, especially for flight\nconditions, but not fundamental. $EndSCHEMATC diff --git a/doc/Mega2560/MEGA2560_Listing.xls b/doc/Mega2560/MEGA2560_Listing.xls index a16da90ed..aa25c927f 100644 Binary files a/doc/Mega2560/MEGA2560_Listing.xls and b/doc/Mega2560/MEGA2560_Listing.xls differ diff --git a/doc/Mega2560/MEGA2560_SCH.pdf b/doc/Mega2560/MEGA2560_SCH.pdf index 3c24db985..87281add8 100644 Binary files a/doc/Mega2560/MEGA2560_SCH.pdf and b/doc/Mega2560/MEGA2560_SCH.pdf differ diff --git a/doc/Mega2560/MEGA2560_bootloader.rar b/doc/Mega2560/MEGA2560_bootloader.rar index bd7ceeab3..215c50495 100644 Binary files a/doc/Mega2560/MEGA2560_bootloader.rar and b/doc/Mega2560/MEGA2560_bootloader.rar differ diff --git a/doc/Mega2560/MEGA2560_case_black.pdf b/doc/Mega2560/MEGA2560_case_black.pdf deleted file mode 100644 index 2ac550e91..000000000 Binary files a/doc/Mega2560/MEGA2560_case_black.pdf and /dev/null differ diff --git a/doc/Mega2560/MEGA2560_case_color.pdf b/doc/Mega2560/MEGA2560_case_color.pdf deleted file mode 100644 index 0dfd4e660..000000000 Binary files a/doc/Mega2560/MEGA2560_case_color.pdf and /dev/null differ diff --git a/doc/Mega2560/MEGA2560_pcb.pdf b/doc/Mega2560/MEGA2560_pcb.pdf index fc1293fbe..5bf5a64bf 100644 Binary files a/doc/Mega2560/MEGA2560_pcb.pdf and b/doc/Mega2560/MEGA2560_pcb.pdf differ diff --git a/radio/src/Makefile b/radio/src/Makefile index 890a28833..136cc5dc6 100644 --- a/radio/src/Makefile +++ b/radio/src/Makefile @@ -57,6 +57,10 @@ PCB = 9X # PCB revision PCBREV = +# LCD +# Values: DEFAULT, ST7565P, ST7565R, ERC12864FSF, ST7920 +LCD = DEFAULT + # Enable JETI-Telemetry or FrSky Telemetry reception on UART0 # For this option you need to modify your hardware! # More information at [insertURLhere] @@ -443,7 +447,12 @@ ifeq ($(PCB), $(filter $(PCB), STD 9X 9XR)) ifeq ($(AUDIO), YES) CPPDEFS += -DAUDIO CPPSRC += audio_avr.cpp + BUZZER = NO else + BUZZER = YES + endif + + ifeq ($(BUZZER), YES) CPPDEFS += -DBUZZER CPPSRC += buzzer.cpp endif @@ -517,7 +526,12 @@ ifeq ($(PCB), $(filter $(PCB), STD128 9X128 9XR128)) ifeq ($(AUDIO), YES) CPPDEFS += -DAUDIO CPPSRC += audio_avr.cpp + BUZZER = NO else + BUZZER = YES + endif + + ifeq ($(BUZZER), YES) CPPDEFS += -DBUZZER CPPSRC += buzzer.cpp endif @@ -579,7 +593,12 @@ ifeq ($(PCB), $(filter $(PCB), 9X2561)) ifeq ($(AUDIO), YES) CPPDEFS += -DAUDIO CPPSRC += audio_avr.cpp + BUZZER = NO else + BUZZER = YES + endif + + ifeq ($(BUZZER), YES) CPPDEFS += -DBUZZER CPPSRC += buzzer.cpp endif @@ -680,13 +699,7 @@ ifeq ($(PCB), MEGA2560) BITMAPS += bitmaps/9X/splash.lbm bitmaps/9X/asterisk.lbm bitmaps/9X/about.lbm GUIGENERALSRC += gui/$(GUIDIRECTORY)/menu_general_diagkeys.cpp gui/$(GUIDIRECTORY)/menu_general_diaganas.cpp - # LCD driver - # Values: ST7565P(default), ST7565R - LCD = ST7565P - ifeq ($(LCD), ST7565R) - CPPDEFS += -DLCD_ST7565R - endif - EXTRABOARDSRC = targets/common_avr/adc_driver.cpp targets/stock/lcd_driver.cpp targets/common_avr/telemetry_driver.cpp + EXTRABOARDSRC = targets/common_avr/adc_driver.cpp targets/stock/lcd_driver.cpp targets/common_avr/telemetry_driver.cpp ifeq ($(SDCARD), YES) EXTRABOARDSRC += $(FATFSDIR)/ff.c $(FATFSDIR)/fattime.c $(FATFSDIR)/option/ccsbcs.c targets/gruvin9x/diskio.cpp @@ -1071,6 +1084,14 @@ ifeq ($(TURNIGY_TRANSMITTER_FIX), YES) endif endif +ifeq ($(LCD), ST7565R) + CPPDEFS += -DLCD_ST7565R +else ifeq ($(LCD), ERC12864FSF) + CPPDEFS += -DLCD_ERC12864FSF +else ifeq ($(LCD), ST7920) + CPPDEFS += -DLCD_ST7920 +endif + ifeq ($(AUTOSWITCH), YES) CPPDEFS += -DAUTOSWITCH endif diff --git a/radio/src/buzzer.h b/radio/src/buzzer.h index 7d1c15d0d..c430ec118 100644 --- a/radio/src/buzzer.h +++ b/radio/src/buzzer.h @@ -37,6 +37,7 @@ #ifndef BUZZER_H #define BUZZER_H +#if defined(BUZZER) extern uint8_t g_beepCnt; extern uint8_t beepAgain; extern uint8_t beepAgainOrig; @@ -45,23 +46,31 @@ extern bool warble; extern bool warbleC; #if defined(HAPTIC) extern uint8_t hapticTick; -#endif +#endif /* HAPTIC */ +#endif /* BUZZER */ + +#if defined(BUZZER) #if defined(CPUARM) && !defined(SIMU) inline void _beep(uint8_t b) { buzzerSound(b); } -#else +#else /* CPUARM && !SIMU */ inline void _beep(uint8_t b) { g_beepCnt = b; } -#endif +#endif /* CPUARM && !SIMU */ void beep(uint8_t val); +#else /* BUZZER */ +#define beep(...) +#endif /* BUZZER */ #if !defined(AUDIO) + +#if defined(BUZZER) #if defined(VOICE) #define AUDIO_TADA() PUSH_SYSTEM_PROMPT(AU_TADA) #define AUDIO_BYE() @@ -100,6 +109,34 @@ void beep(uint8_t val); #define AUDIO_PLAY(p) beep(3) #define IS_AUDIO_BUSY() (g_beepCnt || beepAgain || beepOn) +#else /* BUZZER */ + #define AUDIO_TADA() + #define AUDIO_BYE() + #define AUDIO_TX_BATTERY_LOW() + #define AUDIO_INACTIVITY() + #define AUDIO_ERROR_MESSAGE(e) + #define AUDIO_TIMER_MINUTE(t) + #define AUDIO_TIMER_30() + #define AUDIO_TIMER_20() + #define AUDIO_KEYPAD_UP() + #define AUDIO_KEYPAD_DOWN() + #define AUDIO_MENUS() + #define AUDIO_WARNING2() + #define AUDIO_WARNING1() + #define AUDIO_ERROR() + #define AUDIO_MIX_WARNING(x) + #define AUDIO_POT_MIDDLE() + #define AUDIO_TIMER_LT10(m, x) + #define AUDIO_TIMER_00(m) + #define AUDIO_VARIO_UP() + #define AUDIO_VARIO_DOWN() + #define AUDIO_TRIM(event, f) + #define AUDIO_TRIM_MIDDLE(f) + #define AUDIO_TRIM_END(f) + #define AUDIO_PLAY(p) + #define IS_AUDIO_BUSY() false +#endif /* BUZZER */ + #define AUDIO_RESET() #define AUDIO_FLUSH() @@ -109,9 +146,10 @@ void beep(uint8_t val); #define PLAY_LOGICAL_SWITCH_OFF(sw) #define PLAY_LOGICAL_SWITCH_ON(sw) #define START_SILENCE_PERIOD() -#endif +#endif /* !AUDIO */ #if !defined(CPUARM) +#if defined(BUZZER) inline void BUZZER_HEARTBEAT() { if (g_beepCnt) { @@ -145,6 +183,9 @@ inline void BUZZER_HEARTBEAT() buzzerOff(); } } -#endif +#else // BUZZER +#define BUZZER_HEARTBEAT() +#endif // BUZZER +#endif // CPUARM #endif diff --git a/radio/src/eeprom_conversions.cpp b/radio/src/eeprom_conversions.cpp index bb58582a0..d2e1a7495 100644 --- a/radio/src/eeprom_conversions.cpp +++ b/radio/src/eeprom_conversions.cpp @@ -526,49 +526,21 @@ PACK(typedef struct { // Swash Ring data }) SwashRingData_v215; PACK(typedef struct { - ModelHeader header; - TimerData_v215 timers[2]; - uint8_t protocol:3; - uint8_t thrTrim:1; // Enable Throttle Trim - int8_t ppmNCH:4; - uint8_t trimInc:3; // Trim Increments - uint8_t disableThrottleWarning:1; - uint8_t pulsePol:1; - uint8_t extendedLimits:1; - uint8_t extendedTrims:1; - uint8_t throttleReversed:1; - int8_t ppmDelay; - BeepANACenter beepANACenter; // 1<<0->A1.. 1<<6->A7 - MixData_v215 mixData[MAX_MIXERS]; - LimitData_v215 limitData[NUM_CHNOUT]; - ExpoData_v215 expoData[32]; - - int16_t curves[16]; - int8_t points[NUM_POINTS]; - - LogicalSwitchData_v215 logicalSw[NUM_LOGICAL_SWITCH]; - CustomFunctionData_v215 customFn[32]; - SwashRingData_v215 swashR; - FlightModeData_v215 flightModeData[MAX_FLIGHT_MODES]; - - int8_t ppmFrameLength; // 0=22.5ms (10ms-30ms) 0.5ms increments - uint8_t thrTraceSrc; - - swarnstate_t switchWarningState; - - char gvar_names[5][LEN_GVAR_NAME]; - - FrSkyData_v215 frsky; - - MODELDATA_EXTRA - -}) ModelData_v215; + int8_t rfProtocol; + uint8_t channelsStart; + int8_t channelsCount; // 0=8 channels + uint8_t failsafeMode; + int16_t failsafeChannels[NUM_CHNOUT]; + int8_t ppmDelay; + int8_t ppmFrameLength; + uint8_t ppmPulsePol; +}) ModuleData_v216; #if defined(PCBTARANIS) #define MODELDATA_EXTRA_216 \ uint8_t externalModule; \ uint8_t trainerMode; \ - ModuleData moduleData[NUM_MODULES+1]; \ + ModuleData_v216 moduleData[NUM_MODULES+1]; \ char curveNames[MAX_CURVES][6]; \ ScriptData_v216 scriptsData[MAX_SCRIPTS]; \ char inputNames[MAX_INPUTS][LEN_INPUT_NAME]; \ @@ -578,14 +550,27 @@ PACK(typedef struct { #elif defined(PCBSKY9X) #define MODELDATA_EXTRA_216 \ uint8_t externalModule; \ - ModuleData moduleData[NUM_MODULES+1]; \ + ModuleData_v216 moduleData[NUM_MODULES+1]; \ uint8_t nPotsToWarn; \ int8_t potPosition[NUM_POTS]; \ uint8_t rxBattAlarms[2]; #endif +#if defined(PCBTARANIS) PACK(typedef struct { - ModelHeader header; + char name[LEN_MODEL_NAME]; + uint8_t modelId; + char bitmap[LEN_BITMAP_NAME]; +}) ModelHeader_v216; +#else +PACK(typedef struct { + char name[LEN_MODEL_NAME]; + uint8_t modelId; +}) ModelHeader_v216; +#endif + +PACK(typedef struct { + ModelHeader_v216 header; TimerData_v216 timers[2]; AVR_FIELD(uint8_t protocol:3) ARM_FIELD(uint8_t telemetryProtocol:3) @@ -885,433 +870,6 @@ int16_t ConvertGVAR_215_to_216(int16_t var) return var; } -void ConvertModel_215_to_216(ModelData &model) -{ - // Virtual inputs added instead of Expo/DR - // LUA scripts added - // GVARS: now 9 GVARS, popup param added - // Curves: structure changed, 32 curves - // Limits: min and max with PREC1 - // Custom Functions: play repeat * 5 - // Logical Switches: better precision for x when A comes from telemetry - // Main View: altitude in top bar - // Mixes: GVARS in weight moved from 512 to 4096 and -512 to -4096, because GVARS may be used in limits [-1250:1250] - // Switches: two 6-pos pots added, REa added to Sky9x - - assert(sizeof(ModelData_v215) <= sizeof(ModelData)); - - ModelData_v215 oldModel; - memcpy(&oldModel, &model, sizeof(oldModel)); - ModelData_v216 &newModel = *(ModelData_v216*)&model; - memset(&newModel, 0, sizeof(ModelData_v216)); - - char name[LEN_MODEL_NAME+1]; - zchar2str(name, oldModel.header.name, LEN_MODEL_NAME); - TRACE("Model %s conversion from v215 to v216", name); - - memcpy(&newModel.header, &oldModel.header, sizeof(newModel.header)); - for (uint8_t i=0; i<2; i++) { - TimerData_v216 & timer = newModel.timers[i]; - if (oldModel.timers[i].mode >= TMRMODE_COUNT) - timer.mode = TMRMODE_COUNT + ConvertSwitch_215_to_216(oldModel.timers[i].mode - TMRMODE_COUNT + 1) - 1; - else - timer.mode = oldModel.timers[i].mode; - timer.start = oldModel.timers[i].start; - timer.minuteBeep = oldModel.timers[i].minuteBeep; - timer.persistent = oldModel.timers[i].persistent; - timer.countdownBeep = oldModel.timers[i].countdownBeep; - timer.value = oldModel.timers[i].value; - } - newModel.thrTrim = oldModel.thrTrim; - newModel.trimInc = oldModel.trimInc - 2; - newModel.disableThrottleWarning = oldModel.disableThrottleWarning; - newModel.extendedLimits = oldModel.extendedLimits; - newModel.extendedTrims = oldModel.extendedTrims; - newModel.throttleReversed = oldModel.throttleReversed; - -#if defined(PCBTARANIS) - newModel.beepANACenter = (oldModel.beepANACenter & 0x3f) | ((oldModel.beepANACenter & 0xc0) << 1); -#else - newModel.beepANACenter = oldModel.beepANACenter; -#endif - - for (uint8_t i=0; i<64; i++) { - MixData_v216 * mix = &newModel.mixData[i]; - MixData_v215 * oldMix = &oldModel.mixData[i]; -#if defined(PCBTARANIS) - mix->destCh = oldMix->destCh; - mix->flightModes = oldMix->flightModes; - mix->mltpx = oldMix->mltpx; - if (oldMix->carryTrim == TRIM_OFF) mix->carryTrim = TRIM_OFF; - mix->weight = ConvertGVAR_215_to_216(oldMix->weight); - mix->swtch = ConvertSwitch_215_to_216(oldMix->swtch); - if (oldMix->curveMode==0/*differential*/) { - mix->curve.type = CURVE_REF_DIFF; - mix->curve.value = oldMix->curveParam; - } - else if (oldMix->curveParam <= 6) { - mix->curve.type = CURVE_REF_FUNC; - mix->curve.value = oldMix->curveParam; - } - else { - mix->curve.type = CURVE_REF_CUSTOM; - mix->curve.value = oldMix->curveParam - 6; - } - mix->mixWarn = oldMix->mixWarn; - mix->delayUp = oldMix->delayUp; - mix->delayDown = oldMix->delayDown; - mix->speedUp = oldMix->speedUp; - mix->speedDown = oldMix->speedDown; - mix->srcRaw = oldMix->srcRaw; - if (mix->srcRaw > 4 || oldMix->noExpo) - mix->srcRaw = ConvertSource_215_to_216(mix->srcRaw); - mix->offset = ConvertGVAR_215_to_216(oldMix->offset); - memcpy(mix->name, oldMix->name, LEN_EXPOMIX_NAME); - if (!GV_IS_GV_VALUE(mix->weight, 500, 500) && !GV_IS_GV_VALUE(mix->offset, 500, 500)) { - mix->offset = divRoundClosest(mix->offset * mix->weight, 100); - } -#else - mix->destCh = oldMix->destCh; - mix->mixWarn = oldMix->mixWarn; - mix->flightModes = oldMix->flightModes; - mix->curveMode = oldMix->curveMode; - mix->noExpo = oldMix->noExpo; - mix->carryTrim = oldMix->carryTrim; - mix->mltpx = oldMix->mltpx; - mix->weight = oldMix->weight; - mix->swtch = ConvertSwitch_215_to_216(oldMix->swtch); - mix->curveParam = oldMix->curveParam; - mix->delayUp = oldMix->delayUp; - mix->delayDown = oldMix->delayDown; - mix->speedUp = oldMix->speedUp; - mix->speedDown = oldMix->speedDown; - mix->srcRaw = ConvertSource_215_to_216(oldMix->srcRaw); - mix->offset = oldMix->offset; - memcpy(mix->name, oldMix->name, LEN_EXPOMIX_NAME); -#endif - } - for (uint8_t i=0; i<32; i++) { -#if defined(PCBTARANIS) - newModel.limitData[i].min = 10 * oldModel.limitData[i].min; - newModel.limitData[i].max = 10 * oldModel.limitData[i].max; -#else - newModel.limitData[i].min = oldModel.limitData[i].min; - newModel.limitData[i].max = oldModel.limitData[i].max; -#endif - newModel.limitData[i].ppmCenter = oldModel.limitData[i].ppmCenter; - newModel.limitData[i].offset = oldModel.limitData[i].offset; - newModel.limitData[i].symetrical = oldModel.limitData[i].symetrical; - newModel.limitData[i].revert = oldModel.limitData[i].revert; -#if defined(PCBTARANIS) - memcpy(newModel.limitData[i].name, oldModel.limitData[i].name, LEN_CHANNEL_NAME); -#endif - } -#if defined(PCBTARANIS) - int indexes[NUM_STICKS] = { 0, 0, 0, 0 }; -#endif - for (uint8_t i=0; i<32; i++) { - ExpoData_v216 * expo = &newModel.expoData[i]; - ExpoData_v215 * oldExpo = &oldModel.expoData[i]; - if (oldExpo->mode) { -#if defined(PCBTARANIS) - uint8_t chn = oldExpo->chn; - if (!oldExpo->swtch && !oldExpo->flightModes) { - indexes[chn] = -1; - } - else if (indexes[chn] != -1) { - indexes[chn] = i+1; - } - for (uint8_t j=chn+1; jsrcRaw = MIXSRC216_Rud+chn; - expo->chn = chn; - expo->mode = oldExpo->mode; - expo->swtch = ConvertSwitch_215_to_216(oldExpo->swtch); - expo->flightModes = oldExpo->flightModes; - expo->weight = oldExpo->weight; - memcpy(expo->name, oldExpo->name, LEN_EXPOMIX_NAME); - if (oldExpo->curveMode==0/*expo*/) { - if (oldExpo->curveParam) { - expo->curve.type = CURVE_REF_EXPO; - expo->curve.value = oldExpo->curveParam; - } - } - else if (oldExpo->curveParam <= 6) { - expo->curve.type = CURVE_REF_FUNC; - expo->curve.value = oldExpo->curveParam; - } - else { - expo->curve.type = CURVE_REF_CUSTOM; - expo->curve.value = oldExpo->curveParam - 6; - } -#else - expo->mode = oldExpo->mode; - expo->chn = oldExpo->chn; - expo->curveMode = oldExpo->curveMode; - expo->swtch = oldExpo->swtch; - expo->flightModes = oldExpo->flightModes; - expo->weight = oldExpo->weight; - memcpy(expo->name, oldExpo->name, LEN_EXPOMIX_NAME); - expo->curveParam = oldExpo->curveParam; -#endif - } - else { - break; - } - } -#if defined(PCBTARANIS) - for (int i=NUM_STICKS-1; i>=0; i--) { - int idx = indexes[i]; - if (idx >= 0) { - ExpoData_v216 * expo = &newModel.expoData[idx]; - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData_v216)); - memclear(expo, sizeof(ExpoData_v216)); - expo->srcRaw = MIXSRC216_Rud + i; - expo->chn = i; - expo->weight = 100; - expo->curve.type = CURVE_REF_EXPO; - expo->mode = 3; - } - for (int c=0; c<4; c++) { - newModel.inputNames[i][c] = char2idx(STR_VSRCRAW[2+STR_VSRCRAW[0]*(i+1)+c]); - } - } -#endif - for (uint8_t i=0; i<16; i++) { -#if defined(PCBTARANIS) - int8_t *cur = &oldModel.points[i==0 ? 0 : 5*i+oldModel.curves[i-1]]; - int8_t *nxt = &oldModel.points[5+5*i+oldModel.curves[i]]; - uint8_t size = nxt - cur; - if ((size & 1) == 0) { - newModel.curves[i].type = CURVE_TYPE_CUSTOM; - newModel.curves[i].points = (size / 2) - 4; - } - else { - newModel.curves[i].points = size-5; - } -#else - newModel.curves[i] = oldModel.curves[i]; -#endif - } - for (uint16_t i=0; i<512; i++) { - newModel.points[i] = oldModel.points[i]; - } - for (uint8_t i=0; i<32; i++) { - LogicalSwitchData_v216 & sw = newModel.logicalSw[i]; - sw.func = oldModel.logicalSw[i].func; - if (sw.func >= LS_FUNC_VEQUAL) sw.func += 1; - if (sw.func >= LS_FUNC_RANGE) sw.func += 1; - if (sw.func >= LS_FUNC_EDGE) sw.func += 1; - sw.v1 = oldModel.logicalSw[i].v1; - sw.v2 = oldModel.logicalSw[i].v2; - sw.delay = oldModel.logicalSw[i].delay * 5; - sw.duration = oldModel.logicalSw[i].duration * 5; - sw.andsw = ConvertSwitch_215_to_216(oldModel.logicalSw[i].andsw); - uint8_t cstate = lswFamily(sw.func); - if (cstate == LS_FAMILY_BOOL) { - sw.v1 = ConvertSwitch_215_to_216(sw.v1); - sw.v2 = ConvertSwitch_215_to_216(sw.v2); - } - else if (cstate == LS_FAMILY_OFS || cstate == LS_FAMILY_COMP || cstate == LS_FAMILY_DIFF) { - sw.v1 = ConvertSource_215_to_216(sw.v1); - if (cstate == LS_FAMILY_OFS || cstate == LS_FAMILY_DIFF) { - if ((uint8_t)sw.v1 >= MIXSRC216_FIRST_TELEM) { - switch ((uint8_t)sw.v1) { - case MIXSRC216_FIRST_TELEM + TELEM216_TIMER1-1: - case MIXSRC216_FIRST_TELEM + TELEM216_TIMER2-1: - sw.v2 = (sw.v2+128) * 3; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_ALT-1: - case MIXSRC216_FIRST_TELEM + TELEM216_GPSALT-1: - case MIXSRC216_FIRST_TELEM + TELEM216_MIN_ALT-1: - case MIXSRC216_FIRST_TELEM + TELEM216_MAX_ALT-1: - sw.v2 = (sw.v2+128) * 8 - 500; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_RPM-1: - case MIXSRC216_FIRST_TELEM + TELEM216_MAX_RPM-1: - sw.v2 = (sw.v2+128) * 50; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_T1-1: - case MIXSRC216_FIRST_TELEM + TELEM216_T2-1: - case MIXSRC216_FIRST_TELEM + TELEM216_MAX_T1-1: - case MIXSRC216_FIRST_TELEM + TELEM216_MAX_T2-1: - sw.v2 = (sw.v2+128) + 30; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_CELL-1: - case MIXSRC216_FIRST_TELEM + TELEM216_HDG-1: - sw.v2 = (sw.v2+128) * 2; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_DIST-1: - case MIXSRC216_FIRST_TELEM + TELEM216_MAX_DIST-1: - sw.v2 = (sw.v2+128) * 8; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_CURRENT-1: - case MIXSRC216_FIRST_TELEM + TELEM216_POWER-1: - sw.v2 = (sw.v2+128) * 5; - break; - case MIXSRC216_FIRST_TELEM + TELEM216_CONSUMPTION-1: - sw.v2 = (sw.v2+128) * 20; - break; - default: - sw.v2 += 128; - break; - } - } - } - else if (cstate == LS_FAMILY_COMP) { - sw.v2 = ConvertSource_215_to_216(sw.v2); - } - } - } - for (uint8_t i=0; i<32; i++) { - CustomFunctionData & fn = newModel.customFn[i]; - fn.swtch = ConvertSwitch_215_to_216(oldModel.customFn[i].swtch); - fn.func = oldModel.customFn[i].func; - if (fn.func <= 15) { - fn.all.param = fn.func; - fn.func = FUNC_OVERRIDE_CHANNEL; - } - else if (fn.func <= 20) { - fn.all.param = fn.func - 16; - fn.func = FUNC_TRAINER; - } - else if (fn.func == 21) { - fn.func = FUNC_INSTANT_TRIM; - } - else if (fn.func == 22) { - fn.func = FUNC_PLAY_SOUND; - } -#if defined(PCBSKY9X) - else if (fn.func == 23) { - fn.func = FUNC_HAPTIC; - } -#endif - else if (fn.func == 23+IS_PCBSKY9X) { - fn.func = FUNC_RESET; - } - else if (fn.func == 24+IS_PCBSKY9X) { - fn.func = FUNC_VARIO; - } - else if (fn.func == 25+IS_PCBSKY9X) { - fn.func = FUNC_PLAY_TRACK; - } - else if (fn.func == 26+IS_PCBSKY9X) { - fn.func = FUNC_PLAY_VALUE; - } - else if (fn.func == 27+IS_PCBSKY9X) { - fn.func = FUNC_LOGS; - } - else if (fn.func == 28+IS_PCBSKY9X) { - fn.func = FUNC_VOLUME; - } - else if (fn.func == 29+IS_PCBSKY9X) { - fn.func = FUNC_BACKLIGHT; - } - else if (fn.func == 30+IS_PCBSKY9X) { - fn.func = FUNC_BACKGND_MUSIC; - } - else if (fn.func == 31+IS_PCBSKY9X) { - fn.func = FUNC_BACKGND_MUSIC_PAUSE; - } - else { - fn.all.param = fn.func - 32 - IS_PCBSKY9X; - fn.all.mode = oldModel.customFn[i].mode; - fn.func = FUNC_ADJUST_GVAR; - } - - fn.active = oldModel.customFn[i].active; - if (HAS_REPEAT_PARAM(fn.func)) { - fn.active *= 5; - } - - if (fn.func == FUNC_PLAY_TRACK || fn.func == FUNC_BACKGND_MUSIC) { - memcpy(fn.play.name, oldModel.customFn[i].param.name, 8); - } - else { - fn.all.val = oldModel.customFn[i].param.composite.val; - } - if (fn.func == FUNC_PLAY_VALUE || fn.func == FUNC_VOLUME || (IS_ADJUST_GV_FUNC(fn.func) && fn.all.mode == FUNC_ADJUST_GVAR_SOURCE)) { -#if defined(PCBTARANIS) - fn.all.val = ConvertSource_215_to_216(fn.all.val, true); -#endif - } - } - - newModel.swashR = oldModel.swashR; - newModel.swashR.collectiveSource = ConvertSource_215_to_216(newModel.swashR.collectiveSource); - - for (uint8_t i=0; i<9; i++) { - if (i==0 || oldModel.flightModeData[i].swtch) { - memcpy(&newModel.flightModeData[i], &oldModel.flightModeData[i], sizeof(oldModel.flightModeData[i])); // the last 4 gvars will remain blank - newModel.flightModeData[i].swtch = ConvertSwitch_215_to_216(oldModel.flightModeData[i].swtch); -#if defined(PCBTARANIS) - for (uint8_t t=0; t<4; t++) { - int trim = oldModel.flightModeData[i].trim[t]; - if (trim > 500) { - trim -= 501; - if (trim >= i) - trim += 1; - newModel.flightModeData[i].trim[t].mode = 2*trim; - newModel.flightModeData[i].trim[t].value = 0; - } - else { - newModel.flightModeData[i].trim[t].mode = 2*i; - newModel.flightModeData[i].trim[t].value = trim; - } - } -#endif - } - } - newModel.thrTraceSrc = oldModel.thrTraceSrc; -#if defined(PCBTARANIS) - // S3 added - if (newModel.thrTraceSrc >= THROTTLE_SOURCE_S3) - newModel.thrTraceSrc += 1; -#endif - newModel.switchWarningState = oldModel.switchWarningState >> 1; - newModel.switchWarningEnable = (oldModel.switchWarningState & 0x01) ? 0xFF : 0; - for (uint8_t i=0; i<5; i++) { - memcpy(newModel.gvars[i].name, oldModel.gvar_names[i], LEN_GVAR_NAME); - } - - memcpy(&newModel.frsky, &oldModel.frsky, 2*sizeof(FrSkyChannelData)); - // gap for A3-A4 - memcpy(((uint8_t *)&newModel.frsky) + 4*sizeof(FrSkyChannelData), ((uint8_t *)&oldModel.frsky) + 2*sizeof(FrSkyChannelData), sizeof(oldModel.frsky) - 2*sizeof(FrSkyChannelData)); - // A3 and A4 introduced - if (newModel.frsky.voltsSource >= FRSKY_VOLTS_SOURCE_A3) - newModel.frsky.voltsSource += 2; - if (newModel.frsky.currentSource >= FRSKY_CURRENT_SOURCE_A3) - newModel.frsky.currentSource += 2; - - for (int i=0; i<3; i++) { - if (newModel.frsky.screensType & (1<= TMRMODE_COUNT) @@ -1463,8 +1026,23 @@ void ConvertModel_216_to_217(ModelData &model) memcpy(newModel.gvars, oldModel.gvars, sizeof(newModel.gvars)); memcpy(&newModel.frsky.rssiAlarms, &oldModel.frsky.rssiAlarms, sizeof(newModel.frsky.rssiAlarms)); - newModel.externalModule = oldModel.externalModule; - memcpy(newModel.moduleData, oldModel.moduleData, sizeof(newModel.moduleData)); + + for (int i=0; i0 || p1valdiff)) { switch (m_posHorz) { case 0: - g_model.externalModule = checkIncDec(event, g_model.externalModule, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable); + g_model.moduleData[EXTERNAL_MODULE].type = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].type, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable); if (checkIncDec_Ret) { g_model.moduleData[EXTERNAL_MODULE].rfProtocol = 0; g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; - if (g_model.externalModule == MODULE_TYPE_PPM) + if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) g_model.moduleData[EXTERNAL_MODULE].channelsCount = 0; else g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_EXTERNAL_MODULE_CHANNELS(); @@ -548,7 +548,7 @@ void menuModelSetup(uint8_t event) break; case 1: CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart)); - if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.externalModule == MODULE_TYPE_PPM)) { + if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM)) { SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx); } break; diff --git a/radio/src/gui/Taranis/menu_model_setup.cpp b/radio/src/gui/Taranis/menu_model_setup.cpp index 4a92a3bad..c8b88c9af 100644 --- a/radio/src/gui/Taranis/menu_model_setup.cpp +++ b/radio/src/gui/Taranis/menu_model_setup.cpp @@ -194,8 +194,7 @@ void menuModelSetup(uint8_t event) #define IF_INTERNAL_MODULE_ON(x) (g_model.internalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) #else #define IF_INTERNAL_MODULE_ON(x) (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF ? HIDDEN_ROW : (uint8_t)(x)) -#endif - #define IF_EXTERNAL_MODULE_ON(x) (g_model.externalModule == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) + #define IF_EXTERNAL_MODULE_ON(x) (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_NONE ? HIDDEN_ROW : (uint8_t)(x)) #define IF_TRAINER_ON(x) (g_model.trainerMode == TRAINER_MODE_SLAVE ? (uint8_t)(x) : HIDDEN_ROW) #define IF_EXTERNAL_MODULE_XJT(x) (IS_MODULE_XJT(EXTERNAL_MODULE) ? (uint8_t)x : HIDDEN_ROW) #define IS_D8_RX(x) (g_model.moduleData[x].rfProtocol == RF_PROTO_D8) @@ -643,7 +642,7 @@ void menuModelSetup(uint8_t event) case ITEM_MODEL_EXTERNAL_MODULE_MODE: lcd_putsLeft(y, STR_MODE); - lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_TARANIS_PROTOCOLS, g_model.externalModule, m_posHorz==0 ? attr : 0); + lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN, y, STR_TARANIS_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].type, m_posHorz==0 ? attr : 0); if (IS_MODULE_XJT(EXTERNAL_MODULE)) lcd_putsiAtt(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol, m_posHorz==1 ? attr : 0); else if (IS_MODULE_DSM2(EXTERNAL_MODULE)) @@ -651,11 +650,11 @@ void menuModelSetup(uint8_t event) if (attr && s_editMode>0) { switch (m_posHorz) { case 0: - g_model.externalModule = checkIncDec(event, g_model.externalModule, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable); + g_model.moduleData[EXTERNAL_MODULE].type = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].type, MODULE_TYPE_NONE, MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable); if (checkIncDec_Ret) { g_model.moduleData[EXTERNAL_MODULE].rfProtocol = 0; g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; - if (g_model.externalModule == MODULE_TYPE_PPM) + if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) g_model.moduleData[EXTERNAL_MODULE].channelsCount = 0; else g_model.moduleData[EXTERNAL_MODULE].channelsCount = MAX_EXTERNAL_MODULE_CHANNELS(); @@ -697,12 +696,7 @@ void menuModelSetup(uint8_t event) break; case 1: CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart)); -#if defined(TARANIS_INTERNAL_PPM) - if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.externalModule == MODULE_TYPE_PPM) || (k == ITEM_MODEL_INTERNAL_MODULE_CHANNELS && g_model.internalModule == MODULE_TYPE_PPM) || (k == ITEM_MODEL_TRAINER_CHANNELS)) { - SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx); - } -#else - if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.externalModule == MODULE_TYPE_PPM) || (k == ITEM_MODEL_TRAINER_CHANNELS)) { + if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) || (k == ITEM_MODEL_TRAINER_CHANNELS)) { SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx); } #endif diff --git a/radio/src/gui/Taranis/menu_model_telemetry.cpp b/radio/src/gui/Taranis/menu_model_telemetry.cpp index 8829ca4da..31bef9b00 100644 --- a/radio/src/gui/Taranis/menu_model_telemetry.cpp +++ b/radio/src/gui/Taranis/menu_model_telemetry.cpp @@ -128,7 +128,7 @@ enum menuModelTelemetryItems { #define SCREEN_TYPE_ROWS 0 #endif #define VARIO_RANGE_ROWS 3 -#define TELEMETRY_TYPE_ROWS (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF && g_model.externalModule == MODULE_TYPE_PPM) ? (uint8_t)0 : HIDDEN_ROW, +#define TELEMETRY_TYPE_ROWS (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) ? (uint8_t)0 : HIDDEN_ROW, #if defined(LUA) #define TELEMETRY_SCREEN_LINE(x) ((TELEMETRY_SCREEN_TYPE(x) == TELEMETRY_SCREEN_TYPE_NONE || TELEMETRY_SCREEN_TYPE(x) == TELEMETRY_SCREEN_TYPE_SCRIPT) ? HIDDEN_ROW : (uint8_t)2) diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index 7bba038ec..1b513bba0 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -409,32 +409,18 @@ enum BeeperMode { #define FAILSAFE_CHANNEL_HOLD 2000 #define FAILSAFE_CHANNEL_NOPULSE 2001 -#if defined(PCBSKY9X) && defined(REVX) PACK(typedef struct { - int8_t rfProtocol; + uint8_t type:4; + int8_t rfProtocol:4; uint8_t channelsStart; int8_t channelsCount; // 0=8 channels uint8_t failsafeMode; int16_t failsafeChannels[NUM_CHNOUT]; - int8_t ppmDelay; - int8_t ppmFrameLength; + int8_t ppmDelay:6; uint8_t ppmPulsePol:1; - uint8_t ppmOutputType:1; // false = open drain, true = push pull - uint8_t spare:6; -}) ModuleData; -#else -PACK(typedef struct { - int8_t rfProtocol; - uint8_t channelsStart; - int8_t channelsCount; // 0=8 channels - uint8_t failsafeMode; - int16_t failsafeChannels[NUM_CHNOUT]; - int8_t ppmDelay; + uint8_t ppmOutputType:1; // false = open drain, true = push pull int8_t ppmFrameLength; - uint8_t ppmPulsePol:1; - uint8_t spare:7; }) ModuleData; -#endif #define SET_DEFAULT_PPM_FRAME_LENGTH(idx) g_model.moduleData[idx].ppmFrameLength = 4 * max((int8_t)0, g_model.moduleData[idx].channelsCount) @@ -467,11 +453,7 @@ PACK(typedef struct { }; #define IS_TRAINER_EXTERNAL_MODULE() (g_model.trainerMode == TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE || g_model.trainerMode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE) #define MODELDATA_BITMAP char bitmap[LEN_BITMAP_NAME]; - #if defined(TARANIS_INTERNAL_PPM) - #define MODELDATA_EXTRA uint8_t externalModule:3; uint8_t internalModule:3; uint8_t trainerMode:3; uint8_t potsWarnMode:2; ModuleData moduleData[NUM_MODULES+1]; char curveNames[MAX_CURVES][6]; ScriptData scriptsData[MAX_SCRIPTS]; char inputNames[MAX_INPUTS][LEN_INPUT_NAME]; uint8_t potsWarnEnabled; int8_t potsWarnPosition[NUM_POTS]; - #else - #define MODELDATA_EXTRA uint8_t externalModule:3; uint8_t trainerMode:3; uint8_t potsWarnMode:2; ModuleData moduleData[NUM_MODULES+1]; char curveNames[MAX_CURVES][6]; ScriptData scriptsData[MAX_SCRIPTS]; char inputNames[MAX_INPUTS][LEN_INPUT_NAME]; uint8_t potsWarnEnabled; int8_t potsWarnPosition[NUM_POTS]; - #endif + #define MODELDATA_EXTRA uint8_t spare:3; uint8_t trainerMode:3; uint8_t potsWarnMode:2; ModuleData moduleData[NUM_MODULES+1]; char curveNames[MAX_CURVES][6]; ScriptData scriptsData[MAX_SCRIPTS]; char inputNames[MAX_INPUTS][LEN_INPUT_NAME]; uint8_t potsWarnEnabled; int8_t potsWarnPosition[NUM_POTS]; #elif defined(PCBSKY9X) enum ModuleIndex { EXTERNAL_MODULE, @@ -479,7 +461,7 @@ PACK(typedef struct { TRAINER_MODULE }; #define MODELDATA_BITMAP - #define MODELDATA_EXTRA uint8_t externalModule:3; uint8_t spare:3; uint8_t potsWarnMode:2; ModuleData moduleData[NUM_MODULES+1]; uint8_t potsWarnEnabled; int8_t potsWarnPosition[NUM_POTS]; uint8_t rxBattAlarms[2]; + #define MODELDATA_EXTRA uint8_t spare:6; uint8_t potsWarnMode:2; ModuleData moduleData[NUM_MODULES+1]; uint8_t potsWarnEnabled; int8_t potsWarnPosition[NUM_POTS]; uint8_t rxBattAlarms[2]; #else #define MODELDATA_BITMAP #define MODELDATA_EXTRA @@ -2048,7 +2030,7 @@ enum ModuleTypes { MODULE_TYPE_COUNT }; -#define IS_PULSES_EXTERNAL_MODULE() (g_model.externalModule != MODULE_TYPE_NONE) +#define IS_PULSES_EXTERNAL_MODULE() (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE) enum FailsafeModes { FAILSAFE_NOT_SET, diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 3fc862d37..340107833 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -417,7 +417,7 @@ void modelDefault(uint8_t id) #endif #if defined(PCBSKY9X) - g_model.externalModule = MODULE_TYPE_PPM; + g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_PPM; #endif #if defined(CPUARM) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 18084f074..4fbde84db 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -470,7 +470,7 @@ extern void boardInit(); #define IS_MODULE_XJT(idx) ((idx==INTERNAL_MODULE || g_model.externalModule==MODULE_TYPE_XJT) && (g_model.moduleData[idx].rfProtocol != RF_PROTO_OFF)) #endif #if defined(DSM2) - #define IS_MODULE_DSM2(idx) (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_DSM2) + #define IS_MODULE_DSM2(idx) (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_DSM2) #else #define IS_MODULE_DSM2(idx) (false) #endif @@ -482,17 +482,17 @@ extern void boardInit(); #define MAX_EXTERNAL_MODULE_CHANNELS() ((g_model.externalModule == MODULE_TYPE_XJT) ? maxChannelsXJT[1+g_model.moduleData[1].rfProtocol] : maxChannelsModules[g_model.externalModule]) #define MAX_CHANNELS(idx) (idx==INTERNAL_MODULE ? MAX_INTERNAL_MODULE_CHANNELS() : (idx==EXTERNAL_MODULE ? MAX_EXTERNAL_MODULE_CHANNELS() : MAX_TRAINER_CHANNELS())) #elif defined(PCBSKY9X) && !defined(REVA) && !defined(REVX) - #define IS_MODULE_PPM(idx) (idx==TRAINER_MODULE || idx==EXTRA_MODULE || (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_PPM)) - #define IS_MODULE_XJT(idx) (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_XJT) - #define IS_MODULE_DSM2(idx) (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_DSM2) - #define MAX_EXTERNAL_MODULE_CHANNELS() ((g_model.externalModule == MODULE_TYPE_XJT) ? maxChannelsXJT[1+g_model.moduleData[0].rfProtocol] : maxChannelsModules[g_model.externalModule]) + #define IS_MODULE_PPM(idx) (idx==TRAINER_MODULE || idx==EXTRA_MODULE || (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_PPM)) + #define IS_MODULE_XJT(idx) (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_XJT) + #define IS_MODULE_DSM2(idx) (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_DSM2) + #define MAX_EXTERNAL_MODULE_CHANNELS() ((g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_XJT) ? maxChannelsXJT[1+g_model.moduleData[0].rfProtocol] : maxChannelsModules[g_model.moduleData[EXTERNAL_MODULE].type]) #define MAX_EXTRA_MODULE_CHANNELS() (0) // Only PPM #define MAX_CHANNELS(idx) (idx==EXTERNAL_MODULE ? MAX_EXTERNAL_MODULE_CHANNELS() : (idx==EXTRA_MODULE ? MAX_EXTRA_MODULE_CHANNELS() : MAX_TRAINER_CHANNELS())) #else - #define IS_MODULE_PPM(idx) (idx==TRAINER_MODULE || (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_PPM)) - #define IS_MODULE_XJT(idx) (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_XJT) - #define IS_MODULE_DSM2(idx) (idx==EXTERNAL_MODULE && g_model.externalModule==MODULE_TYPE_DSM2) - #define MAX_EXTERNAL_MODULE_CHANNELS() ((g_model.externalModule == MODULE_TYPE_XJT) ? maxChannelsXJT[1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol] : maxChannelsModules[g_model.externalModule]) + #define IS_MODULE_PPM(idx) (idx==TRAINER_MODULE || (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_PPM)) + #define IS_MODULE_XJT(idx) (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_XJT) + #define IS_MODULE_DSM2(idx) (idx==EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type==MODULE_TYPE_DSM2) + #define MAX_EXTERNAL_MODULE_CHANNELS() ((g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_XJT) ? maxChannelsXJT[1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol] : maxChannelsModules[g_model.moduleData[EXTERNAL_MODULE].type]) #define MAX_CHANNELS(idx) (idx==EXTERNAL_MODULE ? MAX_EXTERNAL_MODULE_CHANNELS() : MAX_TRAINER_CHANNELS()) #endif @@ -567,7 +567,7 @@ enum BaseCurves { #if defined(PCBTARANIS) #define SPLASH_NEEDED() (g_eeGeneral.splashMode != 3) #elif defined(CPUARM) - #define SPLASH_NEEDED() (g_model.externalModule != MODULE_TYPE_DSM2 && !g_eeGeneral.splashMode) + #define SPLASH_NEEDED() (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_DSM2 && !g_eeGeneral.splashMode) #else #define SPLASH_NEEDED() (!IS_DSM2_PROTOCOL(g_model.protocol) && !g_eeGeneral.splashMode) #endif @@ -1459,9 +1459,7 @@ enum AUDIO_SOUNDS { #endif #endif -#if defined(BUZZER) #include "buzzer.h" -#endif #if defined(PCBSTD) && defined(VOICE) #include "targets/stock/voice.h" diff --git a/radio/src/pulses/pulses_arm.cpp b/radio/src/pulses/pulses_arm.cpp index fd8b5d3aa..526468b83 100644 --- a/radio/src/pulses/pulses_arm.cpp +++ b/radio/src/pulses/pulses_arm.cpp @@ -73,7 +73,7 @@ void setupPulses(unsigned int port) default: port = EXTERNAL_MODULE; // ensure it's external module only - switch (g_model.externalModule) { + switch (g_model.moduleData[EXTERNAL_MODULE].type) { case MODULE_TYPE_PPM: required_protocol = PROTO_PPM; break; diff --git a/radio/src/targets/mega2560/board_mega2560.cpp b/radio/src/targets/mega2560/board_mega2560.cpp index 3c4d04cad..6c743901e 100644 --- a/radio/src/targets/mega2560/board_mega2560.cpp +++ b/radio/src/targets/mega2560/board_mega2560.cpp @@ -48,7 +48,7 @@ inline void boardInit() DDRE = 0b00000010; PORTE = 0b11111100; // 7:N/A, 6:N/A, 5:RENC1_B, 4:RENC1_A, 3:N/A, 2:N/A, 1:TELEM_TX, 0:TELEM_RX DDRF = 0b00000000; PORTF = 0b11111111; // 7-0:Trim switch inputs DDRG = 0b00000000; PORTG = 0b11111111; // 7:N/A, 6:N/A, 5:N/A, 4:N/A, 3:N/A, 2:TCut_SW, 1:Gear_SW, 0: RudDr_SW - DDRH = 0b00011000; PORTH = 0b11110110; // 7:N/A, 6:RFPw, 5:JackPres, 4:HoldPw, 3:Speaker, 2:N/A, 1:N/A, 0:Haptic + DDRH = 0b00011000; PORTH = 0b11110110; // 7:N/A, 6:RF_Activated, 5:DSC_Activated, 4:Hold_Power, 3:Speaker, 2:N/A, 1:N/A, 0:Haptic DDRJ = 0b00000000; PORTJ = 0b11111111; // 7:N/A, 6:N/A, 5:N/A, 4:N/A, 3:N/A, 2:N/A, 1:RENC2_push, 0:RENC1_push DDRK = 0b00000000; PORTK = 0b00000000; // Analogic input (no pull-ups) DDRL = 0b00000000; PORTL = 0b11111111; // 7:TRN_SW 6:EleDR_SW, 5:ESC, 4:MENU 3:Keyb_Left, 2:Keyb_Right, 1:Keyb_Up, 0:Keyb_Down @@ -98,7 +98,7 @@ uint8_t pwrCheck() void pwrOff() { - PORTH = 0xEF; // PortH-4 set to 0 + PORTH &= ~0x10; // PortH-4 set to 0 } FORCEINLINE uint8_t keyDown() diff --git a/radio/src/targets/mega2560/board_mega2560.h b/radio/src/targets/mega2560/board_mega2560.h index 1cbb98d1d..fd0e80e49 100644 --- a/radio/src/targets/mega2560/board_mega2560.h +++ b/radio/src/targets/mega2560/board_mega2560.h @@ -79,7 +79,7 @@ #define PAUSE_PPMIN_INTERRUPT() TIMSK3 &= ~(1<SEG1) + 0xA0, //ADC = 0: normal direction (SEG132->SEG1) 0xA6, //REV = 0: non-reverse display 0xA4, //EON = 0: normal display. non-entire 0xA2, //Select LCD bias @@ -85,11 +79,11 @@ const static pm_uchar lcdInitSequence[] PROGMEM = 0x81, //Set reference voltage Mode 0x22, //24 SV5 SV4 SV3 SV2 SV1 SV0 = 0x18 0xAF, //DON = 1: display ON - 0x60 //sets the display start line to zero -#elif LCD_ERC12864FSF + 0x60 //Set the display start line to zero +#elif defined(LCD_ERC12864FSF) 0xE2, //Initialize the internal functions 0xAE, //DON = 0: display OFF - 0xA1, //ADC = 1: reverse direction(SEG132->SEG1) + 0xA1, //ADC = 1: reverse direction (SEG132->SEG1) 0xA6, //REV = 0: non-reverse display 0xA4, //EON = 0: normal display. non-entire 0xA3, //Select LCD bias @@ -99,7 +93,13 @@ const static pm_uchar lcdInitSequence[] PROGMEM = 0x81, //Set reference voltage Mode 0x2D, //24 SV5 SV4 SV3 SV2 SV1 SV0 0xAF //DON = 1: display ON -#else +#elif defined(LCD_ST7920) + 0x30, //Set 8-bit interface + 0x36, //Repeat with graphics bit set to ON + 0x0C, //Display ON, cursor and blink OFF + 0x01, //Clear display, reset address + 0x06 //Display ON, no cursor +#else //ST7565P (default 9x LCD) 0xE2, //Initialize the internal functions 0xAE, //DON = 0: display OFF 0xA1, //ADC = 1: reverse direction(SEG132->SEG1) @@ -117,18 +117,22 @@ const static pm_uchar lcdInitSequence[] PROGMEM = inline void lcdInit() { - // /home/thus/txt/datasheets/lcd/KS0713.pdf - // ~/txt/flieger/ST7565RV17.pdf from http://www.glyn.de/content.asp?wdid=132&sid= - LCD_LOCK(); - PORTC_LCD_CTRL &= ~(1< 31) { + y_addr = y - 32; //Because there are only 31 addressable lines in the ST7920 + x_addr += 8; //so we overflow x (7 visible bytes per line) to reach the bottom half + } + else { + y_addr = y; + } + lcdSendCtl( 0x80 | y_addr ); //Set Vertical Address + _delay_us(49); + lcdSendCtl( 0x80 | x_addr ); //Set Horizontal Address + _delay_us(49); + PORTC_LCD_CTRL |= (1< 0=0, 1=1..7=7, 8=0, 9=1... + col_offset = 1 << bit_count; //Build a value for a AND operation with the vorrect bitposition + line_offset = ( y / 8 ) * 128; //On the ST7565 there are 8 lines with each 128 bytes width + for (coord_t x=0; x<16; x++) { //Walk through 16 bytes form left to right (128 Pixel) + byte_offset = line_offset + ( x * 8 ); //Calculate the position of the first byte im array + // adressing the bytes sequential and shift the bits at the correct position, afterwards a OR operation to get all bits in one byte + // the position of the LSB is the left-most position of the byte to the ST7920 + PORTA_LCD_DAT = (((displayBuf[byte_offset] & col_offset) >> bit_count) << 7) | (((displayBuf[byte_offset + 1] & col_offset) >> bit_count) << 6) | (((displayBuf[byte_offset + 2] & col_offset) >> bit_count ) << 5) | (((displayBuf[byte_offset + 3] & col_offset) >> bit_count ) << 4) | (((displayBuf[byte_offset + 4] & col_offset) >> bit_count ) << 3) | (((displayBuf[byte_offset + 5] & col_offset) >> bit_count ) << 2) | (((displayBuf[byte_offset + 6] & col_offset) >> bit_count ) << 1) | (((displayBuf[byte_offset + 7] & col_offset) >> bit_count ) << 0); + PORTC_LCD_CTRL |= (1<") //9XR-Pro -#define TR_RANGE TR(INDENT"Bereich",INDENT"Messbereich") //9XR-Pro +#define TR_RANGE TR(INDENT"Bereich", INDENT "Reichweite") // 9XR-Pro #define TR_BAR "Streifen" #define TR_ALARM INDENT"Alarm" #define TR_USRDATA "Daten berechnen aus" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 2182a4d23..7b1f1f996 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -45,8 +45,8 @@ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 86f42fc34..32842f1de 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -35,8 +35,8 @@ */ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index ec8fc6d28..aa3e5e251 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -35,8 +35,8 @@ */ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 4db9f2bd3..9808278aa 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -32,8 +32,8 @@ */ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 161ef118d..31656de7e 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -35,8 +35,8 @@ */ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index e77f7d9ab..4e69fc87c 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -47,8 +47,8 @@ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index ecbc0bd18..433a299f6 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -35,8 +35,8 @@ */ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index d41ba5881..ebd4085db 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -35,8 +35,8 @@ */ /* Formatting octal codes available in TR_ strings: * \037\x -sets LCD x-coord (x value in octal) - * \n -newline - * \t -horizontal tab (ARM only) + * \036 -newline + * \035 -horizontal tab (ARM only) * \001 to \034 -extended spacing (value * FW/2) * \0 -ends current string */ diff --git a/radio/src/vario.cpp b/radio/src/vario.cpp index f45aa5a19..43cdaf2f7 100644 --- a/radio/src/vario.cpp +++ b/radio/src/vario.cpp @@ -141,7 +141,7 @@ void varioWakeup() } #endif -#else // defined(AUDIO) +#elif defined(BUZZER) // && !defined(AUDIO) int8_t verticalSpeed = limit((int16_t)-100, (int16_t)(frskyData.hub.varioSpeed/10), (int16_t)+100);