Make TX volatge for simu more flexible (#7124)
[opentx.git] / companion / src / firmwares / modeldata.cpp
bloba2fdb4981ec94004999e6f4827a7c3325a1da9fc
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "modeldata.h"
23 #include "eeprominterface.h"
24 #include "generalsettings.h"
25 #include "macros.h"
26 #include "radiodataconversionstate.h"
27 #include "helpers.h"
30 * TimerData
33 void TimerData::convert(RadioDataConversionState & cstate)
35 cstate.setComponent(tr("TMR"), 1);
36 cstate.setSubComp(tr("Timer %1").arg(cstate.subCompIdx + 1));
37 mode.convert(cstate);
42 * ModelData
45 ModelData::ModelData()
47 clear();
50 ModelData::ModelData(const ModelData & src)
52 *this = src;
55 ModelData & ModelData::operator = (const ModelData & src)
57 memcpy(reinterpret_cast<void *>(this), &src, sizeof(ModelData));
58 return *this;
61 ExpoData * ModelData::insertInput(const int idx)
63 memmove(&expoData[idx+1], &expoData[idx], (CPN_MAX_EXPOS-(idx+1))*sizeof(ExpoData));
64 expoData[idx].clear();
65 return &expoData[idx];
68 bool ModelData::isInputValid(const unsigned int idx) const
70 for (int i=0; i<CPN_MAX_EXPOS; i++) {
71 const ExpoData * expo = &expoData[i];
72 if (expo->mode == 0) break;
73 if (expo->chn == idx)
74 return true;
76 return false;
79 bool ModelData::hasExpos(uint8_t inputIdx) const
81 for (int i=0; i<CPN_MAX_EXPOS; i++) {
82 const ExpoData & expo = expoData[i];
83 if (expo.chn==inputIdx && expo.mode!=0) {
84 return true;
87 return false;
90 bool ModelData::hasMixes(uint8_t channelIdx) const
92 channelIdx += 1;
93 for (int i=0; i<CPN_MAX_MIXERS; i++) {
94 if (mixData[i].destCh == channelIdx) {
95 return true;
98 return false;
101 QVector<const ExpoData *> ModelData::expos(int input) const
103 QVector<const ExpoData *> result;
104 for (int i=0; i<CPN_MAX_EXPOS; i++) {
105 const ExpoData * ed = &expoData[i];
106 if ((int)ed->chn==input && ed->mode!=0) {
107 result << ed;
110 return result;
113 QVector<const MixData *> ModelData::mixes(int channel) const
115 QVector<const MixData *> result;
116 for (int i=0; i<CPN_MAX_MIXERS; i++) {
117 const MixData * md = &mixData[i];
118 if ((int)md->destCh == channel+1) {
119 result << md;
122 return result;
125 void ModelData::removeInput(const int idx, bool clearName)
127 unsigned int chn = expoData[idx].chn;
129 memmove(&expoData[idx], &expoData[idx+1], (CPN_MAX_EXPOS-(idx+1))*sizeof(ExpoData));
130 expoData[CPN_MAX_EXPOS-1].clear();
132 //also remove input name if removing last line for this input
133 if (clearName && !expos(chn).size())
134 inputNames[chn][0] = 0;
137 void ModelData::clearInputs()
139 for (int i=0; i<CPN_MAX_EXPOS; i++)
140 expoData[i].clear();
142 //clear all input names
143 if (getCurrentFirmware()->getCapability(VirtualInputs)) {
144 for (int i=0; i<CPN_MAX_INPUTS; i++) {
145 inputNames[i][0] = 0;
150 void ModelData::clearMixes()
152 for (int i=0; i<CPN_MAX_MIXERS; i++)
153 mixData[i].clear();
156 void ModelData::clear()
158 memset(reinterpret_cast<void *>(this), 0, sizeof(ModelData));
159 modelIndex = -1; // an invalid index, this is managed by the TreeView data model
160 moduleData[0].protocol = PULSES_OFF;
161 moduleData[1].protocol = PULSES_OFF;
162 moduleData[0].channelsCount = 8;
163 moduleData[1].channelsStart = 0;
164 moduleData[1].channelsCount = 8;
165 moduleData[0].ppm.delay = 300;
166 moduleData[1].ppm.delay = 300;
167 moduleData[2].ppm.delay = 300; //Trainer PPM
168 for (int i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
169 flightModeData[i].clear(i);
171 for (int i=0; i<CPN_MAX_GVARS; i++) {
172 gvarData[i].clear();
174 clearInputs();
175 clearMixes();
176 for (int i=0; i<CPN_MAX_CHNOUT; i++)
177 limitData[i].clear();
178 for (int i=0; i<CPN_MAX_STICKS; i++)
179 expoData[i].clear();
180 for (int i=0; i<CPN_MAX_LOGICAL_SWITCHES; i++)
181 logicalSw[i].clear();
182 for (int i=0; i<CPN_MAX_SPECIAL_FUNCTIONS; i++)
183 customFn[i].clear();
184 for (int i=0; i<CPN_MAX_CURVES; i++)
185 curves[i].clear(5);
186 for (int i=0; i<CPN_MAX_TIMERS; i++)
187 timers[i].clear();
188 swashRingData.clear();
189 frsky.clear();
190 rssiAlarms.clear();
191 for (unsigned i=0; i<CPN_MAX_SENSORS; i++)
192 sensorData[i].clear();
194 static const uint8_t blob[] = { 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x32, 0x50, 0x31, 0x00, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x42, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
195 memcpy(customScreenData[0], blob, sizeof(blob));
198 bool ModelData::isEmpty() const
200 return !used;
203 void ModelData::setDefaultInputs(const GeneralSettings & settings)
205 Board::Type board = getCurrentBoard();
206 if (IS_ARM(board)) {
207 for (int i=0; i<CPN_MAX_STICKS; i++) {
208 ExpoData * expo = &expoData[i];
209 expo->chn = i;
210 expo->mode = INPUT_MODE_BOTH;
211 expo->srcRaw = settings.getDefaultSource(i);
212 expo->weight = 100;
213 strncpy(inputNames[i], Helpers::removeAccents(expo->srcRaw.toString(this)).toLatin1().constData(), sizeof(inputNames[i])-1);
218 void ModelData::setDefaultMixes(const GeneralSettings & settings)
220 Board::Type board = getCurrentBoard();
221 if (IS_ARM(board)) {
222 setDefaultInputs(settings);
225 for (int i=0; i<CPN_MAX_STICKS; i++) {
226 MixData * mix = &mixData[i];
227 mix->destCh = i+1;
228 mix->weight = 100;
229 if (IS_ARM(board)) {
230 mix->srcRaw = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, i);
232 else {
233 mix->srcRaw = RawSource(SOURCE_TYPE_STICK, i);
238 void ModelData::setDefaultValues(unsigned int id, const GeneralSettings & settings)
240 clear();
241 used = true;
242 sprintf(name, "MODEL%02d", id+1);
243 for (int i=0; i<CPN_MAX_MODULES; i++) {
244 moduleData[i].modelId = id+1;
246 setDefaultMixes(settings);
249 int ModelData::getTrimValue(int phaseIdx, int trimIdx)
251 int result = 0;
252 for (int i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
253 FlightModeData & phase = flightModeData[phaseIdx];
254 if (phase.trimMode[trimIdx] < 0) {
255 return result;
257 else {
258 if (phase.trimRef[trimIdx] == phaseIdx || phaseIdx == 0) {
259 return result + phase.trim[trimIdx];
261 else {
262 phaseIdx = phase.trimRef[trimIdx];
263 if (phase.trimMode[trimIdx] != 0)
264 result += phase.trim[trimIdx];
268 return 0;
271 bool ModelData::isGVarLinked(int phaseIdx, int gvarIdx)
273 return flightModeData[phaseIdx].gvars[gvarIdx] > 1024;
276 int ModelData::getGVarFieldValue(int phaseIdx, int gvarIdx)
278 int idx = flightModeData[phaseIdx].gvars[gvarIdx];
279 for (int i=0; idx>GVAR_MAX_VALUE && i<CPN_MAX_FLIGHT_MODES; i++) {
280 int nextPhase = idx - GVAR_MAX_VALUE - 1;
281 if (nextPhase >= phaseIdx) nextPhase += 1;
282 phaseIdx = nextPhase;
283 idx = flightModeData[phaseIdx].gvars[gvarIdx];
285 return idx;
288 void ModelData::setTrimValue(int phaseIdx, int trimIdx, int value)
290 for (uint8_t i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
291 FlightModeData & phase = flightModeData[phaseIdx];
292 int mode = phase.trimMode[trimIdx];
293 int p = phase.trimRef[trimIdx];
294 int & trim = phase.trim[trimIdx];
295 if (mode < 0)
296 return;
297 if (p == phaseIdx || phaseIdx == 0) {
298 trim = value;
299 break;
301 else if (mode == 0) {
302 phaseIdx = p;
304 else {
305 trim = value - getTrimValue(p, trimIdx);
306 if (trim < -500)
307 trim = -500;
308 if (trim > 500)
309 trim = 500;
310 break;
315 void ModelData::removeGlobalVar(int & var)
317 if (var >= 126 && var <= 130)
318 var = flightModeData[0].gvars[var-126];
319 else if (var <= -126 && var >= -130)
320 var = - flightModeData[0].gvars[-126-var];
323 ModelData ModelData::removeGlobalVars()
325 ModelData result = *this;
327 for (int i=0; i<CPN_MAX_MIXERS; i++) {
328 removeGlobalVar(mixData[i].weight);
329 removeGlobalVar(mixData[i].curve.value);
330 removeGlobalVar(mixData[i].sOffset);
333 for (int i=0; i<CPN_MAX_EXPOS; i++) {
334 removeGlobalVar(expoData[i].weight);
335 removeGlobalVar(expoData[i].curve.value);
338 return result;
341 int ModelData::getChannelsMax(bool forceExtendedLimits) const
343 if (forceExtendedLimits || extendedLimits)
344 return IS_HORUS_OR_TARANIS(getCurrentBoard()) ? 150 : 125;
345 else
346 return 100;
349 bool ModelData::isAvailable(const RawSwitch & swtch) const
351 unsigned index = abs(swtch.index) - 1;
353 if (swtch.type == SWITCH_TYPE_VIRTUAL) {
354 return logicalSw[index].func != LS_FN_OFF;
356 else if (swtch.type == SWITCH_TYPE_FLIGHT_MODE) {
357 return index == 0 || flightModeData[index].swtch.type != SWITCH_TYPE_NONE;
359 else if (swtch.type == SWITCH_TYPE_SENSOR) {
360 return strlen(sensorData[index].label) > 0;
362 else {
363 return true;
367 float ModelData::getGVarFieldValuePrec(int phaseIdx, int gvarIdx)
369 return getGVarFieldValue(phaseIdx, gvarIdx) * gvarData[gvarIdx].multiplierGet();
372 void ModelData::convert(RadioDataConversionState & cstate)
374 // Here we can add explicit conversions when moving from one board to another
376 QString origin = QString(name);
377 if (origin.isEmpty())
378 origin = QString::number(cstate.modelIdx+1);
379 cstate.setOrigin(tr("Model: ") % origin);
381 cstate.setComponent("SET", 0);
382 if (thrTraceSrc && (int)thrTraceSrc < cstate.fromBoard.getCapability(Board::Pots) + cstate.fromBoard.getCapability(Board::Sliders)) {
383 cstate.setSubComp(tr("Throttle Source"));
384 thrTraceSrc = RawSource(SOURCE_TYPE_STICK, (int)thrTraceSrc + 3).convert(cstate).index - 3;
387 for (int i=0; i<CPN_MAX_TIMERS; i++) {
388 timers[i].convert(cstate.withComponentIndex(i));
391 for (int i=0; i<CPN_MAX_MIXERS; i++) {
392 mixData[i].convert(cstate.withComponentIndex(i));
395 for (int i=0; i<CPN_MAX_EXPOS; i++) {
396 expoData[i].convert(cstate.withComponentIndex(i));
399 for (int i=0; i<CPN_MAX_LOGICAL_SWITCHES; i++) {
400 logicalSw[i].convert(cstate.withComponentIndex(i));
403 for (int i=0; i<CPN_MAX_SPECIAL_FUNCTIONS; i++) {
404 customFn[i].convert(cstate.withComponentIndex(i));
407 for (int i=0; i<CPN_MAX_FLIGHT_MODES; i++) {
408 flightModeData[i].convert(cstate.withComponentIndex(i));
411 for (int i=0; i<CPN_MAX_MODULES; i++) {
412 moduleData[i].convert(cstate.withComponentIndex(i));