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_
25 #include "definitions.h"
26 #include "opentx_helpers.h"
27 #include "telemetry/telemetry.h"
28 #if defined(MULTIMODULE)
29 #include "telemetry/multi.h"
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
{
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
) {
61 if (g_model
.moduleData
[moduleIdx
].getMultiProtocol() > MODULE_SUBTYPE_MULTI_LAST
) {
63 return (status
.protocolSubNbr
== 0 ? 0 : status
.protocolSubNbr
- 1);
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
;
82 inline bool isModuleMultimodule(uint8_t)
87 inline bool isModuleMultimoduleDSM2(uint8_t)
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
;
144 inline bool isModuleCrossfire(uint8_t idx
)
150 #if defined(PCBSKY9X)
151 inline bool isExtraModule(uint8_t idx
)
153 return idx
== EXTRA_MODULE
;
156 inline bool isExtraModule(uint8_t)
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
)
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
)) {
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;
319 return g_model
.moduleData
[moduleIdx
].pxx
.power
== R9M_LBT_POWER_25_8CH
? 0 : 8;
322 return 8; // always 16 channels in FCC / FLEX
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
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
))
354 else if (isModuleSBUS(idx
))
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
))
373 if (isModuleDSM2(moduleIdx
))
376 if (isModuleISRM(moduleIdx
))
379 if (isModuleMultimodule(moduleIdx
))
385 inline bool isModuleFailsafeAvailable(uint8_t moduleIdx
)
388 if (isModuleISRM(moduleIdx
))
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();
402 const mm_protocol_definition
* pdef
= getMultiProtocolDefinition(g_model
.moduleData
[moduleIdx
].getMultiProtocol());
403 return pdef
->failsafe
;
408 if (isModuleR9M(moduleIdx
))
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
))
424 #if defined(MULTIMODULE)
425 if (isModuleMultimodule(idx
))
427 switch (g_model
.moduleData
[idx
].getMultiProtocol()) {
428 case MODULE_SUBTYPE_MULTI_OLRS
:
430 case MODULE_SUBTYPE_MULTI_BUGS
:
431 case MODULE_SUBTYPE_MULTI_BUGS_MINI
:
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)";
451 inline bool isBindCh9To16Allowed(uint8_t moduleIndex
)
453 if (g_model
.moduleData
[moduleIndex
].channelsCount
<= 0) {
457 if (isModuleR9M_LBT(moduleIndex
)) {
458 if (isModuleR9MLite(moduleIndex
))
459 return g_model
.moduleData
[moduleIndex
].pxx
.power
!= R9M_LBT_POWER_25_8CH
;
461 return g_model
.moduleData
[moduleIndex
].pxx
.power
!= R9M_LITE_LBT_POWER_25_8CH
;
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())
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
;
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
;
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
))
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;
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
:
569 case MODULE_SUBTYPE_MULTI_BAYANG
:
573 case MODULE_SUBTYPE_MULTI_OLRS
:
577 case MODULE_SUBTYPE_MULTI_FS_AFHDS2A
:
581 case MODULE_SUBTYPE_MULTI_XN297DP
:
593 #endif // _MODULES_HELPERS_H_