Companion: Russian UI (#7180)
[opentx.git] / radio / src / pulses / modules_helpers.h
blob2c9a64e947074310a62df436b1785adcb16d09f5
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 #ifndef _MODULES_HELPERS_H_
22 #define _MODULES_HELPERS_H_
24 #include "bitfield.h"
25 #include "definitions.h"
26 #include "opentx_helpers.h"
27 #include "telemetry/telemetry.h"
28 #if defined(MULTIMODULE)
29 #include "telemetry/multi.h"
30 #endif
32 #define CROSSFIRE_CHANNELS_COUNT 16
34 #if defined(MULTIMODULE)
35 // When using packed, the pointer in here end up not being aligned, which clang and gcc complain about
36 // Keep the order of the fields that the so that the size stays small
37 struct mm_options_strings {
38 static const char* options[];
41 struct mm_protocol_definition {
42 uint8_t protocol;
43 uint8_t maxSubtype;
44 bool failsafe;
45 bool disable_ch_mapping;
46 const char *subTypeString;
47 const char *optionsstr;
50 const mm_protocol_definition *getMultiProtocolDefinition (uint8_t protocol);
52 inline uint8_t getMaxMultiSubtype(uint8_t moduleIdx)
54 MultiModuleStatus &status = getMultiModuleStatus(moduleIdx);
55 const mm_protocol_definition *pdef = getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol());
57 if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY) {
58 return 5;
61 if (g_model.moduleData[moduleIdx].getMultiProtocol() > MODULE_SUBTYPE_MULTI_LAST) {
62 if (status.isValid())
63 return (status.protocolSubNbr == 0 ? 0 : status.protocolSubNbr - 1);
64 else
65 return 7;
67 else {
68 return max((uint8_t )(status.protocolSubNbr == 0 ? 0 : status.protocolSubNbr - 1), pdef->maxSubtype);
72 inline bool isModuleMultimodule(uint8_t idx)
74 return g_model.moduleData[idx].type == MODULE_TYPE_MULTIMODULE;
77 inline bool isModuleMultimoduleDSM2(uint8_t idx)
79 return isModuleMultimodule(idx) && g_model.moduleData[idx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2;
81 #else
82 inline bool isModuleMultimodule(uint8_t)
84 return false;
87 inline bool isModuleMultimoduleDSM2(uint8_t)
89 return false;
91 #endif
93 inline bool isModuleTypeXJT(uint8_t type)
95 return type == MODULE_TYPE_XJT_PXX1 || type == MODULE_TYPE_XJT_LITE_PXX2;
98 inline bool isModuleXJT(uint8_t idx)
100 return isModuleTypeXJT(g_model.moduleData[idx].type);
103 inline bool isModuleXJTD8(uint8_t idx)
105 return isModuleXJT(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_PXX1_ACCST_D8;
108 inline bool isModuleXJTLR12(uint8_t idx)
110 return isModuleXJT(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_PXX1_ACCST_LR12;
113 inline bool isModuleXJTD16(uint8_t idx)
115 return isModuleXJT(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_PXX1_ACCST_D16;
118 inline bool isModuleISRM(uint8_t idx)
120 return g_model.moduleData[idx].type == MODULE_TYPE_ISRM_PXX2;
123 inline bool isModuleISRMD16(uint8_t idx)
125 return g_model.moduleData[idx].type == MODULE_TYPE_ISRM_PXX2 && g_model.moduleData[idx].subType == MODULE_SUBTYPE_ISRM_PXX2_ACCST_D16;
128 inline bool isModuleD16(uint8_t idx)
130 return isModuleXJTD16(idx) || isModuleISRMD16(idx);
133 inline bool isModuleISRMAccess(uint8_t idx)
135 return g_model.moduleData[idx].type == MODULE_TYPE_ISRM_PXX2 && g_model.moduleData[idx].subType == MODULE_SUBTYPE_ISRM_PXX2_ACCESS;
138 #if defined(CROSSFIRE)
139 inline bool isModuleCrossfire(uint8_t idx)
141 return idx == EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_CROSSFIRE;
143 #else
144 inline bool isModuleCrossfire(uint8_t idx)
146 return false;
148 #endif
150 #if defined(PCBSKY9X)
151 inline bool isExtraModule(uint8_t idx)
153 return idx == EXTRA_MODULE;
155 #else
156 inline bool isExtraModule(uint8_t)
158 return false;
160 #endif
162 inline bool isModuleTypePPM(uint8_t type)
164 return type == MODULE_TYPE_PPM;
167 inline bool isModulePPM(uint8_t moduleIdx)
169 #if defined(PCBSKY9X)
170 if (moduleIdx == EXTRA_MODULE)
171 return true;
172 #endif
173 return isModuleTypePPM(g_model.moduleData[moduleIdx].type);
176 inline bool isModuleTypeR9MNonAccess(uint8_t type)
178 return type == MODULE_TYPE_R9M_PXX1 || type == MODULE_TYPE_R9M_LITE_PXX1 || type == MODULE_TYPE_R9M_LITE_PRO_PXX1;
181 inline bool isModuleR9MNonAccess(uint8_t idx)
183 return isModuleTypeR9MNonAccess(g_model.moduleData[idx].type);
186 inline bool isModuleTypeR9MAccess(uint8_t type)
188 return type == MODULE_TYPE_R9M_PXX2 || type == MODULE_TYPE_R9M_LITE_PXX2 || type == MODULE_TYPE_R9M_LITE_PRO_PXX2;
191 inline bool isModuleR9MAccess(uint8_t idx)
193 return isModuleTypeR9MAccess(g_model.moduleData[idx].type);
196 inline bool isModuleTypeR9M(uint8_t type)
198 return isModuleTypeR9MNonAccess(type) || isModuleTypeR9MAccess(type);
201 inline bool isModuleR9M(uint8_t idx)
203 return isModuleTypeR9M(g_model.moduleData[idx].type);
206 inline bool isModuleTypeR9MLiteNonPro(uint8_t type)
208 return type == MODULE_TYPE_R9M_LITE_PXX1 || type == MODULE_TYPE_R9M_LITE_PXX2;
211 inline bool isModuleR9MLiteNonPro(uint8_t idx)
213 return isModuleTypeR9MLiteNonPro(g_model.moduleData[idx].type);
216 inline bool isModuleTypeR9MLitePro(uint8_t type)
218 return type == MODULE_TYPE_R9M_LITE_PRO_PXX1 || type == MODULE_TYPE_R9M_LITE_PRO_PXX2;
221 inline bool isModuleTypeR9MLite(uint8_t type)
223 return isModuleTypeR9MLiteNonPro(type) || isModuleTypeR9MLitePro(type);
226 inline bool isModuleR9MLite(uint8_t idx)
228 return isModuleTypeR9MLite(g_model.moduleData[idx].type);
231 inline bool isModuleR9M_FCC(uint8_t idx)
233 return isModuleR9MNonAccess(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_R9M_FCC;
236 inline bool isModuleTypeLite(uint8_t type)
238 return isModuleTypeR9MLite(type) || type == MODULE_TYPE_XJT_LITE_PXX2;
241 inline bool isModuleR9M_LBT(uint8_t idx)
243 return isModuleR9MNonAccess(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_R9M_EU;
246 inline bool isModuleR9M_FCC_VARIANT(uint8_t idx)
248 return isModuleR9MNonAccess(idx) && g_model.moduleData[idx].subType != MODULE_SUBTYPE_R9M_EU;
251 inline bool isModuleR9M_EUPLUS(uint8_t idx)
253 return isModuleR9MNonAccess(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_R9M_EUPLUS;
256 inline bool isModuleR9M_AU_PLUS(uint8_t idx)
258 return isModuleR9MNonAccess(idx) && g_model.moduleData[idx].subType == MODULE_SUBTYPE_R9M_AUPLUS;
261 inline bool isModuleTypePXX1(uint8_t type)
263 return isModuleTypeXJT(type) || isModuleTypeR9MNonAccess(type);
266 inline bool isModulePXX1(uint8_t idx)
268 return isModuleTypePXX1(g_model.moduleData[idx].type);
271 inline bool isModulePXX2(uint8_t idx)
273 return isModuleISRM(idx) || isModuleR9MAccess(idx);
276 inline bool isModuleRFAccess(uint8_t idx)
278 if (isModuleISRM(idx)) {
279 return g_model.moduleData[idx].subType == MODULE_SUBTYPE_ISRM_PXX2_ACCESS;
281 else if (isModuleR9MAccess(idx)) {
282 return true;
284 else {
285 return false;
289 inline bool isModuleDSM2(uint8_t moduleIdx)
291 return g_model.moduleData[moduleIdx].type == MODULE_TYPE_DSM2;
294 inline bool isModuleSBUS(uint8_t moduleIdx)
296 return g_model.moduleData[moduleIdx].type == MODULE_TYPE_SBUS;
299 // order is the same as in enum Protocols in myeeprom.h (none, ppm, pxx, pxx2, dsm, crossfire, multi, r9m, r9m2, sbus)
300 static const int8_t maxChannelsModules[] = { 0, 8, 8, 16, -2, 8, 4, 8, 16, 8}; // relative to 8!
301 static const int8_t maxChannelsXJT[] = { 0, 8, 0, 4 }; // relative to 8!
303 constexpr int8_t MAX_TRAINER_CHANNELS_M8 = MAX_TRAINER_CHANNELS - 8;
304 constexpr int8_t MAX_EXTRA_MODULE_CHANNELS_M8 = 8; // only 16ch PPM
306 inline int8_t maxModuleChannels_M8(uint8_t moduleIdx)
308 if (isExtraModule(moduleIdx)) {
309 return MAX_EXTRA_MODULE_CHANNELS_M8;
311 else if (isModuleXJT(moduleIdx)) {
312 return maxChannelsXJT[1 + g_model.moduleData[moduleIdx].subType];
314 else if (isModuleR9M(moduleIdx)) {
315 if (isModuleR9M_LBT(moduleIdx)) {
316 if (isModuleR9MLite(moduleIdx))
317 return g_model.moduleData[moduleIdx].pxx.power == R9M_LITE_LBT_POWER_25_8CH ? 0 : 8;
318 else
319 return g_model.moduleData[moduleIdx].pxx.power == R9M_LBT_POWER_25_8CH ? 0 : 8;
321 else {
322 return 8; // always 16 channels in FCC / FLEX
325 else {
326 return maxChannelsModules[g_model.moduleData[moduleIdx].type];
330 inline int8_t defaultModuleChannels_M8(uint8_t idx)
332 if (isModulePPM(idx))
333 return 0; // 8 channels
334 else if (isModuleDSM2(idx))
335 return 0; // 8 channels
336 else if (isModuleMultimoduleDSM2(idx))
337 return -1; // 7 channels
338 else if (isModuleXJTD8(idx))
339 return 0; // 8 channels
340 else if (isModuleXJTLR12(idx))
341 return 4; // 12 channels
342 else if (isModulePXX2(idx))
343 return 8; // 16 channels
344 else
345 return maxModuleChannels_M8(idx);
348 inline int8_t sentModuleChannels(uint8_t idx)
350 if (isModuleCrossfire(idx))
351 return CROSSFIRE_CHANNELS_COUNT;
352 else if (isModuleMultimodule(idx) && !isModuleMultimoduleDSM2(idx))
353 return 16;
354 else if (isModuleSBUS(idx))
355 return 16;
356 else
357 return 8 + g_model.moduleData[idx].channelsCount;
360 inline bool isDefaultModelRegistrationID()
362 return memcmp(g_model.modelRegistrationID, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID) == 0;
365 inline bool isModuleRxNumAvailable(uint8_t moduleIdx)
367 if (isModuleXJT(moduleIdx))
368 return g_model.moduleData[moduleIdx].subType != MODULE_SUBTYPE_PXX1_ACCST_D8;
370 if (isModuleR9M(moduleIdx))
371 return true;
373 if (isModuleDSM2(moduleIdx))
374 return true;
376 if (isModuleISRM(moduleIdx))
377 return true;
379 if (isModuleMultimodule(moduleIdx))
380 return true;
382 return false;
385 inline bool isModuleFailsafeAvailable(uint8_t moduleIdx)
387 #if defined(PXX2)
388 if (isModuleISRM(moduleIdx))
389 return true;
390 #endif
392 if (isModuleXJT(moduleIdx))
393 return g_model.moduleData[moduleIdx].subType == MODULE_SUBTYPE_PXX1_ACCST_D16;
395 #if defined(MULTIMODULE)
396 if (isModuleMultimodule(moduleIdx)){
397 MultiModuleStatus &status = getMultiModuleStatus(moduleIdx);
398 if (status.isValid()) {
399 return status.supportsFailsafe();
401 else {
402 const mm_protocol_definition * pdef = getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol());
403 return pdef->failsafe;
406 #endif
408 if (isModuleR9M(moduleIdx))
409 return true;
411 return false;
414 inline bool isModuleBindRangeAvailable(uint8_t moduleIdx)
416 return isModulePXX2(moduleIdx) || isModulePXX1(moduleIdx) || isModuleDSM2(moduleIdx) || isModuleMultimodule(moduleIdx);
419 inline uint8_t getMaxRxNum(uint8_t idx)
421 if (isModuleDSM2(idx))
422 return 20;
424 #if defined(MULTIMODULE)
425 if (isModuleMultimodule(idx))
427 switch (g_model.moduleData[idx].getMultiProtocol()) {
428 case MODULE_SUBTYPE_MULTI_OLRS:
429 return 4;
430 case MODULE_SUBTYPE_MULTI_BUGS:
431 case MODULE_SUBTYPE_MULTI_BUGS_MINI:
432 return 15;
435 #endif
437 return 63;
440 inline const char * getModuleDelay(uint8_t idx)
442 if (isModuleISRMAccess(idx))
443 return sentModuleChannels(idx) > 16 ? "(21ms)" : (sentModuleChannels(idx) > 8 ? "(14ms)" : "(7ms)");
445 if (isModuleXJTD16(idx) || isModuleR9MNonAccess(idx))
446 return sentModuleChannels(idx) > 8 ? "(18ms)" : "(9ms)";
448 return nullptr;
451 inline bool isBindCh9To16Allowed(uint8_t moduleIndex)
453 if (g_model.moduleData[moduleIndex].channelsCount <= 0) {
454 return false;
457 if (isModuleR9M_LBT(moduleIndex)) {
458 if (isModuleR9MLite(moduleIndex))
459 return g_model.moduleData[moduleIndex].pxx.power != R9M_LBT_POWER_25_8CH;
460 else
461 return g_model.moduleData[moduleIndex].pxx.power != R9M_LITE_LBT_POWER_25_8CH;
463 else {
464 return true;
468 inline bool isTelemAllowedOnBind(uint8_t moduleIndex)
470 #if defined(HARDWARE_INTERNAL_MODULE)
471 if (moduleIndex == INTERNAL_MODULE)
472 return isModuleISRM(moduleIndex) || isSportLineUsedByInternalModule();
474 if (isSportLineUsedByInternalModule())
475 return false;
476 #endif
478 if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_LITE_PXX1) {
479 if (isModuleR9M_LBT(EXTERNAL_MODULE))
480 return g_model.moduleData[EXTERNAL_MODULE].pxx.power < R9M_LITE_LBT_POWER_100_16CH_NOTELEM;
481 else
482 return true;
485 if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_PXX1 || g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_R9M_LITE_PRO_PXX1) {
486 if (isModuleR9M_LBT(EXTERNAL_MODULE))
487 return g_model.moduleData[EXTERNAL_MODULE].pxx.power < R9M_LBT_POWER_200_16CH_NOTELEM;
488 else
489 return true;
492 return true;
495 inline bool isPXX2ReceiverUsed(uint8_t moduleIdx, uint8_t receiverIdx)
497 return g_model.moduleData[moduleIdx].pxx2.receivers & (1 << receiverIdx);
500 inline void setPXX2ReceiverUsed(uint8_t moduleIdx, uint8_t receiverIdx)
502 g_model.moduleData[moduleIdx].pxx2.receivers |= (1 << receiverIdx);
505 inline bool isPXX2ReceiverEmpty(uint8_t moduleIdx, uint8_t receiverIdx)
507 return is_memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME);
510 inline void removePXX2Receiver(uint8_t moduleIdx, uint8_t receiverIdx)
512 memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME);
513 g_model.moduleData[moduleIdx].pxx2.receivers &= ~(1 << receiverIdx);
514 storageDirty(EE_MODEL);
517 inline void removePXX2ReceiverIfEmpty(uint8_t moduleIdx, uint8_t receiverIdx)
519 if (isPXX2ReceiverEmpty(moduleIdx, receiverIdx)) {
520 removePXX2Receiver(moduleIdx, receiverIdx);
524 inline void setDefaultPpmFrameLength(uint8_t moduleIdx)
526 g_model.moduleData[moduleIdx].ppm.frameLength = 4 * max<int>(0, g_model.moduleData[moduleIdx].channelsCount);
529 inline void setModuleType(uint8_t moduleIdx, uint8_t moduleType)
531 ModuleData & moduleData = g_model.moduleData[moduleIdx];
532 memclear(&moduleData, sizeof(ModuleData));
533 moduleData.type = moduleType;
534 moduleData.channelsCount = defaultModuleChannels_M8(moduleIdx);
535 if (moduleData.type == MODULE_TYPE_SBUS)
536 moduleData.sbus.refreshRate = -31;
537 else if (moduleData.type == MODULE_TYPE_PPM)
538 setDefaultPpmFrameLength(moduleIdx);
541 extern bool isExternalAntennaEnabled();
543 #if defined(MULTIMODULE)
544 inline void resetMultiProtocolsOptions(uint8_t moduleIdx)
546 if (!isModuleMultimodule(moduleIdx))
547 return;
549 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
550 if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) {
551 g_model.moduleData[moduleIdx].multi.autoBindMode = 1;
553 else {
554 g_model.moduleData[moduleIdx].multi.autoBindMode = 0;
556 g_model.moduleData[moduleIdx].multi.optionValue = 0;
557 g_model.moduleData[moduleIdx].multi.disableTelemetry = 0;
558 g_model.moduleData[moduleIdx].multi.disableMapping = 0;
559 g_model.moduleData[moduleIdx].multi.lowPowerMode = 0;
562 inline void getMultiOptionValues(int8_t multi_proto, int8_t & min, int8_t & max)
564 switch (multi_proto) {
565 case MODULE_SUBTYPE_MULTI_DSM2:
566 min = 0;
567 max = 1;
568 break;
569 case MODULE_SUBTYPE_MULTI_BAYANG:
570 min = 0;
571 max = 3;
572 break;
573 case MODULE_SUBTYPE_MULTI_OLRS:
574 min = -1;
575 max = 7;
576 break;
577 case MODULE_SUBTYPE_MULTI_FS_AFHDS2A:
578 min = 0;
579 max = 70;
580 break;
581 case MODULE_SUBTYPE_MULTI_XN297DP:
582 min = -1;
583 max = 84;
584 break;
585 default:
586 min = -128;
587 max = 127;
588 break;
591 #endif
593 #endif // _MODULES_HELPERS_H_