diff --git a/CMakeLists.txt b/CMakeLists.txt index 22af0ad2a95..8584ed99f0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(EdgeTX) set(VERSION_MAJOR "2") set(VERSION_MINOR "12") -set(VERSION_REVISION "3") +set(VERSION_REVISION "4") set(CODENAME "Queen Anne's Revenge") string(TIMESTAMP BUILD_YEAR "%Y") @@ -45,12 +45,23 @@ if(EdgeTX_SUPERBUILD) else() set(CACHE_VAR_TYPE :${CACHE_VAR_TYPE}) endif() - list(APPEND CMAKE_ARGS "-D${CACHE_VAR}${CACHE_VAR_TYPE}=${${CACHE_VAR}}") + # Escape CMake's ';' so a multi-path value (CMAKE_PREFIX_PATH) stays one argument + # instead of splitting; LIST_SEPARATOR below restores it. + string(REPLACE ";" "|" CACHE_VAR_VALUE "${${CACHE_VAR}}") + list(APPEND CMAKE_ARGS "-D${CACHE_VAR}${CACHE_VAR_TYPE}=${CACHE_VAR_VALUE}") endif() endforeach() message("-- CMAKE_ARGS: ${CMAKE_ARGS}") message("-- CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + # Darwin-Initialize.cmake rewrites CMAKE_OSX_DEPLOYMENT_TARGET's helpstring, so the loop + # above never forwards it - pass it explicitly or sub-builds get the SDK default (#7732). + set(EDGETX_EP_CACHE_ARGS) + if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET) + list(APPEND EDGETX_EP_CACHE_ARGS + -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET}) + endif() + # Add explicit targets for triggering cmake in the external projects set_property(DIRECTORY PROPERTY EP_STEP_TARGETS configure clean) @@ -58,8 +69,10 @@ if(EdgeTX_SUPERBUILD) ExternalProject_Add(native SOURCE_DIR ${CMAKE_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/native + LIST_SEPARATOR | CMAKE_ARGS ${CMAKE_ARGS} -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} CMAKE_CACHE_ARGS + ${EDGETX_EP_CACHE_ARGS} -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_SOURCE_DIR}/cmake/toolchain/native.cmake -DEdgeTX_SUPERBUILD:BOOL=0 -DNATIVE_BUILD:BOOL=1 @@ -71,6 +84,7 @@ if(EdgeTX_SUPERBUILD) ExternalProject_Add(arm-none-eabi SOURCE_DIR ${CMAKE_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/arm-none-eabi + LIST_SEPARATOR | CMAKE_ARGS ${CMAKE_ARGS} -Wno-dev -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} CMAKE_CACHE_ARGS -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_SOURCE_DIR}/cmake/toolchain/arm-none-eabi.cmake diff --git a/companion/src/firmwares/edgetx/yaml_moduledata.cpp b/companion/src/firmwares/edgetx/yaml_moduledata.cpp index 37658220d42..225199dd352 100644 --- a/companion/src/firmwares/edgetx/yaml_moduledata.cpp +++ b/companion/src/firmwares/edgetx/yaml_moduledata.cpp @@ -138,6 +138,20 @@ static const YamlLookupTable failsafeLut = { { FAILSAFE_RECEIVER, "RECEIVER" }, }; +static const YamlLookupTable moduleAntennaModeLut = { + { GeneralSettings::ANTENNA_MODE_INTERNAL, "MODE_INTERNAL" }, + { GeneralSettings::ANTENNA_MODE_ASK, "MODE_ASK" }, + { GeneralSettings::ANTENNA_MODE_PER_MODEL, "MODE_PER_MODEL" }, + { GeneralSettings::ANTENNA_MODE_EXTERNAL, "MODE_EXTERNAL" }, +}; + +// Firmware built before the field was made unconditional emitted antennaMode on +// boards without an external antenna, where it aliased the top bits of subType. +static bool hasModuleAntennaMode() +{ + return Boards::getCapability(getCurrentBoard(), Board::HasExternalAntenna); +} + static int exportPpmDelay(int delay) { return (delay - 300) / 50; } static int importPpmDelay(int delay) { return 300 + 50 * delay; } @@ -197,8 +211,8 @@ Node convert::encode(const ModuleData& rhs) node["channelsStart"] = rhs.channelsStart; node["channelsCount"] = rhs.channelsCount; node["failsafeMode"] = LookupValue(failsafeLut, rhs.failsafeMode); - if (rhs.antennaMode) - node["antennaMode"] = rhs.antennaMode; + if (rhs.antennaMode && hasModuleAntennaMode()) + node["antennaMode"] = LookupValue(moduleAntennaModeLut, rhs.antennaMode); Node mod; switch (protocol) { @@ -363,8 +377,8 @@ bool convert::decode(const Node& node, ModuleData& rhs) node["channelsStart"] >> rhs.channelsStart; node["channelsCount"] >> rhs.channelsCount; node["failsafeMode"] >> failsafeLut >> rhs.failsafeMode; - if (node["antennaMode"]) - node["antennaMode"] >> rhs.antennaMode; + if (node["antennaMode"] && hasModuleAntennaMode()) + node["antennaMode"] >> moduleAntennaModeLut >> rhs.antennaMode; if (node["mod"]) { const Node& mod = node["mod"]; @@ -389,8 +403,8 @@ bool convert::decode(const Node& node, ModuleData& rhs) pxx["power"] >> rhs.pxx.power; // pxx["receiverTelemetryOff"] >> rhs.pxx.receiverTelemetryOff; // pxx["receiverHigherChannels"] >> rhs.pxx.receiverHigherChannels; - // Migration: read old pxx.antennaMode into top-level antennaMode - if (pxx["antennaMode"]) { + // Migration: legacy raw-int antennaMode, only if not already set + if (!node["antennaMode"] && pxx["antennaMode"] && hasModuleAntennaMode()) { pxx["antennaMode"] >> rhs.antennaMode; } } else if (mod["sbus"]) { diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 5e407b2312d..7aa380ab324 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -31,7 +31,6 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, functions(model ? model->customFn : generalSettings.customFn), mediaPlayerCurrent(-1), mediaPlayer(nullptr), - audioOutput(new QAudioOutput()), modelsUpdateCnt(0) { lock = true; @@ -290,7 +289,7 @@ bool CustomFunctionsPanel::playSound(int index) stopSound(mediaPlayerCurrent); mediaPlayer = new QMediaPlayer(this); - mediaPlayer->setAudioOutput(audioOutput); + mediaPlayer->setAudioOutput(new QAudioOutput(mediaPlayer)); if (functions[index].func == FuncPlaySound) mediaPlayer->setSource(QUrl(path.prepend("qrc"))); diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index e2a765fe902..47a0a499419 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -110,7 +110,6 @@ class CustomFunctionsPanel : public GenericPanel QComboBox * fswtchRepeat[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchGVmode[CPN_MAX_SPECIAL_FUNCTIONS]; QMediaPlayer * mediaPlayer; - QAudioOutput * audioOutput; int selectedIndex; int fswCapability; diff --git a/companion/targets/mac/MacOSXBundleInfo.plist.in b/companion/targets/mac/MacOSXBundleInfo.plist.in index 82053215535..94fa7dfe204 100644 --- a/companion/targets/mac/MacOSXBundleInfo.plist.in +++ b/companion/targets/mac/MacOSXBundleInfo.plist.in @@ -34,6 +34,8 @@ + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} NSPrincipalClass NSApplication NSHighResolutionCapable diff --git a/companion/targets/mac/bundle_verify.cmake.in b/companion/targets/mac/bundle_verify.cmake.in index a550238d892..382154729f9 100644 --- a/companion/targets/mac/bundle_verify.cmake.in +++ b/companion/targets/mac/bundle_verify.cmake.in @@ -27,7 +27,10 @@ endif() file(GLOB_RECURSE _candidates "${_app}/Contents/*") set(_offenders "") +set(_toonew "") set(_checked 0) +set(_max_minos "0.0") +set(_target_minos "@CMAKE_OSX_DEPLOYMENT_TARGET@") foreach(_file IN LISTS _candidates) if(IS_SYMLINK "${_file}" OR IS_DIRECTORY "${_file}") continue() @@ -45,6 +48,25 @@ foreach(_file IN LISTS _candidates) endif() math(EXPR _checked "${_checked} + 1") + # macOS refuses to launch an app stamped above the running system - that is how 2.12.3 + # became "requires macOS 15.0 or later" (issue #7732). + execute_process( + COMMAND otool -l "${_file}" + OUTPUT_VARIABLE _otool_l_out + ERROR_QUIET + RESULT_VARIABLE _otool_l_res + ) + if(_otool_l_res EQUAL 0 AND _otool_l_out MATCHES "minos ([0-9]+(\.[0-9]+)+)") + set(_minos "${CMAKE_MATCH_1}") + if(_minos VERSION_GREATER _max_minos) + set(_max_minos "${_minos}") + endif() + if(_target_minos AND _minos VERSION_GREATER _target_minos) + file(RELATIVE_PATH _rel "${_app}" "${_file}") + list(APPEND _toonew "${_rel}: minos ${_minos}") + endif() + endif() + string(REPLACE "\n" ";" _lines "${_otool_out}") foreach(_line IN LISTS _lines) string(REGEX MATCH "^\t([^ ]+)" _matched "${_line}") @@ -77,7 +99,20 @@ if(_offenders) "Re-run with -DCOMPANION_VERIFY_BUNDLE=OFF to package anyway.") endif() -message(STATUS "Bundle verification: ${_checked} Mach-O files, no external references") +if(_toonew) + list(REMOVE_DUPLICATES _toonew) + string(REPLACE ";" "\n " _report "${_toonew}") + message(FATAL_ERROR + "Bundle contains Mach-O files built for a newer macOS than the deployment target " + "(${_target_minos}) - macOS refuses to launch an app stamped above the running " + "system:\n ${_report}\n" + "Re-run with -DCOMPANION_VERIFY_BUNDLE=OFF to package anyway.") +endif() + +# CPack discards message(STATUS) from install scripts, so echo via a subprocess - otherwise +# the bundle's real macOS floor never reaches the packaging log. +execute_process(COMMAND ${CMAKE_COMMAND} -E echo + "-- Bundle verification: ${_checked} Mach-O files, no external references, minimum macOS ${_max_minos}") # The bundle seal is a separate concern from the per-Mach-O signatures: dyld does not consult # CodeResources when loading, so a stale seal does not stop the app launching. It would matter diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index ad3323afa0d..3fb00c7f2b7 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -1540,6 +1540,11 @@ int cliDisplay(const char ** argv) gettime(&utm); cliSerialPrint("rtc = %4d-%02d-%02d %02d:%02d:%02d.%02d0", utm.tm_year+TM_YEAR_BASE, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100); } +#if defined(VOLUME_I2C_ADDRESS) + else if (!strcmp(argv[1], "volume")) { + cliSerialPrint("volume = %d", getVolume()); + } +#endif else if (!strcmp(argv[1], "uid")) { char str[LEN_CPU_UID+1]; getCPUUniqueID(str); diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index f7aeb1bfd25..d68ea7f9791 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -499,17 +499,15 @@ PACK(struct PpmModule { }); PACK(struct ModuleData { - uint8_t type ENUM(ModuleType) CUST(r_moduleType, w_moduleType); + // antennaMode stays unconditional as boards differing on EXTERNAL_ANTENNA share + // generated YAML descriptors. + uint8_t type:6 ENUM(ModuleType) CUST(r_moduleType, w_moduleType); + int8_t antennaMode:2 ENUM(AntennaModes); CUST_ATTR(subType,r_modSubtype,w_modSubtype); uint8_t channelsStart; int8_t channelsCount CUST(r_channelsCount,w_channelsCount); // 0=8 channels uint8_t failsafeMode:4 ENUM(FailsafeModes); // only 3 bits used - #if defined(EXTERNAL_ANTENNA) - uint8_t subType:2 SKIP; - int8_t antennaMode:2 ENUM(AntennaModes); - #else uint8_t subType:4 SKIP; - #endif union { uint8_t raw[PXX2_MAX_RECEIVERS_PER_MODULE * PXX2_LEN_RX_NAME + 1]; diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 429c28b1219..89ef1c2e4ce 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -2452,8 +2452,10 @@ void menuModelSetup(event_t event) #if defined(AFHDS3) && defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AFHDS3_STATUS: #endif -#if (defined(MULTIMODULE) | defined(DSMP) | defined(AFHDS3)) && defined(HARDWARE_EXTERNAL_MODULE) +#if defined(DSMP) && defined(HARDWARE_EXTERNAL_MODULE) case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DSMP_STATUS: +#endif +#if defined(MULTIMODULE) || defined(AFHDS3) || defined(DSMP) { // MultiModule & LemonDSMP & AFHDS3 Status lcdDrawTextIndented(y, STR_MODULE_STATUS); diff --git a/radio/src/pulses/crossfire.cpp b/radio/src/pulses/crossfire.cpp index 36cbe64fa06..1cd9259c405 100644 --- a/radio/src/pulses/crossfire.cpp +++ b/radio/src/pulses/crossfire.cpp @@ -96,20 +96,20 @@ uint8_t createCrossfireModelIDFrame(uint8_t moduleIdx, uint8_t * frame) uint8_t createCrossfireChannelsFrame(uint8_t moduleIdx, uint8_t * frame, int16_t * pulses) { // - // sends channel data and also communicates commanded armed status in arming mode Switch. - // frame len 24 -> arming mode CH5: module will use channel 5 - // frame len 25 -> arming mode Switch: send commanded armed status in extra byte after channel data - // - ModuleData *md = &g_model.moduleData[moduleIdx]; - - uint8_t armingMode = md->crsf.crsfArmingMode; // 0 = Channel mode, 1 = Switch mode - uint8_t lenAdjust = (armingMode == ARMING_MODE_SWITCH) ? 1 : 0; - + // sends channel data and also communicates status information in status byte: + // - arming status in Switch mode (bit 0) + // - arming mode Switch or CH5 (bit 1) + // - bits 2-7 spare + // uint8_t * buf = frame; *buf++ = MODULE_ADDRESS; - *buf++ = 24 + lenAdjust; // 1(ID) + 22(channel data) + (+1 extra byte if Switch mode) + 1(CRC) + *buf++ = 25; // 1(ID) + 22(channel data) + 1(extra status byte) + 1(CRC) uint8_t * crc_start = buf; *buf++ = CHANNELS_ID; + + // + // assemble channel data + // uint32_t bits = 0; uint8_t bitsavailable = 0; for (int i=0; icrsf.crsfArmingMode == ARMING_MODE_SWITCH) { swsrc_t sw = md->crsf.crsfArmingTrigger; - *buf++ = (sw != SWSRC_NONE) && getSwitch(sw, 0); // commanded armed status in Switch mode + *buf = (sw != SWSRC_NONE) && getSwitch(sw, 0); // commanded armed status in Switch mode + } else { + *buf = 0x02; // flag arming mode CH5 } + + buf++; - *buf++ = crc8(crc_start, 23 + lenAdjust); + // + // add crc + // + *buf++ = crc8(crc_start, 24); + return buf - frame; } diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index b7696a19d38..b5b352977ca 100644 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -124,10 +124,10 @@ void pulsesRestartModuleUnsafe(uint8_t module) { if (module >= MAX_MODULES) return; - + auto mod_drv = pulsesGetModuleDriver(module); if (!mod_drv->drv) return; - + auto drv = mod_drv->drv; drv->deinit(mod_drv->ctx); mod_drv->ctx = drv->init(module); @@ -351,7 +351,7 @@ uint8_t getRequiredProtocol(uint8_t module) case MODULE_TYPE_LEMON_DSMP: protocol = PROTOCOL_CHANNELS_DSMP; break; - + default: protocol = PROTOCOL_CHANNELS_NONE; break; @@ -390,7 +390,7 @@ static void _init_module(uint8_t module, const etx_proto_driver_t* drv) // board specific hook if (_on_module_init) _on_module_init(module, drv); - + // power ON modulePortSetPower(module, true); TRACE("Module #%d init succeeded", module); @@ -408,7 +408,7 @@ static void _deinit_module(uint8_t module) auto drv = mod->drv; if (_on_module_deinit) _on_module_deinit(module, drv); - + // de-init auto ctx = mod->ctx; drv->deinit(ctx); @@ -486,7 +486,7 @@ static void pulsesEnableModule(uint8_t module, uint8_t protocol) break; #endif -#if defined(DSM2) +#if defined(DSMP) case PROTOCOL_CHANNELS_DSMP: _init_module(module, &DSMPDriver); break; @@ -548,7 +548,7 @@ void pulsesSendNextFrame(uint8_t module) if (_handle_async_restart(module)) return; - + pulsesEnableModule(module, protocol); moduleState[module].protocol = protocol; return; diff --git a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp index fad80f5e1bd..293bad56d6a 100644 --- a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp @@ -651,7 +651,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_c14.cpp b/radio/src/storage/yaml/yaml_datastructs_c14.cpp index 4ef832fd7c8..679a76e9ec3 100644 --- a/radio/src/storage/yaml/yaml_datastructs_c14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_c14.cpp @@ -720,7 +720,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_f16.cpp b/radio/src/storage/yaml/yaml_datastructs_f16.cpp index 765f94090d2..32cf1db4024 100644 --- a/radio/src/storage/yaml/yaml_datastructs_f16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_f16.cpp @@ -721,7 +721,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp index 4517ad8c93c..823840492ae 100644 --- a/radio/src/storage/yaml/yaml_datastructs_gx12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_gx12.cpp @@ -675,7 +675,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_gx15.cpp b/radio/src/storage/yaml/yaml_datastructs_gx15.cpp index 08a369c420d..211f560a86f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_gx15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_gx15.cpp @@ -745,7 +745,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp index d9ac4df4bda..cae5555774b 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nb4p.cpp @@ -711,7 +711,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 918909b82ab..04e6c087133 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -718,7 +718,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp index c96e0936b7c..249e2b0f32a 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pa01.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pa01.cpp @@ -744,7 +744,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp index 8eb4d6c2599..2e78f25b1f6 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18.cpp @@ -718,7 +718,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp index 54bb3aa2128..c857331aecf 100644 --- a/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_pl18u.cpp @@ -711,7 +711,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_st16.cpp b/radio/src/storage/yaml/yaml_datastructs_st16.cpp index 9af5eb55d21..ab56da21d76 100644 --- a/radio/src/storage/yaml/yaml_datastructs_st16.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_st16.cpp @@ -744,7 +744,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_t15.cpp b/radio/src/storage/yaml/yaml_datastructs_t15.cpp index 153c25ee0ec..313141ecc44 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15.cpp @@ -729,7 +729,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp index f8b43873a66..65862732d3c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t15pro.cpp @@ -744,7 +744,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_t20.cpp b/radio/src/storage/yaml/yaml_datastructs_t20.cpp index e5f97c7c95b..e88dc6f45e4 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t20.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t20.cpp @@ -660,7 +660,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_t22.cpp b/radio/src/storage/yaml/yaml_datastructs_t22.cpp index f8b43873a66..65862732d3c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t22.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t22.cpp @@ -744,7 +744,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 2488e36f16c..a51f828dde9 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -660,7 +660,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp index 08a369c420d..211f560a86f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx15.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx15.cpp @@ -745,7 +745,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp index 148deeb0370..a9d3c0419e0 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx16smk3.cpp @@ -746,7 +746,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_v12.cpp b/radio/src/storage/yaml/yaml_datastructs_v12.cpp index 6a4d2b60066..2d8a5eb5376 100644 --- a/radio/src/storage/yaml/yaml_datastructs_v12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_v12.cpp @@ -739,13 +739,13 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), YAML_ENUM("failsafeMode", 4, enum_FailsafeModes, NULL), - YAML_PADDING( 2 ), - YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), + YAML_PADDING( 4 ), YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index e73618ae7c9..8a5f193612f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -720,13 +720,13 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), YAML_ENUM("failsafeMode", 4, enum_FailsafeModes, NULL), - YAML_PADDING( 2 ), - YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), + YAML_PADDING( 4 ), YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp index 9dc0fdd0e11..ccdf3b89f0c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp @@ -652,7 +652,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp index 9dc0fdd0e11..ccdf3b89f0c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9dp2019.cpp @@ -652,7 +652,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp index 0ea5b2c9e05..df269253101 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp @@ -652,7 +652,8 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), diff --git a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp index 33702e84f1d..293bad56d6a 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp @@ -651,13 +651,13 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), YAML_ENUM("failsafeMode", 4, enum_FailsafeModes, NULL), - YAML_PADDING( 2 ), - YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), + YAML_PADDING( 4 ), YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp index d0db0d825c2..1703f359cb9 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -656,13 +656,13 @@ static const struct YamlNode union_anonymous_4_elmts[] = { }; static const struct YamlNode struct_ModuleData[] = { YAML_IDX, - YAML_UNSIGNED_CUST( "type", 8, r_moduleType, w_moduleType ), + YAML_UNSIGNED_CUST( "type", 6, r_moduleType, w_moduleType ), + YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), YAML_CUSTOM("subType",r_modSubtype,w_modSubtype), YAML_UNSIGNED( "channelsStart", 8 ), YAML_SIGNED_CUST( "channelsCount", 8, r_channelsCount, w_channelsCount ), YAML_ENUM("failsafeMode", 4, enum_FailsafeModes, NULL), - YAML_PADDING( 2 ), - YAML_ENUM("antennaMode", 2, enum_AntennaModes, NULL), + YAML_PADDING( 4 ), YAML_UNION("mod", 200, union_anonymous_4_elmts, select_mod_type), YAML_END }; diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index 95a01bf2aa4..30e342f33e6 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -34,6 +34,7 @@ #include "os/task.h" #include "os/timer_native_impl.h" +#include #include #include #include @@ -90,6 +91,11 @@ uint32_t timersGetMsTick(void) void simuInit() { + // Force the C locale for numeric conversions (strtof/strtod, etc.), + // otherwise the simulator picks up the host locale (e.g. es_ES) which + // uses a comma as decimal separator and breaks Lua number parsing. + setlocale(LC_NUMERIC, "C"); + #if defined(ROTARY_ENCODER_NAVIGATION) rotencValue = 0; #endif diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 2e8688901f6..10def66c34f 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2756,6 +2756,9 @@ #if !defined(SOFTWARE_VOLUME) #define VOLUME_I2C_ADDRESS 0x2E #define VOLUME_I2C_BUS I2C_Bus_1 + + #include + int32_t getVolume(); #endif #define I2C_B1_CLK_RATE 400000 diff --git a/radio/src/targets/taranis/volume_i2c.cpp b/radio/src/targets/taranis/volume_i2c.cpp index d1ed2365cf0..48b5c942e15 100644 --- a/radio/src/targets/taranis/volume_i2c.cpp +++ b/radio/src/targets/taranis/volume_i2c.cpp @@ -57,4 +57,9 @@ void audioSetVolume(uint8_t volume) write_i2c_volume(volumeScale[volume]); } +int32_t getVolume() +{ + return read_i2c_volume(); +} + #endif diff --git a/radio/src/tests/CMakeLists.txt b/radio/src/tests/CMakeLists.txt index 1fe933fdfe8..8f855b289bd 100644 --- a/radio/src/tests/CMakeLists.txt +++ b/radio/src/tests/CMakeLists.txt @@ -11,8 +11,24 @@ set(TESTS_BUILD_PATH ${CMAKE_CURRENT_BINARY_DIR}) configure_file(${RADIO_SRC_DIR}/tests/location.h.in ${CMAKE_CURRENT_BINARY_DIR}/location.h @ONLY) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_DEBUG} -O0 -fsanitize=address") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -O0 ${WARNING_FLAGS} -fsanitize=address") +# AppleClang >=17 ASan hangs at startup on recent macOS (e.g. "Tahoe"). +set(TESTS_ASAN_DEFAULT ON) +if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND + CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0) + message(WARNING "Disabling ASan for gtests-radio: AppleClang " + "${CMAKE_CXX_COMPILER_VERSION} is known to hang at startup on this " + "platform. Pass -DTESTS_ASAN=ON to force it back on, e.g. with a " + "non-Apple clang.") + set(TESTS_ASAN_DEFAULT OFF) +endif() +option(TESTS_ASAN "Enable AddressSanitizer for gtests-radio" ${TESTS_ASAN_DEFAULT}) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_DEBUG} -O0") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -O0 ${WARNING_FLAGS}") +if(TESTS_ASAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") +endif() file(GLOB TEST_SRC_FILES ${RADIO_SRC_DIR}/tests/*.cpp CONFIGURE_DEPENDS "${RADIO_SRC_DIR}/tests/*.cpp") diff --git a/radio/src/tests/crossfire.cpp b/radio/src/tests/crossfire.cpp index 0c5a52010a6..b904a454f85 100644 --- a/radio/src/tests/crossfire.cpp +++ b/radio/src/tests/crossfire.cpp @@ -22,12 +22,20 @@ #include "gtest/gtest.h" #include "gtests.h" #include "telemetry/telemetry.h" +#include "telemetry/crossfire.h" +#include "crc.h" #if defined(CROSSFIRE) uint8_t createCrossfireChannelsFrame(uint8_t moduleIdx, uint8_t * frame, int16_t * pulses); + +// Spec-defined part of the frame (0x16 RC Channels Packed): sync byte, type, +// 16 x 11-bit channel packing. Expected bytes computed independently, not +// derived from createCrossfireChannelsFrame() itself. TEST(Crossfire, createCrossfireChannelsFrame) { + MODEL_RESET(); + int16_t pulsesStart[MAX_TRAINER_CHANNELS]; uint8_t crossfire[CROSSFIRE_FRAME_MAXLEN]; @@ -38,7 +46,71 @@ TEST(Crossfire, createCrossfireChannelsFrame) createCrossfireChannelsFrame(EXTERNAL_MODULE, crossfire, pulsesStart); - // TODO check + ASSERT_EQ(crossfire[0], MODULE_ADDRESS); + ASSERT_EQ(crossfire[2], CHANNELS_ID); + + const uint8_t expectedChannelData[22] = { + 0xAD, 0xA0, 0x88, 0x5E, 0xC0, 0x73, 0xA4, 0x56, 0x51, 0x4C, 0x6F, + 0xE0, 0x33, 0x22, 0x2B, 0x27, 0x9A, 0x57, 0xF0, 0x1A, 0x99, 0xD5 + }; + ASSERT_EQ(memcmp(&crossfire[3], expectedChannelData, sizeof(expectedChannelData)), 0); +} + +// Status byte after the 0x16 payload is an ExpressLRS extension, not TBS CRSF +// spec (semantics per ExpressLRS's TXModuleEndpoint.cpp / crsf_protocol.h). +// Frame is always 25 bytes (1 ID + 22 channel data + 1 status + 1 CRC); +// bit 0 = commanded armed status (Switch mode only), bit 1 = arming mode is CH5. +TEST(Crossfire, ExpressLRSArmingExtension_CH5Mode) +{ + MODEL_RESET(); + + int16_t pulsesStart[MAX_TRAINER_CHANNELS]; + uint8_t crossfire[CROSSFIRE_FRAME_MAXLEN]; + + memset(crossfire, 0, sizeof(crossfire)); + for (int i=0; i not armed, bit 1 clear (Switch mode) + + uint8_t crc = crc8(&crossfire[2], 24); + ASSERT_EQ(crossfire[26], crc); } TEST(Crossfire, crc8) diff --git a/radio/src/tests/lua.cpp b/radio/src/tests/lua.cpp index 83e1bd4b05d..a90408168cb 100644 --- a/radio/src/tests/lua.cpp +++ b/radio/src/tests/lua.cpp @@ -198,6 +198,46 @@ TEST(Lua, Switches) #endif } +TEST(Lua, testFloatIntegerEquality) +{ + // 0.5 is not an integer, so it must not equal 0 (regression #7587) + // both directions asserted explicitly so the intent is obvious at a glance + luaExecStr("if 0.5 == 0 then error('0.5 == 0') end"); + luaExecStr("if not (0.5 ~= 0) then error('0.5 ~= 0') end"); + luaExecStr("if 0.50 == 0 then error('0.50 == 0') end"); + luaExecStr("if not (0.50 ~= 0) then error('0.50 ~= 0') end"); + luaExecStr("if 0.50 == 0.0 then error('0.50 == 0.0') end"); + // ... even when the value comes from a variable, as in the reported issue + luaExecStr("local v = 0.50; if v == 0 then error('v == 0') end"); + luaExecStr("local v = 0.50; if not (v ~= 0) then error('v ~= 0') end"); + // negative non-integral floats must not equal integers either + luaExecStr("if -0.5 == 0 then error('-0.5 == 0') end"); + luaExecStr("if not (-0.5 ~= 0) then error('-0.5 ~= 0') end"); + luaExecStr("if not (-0.5 < 0) then error('-0.5 < 0') end"); + luaExecStr("if -0.5 > 0 then error('-0.5 > 0') end"); + // integral floats still compare equal to their integer counterpart + luaExecStr("if 1.0 ~= 1 then error('1.0 ~= 1') end"); + luaExecStr("if 0.0 ~= 0 then error('0.0 ~= 0') end"); + luaExecStr("if -1.0 ~= -1 then error('-1.0 ~= -1') end"); + // order comparisons on the same values must remain consistent + luaExecStr("if not (0.5 >= 0) then error('0.5 >= 0') end"); + luaExecStr("if 0.5 <= 0 then error('0.5 <= 0') end"); + luaExecStr("if not (0.5 > 0) then error('0.5 > 0') end"); + luaExecStr("if 0.5 < 0 then error('0.5 < 0') end"); + luaExecStr("if not (0.50 >= 0) then error('0.50 >= 0') end"); + luaExecStr("if 0.50 <= 0 then error('0.50 <= 0') end"); + luaExecStr("if not (1.0 >= 1) then error('1.0 >= 1') end"); + luaExecStr("if not (1.0 <= 1) then error('1.0 <= 1') end"); + luaExecStr("if not (0.0 >= 0) then error('0.0 >= 0') end"); + luaExecStr("if not (0.0 <= 0) then error('0.0 <= 0') end"); + // mixed arithmetic must still yield floats; the fix only affects equality + luaExecStr("if math.type(0.5 + 0) ~= 'float' then error('0.5 + 0') end"); + luaExecStr("if math.type(1.0 + 1) ~= 'float' then error('1.0 + 1') end"); + luaExecStr("if math.type(1 / 2) ~= 'float' then error('1 / 2') end"); + luaExecStr("if math.type(7.5 % 2) ~= 'float' then error('7.5 % 2') end"); + luaExecStr("if math.type(0.5 * 2) ~= 'float' then error('0.5 * 2') end"); +} + TEST(Lua, testLegacyNames) { MODEL_RESET(); diff --git a/radio/src/thirdparty/Lua/src/lvm.c b/radio/src/thirdparty/Lua/src/lvm.c index c186e5e7c26..7a2d5a6ed39 100644 --- a/radio/src/thirdparty/Lua/src/lvm.c +++ b/radio/src/thirdparty/Lua/src/lvm.c @@ -401,7 +401,7 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { return 0; /* only numbers can be equal with different variants */ else { /* two numbers with different variants */ lua_Integer i1, i2; /* compare them as integers */ - return (tointeger(t1, &i1) && tointeger(t2, &i2) && i1 == i2); + return (tointegerexact(t1, &i1) && tointegerexact(t2, &i2) && i1 == i2); } } /* values have same type and same variant */ diff --git a/radio/src/thirdparty/Lua/src/lvm.h b/radio/src/thirdparty/Lua/src/lvm.h index a8f954f04ce..c066bc07f28 100644 --- a/radio/src/thirdparty/Lua/src/lvm.h +++ b/radio/src/thirdparty/Lua/src/lvm.h @@ -43,6 +43,15 @@ #define tointeger(o,i) \ (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) +/* non-soft conversion: only integral values are converted. +** This is the strict semantic used by equality ('==' and '~='), where a +** non-integral float such as 0.5 must not compare equal to an integer. +** 'tointeger' (soft, LUA_FLOORN2I) is intentionally kept for API argument +** coercion so that EdgeTX API functions accept unrounded floats; do not +** switch this macro back to the soft conversion. */ +#define tointegerexact(o,i) \ + (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,0)) + #define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) #define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) diff --git a/tools/build-companion.sh b/tools/build-companion.sh index cd4b6aa78f9..fe383add2d3 100755 --- a/tools/build-companion.sh +++ b/tools/build-companion.sh @@ -29,8 +29,11 @@ else fi COMMON_OPTIONS="${COMMON_OPTIONS} -DCMAKE_BUILD_TYPE=Release -DCMAKE_MESSAGE_LOG_LEVEL=WARNING -Wno-dev -DGVARS=YES -DHELI=YES -DLUA=YES" +# Qt 6.9's frameworks are macOS 12, so the bundle cannot start below that anyway. +# Exported as well as passed as -D: the superbuild's forwarding loop drops this one. if [ "$(uname)" = "Darwin" ]; then - COMMON_OPTIONS="${COMMON_OPTIONS} -DCMAKE_OSX_DEPLOYMENT_TARGET='11.0'" + export MACOSX_DEPLOYMENT_TARGET='12.0' + COMMON_OPTIONS="${COMMON_OPTIONS} -DCMAKE_OSX_DEPLOYMENT_TARGET='12.0'" fi # find_package(... CONFIG) skips the lib/cmake/ search pattern for prefixes that only