diff --git a/.clang-format b/.clang-format index 5649307e1055..36cdf8b7ede8 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,93 @@ +# QGroundControl C++ Code Formatting +# Based on Google style with QGC customizations +# Matches CodingStyle.cc/h patterns + BasedOnStyle: Google +Language: Cpp + +# Line length and indentation ColumnLimit: 120 IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ContinuationIndentWidth: 4 + +# Bracing - Custom QGC style +# Functions/Classes: New line (Allman) +# Control statements: Same line (K&R) +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: Never + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterStruct: true + AfterUnion: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false + +# Pointer and reference alignment +PointerAlignment: Left +ReferenceAlignment: Left +DerivePointerAlignment: false + +# Spacing +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false + +# Include sorting +SortIncludes: CaseSensitive +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' # Qt headers + Priority: 1 + - Regex: '^<.*>' # System/STL headers + Priority: 2 + - Regex: '.*' # Project headers + Priority: 3 + +# Alignment +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignConsecutiveDeclarations: None +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 1 + +# Line breaks +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ConstructorInitializerIndentWidth: 4 + +# Namespace formatting +CompactNamespaces: false +NamespaceIndentation: None + +# Other formatting +BinPackArguments: true +BinPackParameters: true +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +ReflowComments: true +Standard: c++20 diff --git a/.clang-tidy b/.clang-tidy index 5e2e27df0e17..0b8dc84b8e11 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,66 @@ +# QGroundControl C++ Static Analysis +# Clang-Tidy configuration for C++20 and Qt 6 + --- Checks: > - clang-analyzer-*, -WarningsAsErrors: '*' + -*, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + clang-analyzer-*, + -clang-analyzer-cplusplus.NewDeleteLeaks, + modernize-*, + -modernize-use-trailing-return-type, + -modernize-avoid-c-arrays, + performance-*, + readability-*, + -readability-magic-numbers, + -readability-identifier-length, + -readability-function-cognitive-complexity, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-avoid-const-or-ref-data-members, + +WarningsAsErrors: '' # Don't treat warnings as errors by default + +CheckOptions: + # Naming conventions (match QGC style) + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.StructCase + value: CamelCase + - key: readability-identifier-naming.EnumCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.VariableCase + value: camelBack + - key: readability-identifier-naming.PrivateMemberPrefix + value: '_' + - key: readability-identifier-naming.ProtectedMemberPrefix + value: '_' + - key: readability-identifier-naming.ConstexprVariableCase + value: CamelCase + + # Modernization options + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: modernize-loop-convert.MinConfidence + value: 'reasonable' + + # Performance options + - key: performance-for-range-copy.WarnOnAllAutoCopies + value: 'true' + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: 'true' + + # Readability options + - key: readability-braces-around-statements.ShortStatementLines + value: '0' # Always require braces + +HeaderFilterRegex: '.*' +FormatStyle: file ... diff --git a/.clangd b/.clangd index fa8caec073e9..80dc126a18ab 100644 --- a/.clangd +++ b/.clangd @@ -1,3 +1,32 @@ +# QGroundControl Clangd Configuration +# Language server for C++ code completion and navigation + +CompileFlags: + Add: + - "-std=c++20" + - "-Wall" + - "-Wextra" + Remove: + - "-m*" # Remove machine-specific flags + Diagnostics: + # Qt macros and moc generate code that triggers false positives UnusedIncludes: None MissingIncludes: None + # Suppress common Qt-related warnings + Suppress: + - "unknown_typename" + - "expansion_to_defined" + +Index: + Background: Build + StandardLibrary: Yes + +InlayHints: + Enabled: Yes + ParameterNames: Yes + DeducedTypes: Yes + Designators: Yes + +Hover: + ShowAKA: Yes diff --git a/.cmake-format b/.cmake-format index 81c2f7d06aed..0167a8c388b4 100644 --- a/.cmake-format +++ b/.cmake-format @@ -1,11 +1,23 @@ +# QGroundControl CMake Formatting Configuration +# Used by cmake-format for consistent CMake file formatting +# Install: pip install cmake-format + format: - line_width: 120 - tab_size: 4 - max_prefix_chars: 40 - use_tabchars: false + line_width: 120 + tab_size: 4 + max_prefix_chars: 40 + use_tabchars: false + dangle_parens: true + dangle_align: prefix + max_subgroups_hwrap: 2 + max_pargs_hwrap: 6 + separate_ctrl_name_with_space: false + separate_fn_name_with_space: false + command_case: canonical parse: additional_commands: + # CPM Package Manager commands cpmaddpackage: pargs: nargs: '*' @@ -41,3 +53,40 @@ parse: flags: [] spelling: CPMFindPackage kwargs: *cpmaddpackagekwargs + + # Qt macros + qt_add_qml_module: + pargs: + nargs: '1+' + kwargs: + URI: 1 + VERSION: 1 + QML_FILES: + + SOURCES: + + RESOURCES: + + OUTPUT_DIRECTORY: 1 + RESOURCE_PREFIX: 1 + + qt_add_resources: + pargs: + nargs: '2+' + kwargs: + PREFIX: 1 + FILES: + + +markup: + enable_markup: false + +lint: + disabled_codes: [] + function_pattern: '[0-9a-z_]+' + macro_pattern: '[0-9A-Z_]+' + global_var_pattern: '[A-Z][0-9A-Z_]+' + internal_var_pattern: '_[A-Z][0-9A-Z_]+' + local_var_pattern: '[a-z][a-z0-9_]+' + private_var_pattern: '_[0-9a-z_]+' + public_var_pattern: '[A-Z][0-9A-Z_]+' + keyword_pattern: '[A-Z][0-9A-Z_]+' + max_conditionals_custom_parser: 2 + min_statement_spacing: 1 + max_statement_spacing: 2 diff --git a/.editorconfig b/.editorconfig index 509c02a01145..2fbe0ce251ec 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,8 +1,64 @@ +# QGroundControl EditorConfig +# https://editorconfig.org/ + root = true +# Default settings for all files [*] charset = utf-8 insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf + +# C++ source files +[*.{cc,h,cpp,hpp,cxx}] +indent_style = space +indent_size = 4 +max_line_length = 120 + +# CMake files +[{CMakeLists.txt,*.cmake}] +indent_style = space +indent_size = 4 + +# QML and JavaScript +[*.{qml,js}] +indent_style = space +indent_size = 4 + +# JSON files +[*.json] +indent_style = space +indent_size = 2 + +# YAML files +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +# Markdown files +[*.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false # Preserve trailing spaces for line breaks + +# XML files (including Qt resource files) +[*.{xml,qrc,ui}] +indent_style = space +indent_size = 2 + +# Shell scripts +[*.sh] +indent_style = space +indent_size = 2 +end_of_line = lf + +# Python scripts +[*.py] indent_style = space indent_size = 4 -trim_trailing_whitespace=true +max_line_length = 100 + +# Makefiles (require tabs) +[{Makefile,*.mk}] +indent_style = tab diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f78245fb2a27..ff794fcc9826 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,17 +1,324 @@ -# QGroundControl License +# Contributing to QGroundControl -Thank you for considering to contribute to QGroundControl. +Thank you for considering contributing to QGroundControl! This guide will help you get started with contributing code, reporting issues, and improving documentation. -Contributions must be made under QGroundControl's dual-license system, under GPLv3 and Apache 2.0. This by definition rules out the re-use of any copyleft (e.g. GPL) licensed code. All contributions must be original or from a compatible license (BSD 2/3 clause, MIT, Apache 2.0). +## Table of Contents -## Apache 2.0 License +1. [Getting Started](#getting-started) +2. [How to Contribute](#how-to-contribute) +3. [Coding Standards](#coding-standards) +4. [Testing Requirements](#testing-requirements) +5. [Pull Request Process](#pull-request-process) +6. [License Requirements](#license-requirements) -The [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) License is a permissive license which allows QGC to be built and used in any environment, including proprietary applications. It allows QGC to be built for mobile app stores. When building with Apache 2.0 a commercial Qt license is required. +--- -## GPL v3 License +## Getting Started -The [GPL v3 License](http://www.gnu.org/licenses/gpl-3.0.en.html) is a strong copyleft license. When building QGC under this license the open source version of Qt can be used. Our licensing grants the permission to use a later version of the license, however, contributions have to be made under 3.0. +### Prerequisites -## Contact +Before you begin, please: -If you have questions regarding the licensing, please contact the maintainer Lorenz Meier, [lm@groundcontrol.org]. +1. Read the [Developer Guide](https://dev.qgroundcontrol.com/en/) +2. Review the [Build Instructions](https://dev.qgroundcontrol.com/en/getting_started/) +3. Familiarize yourself with the [Architecture](copilot-instructions.md) + +### Development Environment + +- **Language**: C++20 with Qt 6.10+ framework +- **Build System**: CMake 3.25+ +- **Platforms**: Windows, macOS, Linux, Android, iOS +- **IDE**: Qt Creator (recommended), VS Code, or your preferred IDE + +--- + +## How to Contribute + +### Reporting Issues + +Before creating a new issue: + +1. **Search existing issues** to avoid duplicates +2. **Provide complete information**: + - QGroundControl version + - Operating system and version + - Detailed steps to reproduce + - Log files (from `~/.local/share/QGroundControl/`) + - Screenshots or videos if applicable + +**Create an issue**: https://github.com/mavlink/qgroundcontrol/issues + +**For security vulnerabilities**: See our [Security Policy](SECURITY.md) for responsible disclosure procedures. + +### Suggesting Enhancements + +Feature requests are welcome! Please: + +1. Check if the feature already exists or has been requested +2. Explain the use case and benefits +3. Consider implementation complexity +4. Be prepared to contribute code if possible + +### Contributing Code + +1. **Fork the repository** + ```bash + git clone https://github.com/YOUR-USERNAME/qgroundcontrol.git + cd qgroundcontrol + ``` + +2. **Create a feature branch** + ```bash + git checkout -b feature/my-new-feature + ``` + +3. **Make your changes** following our [coding standards](#coding-standards) + +4. **Test your changes thoroughly** + - Run unit tests: `./qgroundcontrol --unittest` + - Test on all relevant platforms when possible + - Test with both PX4 and ArduPilot if applicable + +5. **Commit your changes** + ```bash + git add . + git commit -m "Add feature: brief description" + ``` + +6. **Push to your fork** + ```bash + git push origin feature/my-new-feature + ``` + +7. **Create a Pull Request** from your fork to `mavlink/qgroundcontrol:master` + +--- + +## Coding Standards + +### C++ Guidelines + +- **Standard**: C++20 +- **Framework**: Qt 6 guidelines +- **Naming Conventions**: + - Classes: `PascalCase` + - Methods/functions: `camelCase` + - Private members: `_leadingUnderscore` + - Constants: `ALL_CAPS` or `kPascalCase` + +- **Always use braces** for if/else/for/while statements + ```cpp + // Good + if (condition) { + doSomething(); + } + + // Bad + if (condition) doSomething(); + ``` + +- **Defensive coding**: + - Always null-check pointers before use + - Validate all inputs + - Use Q_ASSERT for debug-build development checks only (compiled out in release builds) + - Always use defensive error handling in production code paths (never rely on Q_ASSERT) + - Handle errors gracefully in production code + +- **Code formatting**: + - Run `clang-format` before committing + - Follow `.clang-format` in the repository + - 4 spaces for indentation (no tabs) + +### QML Guidelines + +- Follow Qt QML coding conventions +- Use type annotations +- Prefer declarative over imperative code +- See `src/QmlControls/QGCButton.qml` for examples + +### Logging + +Use Qt logging categories: + +```cpp +Q_DECLARE_LOGGING_CATEGORY(MyComponentLog) +QGC_LOGGING_CATEGORY(MyComponentLog, "qgc.component.name") + +qCDebug(MyComponentLog) << "Debug message:" << value; +qCWarning(MyComponentLog) << "Warning message"; +qCCritical(MyComponentLog) << "Critical error"; +``` + +### Architecture Patterns + +#### Fact System (Required for Parameters) + +Always use the Fact System for vehicle parameters: + +```cpp +Fact* param = vehicle->parameterManager()->getParameter(-1, "PARAM_NAME"); +if (param) { + param->setCookedValue(newValue); // For display values + // param->setRawValue(newValue); // For MAVLink values +} +``` + +#### Multi-Vehicle Awareness + +Always check for null vehicles: + +```cpp +Vehicle* vehicle = MultiVehicleManager::instance()->activeVehicle(); +if (vehicle) { + // Use vehicle +} +``` + +#### Firmware Plugin System + +Use FirmwarePlugin for firmware-specific behavior instead of hardcoding: + +```cpp +vehicle->firmwarePlugin()->isCapable(capability); +vehicle->firmwarePlugin()->flightModes(); +``` + +--- + +## Testing Requirements + +### Unit Tests + +- Add unit tests for new functionality +- Place tests in `test/` directory mirroring `src/` structure +- Use Qt Test framework with `UnitTest` base class +- Run tests before submitting: + ```bash + ./qgroundcontrol --unittest + ``` + +### Manual Testing + +Test your changes on: +- Multiple platforms (Windows, macOS, Linux if possible) +- Both PX4 and ArduPilot firmware (if applicable) +- Different vehicle types (multirotor, fixed-wing, VTOL, rover) + +### Pre-commit Checks + +Run before committing: + +```bash +# Format code +clang-format -i path/to/changed/files.cc + +# Run pre-commit hooks (optional) +pre-commit run --all-files +``` + +--- + +## Pull Request Process + +### Before Submitting + +1. **Rebase on latest master** + ```bash + git fetch upstream + git rebase upstream/master + ``` + +2. **Ensure all tests pass** +3. **Update documentation** if needed +4. **Write a clear PR description**: + - What problem does it solve? + - How was it tested? + - Breaking changes (if any) + - Screenshots for UI changes + +### PR Requirements + +- ✅ All CI checks must pass +- ✅ Code follows style guidelines +- ✅ Tests added for new features +- ✅ No unrelated changes +- ✅ Commit messages are clear and descriptive + +### Review Process + +- Maintainers will review your PR +- Address feedback in new commits (don't force-push during review) +- Once approved, a maintainer will merge your PR + +### After Merging + +- Delete your feature branch +- Your contribution will appear in the next release +- Thank you for contributing! 🎉 + +--- + +## License Requirements + +### Dual-License Requirement + +**Important**: All contributions to QGroundControl must be compatible with our **dual-license system** (Apache 2.0 AND GPL v3). + +### What This Means + +- **Your code must be original** or from a compatible license +- **Compatible licenses**: BSD 2-clause, BSD 3-clause, MIT, Apache 2.0 +- **Incompatible licenses**: GPL-only, proprietary, copyleft-only licenses + +By contributing, you agree that: +1. Your contributions are your original work or properly licensed +2. You grant QGroundControl rights under **both** Apache 2.0 and GPL v3 licenses +3. You have the right to submit the contribution + +### License Background + +QGroundControl uses a dual-license system: + +#### Apache License 2.0 +- Permissive license +- Allows use in proprietary applications +- Allows distribution via app stores +- **Requires commercial Qt license** + +Full text: [LICENSE-APACHE](../LICENSE-APACHE) + +#### GNU General Public License v3 (GPL v3) +- Copyleft license +- Ensures software remains open source +- **Can use open-source Qt** +- Users can use later GPL versions (v3 is minimum for contributions) + +Full text: [LICENSE-GPL](../LICENSE-GPL) + +### Questions About Licensing + +If you have questions about licensing, please contact: +- Lorenz Meier: lm@qgroundcontrol.org + +For more details, see [COPYING.md](COPYING.md). + +--- + +## Additional Resources + +- **User Manual**: https://docs.qgroundcontrol.com/en/ +- **Developer Guide**: https://dev.qgroundcontrol.com/en/ +- **API Documentation**: In-code documentation and `copilot-instructions.md` +- **Support Guide**: For help and community resources, see [SUPPORT.md](SUPPORT.md) +- **Discussion Forum**: https://discuss.px4.io/c/qgroundcontrol +- **Discord**: https://discord.gg/dronecode + +--- + +## Code of Conduct + +QGroundControl is part of the Dronecode Foundation. Please follow our [Code of Conduct](CODE_OF_CONDUCT.md). + +--- + +Thank you for contributing to QGroundControl! Your efforts help make drone control accessible to everyone. diff --git a/.github/COPYING.md b/.github/COPYING.md new file mode 100644 index 000000000000..6ad26798a633 --- /dev/null +++ b/.github/COPYING.md @@ -0,0 +1,45 @@ +# QGroundControl License Information + +QGroundControl is licensed under a dual-license system to allow maximum flexibility for users and contributors. + +## Dual-License System + +QGroundControl is available under **both** the Apache License 2.0 and the GNU General Public License v3 (GPL v3). You may choose which license you want to use based on your use case. + +### Apache License 2.0 + +The [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0) is a permissive license which allows QGroundControl to be built and used in any environment, including proprietary applications. It allows QGroundControl to be built for mobile app stores. + +**Note**: When building with Apache 2.0, a commercial Qt license is required. + +**Full License**: See [LICENSE-APACHE](../LICENSE-APACHE) in the root directory. + +### GNU General Public License v3 (GPL v3) + +The [GPL v3 License](http://www.gnu.org/licenses/gpl-3.0.en.html) is a strong copyleft license. When building QGroundControl under this license, the open source version of Qt can be used. + +Our licensing grants the permission to use a later version of the license; however, contributions must be made under GPL v3. + +**Full License**: See [LICENSE-GPL](../LICENSE-GPL) in the root directory. + +## Contributing to QGroundControl + +Contributions must be made under QGroundControl's dual-license system (both GPL v3 AND Apache 2.0). This by definition rules out the re-use of any copyleft (e.g., GPL-only) licensed code. + +**All contributions must be:** +- Original work, OR +- From a compatible license: BSD 2-clause, BSD 3-clause, MIT, or Apache 2.0 + +For contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md). + +## Questions + +If you have questions regarding the licensing, please contact the maintainer: +- Lorenz Meier: lm@qgroundcontrol.org + +--- + +**Quick Summary:** +- **Use GPL v3** if you want to use the open-source Qt version (copyleft) +- **Use Apache 2.0** if you need to use QGroundControl in proprietary applications (requires commercial Qt license) +- **Contributing** requires code compatible with BOTH licenses diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index cb3e3b43662e..ef52ed76da6f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,7 @@ name: Bug report description: Create a report to help us improve labels: ["Report: Bug"] +type: bug body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 1b0aebe6bbb1..7c29789ac7d0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,7 @@ name: Feature request description: Tell us about your new idea labels: ["Feature Request"] +type: feature body: - type: markdown diff --git a/.github/actions/checks/action.yml b/.github/actions/checks/action.yml index bf75189c3476..949a76acd196 100644 --- a/.github/actions/checks/action.yml +++ b/.github/actions/checks/action.yml @@ -10,15 +10,15 @@ inputs: runs: using: "composite" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Run clang-format style check for C++ Source Files. - if: inputs.format == 'true' - uses: jidicula/clang-format-action@main - with: - clang-format-version: '17' - check-path: 'src' + - name: Run clang-format style check for C++ Source Files. + if: inputs.format == 'true' + uses: jidicula/clang-format-action@main + with: + clang-format-version: '17' + check-path: 'src' - - name: Check spelling - if: inputs.spelling == 'true' - uses: crate-ci/typos@master + - name: Check spelling + if: inputs.spelling == 'true' + uses: crate-ci/typos@master diff --git a/.github/actions/gstreamer/action.yml b/.github/actions/gstreamer/action.yml index 0eb486c6bef7..650a4c434ca0 100644 --- a/.github/actions/gstreamer/action.yml +++ b/.github/actions/gstreamer/action.yml @@ -95,7 +95,9 @@ runs: - name: Setup Environment working-directory: ${{ runner.temp }}/gstreamer - run: echo "PKG_CONFIG_PATH=${{ inputs.install_directory }}/lib/x86_64-linux-gnu/pkgconfig:${{ inputs.install_directory }}/lib/x86_64-linux-gnu/gstreamer-1.0/pkgconfig:${{ env.PKG_CONFIG_PATH }}" >> "$GITHUB_ENV" + run: > + echo "PKG_CONFIG_PATH=${{ inputs.install_directory }}/lib/x86_64-linux-gnu/pkgconfig:${{ + inputs.install_directory }}/lib/x86_64-linux-gnu/gstreamer-1.0/pkgconfig:${{ env.PKG_CONFIG_PATH }}" >> "$GITHUB_ENV" shell: bash - name: Save artifact diff --git a/.github/actions/qt-android/action.yml b/.github/actions/qt-android/action.yml index e44941c11163..3a2283354240 100644 --- a/.github/actions/qt-android/action.yml +++ b/.github/actions/qt-android/action.yml @@ -10,7 +10,7 @@ inputs: version: description: Qt Version required: false - default: 6.8.3 + default: 6.10.1 abis: description: ABIs to Build required: false @@ -33,9 +33,10 @@ runs: - name: Setup Android Environment uses: android-actions/setup-android@v3 + id: setup-android with: - cmdline-tools-version: 12266719 - packages: 'platform-tools platforms;android-36 build-tools;36.0.0' # ndk;26.1.10909125,27.2.12479018' + cmdline-tools-version: 13114758 + packages: 'platform-tools platforms;android-35 build-tools;35.0.0' # ndk;27.2.12479018' log-accepted-android-sdk-licenses: false - name: Install Android NDK @@ -44,17 +45,21 @@ runs: with: ndk-version: r27c add-to-path: false + local-cache: true - - run: | + - name: Update Android SDK / NDK / Tools + shell: bash + run: | echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV" echo "ANDROID_NDK_HOME=${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV" echo "ANDROID_NDK=${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV" - shell: bash - - - name: Update Android SDK / NDK / Tools - if: runner.os != 'Windows' - run: sdkmanager --update - shell: bash + if [[ "$RUNNER_OS" == "Windows" ]]; then + "${ANDROID_SDK_ROOT}"/cmdline-tools/latest/bin/sdkmanager.bat --update + "${GITHUB_WORKSPACE}"/android/gradlew.bat --version + else + sdkmanager --update + "${GITHUB_WORKSPACE}"/android/gradlew --version + fi - name: Setup Caching uses: ./.github/actions/cache @@ -72,7 +77,7 @@ runs: target: desktop arch: ${{ inputs.arch }} dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -85,7 +90,7 @@ runs: target: android arch: android_arm64_v8a dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -98,7 +103,7 @@ runs: target: android arch: android_armv7 dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -111,7 +116,7 @@ runs: target: android arch: android_x86_64 dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -124,15 +129,6 @@ runs: target: android arch: android_x86 dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true - - - name: Build Info - shell: bash - run: | - if [[ "$RUNNER_OS" == "Windows" ]]; then - "${GITHUB_WORKSPACE}"/android/gradlew.bat --version - else - "${GITHUB_WORKSPACE}"/android/gradlew --version - fi diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml index 9601f0b73473..be52339f418e 100644 --- a/.github/actions/upload/action.yml +++ b/.github/actions/upload/action.yml @@ -22,7 +22,7 @@ inputs: upload_aws: description: Upload artifact to AWS required: true - default: true + default: 'true' runs: using: composite @@ -34,7 +34,7 @@ runs: path: ${{ runner.temp }}/build/${{ inputs.artifact_name }} - name: Configure AWS Credentials - if: ${{ inputs.upload_aws && github.event_name == 'push' && !github.event.pull_request.head.repo.fork && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' }} + if: ${{ fromJSON(inputs.upload_aws) && github.event_name == 'push' && github.repository_owner == 'mavlink' && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' }} uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ inputs.aws_key_id }} @@ -42,53 +42,32 @@ runs: aws-region: us-west-2 - name: Upload all push builds to S3 Bucket - if: ${{ inputs.upload_aws && github.event_name == 'push' && !github.event.pull_request.head.repo.fork && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' }} - working-directory: ${{ runner.temp }}/build/${{ inputs.source }} - run: aws s3 cp ${{ inputs.artifact_name }} s3://qgroundcontrol/builds/${{ github.ref_name }}/${{ inputs.artifact_name }} --acl public-read + if: ${{ fromJSON(inputs.upload_aws) && github.event_name == 'push' && github.repository_owner == 'mavlink' && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' }} + working-directory: ${{ runner.temp }}/build + run: aws s3 cp "${{ inputs.artifact_name }}" "s3://qgroundcontrol/builds/${{ github.ref_name }}/${{ inputs.artifact_name }}" --acl public-read shell: bash - - name: Invalidate daily/continuous push build CloudFront Cache - if: ${{ inputs.upload_aws && github.event_name == 'push' && github.ref_name == 'master' && !github.event.pull_request.head.repo.fork && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' && inputs.aws_distribution_id != '' }} - run: | - aws cloudfront create-invalidation \ - --distribution-id ${{ inputs.aws_distribution_id }} \ - --paths s3://qgroundcontrol/builds/${{ github.ref_name }}/${{ inputs.artifact_name }} - shell: bash - env: - AWS_ACCESS_KEY_ID: ${{ inputs.aws_key_id }} - AWS_SECRET_ACCESS_KEY: ${{ inputs.aws_secret_access_key }} - AWS_REGION: us-west-2 - - name: Upload tagged stable push build to S3 latest Bucket - if: ${{ inputs.upload_aws && github.event_name == 'push' && github.ref_type == 'tag' && !github.event.pull_request.head.repo.fork && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' }} - working-directory: ${{ runner.temp }}/build/${{ inputs.source }} - run: aws s3 cp ${{ inputs.artifact_name }} s3://qgroundcontrol/latest/${{ inputs.artifact_name }} --acl public-read + if: >- + ${{ fromJSON(inputs.upload_aws) && github.event_name == 'push' && + github.repository_owner == 'mavlink' && github.ref_type == 'tag' && + inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' }} + working-directory: ${{ runner.temp }}/build + run: aws s3 cp "${{ inputs.artifact_name }}" "s3://qgroundcontrol/latest/${{ inputs.artifact_name }}" --acl public-read shell: bash - name: Invalidate tagged stable push build CloudFront Cache - if: ${{ inputs.upload_aws && github.event_name == 'push' && github.ref_type == 'tag' && !github.event.pull_request.head.repo.fork && inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' && inputs.aws_distribution_id != '' }} + if: >- + ${{ fromJSON(inputs.upload_aws) && github.event_name == 'push' && + github.repository_owner == 'mavlink' && github.ref_type == 'tag' && + inputs.aws_key_id != '' && inputs.aws_secret_access_key != '' && + inputs.aws_distribution_id != '' }} run: | aws cloudfront create-invalidation \ - --distribution-id ${{ inputs.aws_distribution_id }} \ - --paths s3://qgroundcontrol/latest/${{ inputs.artifact_name }} + --distribution-id "${{ inputs.aws_distribution_id }}" \ + --paths "/latest/${{ inputs.artifact_name }}" shell: bash env: AWS_ACCESS_KEY_ID: ${{ inputs.aws_key_id }} AWS_SECRET_ACCESS_KEY: ${{ inputs.aws_secret_access_key }} AWS_REGION: us-west-2 - -# Commented out since it is 100% fail in CI -# - name: Create Continuous Release -# if: ${{ github.event_name != 'pull_request' && github.ref_name == 'master' && inputs.github_token != '' }} -# uses: Wandalen/wretry.action@master -# with: -# action: softprops/action-gh-release@v2 -# with: | -# tag_name: latest -# target_commitish: master -# files: ${{ runner.temp }}/build/${{ inputs.source }}/${{ inputs.artifact_name }} -# name: "Continuous Release" -# body: "This release is continuously updated with every commit to master." -# draft: false -# prerelease: true -# token: ${{ inputs.github_token }} diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000000..e6419818cc34 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,384 @@ +# QGroundControl Development Guide + +Ground Control Station for MAVLink-enabled UAVs supporting PX4 and ArduPilot. + +## Quick Start for AI Assistants + +**First-time here?** Start with these critical files: +1. **This file** - Architecture patterns and coding standards +2. `.github/CONTRIBUTING.md` - Contribution workflow +3. `src/FactSystem/Fact.h` - The most important pattern in QGC +4. `src/Vehicle/Vehicle.h` - Core vehicle model + +**Most Critical Pattern**: The **Fact System** handles ALL vehicle parameters. Never create custom parameter storage - always use Facts. + +**Golden Rule**: Multi-vehicle support means ALWAYS null-check `MultiVehicleManager::instance()->activeVehicle()`. + +## Tech Stack +- **C++20** with **Qt 6.10.1** (QtQml, QtQuick) +- **Build**: CMake 3.25+, Ninja +- **Protocol**: MAVLink 2.0 +- **Platforms**: Windows, macOS, Linux, Android, iOS + +## Critical Architecture Patterns + +### 1. Fact System (Parameter Management) +**Most important pattern in QGC** - All vehicle parameters use this. + +```cpp +// Access parameters (always null-check!) +Fact* param = vehicle->parameterManager()->getParameter(-1, "PARAM_NAME"); +if (param && param->validate(newValue, false).isEmpty()) { + param->setCookedValue(newValue); // Use cookedValue for UI (with units) + // param->rawValue() for MAVLink/storage +} + +// Expose to QML +Q_PROPERTY(Fact* myParam READ myParam CONSTANT) +``` + +**Key classes:** +- `Fact` - Single parameter with validation, units, metadata +- `FactGroup` - Hierarchical container (handles MAVLink via `handleMessage()`) +- `FactMetaData` - JSON-based metadata (min/max, enums, descriptions) + +**Rules:** +- Wait for `parametersReady` signal before accessing +- Use `cookedValue` (display) vs `rawValue` (storage) +- Metadata in `*.FactMetaData.json` files + +### 2. Plugin Architecture +Three types handle firmware customization: + +**FirmwarePlugin** - Firmware behavior (flight modes, capabilities) +```cpp +virtual QList flightModes() override; +virtual bool setFlightMode(const QString& mode) override; +``` + +**AutoPilotPlugin** - Vehicle setup UI (returns `VehicleComponent` list) + +**VehicleComponent** - Individual setup items (Radio, Sensors, Safety) +```cpp +virtual QString name() const override; +virtual bool setupComplete() const override; +virtual QUrl setupSource() const override; // QML UI +``` + +### 3. Manager Singletons +```cpp +// Always null-check activeVehicle (multi-vehicle support) +Vehicle* vehicle = MultiVehicleManager::instance()->activeVehicle(); +if (vehicle) { + vehicle->parameterManager()->getParameter(...); +} + +// Other managers +SettingsManager::instance()->appSettings()->... +LinkManager::instance()->... +``` + +### 4. QML/C++ Integration +```cpp +Q_OBJECT +QML_ELEMENT // Creatable in QML +QML_SINGLETON // Singleton +QML_UNCREATABLE("") // C++-only + +Q_PROPERTY(Type name READ getter WRITE setter NOTIFY signal) +Q_INVOKABLE void method(); +Q_ENUM(EnumType) +``` + +### 5. State Machines +For complex workflows (parameter loading, calibration): +```cpp +QGCStateMachine machine("Workflow", vehicle); +auto* sendCmd = new SendMavlinkCommandState("SendCmd", &machine); +auto* waitMsg = new WaitForMavlinkMessageState("WaitResponse", &machine); +sendCmd->addTransition(sendCmd, &SendMavlinkCommandState::done, waitMsg); +machine.start(); +``` + +### 6. Settings Framework +```cpp +class MySettings : public SettingsGroup { + DEFINE_SETTINGFACT(settingName) // Creates Fact with JSON metadata +}; +// Access: SettingsManager::instance()->mySettings()->settingName() +``` + +## Code Structure +``` +src/ +├── Vehicle/ # Vehicle state/comms (Vehicle.h is key) +├── FactSystem/ # Parameter management (READ THIS FIRST!) +├── FirmwarePlugin/ # PX4/ArduPilot abstraction +├── AutoPilotPlugins/ # Vehicle setup UI +├── MissionManager/ # Mission planning +├── MAVLink/ # Protocol handling +├── Comms/ # Serial/UDP/TCP/Bluetooth +├── Settings/ # Persistent settings +├── UI/ # QML UI (MainWindow.qml) +└── Utilities/ # StateMachine, helpers +``` + +## Coding Standards + +**Naming:** +- Classes/Enums: `PascalCase` +- Methods/properties: `camelCase` +- Private members: `_leadingUnderscore` +- Files: `ClassName.h`, `ClassName.cc` + +**Logging:** +```cpp +Q_DECLARE_LOGGING_CATEGORY(MyLog) +QGC_LOGGING_CATEGORY(MyLog, "qgc.component.name") +qCDebug(MyLog) << "Message:" << value; +``` + +**Defensive Coding (Critical!):** +```cpp +void method(Vehicle* vehicle) { + if (!vehicle) { + qCWarning(Log) << "Invalid vehicle"; + return; // Early return on invalid state + } + // Always use braces + if (condition) { + doSomething(); + } +} +``` + +**Code Style Tools:** +- Use `.clang-format`, `.clang-tidy`, `.editorconfig` configured in repo root +- Keep comments minimal - code should be self-documenting +- Do NOT create verbose file headers or unnecessary documentation files +- Do NOT add README files unless explicitly requested + +**Security & Dependencies:** +- Never commit secrets, API keys, or credentials +- Validate all external inputs (MAVLink messages, file uploads, user input) +- Use Qt's built-in sanitization for SQL and string operations +- When adding dependencies, check for known vulnerabilities +- Prefer Qt's built-in functionality over external libraries + +## Common Pitfalls (DO NOT!) + +1. ❌ Assume single vehicle - Always null-check `activeVehicle()` +2. ❌ Access Facts before `parametersReady` signal +3. ❌ Hardcode parameter names - Use Fact System +4. ❌ Bypass FirmwarePlugin for firmware behavior +5. ❌ Use `Q_ASSERT` in production - Use defensive checks +6. ❌ Ignore platform differences - Check `#ifdef Q_OS_*` +7. ❌ Mix cookedValue/rawValue without conversion +8. ❌ Create custom parameter storage - Use Fact System +9. ❌ Access FactGroups before `telemetryAvailable` +10. ❌ Forget to emit property signals (breaks QML bindings) + +## Build Commands +```bash +git submodule update --init --recursive +~/Qt/6.10.1/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug +cmake --build build --config Debug +./build/Debug/QGroundControl --unittest # Run tests +``` + +**Key CMake Options** (cmake/CustomOptions.cmake): +- `QGC_STABLE_BUILD` - Release vs daily +- `QGC_BUILD_TESTING` - Unit tests +- `QGC_ENABLE_BLUETOOTH` - Bluetooth support +- `QGC_DISABLE_APM_PLUGIN` / `QGC_DISABLE_PX4_PLUGIN` + +## Testing + +### Running Tests +```bash +# Run all unit tests +./build/Debug/QGroundControl --unittest + +# Run specific test +./build/Debug/QGroundControl --unittest: + +# Run with verbose output +./build/Debug/QGroundControl --unittest --logging:full +``` + +### Test Structure +- Tests mirror `src/` structure in `test/` directory +- Use `UnitTest` base class from Qt Test framework +- Mock vehicle connections when testing vehicle-dependent code +- Always test with null vehicle checks + +### Adding New Tests +```cpp +class MyComponentTest : public UnitTest { + Q_OBJECT +private slots: + void init(); // Called before each test + void cleanup(); // Called after each test + void testMyFunction(); +}; +``` + +## Troubleshooting + +### Build Issues +- **Qt not found**: Set `CMAKE_PREFIX_PATH` to Qt installation, or use qt-cmake +- **Submodule errors**: Run `git submodule update --init --recursive` +- **Missing dependencies**: Check platform-specific build instructions at https://dev.qgroundcontrol.com/ +- **CMake cache issues**: Delete `build/` directory and reconfigure + +### Runtime Issues +- **Crash on startup**: Check log files in `~/.local/share/QGroundControl/` (Linux/macOS) or `%LOCALAPPDATA%\QGroundControl` (Windows) +- **Vehicle not connecting**: Verify MAVLink protocol compatibility, check link configuration +- **Parameter load failures**: Ensure `parametersReady` signal before accessing Facts + +### Development Environment +- **Qt Creator recommended**: Import CMakeLists.txt as project +- **clangd for VSCode**: Uses `.clangd` config in repo root +- **Pre-commit hooks**: Run `pre-commit install` to enable automatic formatting + +## Performance Tips +- Batch updates: `fact->setSendValueChangedSignals(false)` +- Suppress live updates: `factGroup->setLiveUpdates(false)` +- Cache frequently accessed values +- MAVLink handled on separate thread +- Qt parent/child ownership for cleanup + +## Common Tasks + +### Working with Vehicle Parameters +```cpp +// 1. Get parameter (always null-check!) +Vehicle* vehicle = MultiVehicleManager::instance()->activeVehicle(); +if (!vehicle) return; + +Fact* param = vehicle->parameterManager()->getParameter(-1, "PARAM_NAME"); +if (!param) { + qCWarning(Log) << "Parameter not found"; + return; +} + +// 2. Validate before setting +QString error = param->validate(newValue, false); +if (!error.isEmpty()) { + qCWarning(Log) << "Invalid value:" << error; + return; +} + +// 3. Set value (cookedValue for UI with units) +param->setCookedValue(newValue); + +// 4. Listen to changes +connect(param, &Fact::valueChanged, this, [](QVariant value) { + qCDebug(Log) << "Parameter changed:" << value; +}); +``` + +### Creating a Settings Group +```cpp +// 1. Define in MySettings.h +class MySettings : public SettingsGroup { + Q_OBJECT +public: + DEFINE_SETTINGFACT(mySetting) // Creates Fact with JSON metadata +}; + +// 2. Create MySettings.SettingsGroup.json with metadata +{ + "mySetting": { + "shortDescription": "My setting", + "type": "uint32", + "default": 100, + "min": 0, + "max": 1000 + } +} + +// 3. Access anywhere +int value = SettingsManager::instance()->mySettings()->mySetting()->rawValue().toInt(); +``` + +### Adding a Vehicle Component +```cpp +// 1. Create MyComponent.h (subclass VehicleComponent) +class MyComponent : public VehicleComponent { + Q_OBJECT +public: + MyComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); + + QString name() const override { return "My Component"; } + QString description() const override { return "Component description"; } + QString iconResource() const override { return "/qmlimages/MyComponentIcon.svg"; } + bool requiresSetup() const override { return true; } + bool setupComplete() const override { return _setupComplete; } + QUrl setupSource() const override { return QUrl::fromUserInput("qrc:/qml/MyComponentSetup.qml"); } +}; + +// 2. Register in AutoPilotPlugin::getVehicleComponents() +``` + +### Handling MAVLink Messages +```cpp +// In a FactGroup or custom component +void MyFactGroup::handleMessage(Vehicle* vehicle, mavlink_message_t& message) { + switch (message.msgid) { + case MAVLINK_MSG_ID_MY_MESSAGE: { + mavlink_my_message_t msg; + mavlink_msg_my_message_decode(&message, &msg); + + // Update Facts (thread-safe via Qt signals) + myFact()->setRawValue(msg.value); + break; + } + } +} +``` + +### Adding a QML UI Component +```qml +// 1. Create MyControl.qml +import QtQuick +import QGroundControl +import QGroundControl.Controls + +QGCButton { + text: "My Action" + + property var vehicle: QGroundControl.multiVehicleManager.activeVehicle + + enabled: vehicle && vehicle.armed + + onClicked: { + if (vehicle) { + // Always null-check vehicle! + vehicle.sendMavCommand(...) + } + } +} +``` + +## Essential Files to Read +1. `.github/CONTRIBUTING.md` - Contribution guidelines +2. `src/FactSystem/Fact.h` - Parameter system (CRITICAL!) +3. `src/Vehicle/Vehicle.h` - Vehicle model +4. `src/FirmwarePlugin/FirmwarePlugin.h` - Firmware abstraction + +## Resources +- **User Manual**: https://docs.qgroundcontrol.com/ +- **Dev Guide**: https://dev.qgroundcontrol.com/ +- **MAVLink**: https://mavlink.io/ +- **Qt Docs**: https://doc.qt.io/qt-6/ + +## Memory & Threading +- Qt parent/child ownership (auto-cleanup) +- Use `deleteLater()` for objects with active signals +- `Qt::QueuedConnection` for cross-thread signals +- MAVLink on LinkManager thread, UI on main thread + +--- + +**Key Principle**: Match the style of code you're editing. Use defensive coding, validate inputs, handle errors gracefully, and respect the Fact System architecture. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 79f10533b04b..6237a6b2c8dd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,30 @@ version: 2 updates: + # Monitor GitHub Actions in workflows and all custom actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" commit-message: - prefix: "[ci] " + prefix: "[CI] " + open-pull-requests-limit: 10 + groups: + github-actions: + patterns: + - "*" + + # Monitor npm dependencies for documentation + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "[Deps] " + open-pull-requests-limit: 5 + groups: + npm-minor-patch: + patterns: + - "*" + update-types: + - "minor" + - "patch" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3bae6e52e23f..83187b3d7819 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,4 +20,4 @@ Related Issue -By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. \ No newline at end of file +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000000..e7de10f55d71 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,20 @@ +changelog: + categories: + - title: Features + labels: + - "RN: MAJOR FEATURE" + - "RN: MINOR FEATURE" + - "RN: MAJOR FEATURE - CUSTOM BUILD" + - "RN: MINOR FEATURE - CUSTOM BUILD" + - title: Improvements + labels: + - "RN: IMPROVEMENT" + - "RN: IMPROVEMENT - CUSTOM BUILD" + - "RN: REFACTORING" + - title: Fixes + labels: + - "RN: BUGFIX" + - "RN: BUGFIX - CUSTOM BUILD" + - title: Targets + labels: + - "RN: NEW BOARD SUPPORT" diff --git a/.github/workflows/android-linux.yml b/.github/workflows/android-linux.yml index d37f668fdc90..898a85cff51e 100644 --- a/.github/workflows/android-linux.yml +++ b/.github/workflows/android-linux.yml @@ -32,7 +32,7 @@ jobs: fail-fast: false matrix: build_type: [Release] - qt_version: [6.8.3] + qt_version: [6.10.1] defaults: run: @@ -49,7 +49,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -58,6 +58,14 @@ jobs: - name: Initial Setup uses: ./.github/actions/common + - name: Free Disk Space + if: runner.os == 'Linux' + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: false + large-packages: false + - name: Install Qt for Android uses: ./.github/actions/qt-android with: @@ -92,6 +100,7 @@ jobs: package_name: ${{ env.PACKAGE }} aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws_distribution_id: ${{ secrets.AWS_DISTRIBUTION_ID }} github_token: ${{ secrets.GITHUB_TOKEN }} upload_aws: true diff --git a/.github/workflows/android-macos.yml b/.github/workflows/android-macos.yml index 0eb622729800..89c208608973 100644 --- a/.github/workflows/android-macos.yml +++ b/.github/workflows/android-macos.yml @@ -37,7 +37,7 @@ jobs: env: ARTIFACT: QGroundControl.apk - QT_VERSION: 6.8.3 + QT_VERSION: 6.10.1 QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/deploy/android/android_release.keystore QT_ANDROID_KEYSTORE_ALIAS: QGCAndroidKeyStore QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -80,7 +80,7 @@ jobs: run: cmake --build . --target all --config ${{ matrix.BuildType }} --parallel - name: Save APK - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ env.ARTIFACT }} path: ${{ runner.temp }}/build/android-build/*.apk diff --git a/.github/workflows/android-windows.yml b/.github/workflows/android-windows.yml index 9eaefba13d97..aeacbc26b35d 100644 --- a/.github/workflows/android-windows.yml +++ b/.github/workflows/android-windows.yml @@ -37,7 +37,7 @@ jobs: env: ARTIFACT: QGroundControl.apk - QT_VERSION: 6.8.3 + QT_VERSION: 6.10.1 QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}\deploy\android\android_release.keystore QT_ANDROID_KEYSTORE_ALIAS: QGCAndroidKeyStore QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -80,7 +80,7 @@ jobs: run: cmake --build . --target all --config ${{ matrix.BuildType }} --parallel - name: Save APK - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ env.ARTIFACT }} path: ${{ runner.temp }}/build/android-build/*.apk diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml index ee095b609103..876007676c2c 100644 --- a/.github/workflows/cache-cleanup.yml +++ b/.github/workflows/cache-cleanup.yml @@ -15,7 +15,7 @@ jobs: contents: read steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Cleanup run: | diff --git a/.github/workflows/crowdin_docs_download.yml b/.github/workflows/crowdin_docs_download.yml index 28b7769e8780..da8aa03d8ae1 100644 --- a/.github/workflows/crowdin_docs_download.yml +++ b/.github/workflows/crowdin_docs_download.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: crowdin action uses: crowdin/github-action@v2 diff --git a/.github/workflows/crowdin_docs_upload.yml b/.github/workflows/crowdin_docs_upload.yml index ae644c809032..43a1d0ef7fac 100644 --- a/.github/workflows/crowdin_docs_upload.yml +++ b/.github/workflows/crowdin_docs_upload.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: crowdin push uses: crowdin/github-action@v2 with: diff --git a/.github/workflows/custom.yml b/.github/workflows/custom.yml index 90e9907de127..e490b07bab77 100644 --- a/.github/workflows/custom.yml +++ b/.github/workflows/custom.yml @@ -32,12 +32,12 @@ jobs: shell: cmd env: - QT_VERSION: 6.8.3 + QT_VERSION: 6.10.1 GST_VERSION: 1.22.12 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -74,7 +74,7 @@ jobs: target: desktop arch: win64_msvc2022_64 dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true diff --git a/.github/workflows/docker-linux.yml b/.github/workflows/docker-linux.yml index 1767dca13953..7db2a1d64b43 100644 --- a/.github/workflows/docker-linux.yml +++ b/.github/workflows/docker-linux.yml @@ -32,14 +32,14 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 fetch-tags: true - name: Get all tags for correct version determination - working-directory: ${{ github.workspace }} + working-directory: ${{ github.workspace }} run: git fetch --all --tags --force --depth 1 - run: chmod a+x ./deploy/docker/run-docker-ubuntu.sh diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 5864fb98b650..94ee4aad16d8 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -22,10 +22,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 20 cache: npm @@ -39,7 +39,7 @@ jobs: touch docs/.vitepress/dist/.nojekyll - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: qgc_docs_build path: docs/.vitepress/dist/ @@ -52,7 +52,7 @@ jobs: steps: - name: Download Artifact - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: qgc_docs_build path: ~/_book diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index c8fa3f9e548b..65c847a4db2e 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -8,7 +8,7 @@ jobs: image: bilelmoussaoui/flatpak-github-actions:gnome-47 options: --privileged steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v6 with: bundle: org.mavlink.qgroundcontrol.flatpak diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 816b08c56150..9abdc12ddb81 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -22,11 +22,11 @@ jobs: env: ARTIFACT: QGroundControl.app PACKAGE: QGroundControl - QT_VERSION: 6.8.3 + QT_VERSION: 6.10.1 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -56,7 +56,7 @@ jobs: target: desktop arch: clang_64 dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -83,7 +83,7 @@ jobs: run: cmake --build . --target all --config ${{ matrix.BuildType }} --parallel - name: Save App - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ env.PACKAGE }} path: ${{ runner.temp }}/build/*.app diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c173c062227f..2efb5e8513d1 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -54,11 +54,11 @@ jobs: shell: bash env: - QT_VERSION: 6.8.3 + QT_VERSION: 6.10.1 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -88,7 +88,7 @@ jobs: target: desktop arch: ${{ matrix.arch }} dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -133,4 +133,5 @@ jobs: package_name: ${{ matrix.package }} aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws_distribution_id: ${{ secrets.AWS_DISTRIBUTION_ID }} github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lupdate.yaml b/.github/workflows/lupdate.yaml index b5c9c828410f..5418aa2420b2 100644 --- a/.github/workflows/lupdate.yaml +++ b/.github/workflows/lupdate.yaml @@ -21,19 +21,18 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Qt for Linux uses: jurplel/install-qt-action@v4 with: - version: 6.8.3 + version: 6.10.1 cache: true - name: Update translation files run: | - cd translations - ${{ env.QT_ROOT_DIR }}/bin/lupdate ../src -ts qgc.ts -no-obsolete - python3 qgc-lupdate-json.py + ${{ env.QT_ROOT_DIR }}/bin/lupdate src -ts translations/qgc.ts -no-obsolete + python3 tools/translations/qgc-lupdate-json.py - name: Configure Git run: | diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a38e728cd206..3730dd03fbb9 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -34,12 +34,11 @@ jobs: shell: bash env: - QT_VERSION: 6.8.3 - QGC_MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }} + QT_VERSION: 6.10.1 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -51,7 +50,7 @@ jobs: - name: Setup Xcode uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '<=16.x' - name: Install Dependencies (include GStreamer) working-directory: ${{ github.workspace }}/tools/setup @@ -73,43 +72,46 @@ jobs: target: desktop arch: clang_64 dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true - - name: Import Code Signing Certificate - if: github.event_name != 'pull_request' - uses: apple-actions/import-codesign-certs@v5 - with: - p12-file-base64: ${{ secrets.MACOS_SIGNING_CERTS_P12 }} - p12-password: ${{ secrets.MACOS_SIGNING_CERTS_PASS }} - - - name: Configure + - name: CMake configure working-directory: ${{ runner.temp }}/build run: ${{ env.QT_ROOT_DIR }}/bin/qt-cmake -S ${{ github.workspace }} -B . -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.BuildType }} -DQGC_STABLE_BUILD=${{ github.ref_type == 'tag' || contains(github.ref, 'Stable') && 'ON' || 'OFF' }} + -DQGC_MACOS_SIGN_WITH_IDENTITY=${{ github.event_name != 'pull_request' && 'ON' || 'OFF' }} - name: Build working-directory: ${{ runner.temp }}/build run: cmake --build . --target all --config ${{ matrix.BuildType }} --parallel - name: Sanity check dev build executable - if: matrix.BuildType == 'Release' working-directory: ${{ runner.temp }}/build/Release/QGroundControl.app/Contents/MacOS run: ./QGroundControl --simple-boot-test - - name: Create DMG + - name: Import Code Signing Certificate + if: github.event_name != 'pull_request' + uses: apple-actions/import-codesign-certs@v5 + with: + p12-file-base64: ${{ secrets.MACOS_CERT_P12_BASE64 }} + p12-password: ${{ secrets.MACOS_CERT_P12_PASSWORD }} + + - name: Create signed/notarized/stapled app bundle working-directory: ${{ runner.temp }}/build run: cmake --install . --config ${{ matrix.BuildType }} + env: + QGC_MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }} + QGC_MACOS_NOTARIZATION_USERNAME: ${{ secrets.MACOS_NOTARIZATION_USERNAME }} + QGC_MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }} + QGC_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }} - name: Mount DMG - if: matrix.BuildType == 'Release' working-directory: ${{ runner.temp }}/build run: hdiutil attach QGroundControl.dmg - name: Sanity check DMG executable - if: matrix.BuildType == 'Release' working-directory: /Volumes/QGroundControl/QGroundControl.app/Contents/MacOS run: ./QGroundControl --simple-boot-test @@ -121,4 +123,5 @@ jobs: package_name: QGroundControl aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws_distribution_id: ${{ secrets.AWS_DISTRIBUTION_ID }} github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 82ed2023ca46..f484f6b15571 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,6 +6,21 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + + - name: Install Qt and qmllint + run: | + sudo apt-get update -y --quiet + sudo apt-get install python3 python3-pip pipx -y + pipx install aqtinstall + pipx ensurepath + USER_BASE_BIN="$(python3 -m site --user-base)/bin" + export PATH="$USER_BASE_BIN:$PATH" + QT_VERSION=6.10.1 + QT_ARCH=linux_gcc_64 + QT_PATH=/opt/Qt + aqt install-qt linux desktop $QT_VERSION $QT_ARCH -O $QT_PATH + echo "$QT_PATH/$QT_VERSION/gcc_64/bin" >> $GITHUB_PATH + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1e263b062c83..ddfd97eae73c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 with: days-before-stale: 30 days-before-close: -1 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 7f11ee5e04d3..25189b49d67b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -43,22 +43,25 @@ jobs: build_type: Release package: QGroundControl-installer-ARM64 + # Qt 6.10.0 for cross-compiled until aqtinstall fix is released + # See: https://github.com/miurahr/aqtinstall/pull/952 - os: windows-2022 host: windows arch: win64_msvc2022_arm64_cross_compiled build_type: Release package: QGroundControl-installer-AMD64-ARM64 + qt_version: '6.10.0' defaults: run: shell: cmd env: - QT_VERSION: 6.8.3 + QT_VERSION: 6.10.1 steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 1 @@ -85,15 +88,15 @@ jobs: build-type: ${{ matrix.build_type }} cpm-modules: ${{ runner.temp }}\build\cpm_modules - - name: Install Qt ${{ env.QT_VERSION }} + - name: Install Qt ${{ matrix.qt_version || env.QT_VERSION }} uses: jurplel/install-qt-action@v4 with: - version: ${{ env.QT_VERSION }} + version: ${{ matrix.qt_version || env.QT_VERSION }} host: ${{ matrix.host }} target: desktop arch: ${{ matrix.arch }} dir: ${{ runner.temp }} - modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors + modules: qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml setup-python: false cache: true @@ -132,5 +135,6 @@ jobs: package_name: ${{ matrix.package }} aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws_distribution_id: ${{ secrets.AWS_DISTRIBUTION_ID }} github_token: ${{ secrets.GITHUB_TOKEN }} - upload_aws: ${{ matrix.arch == 'win64_msvc2022_arm64_cross_compiled' && false || true }} + upload_aws: ${{ matrix.arch == 'win64_msvc2022_arm64_cross_compiled' && 'false' || 'true' }} diff --git a/.gitignore b/.gitignore index b200f76138e7..2e3cd5566edd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,96 +1,160 @@ +# ============================================================================== +# QGroundControl - Git Ignore Patterns +# ============================================================================== + +# ------------------------------------------------------------------------------ +# IDEs & Editors +# ------------------------------------------------------------------------------ .idea/ .vscode/ -.cache/ -cmake-build-*/ -out/ -libs/lib/Frameworks/GStreamer.framework -*_qmlcache.qrc +.qtcreator/ +.fleet/ +.vs/ +.settings/ + +# Editor temp/backup files *.swp -*.nfs -CMakeFiles -tags -build/ -obj -.DS_Store -*.log *~ *~.skp -bin/*.exe -bin/*.txt -bin/mac -*pro.user* -.qmake.stash +*.autosave +tags + +# ------------------------------------------------------------------------------ +# Build System & CMake +# ------------------------------------------------------------------------------ +build/ +cmake-build-*/ +out/ +CMakeFiles +CMakeLists.txt.user* +CMakeUserPresets.json +compile_commands.json +cpm_modules +.cache/ + +# ------------------------------------------------------------------------------ +# Qt Generated Files +# ------------------------------------------------------------------------------ +*_qmlcache.qrc qrc_*.cpp +moc_* +ui_* +*.moc +*.prl +*.qm +.qmlls.ini + +# ------------------------------------------------------------------------------ +# Compiled Objects & Binaries +# ------------------------------------------------------------------------------ +*.o *.Debug *.Release -tmp -debug -release -/qgroundcontrol -qgroundcontrol.xcodeproj/** -doc/html -doc/doxy.log -controller_log* -user_config.pri *.app -*.ncb -*.vcproj -*.vcxproj* -*.sdf -*.ipch +*.exe +*.idb *.pdb -*.sln -*.sln +tmp/ +obj/ +debug/ +release/ +/qgroundcontrol +bin/*.exe +bin/*.txt +bin/mac + +# Libraries & dynamic/static objects +*.a +*.lib +*.so +*.so.* +*.dylib +*.dll + +# Build tool artifacts +Makefile +*.ninja +.ninja_log +build.ninja + +# Packaging & distribution +*.dmg +*.deb +*.rpm +*.AppImage +*.apk +*.aab +*.ipa + +# ------------------------------------------------------------------------------ +# Platform-Specific +# ------------------------------------------------------------------------------ + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Windows - Visual Studio *.vcproj -*.user -*.ncb -*.idb -*.project -*.cproject +*.vcxproj* *.sln *.suo -*.uhf.txt +*.ncb +*.sdf +*.ipch *.opensdf -thirdParty/qserialport-build-desktop/ -thirdParty/qserialport/bin/ -thirdParty/qserialport/lib/ -libs/thirdParty/libxbee/lib/ -GeneratedFiles/ -gstreamer-1.0-android* -localization/qgroundcontrol/ - -*.autosave -.settings/ +*.uhf.txt -# iOS Generated files +# iOS ios/iOSForAppStore-Info.plist -# Generated files -moc_* -ui_* -*.o -*.moc -*.prl -.qmlls.ini - -# android +# Android android/local.properties *.class -# doxygen -src/html/ -src/latex/ +# Linux +*.nfs + +# ------------------------------------------------------------------------------ +# Dependencies & Third Party +# ------------------------------------------------------------------------------ +# Node.js (vitepress for user guide) +node_modules/ +docs/.vitepress/cache/ +docs/.vitepress/dist/ + +# Python +*.pyc +__pycache__/ + +# ------------------------------------------------------------------------------ +# Runtime & Logs +# ------------------------------------------------------------------------------ +*.log + +# Code coverage +*.gcov +*.gcda +*.gcno +*.coverage -# vagrant +# ------------------------------------------------------------------------------ +# Vagrant +# ------------------------------------------------------------------------------ .vagrant/ Qt*-linux*.tar.* -.vs/ +# ------------------------------------------------------------------------------ +# Local Development Tools +# ------------------------------------------------------------------------------ -# vitepress - user guide -node_modules/ -docs/.vitepress/cache/ -docs/.vitepress/dist/ +# Claude Code +.claude/ -CMakeUserPresets.json -cpm_modules +# cc-sessions - personal AI development workflow +sessions/ +CLAUDE.md + +# Pre-commit hooks +.ruff_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 47f7a26f76bf..62121cbb2eb5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,104 @@ +# QGroundControl Pre-Commit Configuration +# Install: pre-commit install +# Run: pre-commit run --all-files +# Update: pre-commit autoupdate + repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + # Basic file checks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 hooks: - - id: check-yaml + - id: check-yaml args: [--allow-multiple-documents, --unsafe] - - id: check-json + - id: check-json + - id: check-xml + exclude: ^test/ + - id: check-merge-conflict + - id: check-added-large-files + args: ['--maxkb=1000'] + - id: trailing-whitespace + args: ['--markdown-linebreak-ext=md'] + exclude: ^translations/ + - id: end-of-file-fixer + exclude: ^(translations/.*\.ts|.*\.(png|jpg|jpeg|gif|svg|ico|bin|dat))$ + - id: mixed-line-ending + args: ['--fix=lf'] + exclude: \.(bat|ps1)$ + + # C++ formatting (commented out - uncomment to enable) + # - repo: https://github.com/pre-commit/mirrors-clang-format + # rev: v19.1.7 + # hooks: + # - id: clang-format + # types_or: [c++, c] + # args: ['-i'] + # exclude: ^build/ + + # Python formatting + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.1 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + + # Shell scripts + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.11.0.1 + hooks: + - id: shellcheck + + # YAML validation + - repo: https://github.com/adrienverge/yamllint + rev: v1.37.1 + hooks: + - id: yamllint + args: ['-d', '{extends: default, rules: {line-length: {max: 200}, document-start: disable, truthy: {check-keys: false}, comments: {min-spaces-from-content: 1}}}'] + + # QGC-specific checks + - repo: local + hooks: + - id: qmllint + name: Lint QML files + entry: bash -c 'if command -v qmllint &>/dev/null; then qmllint "$@" || true; else echo "qmllint not found - skipping QML linting (install qt6-declarative-dev-tools or Qt SDK)"; fi' -- + language: system + files: \.qml$ + exclude: ^build/ + pass_filenames: true + + - id: check-fact-metadata + name: Validate Fact metadata JSON + entry: python3 + language: system + files: '\.FactMetaData\.json$|\.SettingsGroup\.json$' + pass_filenames: true + args: + - -c + - | + import sys, json + for f in sys.argv[1:]: + try: + with open(f) as fp: + json.load(fp) + except Exception as e: + print(f"Invalid JSON in {f}: {e}") + sys.exit(1) + + - id: check-no-qassert + name: Check for Q_ASSERT in production code + entry: bash + language: system + files: \.(h|cc|cpp)$ + exclude: ^(test/|UnitTest\.h|UnitTest\.cc) + pass_filenames: false + args: + - -c + - | + FILES=$(git diff --cached --name-only --diff-filter=d | grep -E '\.(h|cc|cpp)$' | grep -v '^test/' || true) + if [ -n "$FILES" ]; then + if echo "$FILES" | xargs grep -n 'Q_ASSERT' 2>/dev/null; then + echo "Warning: Q_ASSERT found in production code. Consider using defensive checks with early returns." + fi + fi + +exclude: ^build/ diff --git a/.qmllint.ini b/.qmllint.ini new file mode 100644 index 000000000000..053b125a7e06 --- /dev/null +++ b/.qmllint.ini @@ -0,0 +1,95 @@ +# QGroundControl QML Linting Configuration +# Requires Qt 6.10+ for "error" level support +# See: https://doc.qt.io/qt-6/qtquick-tool-qmllint.html + +[General] +DisableDefaultImports=false +MaxWarnings=-1 + +[Warnings] +# Critical errors that should always be caught +AccessSingletonViaObject=warning +AliasCycle=warning +AssignmentInCondition=warning +BadSignalHandlerParameters=warning +DuplicateEnumEntries=warning +DuplicateImport=warning +DuplicateInlineComponent=warning +DuplicatePropertyBinding=warning +DuplicatedName=warning +# Disabled - QGC has custom modules that aren't available during linting +ImportFailure=disable +IncompatibleType=warning +InheritanceCycle=warning +# Disabled - QGC custom types have properties not known to qmllint +MissingProperty=disable +MissingType=disable +NonListProperty=warning +ReadOnlyProperty=warning +UncreatableType=warning +UnqualifiedAccess=warning +UnreachableCode=warning +UnresolvedAlias=warning +UnresolvedType=warning + +# Warnings that improve code quality +Comma=warning +ComponentChildrenCount=warning +ConfusingExpressionStatement=warning +ConfusingMinuses=warning +ConfusingPluses=warning +Deprecated=warning +EnumEntryMatchesEnum=warning +EnumsAreNotTypes=warning +EqualityTypeCoercion=warning +Eval=warning +InvalidLintDirective=warning +LiteralConstructor=warning +MissingEnumEntry=warning +PreferNonVarProperties=warning +PrefixedImportType=warning +RedundantOptionalChaining=warning +RequiredProperty=warning +RestrictedType=warning +StalePropertyRead=warning +TopLevelComponent=warning +TranslationFunctionMismatch=warning +UnintentionalEmptyBlock=warning +UnterminatedCase=warning +UseProperFunction=warning +VarUsedBeforeDeclaration=warning +WithStatement=warning + +# Info-level (non-blocking) +MultilineStrings=info +UnusedImports=info + +# Disabled (too noisy or not applicable for QGC) +AttachedPropertyReuse=disable +CompilerWarnings=disable +# QGC uses context properties extensively +ContextProperties=disable +FunctionUsedBeforeDeclaration=disable +ImportFileSelector=disable +LintPluginWarnings=disable +Void=disable + +# Qt Quick specific warnings +Quick.Anchors=warning +Quick.AttachedPropertyReuse=disable +Quick.AttachedPropertyType=warning +Quick.Color=warning +Quick.ControlsAttachedPropertyReuse=disable +Quick.ControlsNativeCustomize=warning +Quick.LayoutsPositioning=warning +Quick.PropertyChangesParsed=warning +Quick.StateNoChildItem=warning +Quick.UnexpectedVarType=warning + +# Qt Design Studio (not used by QGC) +QtDesignStudio.FunctionsNotSupportedInQmlUi=disable +QtDesignStudio.ImperativeCodeNotEditableInVisualDesigner=disable +QtDesignStudio.InvalidIdeInVisualDesigner=disable +QtDesignStudio.ReferenceToParentItemNotSupportedByVisualDesigner=disable +QtDesignStudio.UnsupportedRootTypeInQmlUi=disable +QtDesignStudio.UnsupportedTypeInQmlUi=disable diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..a212caa12fd6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,85 @@ +# QGroundControl Quick Reference for AI Assistants + +**Ground Control Station** for UAVs using MAVLink protocol. **C++20/Qt 6.10.1** with QML UI. + +## 🔑 Most Critical Architecture Pattern + +**Fact System** - QGC's type-safe parameter management. Every vehicle parameter uses it. + +```cpp +// ALWAYS use this pattern for parameters +Fact* param = vehicle->parameterManager()->getParameter(-1, "PARAM_NAME"); +if (param) { + param->setCookedValue(newValue); // cookedValue = UI (with units) + // param->rawValue() = MAVLink/storage +} +``` + +**Rules:** +- Wait for `parametersReady` signal before accessing +- Use cookedValue (display) vs rawValue (storage) +- Never create custom parameter storage + +## 🏗️ Key Patterns + +1. **Plugins**: FirmwarePlugin (PX4/ArduPilot behavior), AutoPilotPlugin (setup UI), VehicleComponent (individual items) +2. **Managers**: Singleton pattern - `MultiVehicleManager::instance()->activeVehicle()` (always null-check!) +3. **QML Integration**: `QML_ELEMENT`, `Q_PROPERTY`, `Q_INVOKABLE` +4. **State Machines**: Use `QGCStateMachine` for complex workflows (calibration, parameter loading) + +## 📂 Code Structure + +``` +src/ +├── FactSystem/ # Parameter management (READ FIRST!) +├── Vehicle/ # Vehicle state (Vehicle.h is critical) +├── FirmwarePlugin/ # PX4/ArduPilot abstraction +├── AutoPilotPlugins/ # Vehicle setup UI +├── MissionManager/ # Mission planning +├── Comms/ # Serial/UDP/TCP/Bluetooth links +``` + +## ⚡ Quick Build + +```bash +git submodule update --init --recursive +~/Qt/6.10.1/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug +cmake --build build --config Debug +./build/Debug/QGroundControl --unittest # Run tests +``` + +## ❌ Common Mistakes (DO NOT!) + +1. Assume single vehicle (always null-check `activeVehicle()`) +2. Access Facts before `parametersReady` +3. Use `Q_ASSERT` in production code +4. Bypass FirmwarePlugin for firmware-specific behavior +5. Mix cookedValue/rawValue without conversion + +## 📖 Essential Reading + +1. **`.github/copilot-instructions.md`** - Detailed architecture guide +2. **`.github/CONTRIBUTING.md`** - Contribution guidelines +3. **`src/FactSystem/Fact.h`** - Parameter system (MOST CRITICAL!) +4. **`src/Vehicle/Vehicle.h`** - Vehicle model (~1477 lines) + +## 🧑‍💻 Coding Style + +- **Naming**: Classes `PascalCase`, methods `camelCase`, privates `_leadingUnderscore` +- **Files**: `ClassName.h`, `ClassName.cc` +- **Defensive**: Always validate inputs, null-check pointers, early returns +- **Logging**: `QGC_LOGGING_CATEGORY(MyLog, "qgc.component")` + `qCDebug(MyLog)` +- **Braces**: Always use, even for single-line if statements +- **Formatting**: `.clang-format`, `.clang-tidy`, `.editorconfig` configured in repo root +- **Comments**: Keep minimal - code should be self-documenting. No verbose headers or documentation files unless explicitly requested. + +## 🔗 Resources + +- **Dev Guide**: https://dev.qgroundcontrol.com/ +- **User Docs**: https://docs.qgroundcontrol.com/ +- **MAVLink**: https://mavlink.io/ +- **Qt 6**: https://doc.qt.io/qt-6/ + +--- + +**Key Principle**: Match existing code style. Use defensive coding. Respect the Fact System architecture. diff --git a/CMakeLists.txt b/CMakeLists.txt index 637e0f285ec0..790fd4b4acb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,60 +1,90 @@ +# ============================================================================ +# QGroundControl CMake Build Configuration +# ============================================================================ + cmake_minimum_required(VERSION 3.25) +# ---------------------------------------------------------------------------- +# CMake Module Paths +# ---------------------------------------------------------------------------- list(APPEND CMAKE_MODULE_PATH - ${CMAKE_SOURCE_DIR}/cmake - ${CMAKE_SOURCE_DIR}/cmake/CPack - ${CMAKE_SOURCE_DIR}/cmake/find-modules - ${CMAKE_SOURCE_DIR}/cmake/install - ${CMAKE_SOURCE_DIR}/cmake/install/CPack - ${CMAKE_SOURCE_DIR}/cmake/modules - ${CMAKE_SOURCE_DIR}/cmake/platform + "${CMAKE_SOURCE_DIR}/cmake" + "${CMAKE_SOURCE_DIR}/cmake/CPack" + "${CMAKE_SOURCE_DIR}/cmake/find-modules" + "${CMAKE_SOURCE_DIR}/cmake/install" + "${CMAKE_SOURCE_DIR}/cmake/install/CPack" + "${CMAKE_SOURCE_DIR}/cmake/modules" + "${CMAKE_SOURCE_DIR}/cmake/platform" ) +# Include helper functions early include(Helpers) +# ---------------------------------------------------------------------------- +# Default Build Type +# ---------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) - set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configuration types" FORCE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (Debug or Release)" FORCE) + set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Available configuration types" FORCE) + message(STATUS "No build type specified. Defaulting to Release.") endif() +# ---------------------------------------------------------------------------- +# CMake Policy Configuration +# ---------------------------------------------------------------------------- set(CMAKE_REQUIRED_QUIET ON) set(CMAKE_POLICY_VERSION_MINIMUM ${CMAKE_MINIMUM_REQUIRED_VERSION}) -####################################################### -# Custom Build Configuration -####################################################### +# ============================================================================ +# Custom Build Configuration +# ============================================================================ +# Load default options and allow custom builds to override include(CustomOptions) if(IS_DIRECTORY "${CMAKE_SOURCE_DIR}/custom") - message(STATUS "QGC: Enabling custom build") + message(STATUS "QGC: Custom build directory detected") set(QGC_CUSTOM_BUILD ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/custom/cmake") include(CustomOverrides) endif() +# Disable testing if not explicitly enabled if(NOT QGC_BUILD_TESTING) - set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE) + set(BUILD_TESTING OFF CACHE INTERNAL "Disable testing by default" FORCE) endif() -####################################################### -# Project Info -####################################################### +# ============================================================================ +# Project Configuration +# ============================================================================ +# ---------------------------------------------------------------------------- +# Apple-Specific Pre-Project Settings +# ---------------------------------------------------------------------------- if(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") - # set(CMAKE_OSX_SYSROOT "iphoneos") + # iOS builds: set(CMAKE_OSX_SYSROOT "iphoneos") if(QGC_MACOS_UNIVERSAL_BUILD) - set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") + # Universal binary for both Intel and Apple Silicon + set(CMAKE_OSX_ARCHITECTURES "x86_64h;arm64") endif() endif() +# ---------------------------------------------------------------------------- +# Git Integration & Version Extraction +# ---------------------------------------------------------------------------- include(Git) +# ---------------------------------------------------------------------------- +# Compiler Caching Configuration +# ---------------------------------------------------------------------------- if(QGC_USE_CACHE) qgc_config_caching() endif() +# ---------------------------------------------------------------------------- +# Project Declaration +# ---------------------------------------------------------------------------- project(${QGC_APP_NAME} VERSION ${QGC_APP_VERSION} DESCRIPTION ${QGC_APP_DESCRIPTION} @@ -62,48 +92,65 @@ project(${QGC_APP_NAME} LANGUAGES C CXX ) -####################################################### -# CMake Configuration Options -####################################################### +# ============================================================================ +# CMake & Build System Configuration +# ============================================================================ +# Standard CMake modules include(GNUInstallDirs) include(FetchContent) include(CMakePrintHelpers) -if(EXISTS "${QGC_CPM_SOURCE_CACHE}") - set(ENV{CPM_SOURCE_CACHE} "${QGC_CPM_SOURCE_CACHE}") -else() - set(ENV{CPM_SOURCE_CACHE} "${CMAKE_BINARY_DIR}/cpm_modules") -endif() +# ---------------------------------------------------------------------------- +# CPM (CMake Package Manager) Configuration +# ---------------------------------------------------------------------------- include(CPM) +if(NOT CPM_SOURCE_CACHE) + set(CPM_SOURCE_CACHE "${CMAKE_BINARY_DIR}/cpm_modules") +endif() +# ---------------------------------------------------------------------------- +# Toolchain Configuration (Compiler, Linker, Build Settings) +# ---------------------------------------------------------------------------- include(Toolchain) +# ---------------------------------------------------------------------------- +# Output Directories +# ---------------------------------------------------------------------------- set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$/lib") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") +if(ANDROID) + set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") +else() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") +endif() -# https://cmake.org/cmake/help/latest/policy/CMP0168.html#policy:CMP0168 +# ---------------------------------------------------------------------------- +# CMake Policies +# ---------------------------------------------------------------------------- +# CMP0168: Modern FetchContent integration if(POLICY CMP0168) cmake_policy(SET CMP0168 NEW) set(CMAKE_POLICY_DEFAULT_CMP0168 NEW) endif() -# https://cmake.org/cmake/help/latest/policy/CMP0141.html#policy:CMP0141 +# CMP0141: MSVC debug information format if(POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_POLICY_DEFAULT_CMP0141 NEW) endif() -# https://cmake.org/cmake/help/latest/policy/CMP0075.html#policy:CMP0075 +# CMP0075: Include files in CheckIncludeFile if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) set(CMAKE_POLICY_DEFAULT_CMP0075 NEW) endif() -####################################################### -# Qt6 Configuration -####################################################### +# ============================================================================ +# Qt6 Configuration +# ============================================================================ + +set(QT_NO_PRIVATE_MODULE_WARNING ON) find_package(Qt6 ${QGC_QT_MINIMUM_VERSION}...${QGC_QT_MAXIMUM_VERSION} @@ -116,6 +163,7 @@ find_package(Qt6 Gui LinguistTools Location + LocationPrivate Multimedia Network Positioning @@ -130,6 +178,7 @@ find_package(Qt6 TextToSpeech Widgets Xml + StateMachine OPTIONAL_COMPONENTS Bluetooth MultimediaQuickPrivate @@ -149,55 +198,73 @@ qt_standard_project_setup( I18N_SOURCE_LANGUAGE en ) +# ---------------------------------------------------------------------------- +# QML Linting Configuration +# ---------------------------------------------------------------------------- +if(QGC_ENABLE_QMLLINT) + set(QT_QML_GENERATE_QMLLINT ON CACHE BOOL "Enable automatic QML linting" FORCE) +else() + set(QT_QML_GENERATE_QMLLINT OFF CACHE BOOL "Disable automatic QML linting" FORCE) +endif() + +# ---------------------------------------------------------------------------- +# Qt Policies (Enable modern Qt6 behaviors) +# ---------------------------------------------------------------------------- qt_policy( - SET QTP0001 NEW - SET QTP0002 NEW - SET QTP0003 NEW - SET QTP0004 NEW - SET QTP0005 NEW + SET QTP0001 NEW # Modern resource handling + SET QTP0002 NEW # New behavior for QML module URI + SET QTP0003 NEW # Better QML type registration + SET QTP0004 NEW # Improved translation handling + SET QTP0005 NEW # Enhanced deployment handling ) -####################################################### -# Custom Build Configuration -####################################################### +# ============================================================================ +# Custom Build Subdirectory +# ============================================================================ if(QGC_CUSTOM_BUILD) add_subdirectory(custom) endif() -####################################################### -# QGroundControl Resources -####################################################### +# ============================================================================ +# QGroundControl Resources +# ============================================================================ -# Note: Adding Resources to Library instead requires using Q_INIT_RESOURCE(qgcresources) +# Note: Resources added to executable (not library) to avoid needing Q_INIT_RESOURCE() list(APPEND QGC_RESOURCES - ${CMAKE_SOURCE_DIR}/qgcimages.qrc - ${CMAKE_SOURCE_DIR}/qgcresources.qrc + "${CMAKE_SOURCE_DIR}/qgcimages.qrc" + "${CMAKE_SOURCE_DIR}/qgcresources.qrc" + "${CMAKE_SOURCE_DIR}/resources/InstrumentValueIcons/InstrumentValueIcons.qrc" ) -list(APPEND QGC_RESOURCES - ${CMAKE_SOURCE_DIR}/resources/InstrumentValueIcons/InstrumentValueIcons.qrc -) - -####################################################### -# QGroundControl Target -####################################################### +# ============================================================================ +# QGroundControl Executable Target +# ============================================================================ +# Create the main executable qt_add_executable(${CMAKE_PROJECT_NAME} - WIN32 - MACOSX_BUNDLE + WIN32 # Windows GUI application + MACOSX_BUNDLE # macOS application bundle ${QGC_RESOURCES} ) +# ---------------------------------------------------------------------------- +# Platform-Specific Configuration +# ---------------------------------------------------------------------------- if(WIN32) include(Windows) elseif(APPLE) include(Apple) elseif(ANDROID) include(Android) +elseif(LINUX) + include(Linux) endif() +# ---------------------------------------------------------------------------- +# QML Module Configuration +# ---------------------------------------------------------------------------- qt_add_qml_module(${CMAKE_PROJECT_NAME} URI QGC VERSION 1.0 @@ -209,13 +276,21 @@ qt_add_qml_module(${CMAKE_PROJECT_NAME} QtQuick.Layouts ) +# ---------------------------------------------------------------------------- +# Source & Test Subdirectories +# ---------------------------------------------------------------------------- add_subdirectory(src) + if(QGC_BUILD_TESTING) add_subdirectory(test) + # Exclude test directory from translation scanning set_property(DIRECTORY test PROPERTY QT_EXCLUDE_FROM_TRANSLATION ON) endif() -file(GLOB TS_SOURCES ${CMAKE_SOURCE_DIR}/translations/qgc_*.ts) +# ---------------------------------------------------------------------------- +# Translation/Localization Configuration +# ---------------------------------------------------------------------------- +file(GLOB TS_SOURCES "${CMAKE_SOURCE_DIR}/translations/qgc_*.ts") set_source_files_properties(${TS_SOURCES} PROPERTIES OUTPUT_LOCATION "${CMAKE_BINARY_DIR}/i18n") qt_add_translations(${CMAKE_PROJECT_NAME} @@ -224,24 +299,50 @@ qt_add_translations(${CMAKE_PROJECT_NAME} LUPDATE_OPTIONS -no-obsolete TS_FILE_DIR "${CMAKE_SOURCE_DIR}/translations" TS_FILE_BASE ${CMAKE_PROJECT_NAME} + # TS_FILES_OUTPUT_VARIABLE _ts_files_generated + # TS_OUTPUT_DIRECTORY "{CMAKE_SOURCE_DIR}/translations" + # QM_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/i18n" + # MERGE_QT_TRANSLATIONS ) -qgc_set_qt_resource_alias(${CMAKE_SOURCE_DIR}/resources/qtquickcontrols2.conf) +# ---------------------------------------------------------------------------- +# Qt Quick Controls Configuration +# ---------------------------------------------------------------------------- +qgc_set_qt_resource_alias("${CMAKE_SOURCE_DIR}/resources/qtquickcontrols2.conf") qt_add_resources(${CMAKE_PROJECT_NAME} "qgcresources_cmake" PREFIX "/" - FILES - "${CMAKE_SOURCE_DIR}/resources/qtquickcontrols2.conf" + FILES "${CMAKE_SOURCE_DIR}/resources/qtquickcontrols2.conf" ) -# cmake_print_variables(QT_ALL_PLUGIN_TYPES_FOUND_VIA_FIND_PACKAGE) +# ---------------------------------------------------------------------------- +# Qt Plugin Configuration +# ---------------------------------------------------------------------------- qt_import_plugins(${CMAKE_PROJECT_NAME} INCLUDE Qt6::QSvgPlugin - EXCLUDE_BY_TYPE geoservices + EXCLUDE_BY_TYPE geoservices # Use built-in location plugins INCLUDE_BY_TYPE sqldrivers Qt6::QSQLiteDriverPlugin - # INCLUDE_BY_TYPE styles Qt6::qtquickcontrols2basicstyleplugin Qt6::qtquickcontrols2basicstyleimplplugin ) +# Linux-specific Qt platform plugins +if(LINUX) + qt_import_plugins(${CMAKE_PROJECT_NAME} + INCLUDE + Qt6::QWaylandIntegrationPlugin # Wayland support + Qt6::QXcbIntegrationPlugin # X11 support + Qt6::QEglFSIntegrationPlugin # Embedded GL support + Qt6::QWaylandEglPlatformIntegrationPlugin + ) +endif() + +# ============================================================================ +# Installation & Packaging +# ============================================================================ + include(Install) +# ============================================================================ +# Build Summary +# ============================================================================ + include(PrintSummary) diff --git a/_CMakePresets.json b/CMakePresets.json.template similarity index 100% rename from _CMakePresets.json rename to CMakePresets.json.template diff --git a/CodingStyle.cc b/CodingStyle.cc index d50968fd0c7f..762d255a8745 100644 --- a/CodingStyle.cc +++ b/CodingStyle.cc @@ -1,6 +1,6 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT + * (c) 2009-2024 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. @@ -12,84 +12,153 @@ // Not all style choices are explained. #include "CodingStyle.h" + +#include + +#include +#include + #include "QGCApplication.h" +#include "QGCLoggingCategory.h" +#include "Vehicle.h" -#include -#include +// Note how the Qt headers, System headers, and the QGroundControl headers above are kept in separate groups +// with blank lines between them. Within each group, headers are sorted alphabetically. -#include +// Use QGC_LOGGING_CATEGORY instead of Q_LOGGING_CATEGORY for runtime log configuration support +QGC_LOGGING_CATEGORY(CodingStyleLog, "Example.CodingStyle") -// Note how the Qt headers and the QGroundControl headers above are kept separate +CodingStyle::CodingStyle(QObject* parent) + : QObject(parent) +{ + // Constructor body - use member initializer list above for initialization + _commonInit(); +} -Q_LOGGING_CATEGORY(CodingStyleLog, "CodingStyleLog") +CodingStyle::~CodingStyle() +{ + // Cleanup code here + // Qt parent/child ownership handles most cleanup automatically +} -const int CodingStyle::_privateStaticVariable = 0; +void CodingStyle::_commonInit() +{ + // Common initialization code + qCDebug(CodingStyleLog) << "CodingStyle initialized"; +} -CodingStyle::CodingStyle(QObject* parent) : - QObject(parent), - _protectedVariable(1), - _privateVariable1(2), - _privateVariable2(3) +void CodingStyle::setExampleProperty(int value) { + if (_exampleProperty != value) { + _exampleProperty = value; + emit examplePropertyChanged(value); // Always emit signals when properties change + } +} +bool CodingStyle::publicMethod1() +{ + // Implementation here + return true; +} + +void CodingStyle::performAction(const QString& param) +{ + // Defensive coding: validate inputs early + if (param.isEmpty()) { + qCWarning(CodingStyleLog) << "performAction called with empty parameter"; + return; + } + + // Always null-check pointers before dereferencing + if (!_vehicle) { + qCWarning(CodingStyleLog) << "No vehicle available"; + return; + } + + qCDebug(CodingStyleLog) << "performAction:" << param; } /// Document non-obvious private methods in the code file. -void CodingStyle::_privateMethod(void) +void CodingStyle::_privateMethod() { - // Always include braces even for single line if/for/... - if (uas != _uasId) { + // Defensive coding example: validate preconditions and return early on errors + if (!_vehicle) { + qCWarning(CodingStyleLog) << "Vehicle not set, cannot proceed"; return; } - + + // Always include braces even for single line if/for/while/etc + if (_exampleProperty == 0) { + return; + } + // Note the brace placement - if (_lastSeenComponent == -1) { - _lastSeenComponent = component; + if (_privateVariable1 == -1) { + _privateVariable1 = 42; } else { - Q_ASSERT(component == _lastSeenComponent); // Asserts are your friend + // Use defensive checks instead of Q_ASSERT in production code + // Q_ASSERT is compiled out in release builds + if (_privateVariable1 != 42) { + qCWarning(CodingStyleLog) << "Unexpected value:" << _privateVariable1; + } } } -void CodingStyle::_privateMethod2(void) +void CodingStyle::_privateSlot() { + // Example: handling Fact value changes Fact* fact = qobject_cast(sender()); - Q_ASSERT(fact); - + if (!fact) { + qCWarning(CodingStyleLog) << "Invalid sender in _privateSlot"; + return; + } + + // Use Fact System properly: cookedValue for display, rawValue for storage + const QVariant cookedValue = fact->cookedValue(); + const QVariant rawValue = fact->rawValue(); + + qCDebug(CodingStyleLog) << "Fact changed:" << fact->name() + << "cooked:" << cookedValue + << "raw:" << rawValue; + + // Example switch statement with proper formatting QVariant typedValue; switch (fact->type()) { case FactMetaData::valueTypeInt8: case FactMetaData::valueTypeInt16: case FactMetaData::valueTypeInt32: - typedValue.setValue(QVariant(value.toInt())); + typedValue.setValue(cookedValue.toInt()); break; - case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint32: - typedValue.setValue(value.toUInt()); + typedValue.setValue(cookedValue.toUInt()); break; - case FactMetaData::valueTypeFloat: { - int localScopedVar = 1; - typedValue.setValue(value.toFloat()); - } + // Use braces for local variable scope in case statements + const int localScopedVar = 1; + Q_UNUSED(localScopedVar); // OK to use Q_UNUSED for intentionally unused variables + typedValue.setValue(cookedValue.toFloat()); break; - + } case FactMetaData::valueTypeDouble: - typedValue.setValue(value.toDouble()); + typedValue.setValue(cookedValue.toDouble()); + break; + default: + qCWarning(CodingStyleLog) << "Unhandled fact type:" << fact->type(); break; } } -void CodingStyle::_methodWithManyArguments(QWidget* parent, - const QString& caption, - const QString& dir, - Options options1, - Options /* options2 */, - Options options3) +void CodingStyle::_methodWithManyArguments( + QObject* parent, + const QString& caption, + const QString& dir, + int options1, + int /* options2 */, // Unused arguments: comment out name but keep type + int options3) { - // options2 is an unused method argument. - // Do not use Q_UNUSUED and do not just remove the argument name and leave the type. - - // Implementataion here... + // Do not use Q_UNUSED for method parameters - comment out the parameter name instead + // This makes it clear the parameter is intentionally unused + // Implementation here... } diff --git a/CodingStyle.h b/CodingStyle.h index 22300e5e2657..c06255c0340e 100644 --- a/CodingStyle.h +++ b/CodingStyle.h @@ -1,6 +1,6 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT + * (c) 2009-2024 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. @@ -13,16 +13,22 @@ #pragma once -#include -#include -#include -#include - #include +#include +#include +#include +#include + #include "Fact.h" +#include "Vehicle.h" -// Note how the Qt headers, System, headers and the QGroundControl headers above are kept in separate groups +// Note how the Qt headers, System headers and the QGroundControl headers above are kept in separate groups +// with blank lines between them. Within each group, headers are sorted alphabetically. +// Use full paths for Qt headers (e.g., QtCore/QObject not QObject). + +// Forward declarations for classes that are only used as pointers/references +class Vehicle; // If you are going to use a logging category for a class it should have the same name as the class // with a suffix of Log. @@ -30,50 +36,90 @@ Q_DECLARE_LOGGING_CATEGORY(CodingStyleLog) /// Here is the class documentation. Class names are PascalCase. If you override any of the Qt base classes to provide /// generic base implementations for widespread use prefix the class name with QGC. For example: -/// QGCMessageBox - is a QGC special vesion of Qt MessageBox -/// QGCPalette - is a QGC special version of Qt Palette -/// For normal single use classes do no prefix them name with QGC. +/// For normal single use classes do not prefix the name with QGC. +/// +/// Qt6 QML Integration: Use QML_ELEMENT for QML-creatable types, QML_SINGLETON for singletons, +/// and QML_UNCREATABLE("reason") for C++-only instantiation. class CodingStyle : public QObject { Q_OBJECT - + QML_ELEMENT + QML_UNCREATABLE("") + Q_MOC_INCLUDE("Vehicle.h") // Use Q_MOC_INCLUDE for forward-declared types used in Q_PROPERTY + /// Q_PROPERTY definitions for QML binding + /// Format: Q_PROPERTY(Type name READ getter WRITE setter NOTIFY signal) + /// Use CONSTANT for properties that never change + /// Use MEMBER for direct member access (rarely needed) + Q_PROPERTY(int exampleProperty READ exampleProperty WRITE setExampleProperty NOTIFY examplePropertyChanged) + Q_PROPERTY(Vehicle* vehicle READ vehicle CONSTANT) + Q_PROPERTY(bool readOnlyProperty READ readOnlyProperty NOTIFY readOnlyPropertyChanged) public: - CodingStyle(QObject* parent = NULL); - ~CodingStyle(); - + explicit CodingStyle(QObject* parent = nullptr); // Use nullptr, not NULL + ~CodingStyle() override; // Use override keyword for virtual destructors + + // Enums exposed to QML must use Q_ENUM + enum class ExampleEnum { + EnumValue1, + EnumValue2, + EnumValue3, + }; + Q_ENUM(ExampleEnum) + /// Document public methods which are non-obvious in the header file - bool publicMethod1(void); - + /// Use Q_INVOKABLE for methods callable from QML + Q_INVOKABLE bool publicMethod1(); + Q_INVOKABLE void performAction(const QString& param); + + // Public getters/setters + int exampleProperty() const { return _exampleProperty; } + void setExampleProperty(int value); + Vehicle* vehicle() const { return _vehicle; } + bool readOnlyProperty() const { return _readOnlyProperty; } + signals: /// Document signals which are non-obvious in the header file - void qtSignal(void); - + /// Signals should be emitted when properties change to maintain QML bindings + void examplePropertyChanged(int newValue); + void readOnlyPropertyChanged(); + void qtSignal(); + public slots: - // Public slots should only be used if the slot is connected to from another class. Majority of slots - // should be private. - void publicSlot(void); - + // Public slots should only be used if the slot is connected to from another class. + // Most slots should be private. Prefer signals/slots over direct method calls for loose coupling. + void publicSlot(); + // Don't use protected methods or variables unless the class is specifically meant to be used as a base class. protected: - int _protectedVariable; ///< variable names are camelCase - - void _protectedMethod(void); ///< method names are camelCase - + int _protectedVariable = 0; ///< variable names are camelCase + + void _protectedMethod(); ///< method names are camelCase + private slots: - void _privateSlot(void); - + void _privateSlot(); + private: // Private methods and variable names begin with an "_". Documentation for - // non-obvious private methods goes in the code file, not the header. - void _privateMethod(void); - - void _methodWithManyArguments(QWidget* parent, const QString& caption, const QString& dir, Options options1, Options options2, Options options3); + // non-obvious private methods goes in the header file. + void _privateMethod(); + void _commonInit(); + + // For methods with many arguments, align parameters vertically + void _methodWithManyArguments( + QObject* parent, + const QString& caption, + const QString& dir, + int options1, + int options2, + int options3); /// Document non-obvious variables in the header file. Long descriptions go here. - int _privateVariable1; - - int _privateVariable2; ///< Short descriptions go here - - static const int _privateStaticVariable; + int _exampleProperty = 0; + bool _readOnlyProperty = false; + Vehicle* _vehicle = nullptr; + + int _privateVariable1 = 2; ///< Short descriptions go here + int _privateVariable2 = 3; + + static constexpr int _privateStaticVariable = 42; // Use constexpr for compile-time constants }; diff --git a/CodingStyle.qml b/CodingStyle.qml index 5959a4a66635..9bf08700bbdf 100644 --- a/CodingStyle.qml +++ b/CodingStyle.qml @@ -1,64 +1,182 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT + * (c) 2009-2024 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ - import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QGroundControl - import QGroundControl.Controls - /// This is an example Qml file which is used to describe the QGroundControl coding style. /// In general almost everything in here has some coding style meaning including order of -/// code. Not all style choices are explained. If there is any confusison please ask +/// code. Not all style choices are explained. If there is any confusion please ask /// and we'll answer and update style as needed. +/// +/// Qt6 Import Style: Use unversioned imports (QtQuick not QtQuick 2.15) +/// Import order: Qt modules, blank line, QGroundControl modules Item { - // Property binding to item properties + id: root // Use descriptive id for root item, not underscore prefix + + // =================================================================================== + // PROPERTY BINDING SECTION + // Bind to item properties first, before property definitions + // =================================================================================== + width: ScreenTools.defaultFontPixelHeight * 10 // No hardcoded sizing. All sizing must be relative to a ScreenTools font size height: ScreenTools.defaultFontPixelHeight * 20 + // =================================================================================== + // PUBLIC PROPERTIES SECTION // Property definitions available to consumers of this Qml Item come first - property int myIntProperty: 10 - property real myRealProperty: 20.0 + // Group by type for readability + // =================================================================================== + + property int myIntProperty: 10 + property real myRealProperty: 20.0 + property string myStringProperty: "example" + property bool myBoolProperty: false + + // =================================================================================== + // PRIVATE PROPERTIES SECTION + // Property definitions which are internal to the item are prepended with an underscore + // to signal private and come second. Use readonly appropriately to increase binding performance. + // =================================================================================== - // Property definitions which are internal to the item are prepending with an underscore - // to signal private and come second - readonly property real _rectWidth: ScreenTools.defaultFontPixelWidth * 10 // Use readonly appropriately to increase binding performance + readonly property real _rectWidth: ScreenTools.defaultFontPixelWidth * 10 readonly property real _rectHeight: ScreenTools.defaultFontPixelWidth * 10 + readonly property bool _debugMode: false + readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + // =================================================================================== + // SIGNALS SECTION + // Define custom signals for communication + // =================================================================================== + + signal buttonClicked() + signal valueChanged(int newValue) + + // =================================================================================== + // FUNCTIONS SECTION + // Function definitions come after properties + // =================================================================================== function myFunction() { console.log("myFunction was called") } + function _privateFunction() { + // Private functions prefixed with underscore + if (_activeVehicle) { + console.log("Active vehicle:", _activeVehicle.id) + } + } + + function calculateSomething(param1, param2) { + // Always validate parameters + if (!param1 || !param2) { + console.warn("Invalid parameters") + return 0 + } + return param1 * param2 + } + + // =================================================================================== + // VISUAL COMPONENTS SECTION + // Child items come last + // =================================================================================== + + // Use QGCPalette for all color theming - no hardcoded colors QGCPalette { id: qgcPal // Note how id does not use an underscore colorGroupEnabled: enabled } - // You should always use the QGC provided variants of base control since they automatically support - // our theming and font support. - QGCButton { - // Also not how there is no id: specified for this control. Only add id: if it is needed. - text: "Button" + // Use ColumnLayout or RowLayout for better organization when appropriate + ColumnLayout { + anchors.fill: parent + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + + // You should always use the QGC provided variants of base controls since they automatically support + // our theming and font support (QGCButton, QGCLabel, QGCTextField, QGCCheckBox, etc.) + QGCButton { + // Note how there is no id: specified for this control. Only add id: if it is needed. + Layout.fillWidth: true + text: qsTr("Click Me") // Use qsTr() for all user-visible strings + + onClicked: { + myFunction() + buttonClicked() // Emit signal + } + } + + QGCLabel { + Layout.fillWidth: true + text: qsTr("Example Label: %1").arg(myIntProperty) + wrapMode: Text.WordWrap + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: qgcPal.window // Use QGC palette colors for everything, no hardcoded colors + border.color: qgcPal.text + border.width: 1 + + QGCLabel { + anchors.centerIn: parent + text: _debugMode ? qsTr("Debug Mode") : qsTr("Normal Mode") + color: qgcPal.text + } + } - onClicked: myFunction() + // Example: Conditional visibility + QGCButton { + Layout.fillWidth: true + text: qsTr("Vehicle Action") + visible: _activeVehicle !== null // Defensive: always check for null vehicle + enabled: _activeVehicle ? _activeVehicle.armed : false + + onClicked: { + if (_activeVehicle) { // Always null-check before use + _privateFunction() + } + } + } + } + + // =================================================================================== + // CONNECTIONS SECTION + // Connections objects come after visual components + // =================================================================================== + + Connections { + target: QGroundControl.multiVehicleManager + + function onActiveVehicleChanged(vehicle) { + if (vehicle) { + console.log("Active vehicle changed:", vehicle.id) + } + } } - Rectangle { - width: _rectWidth - height: _rectHeight - color: qgcPal.window // Use QGC palette colors for everything, no hardcoded colors + // =================================================================================== + // COMPONENT.ONCOMPLETED SECTION + // Initialization code comes at the end + // =================================================================================== + + Component.onCompleted: { + console.log("CodingStyle QML component loaded") + _privateFunction() } // For scoped blocks which are long include a comment so you can tell what the brace is matching. - // This is very handy when the top level brace scrolls off the screen. The endbrace comment in this + // This is very handy when the top level brace scrolls off the screen. The end-brace comment in this // specific file is only included as style info. This example code is not long enough to really need it. } // Item - CodingStyle diff --git a/README.md b/README.md index 799e33fabbcb..b9405404a77c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ - *✈️ Comprehensive Flight Control*: Full flight control and mission management for *PX4* and *ArduPilot* powered UAVs. - *🛠️ Mission Planning*: Easily plan complex missions with a simple drag-and-drop interface. -🔍 For a deeper dive into using QGC, check out the [User Manual](https://docs.qgroundcontrol.com/en/) – although, thanks to QGC's intuitive UI, you may not even need it! +🔍 For a deeper dive into using QGC, check out the [User Manual](https://docs.qgroundcontrol.com/en/) – although thanks to QGC's intuitive UI, you may not even need it! --- diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 6d81650fe722..5d4a9db5cda1 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -6,15 +6,6 @@ - - - - - - - - - @@ -39,13 +30,13 @@ android:requestLegacyExternalStorage="true" android:allowBackup="true" android:fullBackupOnly="false" - android:icon="@drawable/icon" + android:icon="-- %%INSERT_APP_ICON%% --" android:usesCleartextTraffic="true" android:networkSecurityConfig="@xml/network_security_config"> direct: UsbSerialPort.read() - \ No newline at end of file + diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 8c81b304c134..8853b779878a 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -1,5 +1,5 @@ - + USB Devices CDC ACM USB Device Chrome CCD USB Device diff --git a/android/res/xml/qtprovider_paths.xml b/android/res/xml/qtprovider_paths.xml index ae5b4b6074e7..3488bf270811 100644 --- a/android/res/xml/qtprovider_paths.xml +++ b/android/res/xml/qtprovider_paths.xml @@ -1,4 +1,11 @@ + + + + + + + diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index c40ed7273987..aa2f5a55dc28 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -1,17 +1,26 @@ package org.mavlink.qgroundcontrol; +import java.io.File; import java.util.List; import java.lang.reflect.Method; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.os.PowerManager; import android.net.wifi.WifiManager; +import android.provider.Settings; import android.util.Log; import android.view.WindowManager; import android.app.Activity; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import org.qtproject.qt.android.bindings.QtActivity; @@ -119,32 +128,102 @@ private void releaseMulticastLock() { public static String getSDCardPath() { StorageManager storageManager = (StorageManager)m_instance.getSystemService(Activity.STORAGE_SERVICE); List volumes = storageManager.getStorageVolumes(); - Method mMethodGetPath; - String path = ""; + for (StorageVolume vol : volumes) { - try { - mMethodGetPath = vol.getClass().getMethod("getPath"); - } catch (NoSuchMethodException e) { - e.printStackTrace(); + if (!vol.isRemovable()) { continue; } - try { - path = (String) mMethodGetPath.invoke(vol); - } catch (Exception e) { - e.printStackTrace(); - continue; + + String path = null; + + // For Android 11+ (API 30+), use the proper getDirectory() method + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + File directory = vol.getDirectory(); + if (directory != null) { + path = directory.getAbsolutePath(); + } + } else { + // For older versions, use reflection to get the path + try { + Method mMethodGetPath = vol.getClass().getMethod("getPath"); + path = (String) mMethodGetPath.invoke(vol); + } catch (Exception e) { + Log.e(TAG, "Failed to get path via reflection", e); + continue; + } } - if (vol.isRemovable() == true) { - Log.i(TAG, "removable sd card mounted " + path); + if (path != null && !path.isEmpty()) { + Log.i(TAG, "removable sd card mounted at " + path); return path; - } else { - Log.i(TAG, "storage mounted " + path); } } + + Log.w(TAG, "No removable SD card found"); return ""; } + /** + * Checks and requests storage permissions for SD card access. + * For Android 11+ (API 30+), this requires MANAGE_EXTERNAL_STORAGE permission. + * + * @return true if permissions are granted, false otherwise + */ + public static boolean checkStoragePermissions() { + if (m_instance == null) { + Log.e(TAG, "Activity instance is null"); + return false; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Android 11+ (API 30+) requires MANAGE_EXTERNAL_STORAGE for full SD card access + if (!Environment.isExternalStorageManager()) { + Log.i(TAG, "MANAGE_EXTERNAL_STORAGE not granted, requesting..."); + try { + Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); + intent.setData(Uri.parse("package:" + m_instance.getPackageName())); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + m_instance.startActivity(intent); + } catch (Exception e) { + Log.e(TAG, "Failed to open storage permission settings", e); + // Fallback to general settings + Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + m_instance.startActivity(intent); + } + return false; + } + Log.i(TAG, "MANAGE_EXTERNAL_STORAGE already granted"); + return true; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // Android 6.0+ (API 23+) requires runtime permissions + String[] permissions = { + android.Manifest.permission.READ_EXTERNAL_STORAGE, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + + boolean allGranted = true; + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(m_instance, permission) != PackageManager.PERMISSION_GRANTED) { + allGranted = false; + break; + } + } + + if (!allGranted) { + Log.i(TAG, "Storage permissions not granted, requesting..."); + ActivityCompat.requestPermissions(m_instance, permissions, 1); + return false; + } + + Log.i(TAG, "Storage permissions already granted"); + return true; + } else { + // Below Android 6.0, permissions are granted at install time + return true; + } + } + // Native C++ functions public native boolean nativeInit(); public native void qgcLogDebug(final String message); diff --git a/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java b/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java index 76e041fd43cc..006f2dac9c27 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java @@ -60,6 +60,10 @@ public static void initialize(Context context) { } usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + if (usbManager == null) { + QGCLogger.e(TAG, "Failed to get UsbManager"); + return; + } setupUsbPermissionIntent(context); registerUsbReceiver(context); usbSerialProber = UsbSerialProber.getDefaultProber(); @@ -413,6 +417,10 @@ private static UsbSerialPort findPortByDeviceId(final int deviceId) { public static String[] availableDevicesInfo() { // updateCurrentDrivers(); + if (usbManager == null) { + return null; + } + if (usbManager.getDeviceList().size() < 1) { return null; } @@ -420,8 +428,16 @@ public static String[] availableDevicesInfo() { final List deviceInfoList = new ArrayList<>(); for (final UsbDevice device : usbManager.getDeviceList().values()) { - final String deviceInfo = formatDeviceInfo(device); - deviceInfoList.add(deviceInfo); + try { + final String deviceInfo = formatDeviceInfo(device); + deviceInfoList.add(deviceInfo); + } catch (SecurityException e) { + // On some integrated controllers like the Siyi UNIRC7 the usb device is used for video output. + // This in turn causes a security exception when trying to access device info without permission. + // This could also happen if the user decides not to grant permission to access the device for a real + // case of a usb device being plugged in. + // We just eat the exception in these cases to prevent log spamming. + } } return deviceInfoList.toArray(new String[0]); diff --git a/cmake/Coverage.cmake b/cmake/Coverage.cmake new file mode 100644 index 000000000000..891314780aea --- /dev/null +++ b/cmake/Coverage.cmake @@ -0,0 +1,30 @@ +# Code coverage configuration for QGroundControl +# Enabled via: cmake -DQGC_ENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug + +if(NOT QGC_ENABLE_COVERAGE) + return() +endif() + +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage requires Debug build, but CMAKE_BUILD_TYPE is ${CMAKE_BUILD_TYPE}") + return() +endif() + +message(STATUS "Code coverage instrumentation enabled") + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + message(STATUS "Using GCC coverage (gcov/lcov)") + target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE --coverage -O0 -g) + target_link_options(${CMAKE_PROJECT_NAME} PRIVATE --coverage) + +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(STATUS "Using Clang source-based coverage (llvm-cov)") + target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -fprofile-instr-generate -fcoverage-mapping -O0 -g) + target_link_options(${CMAKE_PROJECT_NAME} PRIVATE -fprofile-instr-generate) + +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + message(WARNING "Code coverage not supported for MSVC. Use Visual Studio Enterprise or OpenCppCoverage.") + +else() + message(WARNING "Code coverage not supported for compiler: ${CMAKE_CXX_COMPILER_ID}") +endif() diff --git a/cmake/CustomOptions.cmake b/cmake/CustomOptions.cmake index cef048c40eb3..789f7a2f64ce 100644 --- a/cmake/CustomOptions.cmake +++ b/cmake/CustomOptions.cmake @@ -1,101 +1,172 @@ +# ============================================================================ +# QGroundControl Build Configuration Options +# All options can be overridden by custom builds via CustomOverrides.cmake +# ============================================================================ + include(CMakeDependentOption) -# The following options can be overriden by custom builds using the CustomOverrides.cmake file - -# App -set(QGC_APP_NAME "QGroundControl" CACHE STRING "App Name") -set(QGC_APP_COPYRIGHT "Copyright (c) 2025 QGroundControl. All rights reserved." CACHE STRING "Copyright") -set(QGC_APP_DESCRIPTION "Open Source Ground Control App" CACHE STRING "Description") -set(QGC_ORG_NAME "QGroundControl" CACHE STRING "Org Name") -set(QGC_ORG_DOMAIN "qgroundcontrol.com" CACHE STRING "Domain") -set(QGC_PACKAGE_NAME "org.mavlink.qgroundcontrol" CACHE STRING "Package Name") -set(QGC_SETTINGS_VERSION "9" CACHE STRING "Settings Version") # If you need to make an incompatible changes to stored settings, bump this version number up by 1. This will caused store settings to be cleared on next boot. - -# Build + +# ============================================================================ +# Application Metadata +# ============================================================================ + +set(QGC_APP_NAME "QGroundControl" CACHE STRING "Application name") +set(QGC_APP_COPYRIGHT "Copyright (c) 2025 QGroundControl. All rights reserved." CACHE STRING "Copyright notice") +set(QGC_APP_DESCRIPTION "Open Source Ground Control App" CACHE STRING "Application description") +set(QGC_ORG_NAME "QGroundControl" CACHE STRING "Organization name") +set(QGC_ORG_DOMAIN "qgroundcontrol.com" CACHE STRING "Organization domain") +set(QGC_PACKAGE_NAME "org.mavlink.qgroundcontrol" CACHE STRING "Package identifier") + +# Settings version - increment to clear stored settings on next boot after incompatible changes +set(QGC_SETTINGS_VERSION "9" CACHE STRING "Settings schema version") + +# ============================================================================ +# Build Configuration +# ============================================================================ + option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) -option(QGC_STABLE_BUILD "Stable Build" OFF) -option(QGC_USE_CACHE "Use Build Caching" ON) -cmake_dependent_option(QGC_BUILD_TESTING "Enable testing" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) -cmake_dependent_option(QGC_DEBUG_QML "Build QGroundControl with QML debugging/profiling support." ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) - -# Features -option(QGC_UTM_ADAPTER "Enable UTM Adapter" OFF) -option(QGC_VIEWER3D "Enable Viewer3D" ON) # Qt6Quick3D_FOUND -# option(QGC_DISABLE_MAVLINK_INSPECTOR "Disable Mavlink Inspector" OFF) # This removes QtCharts which is GPL licensed - -# Comms -option(QGC_ENABLE_BLUETOOTH "Enable Bluetooth Links" ON) # Qt6Bluetooth_FOUND -option(QGC_ZEROCONF_ENABLED "Enable ZeroConf Compatibility" OFF) -option(QGC_AIRLINK_DISABLED "Disable AIRLink" ON) -option(QGC_NO_SERIAL_LINK "Disable Serial Links" OFF) # NOT IOS AND Qt6SerialPort_FOUND - -# Video -option(QGC_ENABLE_UVC "Enable UVC Devices" ON) # Qt6Multimedia_FOUND -option(QGC_ENABLE_GST_VIDEOSTREAMING "Enable GStreamer Video Backend" ON) -cmake_dependent_option(QGC_CUSTOM_GST_PACKAGE "Enable Using QGC Provided Custom GStreamer Packages" OFF "QGC_ENABLE_GST_VIDEOSTREAMING" OFF) -option(QGC_ENABLE_QT_VIDEOSTREAMING "Enable QtMultimedia Video Backend" OFF) # Qt6Multimedia_FOUND - -# Joystick -# set(SDL_GAMECONTROLLERCONFIG "0300000009120000544f000011010000,OpenTX Radiomaster TX16S Joystick,leftx:a3,lefty:a2,rightx:a0,righty:a1,platform:Linux" CACHE STRING "Custom SDL Joystick Mappings") - -# MAVLink -set(QGC_MAVLINK_GIT_REPO "https://github.com/mavlink/c_library_v2.git" CACHE STRING "URL to MAVLink Git Repo") -set(QGC_MAVLINK_GIT_TAG "19f9955598af9a9181064619bd2e3c04bd2d848a" CACHE STRING "Tag of MAVLink Git Repo") - -# APM -option(QGC_DISABLE_APM_MAVLINK "Disable APM Dialect" OFF) -option(QGC_DISABLE_APM_PLUGIN "Disable APM Plugin" OFF) -option(QGC_DISABLE_APM_PLUGIN_FACTORY "Disable APM Plugin Factory" OFF) - -# PX4 -option(QGC_DISABLE_PX4_PLUGIN "Disable PX4 Plugin" OFF) -option(QGC_DISABLE_PX4_PLUGIN_FACTORY "Disable PX4 Plugin Factory" OFF) - -# Android -set(QGC_QT_ANDROID_TARGET_SDK_VERSION "36" CACHE STRING "Android Target SDK Version") -set(QGC_ANDROID_PACKAGE_NAME "${QGC_PACKAGE_NAME}" CACHE STRING "Android Package Name") -set(QGC_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/android" CACHE PATH "Android Package Path") -set(QT_ANDROID_DEPLOYMENT_TYPE "" CACHE STRING "Forces Signing if Set to Release") -option(QT_ANDROID_SIGN_APK "Enable Signing APK" OFF) -option(QT_ANDROID_SIGN_AAB "Enable Signing AAB" OFF) -option(QT_USE_TARGET_ANDROID_BUILD_DIR "Use Target Android Build Dir" OFF) -set(QGC_QT_MINIMUM_VERSION "6.8.3" CACHE STRING "Minimum Supported Qt Version") -set(QGC_QT_MAXIMUM_VERSION "6.8.3" CACHE STRING "Maximum Supported Qt Version") -set(QGC_QT_ANDROID_MIN_SDK_VERSION "28" CACHE STRING "Android Min SDK Version") - -# MacOS -set(QGC_MACOS_PLIST_PATH "${CMAKE_SOURCE_DIR}/deploy/macos/MacOSXBundleInfo.plist.in" CACHE FILEPATH "MacOS PList Path") -set(QGC_MACOS_BUNDLE_ID "${QGC_PACKAGE_NAME}" CACHE STRING "MacOS Bundle ID") -set(QGC_MACOS_ICON_PATH "${CMAKE_SOURCE_DIR}/deploy/macos/qgroundcontrol.icns" CACHE FILEPATH "MacOS Icon Path") -set(QGC_MACOS_ENTITLEMENTS_PATH "${CMAKE_SOURCE_DIR}/deploy/macos/qgroundcontrol.entitlements" CACHE FILEPATH "MacOS Entitlements Path") -option(QGC_MACOS_UNIVERSAL_BUILD "Build MacOS Universal Build (arm64;x86_64)" ON) - -# Linux -option(QGC_CREATE_APPIMAGE "Build an AppImage after build" ON) -set(QGC_APPIMAGE_ICON_256_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/QGroundControl_256.png" CACHE FILEPATH "AppImage Icon 256x256 Path") -set(QGC_APPIMAGE_ICON_SCALABLE_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/QGroundControl.svg" CACHE FILEPATH "AppImage Icon SVG Path") -set(QGC_APPIMAGE_APPRUN_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/AppRun" CACHE FILEPATH "AppImage AppRun Path") -set(QGC_APPIMAGE_DESKTOP_ENTRY_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/org.mavlink.qgroundcontrol.desktop.in" CACHE FILEPATH "AppImage Desktop Entry Path") -set(QGC_APPIMAGE_METADATA_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/org.mavlink.qgroundcontrol.appdata.xml.in" CACHE FILEPATH "AppImage Metadata Path") -set(QGC_APPIMAGE_APPDATA_DEVELOPER "qgroundcontrol" CACHE STRING "AppImage Metadata Developer") - -# Windows -set(QGC_WINDOWS_INSTALL_HEADER_PATH "${CMAKE_SOURCE_DIR}/deploy/windows/installheader.bmp" CACHE FILEPATH "Windows Install Header Path") -set(QGC_WINDOWS_ICON_PATH "${CMAKE_SOURCE_DIR}/deploy/windows/WindowsQGC.ico" CACHE FILEPATH "Windows Icon Path") -set(QGC_WINDOWS_RESOURCE_FILE_PATH "${CMAKE_SOURCE_DIR}/deploy/windows/QGroundControl.rc" CACHE FILEPATH "Windows Resource File Path") - -# CPM -set(QGC_CPM_SOURCE_CACHE "" CACHE PATH "Directory to Download CPM Dependencies, Overrides CPM_SOURCE_CACHE Env Variable") -# set(CPM_USE_NAMED_CACHE_DIRECTORIES ON CACHE BOOL "Use additional directory of package name in cache on the most nested level.") - -# Qt -set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml" CACHE PATH "Install path for QML") -set(QML_IMPORT_PATH "${QT_QML_OUTPUT_DIRECTORY}" CACHE STRING "Extra QML Import Paths") -option(QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING "Silence Missing Dependency Warnings" OFF) -option(QT_ENABLE_VERBOSE_DEPLOYMENT "Verbose Deployment" OFF) -option(QT_DEBUG_FIND_PACKAGE "Print Used Search Paths When a Package is Not Found" ON) -option(QT_QML_GENERATE_QMLLS_INI "https://doc.qt.io/qt-6/cmake-variable-qt-qml-generate-qmlls-ini.html" ON) +option(QGC_STABLE_BUILD "Stable release build (disables daily build features)" OFF) +option(QGC_USE_CACHE "Enable compiler caching (ccache/sccache)" ON) +option(QGC_BUILD_INSTALLER "Build platform installers/packages" ON) + +# Debug-dependent options +cmake_dependent_option(QGC_BUILD_TESTING "Enable unit tests" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) +cmake_dependent_option(QGC_DEBUG_QML "Enable QML debugging/profiling" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) +cmake_dependent_option(QGC_ENABLE_COVERAGE "Enable code coverage instrumentation" OFF "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) + +# ============================================================================ +# Feature Flags +# ============================================================================ + +option(QGC_UTM_ADAPTER "Enable UTM (Unmanned Traffic Management) Adapter" OFF) +option(QGC_VIEWER3D "Enable 3D Viewer (requires Qt Quick 3D)" ON) + +# MAVLink Inspector is disabled by default due to GPL licensing of QtCharts +# option(QGC_DISABLE_MAVLINK_INSPECTOR "Disable MAVLink Inspector" OFF) + +# ============================================================================ +# Communication Options +# ============================================================================ + +option(QGC_ENABLE_BLUETOOTH "Enable Bluetooth communication links" ON) +option(QGC_ZEROCONF_ENABLED "Enable ZeroConf/Bonjour discovery" OFF) +option(QGC_AIRLINK_DISABLED "Disable AIRLink support" ON) +option(QGC_NO_SERIAL_LINK "Disable serial port communication" OFF) + +# ============================================================================ +# Video Streaming Options +# ============================================================================ + +option(QGC_ENABLE_UVC "Enable UVC (USB Video Class) device support" ON) +option(QGC_ENABLE_GST_VIDEOSTREAMING "Enable GStreamer video backend" ON) +cmake_dependent_option(QGC_CUSTOM_GST_PACKAGE "Use QGC-provided GStreamer packages" OFF "QGC_ENABLE_GST_VIDEOSTREAMING" OFF) +option(QGC_ENABLE_QT_VIDEOSTREAMING "Enable QtMultimedia video backend" OFF) + +# ============================================================================ +# Joystick/Input Configuration +# ============================================================================ + +# Example custom SDL game controller mapping: +# set(SDL_GAMECONTROLLERCONFIG "0300000009120000544f000011010000,OpenTX Radiomaster TX16S,leftx:a3,lefty:a2,rightx:a0,righty:a1,platform:Linux" CACHE STRING "Custom SDL mappings") + +# ============================================================================ +# MAVLink Configuration +# ============================================================================ + +set(QGC_MAVLINK_GIT_REPO "https://github.com/mavlink/mavlink.git" CACHE STRING "MAVLink repository URL") +set(QGC_MAVLINK_GIT_TAG "dd17c1a65de7b9ad8dd6e3491a8690c0d0b27ba1" CACHE STRING "MAVLink repository commit/tag") +set(QGC_MAVLINK_DIALECT "all" CACHE STRING "MAVLink dialect") +set(QGC_MAVLINK_VERSION "2.0" CACHE STRING "MAVLink protocol version") + +# ============================================================================ +# Autopilot Plugin Configuration +# ============================================================================ + +# ArduPilot (APM) Plugin +option(QGC_DISABLE_APM_MAVLINK "Disable ArduPilot MAVLink dialect" OFF) +option(QGC_DISABLE_APM_PLUGIN "Disable ArduPilot plugin" OFF) +option(QGC_DISABLE_APM_PLUGIN_FACTORY "Disable ArduPilot plugin factory" OFF) + +# PX4 Plugin +option(QGC_DISABLE_PX4_PLUGIN "Disable PX4 plugin" OFF) +option(QGC_DISABLE_PX4_PLUGIN_FACTORY "Disable PX4 plugin factory" OFF) + +# ============================================================================ +# Platform-Specific Configuration +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Android Platform +# ---------------------------------------------------------------------------- +set(QGC_QT_ANDROID_COMPILE_SDK_VERSION "35" CACHE STRING "Android compile SDK version") +set(QGC_QT_ANDROID_TARGET_SDK_VERSION "35" CACHE STRING "Android target SDK version") +set(QGC_QT_ANDROID_MIN_SDK_VERSION "28" CACHE STRING "Android minimum SDK version") +set(QGC_ANDROID_PACKAGE_NAME "${QGC_PACKAGE_NAME}" CACHE STRING "Android package identifier") +set(QGC_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_SOURCE_DIR}/android" CACHE PATH "Android package source directory") +set(QT_ANDROID_DEPLOYMENT_TYPE "" CACHE STRING "Android deployment type (empty or Release)") +option(QT_ANDROID_SIGN_APK "Enable APK signing" OFF) +option(QT_ANDROID_SIGN_AAB "Enable AAB signing" OFF) +option(QT_USE_TARGET_ANDROID_BUILD_DIR "Use target-specific Android build directory" OFF) + +# ---------------------------------------------------------------------------- +# macOS Platform +# ---------------------------------------------------------------------------- +set(QGC_MACOS_PLIST_PATH "${CMAKE_SOURCE_DIR}/deploy/macos/MacOSXBundleInfo.plist.in" CACHE FILEPATH "macOS Info.plist template path") +set(QGC_MACOS_BUNDLE_ID "${QGC_PACKAGE_NAME}" CACHE STRING "macOS bundle identifier") +set(QGC_MACOS_ICON_PATH "${CMAKE_SOURCE_DIR}/deploy/macos/qgroundcontrol.icns" CACHE FILEPATH "macOS application icon path") +set(QGC_MACOS_ENTITLEMENTS_PATH "${CMAKE_SOURCE_DIR}/deploy/macos/qgroundcontrol.entitlements" CACHE FILEPATH "macOS entitlements file path") +option(QGC_MACOS_UNIVERSAL_BUILD "Build macOS universal binary (x86_64h + arm64)" ON) + +# ---------------------------------------------------------------------------- +# Linux Platform +# ---------------------------------------------------------------------------- +option(QGC_CREATE_APPIMAGE "Create AppImage package after build" ON) +set(QGC_APPIMAGE_ICON_256_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/QGroundControl_256.png" CACHE FILEPATH "AppImage 256x256 icon path") +set(QGC_APPIMAGE_ICON_SCALABLE_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/QGroundControl.svg" CACHE FILEPATH "AppImage SVG icon path") +set(QGC_APPIMAGE_APPRUN_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/AppRun" CACHE FILEPATH "AppImage AppRun script path") +set(QGC_APPIMAGE_DESKTOP_ENTRY_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/org.mavlink.qgroundcontrol.desktop.in" CACHE FILEPATH "AppImage desktop entry path") +set(QGC_APPIMAGE_METADATA_PATH "${CMAKE_SOURCE_DIR}/deploy/linux/org.mavlink.qgroundcontrol.appdata.xml.in" CACHE FILEPATH "AppImage metadata path") +set(QGC_APPIMAGE_APPDATA_DEVELOPER "qgroundcontrol" CACHE STRING "AppImage developer name") + +# ---------------------------------------------------------------------------- +# Windows Platform +# ---------------------------------------------------------------------------- +set(QGC_WINDOWS_INSTALL_HEADER_PATH "${CMAKE_SOURCE_DIR}/deploy/windows/installheader.bmp" CACHE FILEPATH "Windows installer header image") +set(QGC_WINDOWS_ICON_PATH "${CMAKE_SOURCE_DIR}/deploy/windows/WindowsQGC.ico" CACHE FILEPATH "Windows application icon") +set(QGC_WINDOWS_RESOURCE_FILE_PATH "${CMAKE_SOURCE_DIR}/deploy/windows/QGroundControl.rc" CACHE FILEPATH "Windows resource file") + +# ============================================================================ +# Qt Configuration +# ============================================================================ + +set(QGC_QT_MINIMUM_VERSION "6.10.0" CACHE STRING "Minimum supported Qt version") +set(QGC_QT_MAXIMUM_VERSION "6.10.1" CACHE STRING "Maximum supported Qt version") + +set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml" CACHE PATH "QML output directory") +set(QML_IMPORT_PATH "${QT_QML_OUTPUT_DIRECTORY}" CACHE STRING "Additional QML import paths") + +option(QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING "Silence missing dependency warnings" OFF) +option(QT_ENABLE_VERBOSE_DEPLOYMENT "Enable verbose deployment output" OFF) +option(QT_DEBUG_FIND_PACKAGE "Print search paths when package not found" ON) +option(QT_QML_GENERATE_QMLLS_INI "Generate qmlls.ini for QML language server" ON) +option(QGC_ENABLE_QMLLINT "Enable automatic QML linting during build" OFF) + +set(QGC_QT_DISABLE_DEPRECATED_UP_TO "0x061000" CACHE STRING "Disable Qt APIs deprecated before this version") +set(QGC_QT_ENABLE_STRICT_MODE_UP_TO "0x061000" CACHE STRING "Enable strict Qt API mode up to this version") + +# Debug environment variables (uncomment to enable) # set(ENV{QT_DEBUG_PLUGINS} "1") # set(ENV{QML_IMPORT_TRACE} "1") -# CMAKE -# option(CMAKE_FIND_DEBUG_MODE "Print Used Search Paths When Finding a Package" OFF) +# ============================================================================ +# CMake Package Manager (CPM) +# ============================================================================ + +# Uncomment to use named cache directories for better organization +# set(CPM_USE_NAMED_CACHE_DIRECTORIES ON CACHE BOOL "Use package name subdirectories in CPM cache") + +# ============================================================================ +# CMake Configuration +# ============================================================================ + +# Uncomment for verbose package finding +# option(CMAKE_FIND_DEBUG_MODE "Print search paths when finding packages" OFF) diff --git a/cmake/Helpers.cmake b/cmake/Helpers.cmake index 12433793e1bb..92b64621cf39 100644 --- a/cmake/Helpers.cmake +++ b/cmake/Helpers.cmake @@ -1,5 +1,23 @@ +# ---------------------------------------------------------------------------- +# QGroundControl CMake Helper Functions +# ---------------------------------------------------------------------------- + +if(QGC_HELPERS_INCLUDED) + return() +endif() +set(QGC_HELPERS_INCLUDED TRUE) + +# ---------------------------------------------------------------------------- +# qgc_set_qt_resource_alias +# Sets Qt resource aliases for files based on their filenames +# Args: List of resource files +# ---------------------------------------------------------------------------- function(qgc_set_qt_resource_alias) foreach(resource_file IN LISTS ARGN) + if(NOT EXISTS "${resource_file}") + message(WARNING "QGC: Resource file does not exist: ${resource_file}") + continue() + endif() get_filename_component(alias "${resource_file}" NAME) set_source_files_properties("${resource_file}" PROPERTIES @@ -8,12 +26,17 @@ function(qgc_set_qt_resource_alias) endforeach() endfunction() +# ---------------------------------------------------------------------------- +# qgc_config_caching +# Configures compiler caching using ccache or sccache if available +# ---------------------------------------------------------------------------- function(qgc_config_caching) function(_qgc_verify_cache_tool _ok _path) execute_process( COMMAND "${_path}" --version RESULT_VARIABLE _res - OUTPUT_QUIET ERROR_QUIET + OUTPUT_QUIET + ERROR_QUIET ) if(NOT _res EQUAL 0) set(${_ok} FALSE PARENT_SCOPE) @@ -21,18 +44,20 @@ function(qgc_config_caching) endfunction() find_program(QGC_CACHE_PROGRAM - NAMES ccache sccache - VALIDATOR _qgc_verify_cache_tool) + NAMES ccache sccache + VALIDATOR _qgc_verify_cache_tool + ) + if(QGC_CACHE_PROGRAM) get_filename_component(_cache_tool "${QGC_CACHE_PROGRAM}" NAME_WE) - message(STATUS "QGC: using ${_cache_tool} (${QGC_CACHE_PROGRAM})") + message(STATUS "QGC: Using ${_cache_tool} (${QGC_CACHE_PROGRAM})") string(TOLOWER "${_cache_tool}" _cache_tool) if(_cache_tool STREQUAL "ccache") # set(ENV{CCACHE_CONFIGPATH} "${CMAKE_SOURCE_DIR}/tools/ccache.conf") # set(ENV{CCACHE_DIR} "${CMAKE_SOURCE_DIR}/.ccache") set(ENV{CCACHE_BASEDIR} "${CMAKE_SOURCE_DIR}") - set(ENV{CCACHE_COMPRESSLEVEL "5") + set(ENV{CCACHE_COMPRESSLEVEL} "5") set(ENV{CCACHE_SLOPPINESS} "pch_defines,time_macros,include_file_mtime,include_file_ctime") # set(ENV{CCACHE_NOHASHDIR} "true") if(APPLE) @@ -55,59 +80,76 @@ function(qgc_config_caching) return() endif() - set(CMAKE_C_COMPILER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "C compiler launcher") - set(CMAKE_CXX_COMPILER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "CXX compiler launcher") - # set(CMAKE_C_LINKER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "C linker cache used") - # set(CMAKE_CXX_LINKER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "CXX linker cache used") + set(CMAKE_C_COMPILER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "C compiler launcher" FORCE) + set(CMAKE_CXX_COMPILER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "CXX compiler launcher" FORCE) + # Linker launchers not currently used but available if needed + # set(CMAKE_C_LINKER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "C linker cache") + # set(CMAKE_CXX_LINKER_LAUNCHER "${QGC_CACHE_PROGRAM}" CACHE STRING "CXX linker cache") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Xclang -fno-pch-timestamp) endif() else() - message(WARNING "QGC: no ccache/sccache found – building without a compiler cache") + message(WARNING "QGC: No ccache/sccache found - building without a compiler cache") endif() endfunction() +# ---------------------------------------------------------------------------- +# qgc_set_linker +# Attempts to use a faster linker (mold, lld, or gold) if available +# Falls back to the system default linker +# ---------------------------------------------------------------------------- function(qgc_set_linker) include(CheckLinkerFlag) + # Try linkers in order of preference: mold > lld > gold foreach(_ld mold lld gold) set(_flag "LINKER:-fuse-ld=${_ld}") check_linker_flag(CXX "${_flag}" HAVE_LD_${_ld}) if(HAVE_LD_${_ld}) add_link_options("${_flag}") - set(QGC_LINKER "${_ld}") - message(STATUS "QGC: using ${_ld} linker (flag ${_flag})") - break() + set(QGC_LINKER "${_ld}" PARENT_SCOPE) + message(STATUS "QGC: Using ${_ld} linker") + return() endif() endforeach() - if(NOT DEFINED QGC_LINKER) - message(WARNING "QGC: no mold / lld / gold found – falling back to default linker") - endif() + message(STATUS "QGC: No alternative linker (mold/lld/gold) found - using system default") endfunction() +# ---------------------------------------------------------------------------- +# qgc_enable_pie +# Enables Position Independent Executables (PIE) for improved security +# ---------------------------------------------------------------------------- function(qgc_enable_pie) include(CheckPIESupported) - check_pie_supported(OUTPUT_VARIABLE _output) + check_pie_supported(OUTPUT_VARIABLE _output LANGUAGES C CXX) + if(CMAKE_C_LINK_PIE_SUPPORTED) - set(CMAKE_POSITION_INDEPENDENT_CODE ON) - message(STATUS "QGC: PIE is enabled") + set(CMAKE_POSITION_INDEPENDENT_CODE ON PARENT_SCOPE) + message(STATUS "QGC: PIE enabled") else() - message(WARNING "QGC: PIE is not supported at link time: ${_output}") + message(WARNING "QGC: PIE not supported - ${_output}") endif() endfunction() +# ---------------------------------------------------------------------------- +# qgc_enable_ipo +# Enables Interprocedural Optimization (IPO/LTO) for Release builds +# ---------------------------------------------------------------------------- function(qgc_enable_ipo) if(CMAKE_BUILD_TYPE STREQUAL "Release") include(CheckIPOSupported) - check_ipo_supported(RESULT _result OUTPUT _output) + check_ipo_supported(RESULT _result OUTPUT _output LANGUAGES C CXX) + if(_result) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) - message(STATUS "QGC: LTO is enabled") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE PARENT_SCOPE) + message(STATUS "QGC: IPO/LTO enabled for Release build") else() - message(WARNING "QGC: IPO is not supported: ${_output}") + message(WARNING "QGC: IPO/LTO not supported - ${_output}") endif() + else() + message(STATUS "QGC: IPO/LTO disabled for ${CMAKE_BUILD_TYPE} build") endif() endfunction() diff --git a/cmake/PrintSummary.cmake b/cmake/PrintSummary.cmake index 02d0803dfdad..11680c03e716 100644 --- a/cmake/PrintSummary.cmake +++ b/cmake/PrintSummary.cmake @@ -1,11 +1,20 @@ +# ============================================================================ +# QGroundControl Build Configuration Summary +# Prints a comprehensive summary of the build configuration +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Configuration Timestamp # ---------------------------------------------------------------------------- -# Print a timestamp for reproducibility string(TIMESTAMP QGC_CONFIGURE_TIME "%Y-%m-%d %H:%M:%S %Z") -message(STATUS "QGroundControl configuration generated at ${QGC_CONFIGURE_TIME}") +message(STATUS "") +message(STATUS "==================================================================") +message(STATUS "QGroundControl Configuration Summary") +message(STATUS "Generated at: ${QGC_CONFIGURE_TIME}") message(STATUS "==================================================================") # ---------------------------------------------------------------------------- -# Macro for printing ON/OFF flags +# Helper Macro for ON/OFF Options # ---------------------------------------------------------------------------- macro(OptionOutput _label) if(${ARGN}) @@ -13,117 +22,121 @@ macro(OptionOutput _label) else() set(_val "OFF") endif() - message(STATUS "${_label}: ${_val}") + message(STATUS " ${_label}: ${_val}") endmacro() # ---------------------------------------------------------------------------- -# Imported CMake variables +# CMake System Information # ---------------------------------------------------------------------------- -message(STATUS "-- CMake System -----------------------------------------------------") -message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") -message(STATUS "Generator: ${CMAKE_GENERATOR} ${CMAKE_GENERATOR_PLATFORM} ${CMAKE_GENERATOR_TOOLSET}") -message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") -message(STATUS "Host system: ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_VERSION}") -message(STATUS "Target system: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}") -message(STATUS "CMake version: ${CMAKE_VERSION}") -message(STATUS "Source directory: ${CMAKE_SOURCE_DIR}") -message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") -message(STATUS "Prefix path: ${CMAKE_PREFIX_PATH}") -message(STATUS "------------------------------------------------------------------") +message(STATUS "") +message(STATUS "CMake System:") +message(STATUS " CMake version: ${CMAKE_VERSION}") +message(STATUS " Generator: ${CMAKE_GENERATOR}") +message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS " Source directory: ${CMAKE_SOURCE_DIR}") +message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS " Host system: ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_VERSION}") +message(STATUS " Target system: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}") +if(CMAKE_TOOLCHAIN_FILE) + message(STATUS " Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +endif() +if(CMAKE_PREFIX_PATH) + message(STATUS " Prefix path: ${CMAKE_PREFIX_PATH}") +endif() -message(STATUS "-- Compiler & Linker -----------------------------------------------") -message(STATUS "C++ compiler: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER}) v${CMAKE_CXX_COMPILER_VERSION}") -message(STATUS "C++ standard: C++${CMAKE_CXX_STANDARD}") -message(STATUS "Compiler flags: ${CMAKE_CXX_FLAGS}") -message(STATUS " Debug: ${CMAKE_CXX_FLAGS_DEBUG}") -message(STATUS " Release: ${CMAKE_CXX_FLAGS_RELEASE}") -message(STATUS "Linker flags: ${CMAKE_EXE_LINKER_FLAGS}") -message(STATUS " Debug: ${CMAKE_EXE_LINKER_FLAGS_DEBUG}") -message(STATUS " Release: ${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -message(STATUS "------------------------------------------------------------------") +message(STATUS "") +message(STATUS "Compiler & Linker:") +message(STATUS " C++ compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") +message(STATUS " C++ standard: C++${CMAKE_CXX_STANDARD}") +if(CMAKE_CXX_FLAGS) + message(STATUS " Compiler flags: ${CMAKE_CXX_FLAGS}") +endif() +if(CMAKE_EXE_LINKER_FLAGS) + message(STATUS " Linker flags: ${CMAKE_EXE_LINKER_FLAGS}") +endif() # ---------------------------------------------------------------------------- -# App metadata +# Application Metadata # ---------------------------------------------------------------------------- -message(STATUS "-- Application ------------------------------------------------------") -message(STATUS "App Name: ${QGC_APP_NAME}") -message(STATUS "Description: ${QGC_APP_DESCRIPTION}") -message(STATUS "Copyright: ${QGC_APP_COPYRIGHT}") -message(STATUS "Organization: ${QGC_ORG_NAME} (${QGC_ORG_DOMAIN})") -message(STATUS "Package name: ${QGC_PACKAGE_NAME}") -message(STATUS "Settings version: ${QGC_SETTINGS_VERSION}") -message(STATUS "------------------------------------------------------------------") +message(STATUS "") +message(STATUS "Application:") +message(STATUS " Name: ${QGC_APP_NAME}") +message(STATUS " Version: ${QGC_APP_VERSION_STR}") +message(STATUS " Description: ${QGC_APP_DESCRIPTION}") +message(STATUS " Organization: ${QGC_ORG_NAME} (${QGC_ORG_DOMAIN})") +message(STATUS " Package name: ${QGC_PACKAGE_NAME}") +message(STATUS " Settings version: ${QGC_SETTINGS_VERSION}") # ---------------------------------------------------------------------------- -# Option flags +# Build & Feature Flags # ---------------------------------------------------------------------------- -message(STATUS "-- Build & Feature Flags -------------------------------------------") -OptionOutput("Build shared libraries" BUILD_SHARED_LIBS) -OptionOutput("Stable build" QGC_STABLE_BUILD) -OptionOutput("Use build caching" QGC_USE_CACHE) -OptionOutput("Enable testing" QGC_BUILD_TESTING) -OptionOutput("Enable QML debugging" QGC_DEBUG_QML) -OptionOutput("Enable Herelink support" QGC_ENABLE_HERELINK) -OptionOutput("Enable UTM Adapter" QGC_UTM_ADAPTER) -OptionOutput("Enable 3D Viewer" QGC_VIEWER3D) -OptionOutput("Enable Bluetooth links" QGC_ENABLE_BLUETOOTH) -OptionOutput("Enable ZeroConf compatibility" QGC_ZEROCONF_ENABLED) -OptionOutput("Disable AIRLink" QGC_AIRLINK_DISABLED) -OptionOutput("Disable serial links" QGC_NO_SERIAL_LINK) -OptionOutput("Enable UVC devices" QGC_ENABLE_UVC) -OptionOutput("Enable GStreamer video" QGC_ENABLE_GST_VIDEOSTREAMING) -OptionOutput("Enable Qt video backend" QGC_ENABLE_QT_VIDEOSTREAMING) -OptionOutput("Disable APM MAVLink dialect" QGC_DISABLE_APM_MAVLINK) -OptionOutput("Disable APM plugin" QGC_DISABLE_APM_PLUGIN) -OptionOutput("Disable APM plugin factory" QGC_DISABLE_APM_PLUGIN_FACTORY) -OptionOutput("Disable PX4 plugin" QGC_DISABLE_PX4_PLUGIN) -OptionOutput("Disable PX4 plugin factory" QGC_DISABLE_PX4_PLUGIN_FACTORY) -message(STATUS "------------------------------------------------------------------") +message(STATUS "") +message(STATUS "Build & Feature Flags:") +OptionOutput("Stable build " QGC_STABLE_BUILD) +OptionOutput("Use build caching " QGC_USE_CACHE) +OptionOutput("Enable testing " QGC_BUILD_TESTING) +OptionOutput("Enable QML debugging " QGC_DEBUG_QML) +OptionOutput("Enable QML linting " QGC_ENABLE_QMLLINT) +OptionOutput("Enable 3D Viewer " QGC_VIEWER3D) +OptionOutput("Enable Bluetooth links " QGC_ENABLE_BLUETOOTH) +OptionOutput("Enable ZeroConf compatibility " QGC_ZEROCONF_ENABLED) +OptionOutput("Disable AIRLink " QGC_AIRLINK_DISABLED) +OptionOutput("Disable serial links " QGC_NO_SERIAL_LINK) +OptionOutput("Enable UVC devices " QGC_ENABLE_UVC) +OptionOutput("Enable GStreamer video " QGC_ENABLE_GST_VIDEOSTREAMING) +OptionOutput("Enable Qt video backend " QGC_ENABLE_QT_VIDEOSTREAMING) +OptionOutput("Disable APM MAVLink dialect " QGC_DISABLE_APM_MAVLINK) +OptionOutput("Disable APM plugin " QGC_DISABLE_APM_PLUGIN) +OptionOutput("Disable PX4 plugin " QGC_DISABLE_PX4_PLUGIN) # ---------------------------------------------------------------------------- -# Repository & dependency settings +# External Dependencies # ---------------------------------------------------------------------------- -message(STATUS "-- External Dependencies -------------------------------------------") -message(STATUS "MAVLink repo URL: ${QGC_MAVLINK_GIT_REPO}") -message(STATUS "MAVLink repo tag: ${QGC_MAVLINK_GIT_TAG}") -message(STATUS "CPM cache directory: ${QGC_CPM_SOURCE_CACHE}") -message(STATUS "QML output directory: ${QT_QML_OUTPUT_DIRECTORY}") -message(STATUS "------------------------------------------------------------------") +message(STATUS "") +message(STATUS "External Dependencies:") +message(STATUS " MAVLink repo: ${QGC_MAVLINK_GIT_REPO}") +message(STATUS " MAVLink tag: ${QGC_MAVLINK_GIT_TAG}") +message(STATUS " CPM cache: ${CPM_SOURCE_CACHE}") +message(STATUS " QML output dir: ${QT_QML_OUTPUT_DIRECTORY}") # ---------------------------------------------------------------------------- -# Platform-specific settings +# Platform-Specific Settings # ---------------------------------------------------------------------------- if(ANDROID) - message(STATUS "-- Android ---------------------------------------------------------") - message(STATUS "Target SDK: ${QGC_QT_ANDROID_TARGET_SDK_VERSION}") - message(STATUS "Package source dir: ${QGC_ANDROID_PACKAGE_SOURCE_DIR}") - message(STATUS "APK signing: ${QT_ANDROID_SIGN_APK} / AAB signing: ${QT_ANDROID_SIGN_AAB}") - message(STATUS "Use target build dir: ${QT_USE_TARGET_ANDROID_BUILD_DIR}") - message(STATUS "NDK host system: ${ANDROID_NDK_HOST_SYSTEM_NAME}") - message(STATUS "SDK root: ${ANDROID_SDK_ROOT}") - message(STATUS "Deployment type: ${QT_ANDROID_DEPLOYMENT_TYPE}") - message(STATUS "------------------------------------------------------------------") + message(STATUS "") + message(STATUS "Android Platform:") + message(STATUS " Target SDK: ${QGC_QT_ANDROID_TARGET_SDK_VERSION}") + message(STATUS " Min SDK: ${QGC_QT_ANDROID_MIN_SDK_VERSION}") + message(STATUS " Package: ${QGC_ANDROID_PACKAGE_NAME}") + message(STATUS " APK signing: ${QT_ANDROID_SIGN_APK}") + message(STATUS " AAB signing: ${QT_ANDROID_SIGN_AAB}") endif() if(MACOS) - message(STATUS "-- macOS -----------------------------------------------------------") - message(STATUS "Bundle ID: ${QGC_MACOS_BUNDLE_ID}") - message(STATUS "Info plist path: ${QGC_MACOS_PLIST_PATH}") - message(STATUS "Icon directory: ${QGC_MACOS_ICON_PATH}") - message(STATUS "Entitlements path: ${QGC_MACOS_ENTITLEMENTS_PATH}") - message(STATUS "------------------------------------------------------------------") + message(STATUS "") + message(STATUS "macOS Platform:") + message(STATUS " Bundle ID: ${QGC_MACOS_BUNDLE_ID}") + message(STATUS " Deployment target: ${CMAKE_OSX_DEPLOYMENT_TARGET}") + if(QGC_MACOS_UNIVERSAL_BUILD) + message(STATUS " Architectures: ${CMAKE_OSX_ARCHITECTURES}") + endif() endif() -if(WIN32) - message(STATUS "-- Windows ---------------------------------------------------------") - message(STATUS "Install header bmp: ${QGC_WINDOWS_INSTALL_HEADER_PATH}") - message(STATUS "Icon path: ${QGC_WINDOWS_ICON_PATH}") - message(STATUS "RC resource file: ${QGC_WINDOWS_RESOURCE_FILE_PATH}") - message(STATUS "------------------------------------------------------------------") +if(WIN32 AND NOT ANDROID) + message(STATUS "") + message(STATUS "Windows Platform:") + message(STATUS " Icon: ${QGC_WINDOWS_ICON_PATH}") + message(STATUS " Resource file: ${QGC_WINDOWS_RESOURCE_FILE_PATH}") endif() -if(LINUX) - message(STATUS "-- Linux -----------------------------------------------------------") - message(STATUS "AppImage icon path: ${QGC_APPIMAGE_ICON_PATH}") - message(STATUS "------------------------------------------------------------------") +if(LINUX AND NOT ANDROID) + message(STATUS "") + message(STATUS "Linux Platform:") + if(QGC_CREATE_APPIMAGE) + message(STATUS " AppImage: Enabled") + endif() endif() + +message(STATUS "") +message(STATUS "==================================================================") +message(STATUS "") diff --git a/cmake/Toolchain.cmake b/cmake/Toolchain.cmake index c47ebcc96f42..47281e700804 100644 --- a/cmake/Toolchain.cmake +++ b/cmake/Toolchain.cmake @@ -1,60 +1,104 @@ +# ---------------------------------------------------------------------------- +# QGroundControl Toolchain Configuration +# Sets compiler, linker, and build tool settings +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# C++ Standard Requirements +# ---------------------------------------------------------------------------- set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +# ---------------------------------------------------------------------------- +# Qt-specific Automation +# ---------------------------------------------------------------------------- set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) +# ---------------------------------------------------------------------------- +# Build Configuration +# ---------------------------------------------------------------------------- set(CMAKE_COLOR_DIAGNOSTICS ON) -# set(CMAKE_EXPORT_BUILD_DATABASE ON) # Causes Configuration Error? -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Conflict with CMAKE_UNITY_BUILD +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Unity builds can improve compile times but may cause issues +# Enable with caution - conflicts with CMAKE_EXPORT_COMPILE_COMMANDS +# set(CMAKE_EXPORT_BUILD_DATABASE ON) # set(CMAKE_UNITY_BUILD ON) # set(CMAKE_UNITY_BUILD_BATCH_SIZE 8) +# ---------------------------------------------------------------------------- +# Security & Optimization Settings +# ---------------------------------------------------------------------------- + qgc_enable_pie() -qgc_enable_ipo() +if(NOT LINUX) + qgc_enable_ipo() +endif() +# ---------------------------------------------------------------------------- +# Compiler & Linker Optimizations +# ---------------------------------------------------------------------------- if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + # Use faster alternative linkers on non-Apple platforms if(NOT APPLE) qgc_set_linker() endif() - add_link_options("$<$:-flto=thin>") + + # Link-Time Optimization (LTO) for Release builds + # Thin LTO provides faster incremental builds (Clang-specific) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_link_options("$<$:-flto=thin>") + else() + # GCC uses standard LTO (handled by qgc_enable_ipo above) + endif() elseif(MSVC) + # MSVC-specific optimizations add_link_options("$<$:/LTCG:INCREMENTAL>") set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") endif() +# ---------------------------------------------------------------------------- +# Install Configuration +# ---------------------------------------------------------------------------- + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) if(LINUX) - # set(ENV{DESTDIR} "${CMAKE_BINARY_DIR}/staging") + # Linux uses AppDir structure for AppImage packaging set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/AppDir/usr" CACHE PATH "Install path prefix for AppImage" FORCE) else() + # Other platforms use staging directory set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/staging" CACHE PATH "Install path prefix" FORCE) endif() endif() +# ---------------------------------------------------------------------------- +# Cross-Compilation Configuration +# ---------------------------------------------------------------------------- if(CMAKE_CROSSCOMPILING) - if(NOT IS_DIRECTORY ${QT_HOST_PATH}) - message(FATAL_ERROR "You need to set QT_HOST_PATH to cross compile Qt.") + if(NOT DEFINED QT_HOST_PATH OR QT_HOST_PATH STREQUAL "") + message(FATAL_ERROR "Cross-compilation requires QT_HOST_PATH to be defined and set to a valid Qt host installation path") + endif() + + if(NOT IS_DIRECTORY "${QT_HOST_PATH}") + message(FATAL_ERROR "Cross-compilation QT_HOST_PATH is not a valid directory: ${QT_HOST_PATH}") endif() if(ANDROID) + # Android cross-compilation: search both target and host paths set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) endif() endif() +# ---------------------------------------------------------------------------- +# Platform Detection Helpers +# ---------------------------------------------------------------------------- if(APPLE AND NOT IOS) set(MACOS TRUE) - - # if(CMAKE_APPLE_SILICON_PROCESSOR MATCHES "arm64") - # if(CMAKE_APPLE_SILICON_PROCESSOR MATCHES "x86_64") - # if("${CMAKE_OSX_ARCHITECTURES}" MATCHES "arm64;x86_64" OR "${CMAKE_OSX_ARCHITECTURES}" MATCHES "x86_64;arm64") - # set(QGC_MACOS_UNIVERSAL_BUILD ON) - # endif() endif() diff --git a/cmake/find-modules/FindGStreamer.cmake b/cmake/find-modules/FindGStreamer.cmake index 3feb56093ebf..af3a1c208231 100644 --- a/cmake/find-modules/FindGStreamer.cmake +++ b/cmake/find-modules/FindGStreamer.cmake @@ -1,3 +1,16 @@ +# ============================================================================ +# FindGStreamer.cmake +# CMake find module for GStreamer multimedia framework +# +# Handles multiple platforms: Windows, Linux, macOS, Android, iOS +# Supports both static and dynamic linking +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Default Configuration +# ---------------------------------------------------------------------------- + +# Set default version based on platform if(NOT DEFINED GStreamer_FIND_VERSION) if(LINUX) set(GStreamer_FIND_VERSION 1.20) @@ -6,6 +19,7 @@ if(NOT DEFINED GStreamer_FIND_VERSION) endif() endif() +# Determine GStreamer root directory from various environment variables if(NOT DEFINED GStreamer_ROOT_DIR) if(DEFINED GSTREAMER_ROOT) set(GStreamer_ROOT_DIR ${GSTREAMER_ROOT}) @@ -14,10 +28,11 @@ if(NOT DEFINED GStreamer_ROOT_DIR) endif() if(DEFINED GStreamer_ROOT_DIR AND NOT EXISTS "${GStreamer_ROOT_DIR}") - message(STATUS "The user provided GStreamer directory does not exist: ${GStreamer_ROOT_DIR}") + message(STATUS "GStreamer: User-provided directory does not exist: ${GStreamer_ROOT_DIR}") endif() endif() +# Static vs dynamic linking preference if(NOT DEFINED GStreamer_USE_STATIC_LIBS) if(ANDROID OR IOS) set(GStreamer_USE_STATIC_LIBS ON) @@ -26,6 +41,7 @@ if(NOT DEFINED GStreamer_USE_STATIC_LIBS) endif() endif() +# Framework usage (macOS/iOS only) if(NOT DEFINED GStreamer_USE_FRAMEWORK) if(APPLE) set(GStreamer_USE_FRAMEWORK ON) @@ -34,10 +50,15 @@ if(NOT DEFINED GStreamer_USE_FRAMEWORK) endif() endif() -################################################################################ +# ============================================================================ +# Platform-Specific Configuration +# ============================================================================ set(PKG_CONFIG_ARGN) +# ---------------------------------------------------------------------------- +# Windows Platform +# ---------------------------------------------------------------------------- if(WIN32) if(NOT DEFINED GStreamer_ROOT_DIR) if(DEFINED ENV{GSTREAMER_1_0_ROOT_X86_64} AND EXISTS "$ENV{GSTREAMER_1_0_ROOT_X86_64}") @@ -71,6 +92,10 @@ if(WIN32) --define-variable=libdir=${GSTREAMER_LIB_PATH} --define-variable=includedir=${GSTREAMER_INCLUDE_PATH} ) + +# ---------------------------------------------------------------------------- +# Linux Platform +# ---------------------------------------------------------------------------- elseif(LINUX) if(NOT DEFINED GStreamer_ROOT_DIR) if(EXISTS "/usr") @@ -95,6 +120,10 @@ elseif(LINUX) set(GSTREAMER_INCLUDE_PATH "${GStreamer_ROOT_DIR}/include") set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_LIB_PATH}/pkgconfig:$ENV{PKG_CONFIG_PATH}") + +# ---------------------------------------------------------------------------- +# Android Platform +# ---------------------------------------------------------------------------- elseif(ANDROID) if(QGC_CUSTOM_GST_PACKAGE) set(_gst_android_url "https://qgroundcontrol.s3.us-west-2.amazonaws.com/android-gstreamer/qgc-android-gstreamer-${GStreamer_FIND_VERSION}.tar.xz") @@ -139,6 +168,21 @@ elseif(ANDROID) set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_LIB_PATH}/pkgconfig;${GSTREAMER_PLUGIN_PATH}/pkgconfig") list(APPEND PKG_CONFIG_ARGN --dont-define-prefix) elseif(CMAKE_HOST_UNIX) + if(CMAKE_HOST_APPLE) + # Try to find pkg-config in common Homebrew locations and in PATH + find_program(PKG_CONFIG_EXECUTABLE + NAMES pkg-config + PATHS /opt/homebrew/bin /usr/local/bin + NO_DEFAULT_PATH + ) + if(NOT PKG_CONFIG_EXECUTABLE) + # Fallback to PATH search if not found in Homebrew locations + find_program(PKG_CONFIG_EXECUTABLE pkg-config) + endif() + if(NOT PKG_CONFIG_EXECUTABLE) + message(FATAL_ERROR "Could not find pkg-config. Please install pkg-config using tools/setup/install-dependencies-osx.sh.") + endif() + endif() set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_LIB_PATH}/pkgconfig:${GSTREAMER_PLUGIN_PATH}/pkgconfig") endif() list(APPEND PKG_CONFIG_ARGN @@ -146,6 +190,10 @@ elseif(ANDROID) --define-variable=libdir=${GSTREAMER_LIB_PATH} --define-variable=includedir=${GSTREAMER_INCLUDE_PATH} ) + +# ---------------------------------------------------------------------------- +# macOS Platform +# ---------------------------------------------------------------------------- elseif(MACOS) if(NOT DEFINED GStreamer_ROOT_DIR) if(EXISTS "/Library/Frameworks/GStreamer.framework") @@ -179,8 +227,12 @@ elseif(MACOS) --define-variable=libdir=${GSTREAMER_LIB_PATH} --define-variable=includedir=${GSTREAMER_INCLUDE_PATH} ) + +# ---------------------------------------------------------------------------- +# iOS Platform (Currently Unsupported) +# ---------------------------------------------------------------------------- elseif(IOS) - message(FATAL_ERROR "GStreamer for iOS is Currently Unsupported.") + message(FATAL_ERROR "GStreamer for iOS is currently unsupported") CPMAddPackage( NAME gstreamer @@ -222,15 +274,20 @@ elseif(IOS) set(GSTREAMER_INCLUDE_PATH "${GSTREAMER_FRAMEWORK_PATH}/Headers") endif() +# ---------------------------------------------------------------------------- +# Validation +# ---------------------------------------------------------------------------- if(NOT EXISTS "${GStreamer_ROOT_DIR}" OR NOT EXISTS "${GSTREAMER_LIB_PATH}" OR NOT EXISTS "${GSTREAMER_PLUGIN_PATH}" OR NOT EXISTS "${GSTREAMER_INCLUDE_PATH}") - message(FATAL_ERROR "Could not locate GStreamer - check installation or set environment/cmake variables") + message(FATAL_ERROR "GStreamer: Could not locate required directories - check installation or set GStreamer_ROOT_DIR") endif() if(GStreamer_USE_FRAMEWORK AND NOT EXISTS "${GSTREAMER_FRAMEWORK_PATH}") - message(FATAL_ERROR "Could not locate GStreamer - check installation or set environment/cmake variables") + message(FATAL_ERROR "GStreamer: Could not locate framework at ${GSTREAMER_FRAMEWORK_PATH}") endif() -################################################################################ +# ============================================================================ +# Plugin & Dependency Configuration +# ============================================================================ if(GStreamer_USE_STATIC_LIBS) set(GSTREAMER_EXTRA_DEPS @@ -291,7 +348,9 @@ if(ANDROID) ) endif() -################################################################################ +# ============================================================================ +# Package Discovery +# ============================================================================ if(GStreamer_USE_FRAMEWORK) list(APPEND CMAKE_FRAMEWORK_PATH "${GSTREAMER_FRAMEWORK_PATH}") @@ -311,8 +370,11 @@ else() endif() set(GStreamer_VERSION "${PC_GSTREAMER_VERSION}") -################################################################################ +# ============================================================================ +# Component Discovery +# ============================================================================ +# Helper function to find individual GStreamer components function(find_gstreamer_component component pkgconfig_name) set(target GStreamer::${component}) @@ -340,8 +402,9 @@ function(find_gstreamer_component component pkgconfig_name) endif() endfunction() -################################################################################ - +# ---------------------------------------------------------------------------- +# Find Core Components (Always Required) +# ---------------------------------------------------------------------------- find_gstreamer_component(Core gstreamer-1.0) find_gstreamer_component(Base gstreamer-base-1.0) find_gstreamer_component(Video gstreamer-video-1.0) @@ -349,8 +412,9 @@ find_gstreamer_component(Gl gstreamer-gl-1.0) find_gstreamer_component(GlPrototypes gstreamer-gl-prototypes-1.0) find_gstreamer_component(Rtsp gstreamer-rtsp-1.0) -################################################################################ - +# ---------------------------------------------------------------------------- +# Find Optional Components (Based on FIND_COMPONENTS) +# ---------------------------------------------------------------------------- if(GlEgl IN_LIST GStreamer_FIND_COMPONENTS) find_gstreamer_component(GlEgl gstreamer-gl-egl-1.0) endif() @@ -363,7 +427,9 @@ if(GlX11 IN_LIST GStreamer_FIND_COMPONENTS) find_gstreamer_component(GlX11 gstreamer-gl-x11-1.0) endif() -################################################################################ +# ============================================================================ +# Package Finalization +# ============================================================================ include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GStreamer @@ -371,6 +437,9 @@ find_package_handle_standard_args(GStreamer HANDLE_COMPONENTS ) +# ---------------------------------------------------------------------------- +# Create Main GStreamer Target +# ---------------------------------------------------------------------------- if(GStreamer_FOUND AND NOT TARGET GStreamer::GStreamer) qt_add_library(GStreamer::GStreamer INTERFACE IMPORTED) @@ -396,8 +465,10 @@ if(GStreamer_FOUND AND NOT TARGET GStreamer::GStreamer) endif() return() else() - message(FATAL_ERROR "Could not locate GStreamer - check installation or set environment/cmake variables") + message(FATAL_ERROR "GStreamer: Could not locate GStreamer.framework") endif() + + # Android-specific link options elseif(ANDROID) target_link_options(GStreamer::GStreamer INTERFACE "-Wl,-Bsymbolic") if(CMAKE_SIZEOF_VOID_P EQUAL 4) @@ -423,8 +494,9 @@ if(GStreamer_FOUND AND NOT TARGET GStreamer::GStreamer) endif() endforeach() -################################################################################ - +# ---------------------------------------------------------------------------- +# Static Plugin Linking (Static Builds Only) +# ---------------------------------------------------------------------------- if(GStreamer_USE_STATIC_LIBS) qt_add_library(GStreamer::Plugins INTERFACE IMPORTED) target_link_directories(GStreamer::Plugins INTERFACE ${GSTREAMER_PLUGIN_PATH}) diff --git a/cmake/find-modules/_FindGStreamerMobile.cmake b/cmake/find-modules/_FindGStreamerMobile.cmake index 80d83efdf1ac..90311b27428e 100644 --- a/cmake/find-modules/_FindGStreamerMobile.cmake +++ b/cmake/find-modules/_FindGStreamerMobile.cmake @@ -108,7 +108,7 @@ if(ANDROID_ABI MATCHES "^armeabi") elseif(ANDROID_ABI STREQUAL "x86") set(NEEDS_NOTEXT_FIX TRUE) set(NEEDS_BSYMBOLIC_FIX TRUE) -# arm64: https://ffmpeg.org/pipermail/ffmpeg-devel/2022-July/298734.html +# arm64: https://ffmpeg.org/pipermail/ffmpeg-devel/2022-July/298734.html elseif(ANDROID_ABI STREQUAL "x86_64" OR ANDROID_ABI STREQUAL "arm64-v8a") set(NEEDS_BSYMBOLIC_FIX TRUE) endif() diff --git a/cmake/install/CPack/CreateCPackArchive.cmake b/cmake/install/CPack/CreateCPackArchive.cmake index 073d1cf1ee01..224b5160b574 100644 --- a/cmake/install/CPack/CreateCPackArchive.cmake +++ b/cmake/install/CPack/CreateCPackArchive.cmake @@ -1,5 +1,13 @@ +# ============================================================================ +# CreateCPackArchive.cmake +# ZIP archive package generator for cross-platform distribution +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# Archive Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "ZIP") set(CPACK_BINARY_ZIP ON) diff --git a/cmake/install/CPack/CreateCPackBundle.cmake b/cmake/install/CPack/CreateCPackBundle.cmake index 3c1caa27f73e..de3c2f689b6f 100644 --- a/cmake/install/CPack/CreateCPackBundle.cmake +++ b/cmake/install/CPack/CreateCPackBundle.cmake @@ -1,14 +1,29 @@ +# ============================================================================ +# CreateCPackBundle.cmake +# macOS application bundle package generator +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# Bundle Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "BUNDLE") set(CPACK_BINARY_BUNDLE ON) set(QGC_INSTALLER_SOURCE "${CMAKE_SOURCE_DIR}/deploy/macos") +# ---------------------------------------------------------------------------- +# Bundle Configuration +# ---------------------------------------------------------------------------- set(CPACK_BUNDLE_NAME ${CMAKE_PROJECT_NAME}) set(CPACK_BUNDLE_PLIST "${QGC_INSTALLER_SOURCE}/MacOSXBundleInfo.plist.in") set(CPACK_BUNDLE_ICON "${QGC_APP_ICON}") # set(CPACK_BUNDLE_STARTUP_COMMAND "") + +# ---------------------------------------------------------------------------- +# Code Signing Options +# ---------------------------------------------------------------------------- # set(CPACK_BUNDLE_APPLE_CERT_APP "") # set(CPACK_BUNDLE_APPLE_ENTITLEMENTS "") # set(CPACK_BUNDLE_APPLE_CODESIGN_FILES "") diff --git a/cmake/install/CPack/CreateCPackCommon.cmake b/cmake/install/CPack/CreateCPackCommon.cmake index db890e46b5b9..8bca881ff367 100644 --- a/cmake/install/CPack/CreateCPackCommon.cmake +++ b/cmake/install/CPack/CreateCPackCommon.cmake @@ -1,4 +1,11 @@ -# Variables common to all CPack Generators +# ============================================================================ +# CreateCPackCommon.cmake +# Common CPack configuration shared across all package generators +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Basic Package Information +# ---------------------------------------------------------------------------- set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME}) set(CPACK_PACKAGE_VENDOR ${QGC_ORG_NAME}) set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}) @@ -9,14 +16,26 @@ set(CPACK_PACKAGE_DESCRIPTION ${PROJECT_DESCRIPTION}) # set(CPACK_PACKAGE_DESCRIPTION_FILE "") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION}) set(CPACK_PACKAGE_HOMEPAGE_URL ${PROJECT_HOMEPAGE_URL}) + +# ---------------------------------------------------------------------------- +# Package Files and Directories +# ---------------------------------------------------------------------------- set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}") set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CMAKE_PROJECT_NAME}) set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/icons/qgroundcontrol.png") # set(CPACK_PACKAGE_CHECKSUM "") # set(CPACK_PROJECT_CONFIG_FILE "") + +# ---------------------------------------------------------------------------- +# Resource Files +# ---------------------------------------------------------------------------- set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/.github/COPYING.md") set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") # set(CPACK_RESOURCE_FILE_WELCOME "${CMAKE_CURRENT_SOURCE_DIR}/desc/welcome.txt") + +# ---------------------------------------------------------------------------- +# Package Options +# ---------------------------------------------------------------------------- # set(CPACK_MONOLITHIC_INSTALL # set(CPACK_GENERATOR # set(CPACK_OUTPUT_CONFIG_FILE @@ -25,14 +44,18 @@ set(CPACK_PACKAGE_EXECUTABLES ${CMAKE_PROJECT_NAME}) set(CPACK_VERBATIM_VARIABLES ON) set(CPACK_THREADS -4) -# Variables for Source Package Generators +# ---------------------------------------------------------------------------- +# Source Package Configuration +# ---------------------------------------------------------------------------- # set(CPACK_SOURCE_PACKAGE_FILE_NAME # set(CPACK_SOURCE_STRIP_FILES # set(CPACK_SOURCE_GENERATOR # set(CPACK_SOURCE_OUTPUT_CONFIG_FILE # set(CPACK_SOURCE_IGNORE_FILES -# Variables for Advanced Use +# ---------------------------------------------------------------------------- +# Advanced Configuration Options +# ---------------------------------------------------------------------------- # set(CPACK_CMAKE_GENERATOR # set(CPACK_INSTALL_CMAKE_PROJECTS # set(CPACK_SYSTEM_NAME diff --git a/cmake/install/CPack/CreateCPackDMG.cmake b/cmake/install/CPack/CreateCPackDMG.cmake index bb84198c33a4..1a2057970818 100644 --- a/cmake/install/CPack/CreateCPackDMG.cmake +++ b/cmake/install/CPack/CreateCPackDMG.cmake @@ -1,10 +1,21 @@ +# ============================================================================ +# CreateCPackDMG.cmake +# macOS DMG disk image package generator +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# DMG Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "DragNDrop") set(CPACK_BINARY_DRAGNDROP ON) set(QGC_INSTALLER_SOURCE "${CMAKE_SOURCE_DIR}/deploy/macos") +# ---------------------------------------------------------------------------- +# DMG Options +# ---------------------------------------------------------------------------- # set(CPACK_DMG_VOLUME_NAME ${CPACK_PACKAGE_FILE_NAME}) set(CPACK_DMG_FORMAT UDBZ) # set(CPACK_DMG_DS_STORE) @@ -16,6 +27,10 @@ set(CPACK_DMG_FORMAT UDBZ) # set(CPACK_DMG_SLA_LANGUAGES "") # set(CPACK_DMG__FILE_NAME "") set(CPACK_DMG_FILESYSTEM APFS) + +# ---------------------------------------------------------------------------- +# macOS Disk Utility Commands +# ---------------------------------------------------------------------------- # set(CPACK_COMMAND_HDIUTIL "/usr/bin/sudo /usr/bin/hdiutil") # set(CPACK_COMMAND_SETFILE "") # set(CPACK_COMMAND_REZ "") diff --git a/cmake/install/CPack/CreateCPackDeb.cmake b/cmake/install/CPack/CreateCPackDeb.cmake index be816520101a..58e8d5eb2a66 100644 --- a/cmake/install/CPack/CreateCPackDeb.cmake +++ b/cmake/install/CPack/CreateCPackDeb.cmake @@ -1,10 +1,21 @@ +# ============================================================================ +# CreateCPackDeb.cmake +# Debian/Ubuntu .deb package generator +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# DEB Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "DEB") set(CPACK_BINARY_DEB ON) set(QGC_INSTALLER_SOURCE "${CMAKE_SOURCE_DIR}/deploy/linux") +# ---------------------------------------------------------------------------- +# Package Metadata +# ---------------------------------------------------------------------------- set(CPACK_DEB_COMPONENT_INSTALL ON) set(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME}) set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) @@ -21,6 +32,10 @@ set(CPACK_DEBIAN_ARCHIVE_TYPE xz) # set(CPACK_DEBIAN_COMPRESSION_TYPE # set(CPACK_DEBIAN_PACKAGE_PRIORITY set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${PROJECT_HOMEPAGE_URL}) + +# ---------------------------------------------------------------------------- +# Advanced DEB Options +# ---------------------------------------------------------------------------- # set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS 1) # set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS # set(CPACK_DEBIAN_PACKAGE_DEBUG diff --git a/cmake/install/CPack/CreateCPackIFW.cmake b/cmake/install/CPack/CreateCPackIFW.cmake index c0ef1086b712..d913a62f0003 100644 --- a/cmake/install/CPack/CreateCPackIFW.cmake +++ b/cmake/install/CPack/CreateCPackIFW.cmake @@ -1,18 +1,31 @@ +# ============================================================================ +# CreateCPackIFW.cmake +# Qt Installer Framework (IFW) package generator for cross-platform installers +# ============================================================================ + include(CreateCPackCommon) -# Hints for Finding QtIFW +# ---------------------------------------------------------------------------- +# Qt Installer Framework Detection +# ---------------------------------------------------------------------------- +# Hints for finding QtIFW set(CPACK_IFW_ROOT "${Qt6_ROOT_DIR}/../../Tools/QtInstallerFramework/*") set(QTIFWDIR "${Qt6_ROOT_DIR}/../../Tools/QtInstallerFramework/*") include(CPackIFW) +# ---------------------------------------------------------------------------- +# IFW Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "IFW") set(CPACK_BINARY_IFW ON) -# Debug +# Debug output set(CPACK_IFW_VERBOSE ON) -# Package +# ---------------------------------------------------------------------------- +# Package Appearance +# ---------------------------------------------------------------------------- set(CPACK_IFW_PACKAGE_TITLE "${QGC_INSTALLER_NAME}") set(CPACK_IFW_PACKAGE_PUBLISHER "${QGC_ORG_NAME}") set(CPACK_IFW_PRODUCT_URL "${CMAKE_PROJECT_HOMEPAGE_URL}") @@ -28,6 +41,10 @@ set(CPACK_IFW_PACKAGE_WIZARD_STYLE "Modern") # set(CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST OFF) # set(CPACK_IFW_PACKAGE_TITLE_COLOR "#007A5C") # set(CPACK_IFW_PACKAGE_STYLE_SHEET "${INSTALLER_ROOT}/config/dev/style.qss") + +# ---------------------------------------------------------------------------- +# Platform-Specific Install Directories +# ---------------------------------------------------------------------------- if(${CMAKE_SYSTEM_NAME} MATCHES Linux) set(CPACK_IFW_TARGET_DIRECTORY "@HomeDir@/${CMAKE_PROJECT_NAME}") set(CPACK_IFW_ADMIN_TARGET_DIRECTORY "@HomeDir@/${CMAKE_PROJECT_NAME}") diff --git a/cmake/install/CPack/CreateCPackNSIS.cmake b/cmake/install/CPack/CreateCPackNSIS.cmake index 2e2844ccfaf5..2331832fe766 100644 --- a/cmake/install/CPack/CreateCPackNSIS.cmake +++ b/cmake/install/CPack/CreateCPackNSIS.cmake @@ -1,16 +1,31 @@ +# ============================================================================ +# CreateCPackNSIS.cmake +# Windows NSIS installer package generator +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# NSIS Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "NSIS") set(CPACK_BINARY_NSIS ON) set(QGC_INSTALLER_SOURCE "${CMAKE_BINARY_DIR}/deploy/windows") +# ---------------------------------------------------------------------------- +# Installer Appearance +# ---------------------------------------------------------------------------- set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") set(CPACK_NSIS_MUI_ICON "${QGC_INSTALLER_SOURCE}/WindowsQGC.ico") set(CPACK_NSIS_MUI_UNIICON "${QGC_INSTALLER_SOURCE}/WindowsQGC.ico") # set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "") # set(CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP "") # set(CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP "") + +# ---------------------------------------------------------------------------- +# Install/Uninstall Commands +# ---------------------------------------------------------------------------- # set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " CreateDirectory \"\$SMPROGRAMS\\${CMAKE_PROJECT_NAME}\" @@ -24,6 +39,9 @@ set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " Delete \"\$SMPROGRAMS\\${CMAKE_PROJECT_NAME}\\${CMAKE_PROJECT_NAME} (GPU Safe Mode).lnk\" RMDir /r /REBOOTOK \"\$SMPROGRAMS\\${CMAKE_PROJECT_NAME}\" ") +# ---------------------------------------------------------------------------- +# Installer Options +# ---------------------------------------------------------------------------- set(CPACK_NSIS_COMPRESSOR "/SOLID /FINAL lzma") set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) set(CPACK_NSIS_MODIFY_PATH ON) @@ -40,6 +58,10 @@ set(CPACK_NSIS_URL_INFO_ABOUT ${CPACK_PACKAGE_HOMEPAGE_URL}) # set(CPACK_NSIS_MUI_FINISHPAGE_RUN "") # set(CPACK_NSIS_MENU_LINKS "") set(CPACK_NSIS_UNINSTALL_NAME "${CMAKE_PROJECT_NAME}-Uninstall") + +# ---------------------------------------------------------------------------- +# Installer UI Customization +# ---------------------------------------------------------------------------- # set(CPACK_NSIS_WELCOME_TITLE "") # set(CPACK_NSIS_WELCOME_TITLE_3LINES "") # set(CPACK_NSIS_FINISH_TITLE "") diff --git a/cmake/install/CPack/CreateCPackProductBuild.cmake b/cmake/install/CPack/CreateCPackProductBuild.cmake index 2b25244c1db6..266c08865368 100644 --- a/cmake/install/CPack/CreateCPackProductBuild.cmake +++ b/cmake/install/CPack/CreateCPackProductBuild.cmake @@ -1,10 +1,21 @@ +# ============================================================================ +# CreateCPackProductBuild.cmake +# macOS .pkg installer package generator using productbuild +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# ProductBuild Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "PRODUCTBUILD") set(CPACK_BINARY_PRODUCTBUILD ON) set(QGC_INSTALLER_SOURCE "${CMAKE_SOURCE_DIR}/deploy/macos") +# ---------------------------------------------------------------------------- +# Package Building Tools and Signing +# ---------------------------------------------------------------------------- # set(CPACK_COMMAND_PRODUCTBUILD) # set(CPACK_PRODUCTBUILD_IDENTIFIER) # set(CPACK_PRODUCTBUILD_IDENTITY_NAME) @@ -20,6 +31,9 @@ set(QGC_INSTALLER_SOURCE "${CMAKE_SOURCE_DIR}/deploy/macos") # set(CPACK_PRODUCTBUILD_DOMAINS_USER) # set(CPACK_PRODUCTBUILD_DOMAINS_ROOT) +# ---------------------------------------------------------------------------- +# Installer Background Images (Light and Dark Mode) +# ---------------------------------------------------------------------------- # set(CPACK_PRODUCTBUILD_BACKGROUND) # set(CPACK_PRODUCTBUILD_BACKGROUND_ALIGNMENT) # set(CPACK_PRODUCTBUILD_BACKGROUND_SCALING) diff --git a/cmake/install/CPack/CreateCPackRPM.cmake b/cmake/install/CPack/CreateCPackRPM.cmake index fa865c05022a..9c91900ff9e4 100644 --- a/cmake/install/CPack/CreateCPackRPM.cmake +++ b/cmake/install/CPack/CreateCPackRPM.cmake @@ -1,10 +1,21 @@ +# ============================================================================ +# CreateCPackRPM.cmake +# Red Hat/Fedora/CentOS .rpm package generator +# ============================================================================ + include(CreateCPackCommon) +# ---------------------------------------------------------------------------- +# RPM Generator Configuration +# ---------------------------------------------------------------------------- list(APPEND CPACK_GENERATOR "RPM") set(CPACK_BINARY_RPM ON) set(QGC_INSTALLER_SOURCE "${CMAKE_SOURCE_DIR}/deploy/linux") +# ---------------------------------------------------------------------------- +# Package Metadata +# ---------------------------------------------------------------------------- set(CPACK_RPM_COMPONENT_INSTALL ON) # CPACK_RPM_PACKAGE_SUMMARY # CPACK_RPM_PACKAGE_NAME @@ -21,6 +32,10 @@ set(CPACK_RPM_PACKAGE_ARCHITECTURE "amd64") # CPACK_RPM_PACKAGE_URL set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) set(CPACK_RPM_COMPRESSION_TYPE xz) + +# ---------------------------------------------------------------------------- +# RPM Dependencies and Requirements +# ---------------------------------------------------------------------------- # CPACK_RPM_PACKAGE_AUTOREQ # CPACK_RPM_PACKAGE_AUTOPROV # CPACK_RPM_PACKAGE_AUTOREQPROV diff --git a/cmake/install/CreateAppImage.cmake b/cmake/install/CreateAppImage.cmake index 54e205b8afa6..322c1851d04e 100644 --- a/cmake/install/CreateAppImage.cmake +++ b/cmake/install/CreateAppImage.cmake @@ -1,13 +1,21 @@ -# TODO: # go-appimage, updateinformation w/ GitHub Releases, signing +# ============================================================================ +# CreateAppImage.cmake +# Creates AppImage packages for Linux distribution +# ============================================================================ +# +# TODO: Implement go-appimage, update information with GitHub Releases, signing +# -message(STATUS "QGC: Creating AppImage") +message(STATUS "QGC: Creating AppImage...") set(APPDIR_PATH "${CMAKE_BINARY_DIR}/AppDir") set(APPIMAGE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${CMAKE_SYSTEM_PROCESSOR}.AppImage") -#===========================================================================# -# Download Tools +# ============================================================================ +# Helper Functions +# ============================================================================ +# Download and cache build tools function(download_tool VAR URL) cmake_path(GET URL FILENAME _name) set(_dest "${CMAKE_BINARY_DIR}/tools/${_name}") @@ -24,14 +32,25 @@ function(download_tool VAR URL) set(${VAR}_PATH "${_dest}" PARENT_SCOPE) endfunction() -download_tool(LINUXDEPLOY https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${CMAKE_SYSTEM_PROCESSOR}.AppImage) -download_tool(APPIMAGETOOL https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CMAKE_SYSTEM_PROCESSOR}.AppImage) +# ============================================================================ +# Download Required Tools +# ============================================================================ + +message(STATUS "QGC: Downloading AppImage build tools...") + +download_tool(LINUXDEPLOY https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${CMAKE_SYSTEM_PROCESSOR}.AppImage) +download_tool(APPIMAGETOOL https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CMAKE_SYSTEM_PROCESSOR}.AppImage) + +# AppImageLint is only available for x86_64 if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") - download_tool(APPIMAGELINT https://github.com/TheAssassin/appimagelint/releases/download/continuous/appimagelint-${CMAKE_SYSTEM_PROCESSOR}.AppImage) + download_tool(APPIMAGELINT https://github.com/TheAssassin/appimagelint/releases/download/continuous/appimagelint-${CMAKE_SYSTEM_PROCESSOR}.AppImage) endif() -#===========================================================================# -# Bundle the runtime +# ============================================================================ +# Bundle Runtime Dependencies +# ============================================================================ + +message(STATUS "QGC: Bundling runtime dependencies with linuxdeploy...") execute_process( COMMAND "${LINUXDEPLOY_PATH}" @@ -44,27 +63,39 @@ execute_process( COMMAND_ERROR_IS_FATAL ANY ) -#===========================================================================# -# Build the final AppImage +# ============================================================================ +# Build Final AppImage +# ============================================================================ + +message(STATUS "QGC: Building AppImage package...") set(ENV{ARCH} ${CMAKE_SYSTEM_PROCESSOR}) set(ENV{VERSION} ${CMAKE_PROJECT_VERSION}) + execute_process( COMMAND "${APPIMAGETOOL_PATH}" "${APPDIR_PATH}" "${APPIMAGE_PATH}" COMMAND_ECHO STDOUT COMMAND_ERROR_IS_FATAL ANY ) -#===========================================================================# -# Lint +message(STATUS "QGC: AppImage created successfully: ${APPIMAGE_PATH}") + +# ============================================================================ +# Validation & Linting +# ============================================================================ if(EXISTS "${APPIMAGELINT_PATH}") + message(STATUS "QGC: Running AppImage linter...") execute_process( COMMAND "${APPIMAGELINT_PATH}" "${APPIMAGE_PATH}" RESULT_VARIABLE LINT_RESULT COMMAND_ECHO STDOUT ) if(NOT LINT_RESULT EQUAL 0) - message(WARNING "QGC: appimagelint reported problems; see log above") + message(WARNING "QGC: AppImageLint reported issues - see output above") + else() + message(STATUS "QGC: AppImage passed validation") endif() +else() + message(STATUS "QGC: AppImageLint not available, skipping validation") endif() diff --git a/cmake/install/CreateMacDMG.cmake b/cmake/install/CreateMacDMG.cmake index b58fe9dc127d..13c1516be268 100644 --- a/cmake/install/CreateMacDMG.cmake +++ b/cmake/install/CreateMacDMG.cmake @@ -1,33 +1,37 @@ -# -- Variables ---------------------------------------- -# QGC_STAGING_BUNDLE_PATH => full path to MyApp.app - -# --------------------------------------------------------------------------- -# 1. Grab or locate create-dmg -# --------------------------------------------------------------------------- -find_program(CREATE_DMG_PROGRAM create-dmg) -if(NOT CREATE_DMG_PROGRAM) - message(STATUS "QGC: Downloading create-dmg") - CPMAddPackage( - NAME create-dmg - GITHUB_REPOSITORY create-dmg/create-dmg - GIT_TAG master - DOWNLOAD_ONLY - ) - set(CREATE_DMG_PROGRAM "${create-dmg_SOURCE_DIR}/create-dmg") -endif() - -# --------------------------------------------------------------------------- -# 2. Build the DMG with a nice drag-and-drop layout -# --------------------------------------------------------------------------- +# ============================================================================ +# CreateMacDMG.cmake +# Creates macOS DMG disk image for distribution +# ============================================================================ +# +# Required Variables (passed from Install.cmake): +# QGC_STAGING_BUNDLE_PATH => Full path to MyApp.app bundle +# CREATE_DMG_PROGRAM => Full path to create-dmg program +# + +message(STATUS "QGC: Creating macOS DMG disk image...") + +# ============================================================================ +# Prepare Package Directory +# ============================================================================ + set(QGC_DMG_PATH "${CMAKE_BINARY_DIR}/package") + +# Clean and create package directory file(REMOVE_RECURSE "${QGC_DMG_PATH}") file(MAKE_DIRECTORY "${QGC_DMG_PATH}") + +# Copy the application bundle to package directory file(COPY "${QGC_STAGING_BUNDLE_PATH}" DESTINATION "${QGC_DMG_PATH}") -cmake_path(GET QGC_STAGING_BUNDLE_PATH STEM QGC_TARGET_APP_NAME) +# ============================================================================ +# Create DMG +# ============================================================================ +cmake_path(GET QGC_STAGING_BUNDLE_PATH STEM QGC_TARGET_APP_NAME) set(QGC_DMG_NAME "${QGC_TARGET_APP_NAME}.dmg") +message(STATUS "QGC: Building ${QGC_DMG_NAME}...") + execute_process( COMMAND "${CREATE_DMG_PROGRAM}" --volname "${QGC_TARGET_APP_NAME}" @@ -39,4 +43,4 @@ execute_process( COMMAND_ERROR_IS_FATAL ANY ) -message(STATUS "QGC: Created ${QGC_DMG_NAME}") +message(STATUS "QGC: DMG created successfully: ${CMAKE_BINARY_DIR}/${QGC_DMG_NAME}") diff --git a/cmake/install/CreateQGCInstaller.cmake b/cmake/install/CreateQGCInstaller.cmake index 37b8563f632c..b3d94659d82e 100644 --- a/cmake/install/CreateQGCInstaller.cmake +++ b/cmake/install/CreateQGCInstaller.cmake @@ -1,12 +1,23 @@ -message(STATUS "Creating QGC Installer") +# ============================================================================ +# CreateQGCInstaller.cmake +# Generates installer packages using Qt Installer Framework +# ============================================================================ + +message(STATUS "QGC: Creating installer package") include(CMakePrintHelpers) +# ---------------------------------------------------------------------------- +# Qt Installer Framework Detection +# ---------------------------------------------------------------------------- set(QT_INSTALLER_FRAMEWORK_DIR ${QT_ROOT_DIR}/../../Tools/QtInstallerFramework) find_program(QT_INSTALLER_FRAMEWORK binarycreator PATHS "${QT_INSTALLER_FRAMEWORK_ROOT}/*/bin" ) +# ---------------------------------------------------------------------------- +# Installer Source Directories +# ---------------------------------------------------------------------------- set(INSTALLER_SOURCE_DIR ${CMAKE_SOURCE_DIR}/deploy/installer) set(INSTALLER_SOURCE_CONFIG_DIR ${INSTALLER_SOURCE_DIR}/config) set(INSTALLER_SOURCE_PACKAGES_DIR ${INSTALLER_SOURCE_DIR}/packages) @@ -15,6 +26,9 @@ set(INSTALLER_SOURCE_PACKAGES_QGC_DIR ${INSTALLER_SOURCE_PACKAGES_DIR}/org.mavli set(INSTALLER_SOURCE_PACKAGES_QGC_DATA_DIR ${INSTALLER_SOURCE_PACKAGES_QGC_DIR}/data) set(INSTALLER_SOURCE_PACKAGES_QGC_META_DIR ${INSTALLER_SOURCE_PACKAGES_QGC_DIR}/meta) +# ---------------------------------------------------------------------------- +# Installer Output Directories +# ---------------------------------------------------------------------------- set(INSTALLER_OUTPUT_DIR ${CMAKE_BINARY_DIR}/installer) set(INSTALLER_OUTPUT_CONFIG_DIR ${INSTALLER_OUTPUT_DIR}/config) set(INSTALLER_OUTPUT_PACKAGES_DIR ${INSTALLER_OUTPUT_DIR}/packages) @@ -23,6 +37,10 @@ set(INSTALLER_OUTPUT_PACKAGES_QGC_DIR ${INSTALLER_OUTPUT_PACKAGES_DIR}/org.mavli set(INSTALLER_OUTPUT_PACKAGES_QGC_DATA_DIR ${INSTALLER_OUTPUT_PACKAGES_QGC_DIR}/data) set(INSTALLER_OUTPUT_PACKAGES_QGC_META_DIR ${INSTALLER_OUTPUT_PACKAGES_QGC_DIR}/meta) +# ---------------------------------------------------------------------------- +# Create Output Directory Structure +# ---------------------------------------------------------------------------- + file(MAKE_DIRECTORY ${INSTALLER_OUTPUT_DIR}) file(MAKE_DIRECTORY ${INSTALLER_OUTPUT_CONFIG_DIR}) file(MAKE_DIRECTORY ${INSTALLER_OUTPUT_PACKAGES_DIR}) @@ -31,6 +49,9 @@ file(MAKE_DIRECTORY ${INSTALLER_OUTPUT_PACKAGES_QGC_DIR}) file(MAKE_DIRECTORY ${INSTALLER_OUTPUT_PACKAGES_QGC_DATA_DIR}) file(MAKE_DIRECTORY ${INSTALLER_OUTPUT_PACKAGES_QGC_META_DIR}) +# ---------------------------------------------------------------------------- +# Configure Installer Templates +# ---------------------------------------------------------------------------- configure_file( ${INSTALLER_SOURCE_CONFIG_DIR}/config.xml.in ${INSTALLER_OUTPUT_CONFIG_DIR}/config.xml @@ -43,27 +64,23 @@ configure_file( @ONLY ) -# file(COPY ${QGC_INSTALLER_ROOT} DESTINATION ${QGC_INSTALLER_OUTPUT_DIR}) - +# TODO: Copy additional resources when needed # file(COPY ${QGC_APP_ICON} DESTINATION ${QGC_INSTALLER_ROOT}/config/) # file(COPY ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${QGC_INSTALLER_ROOT}/README.md) # file(COPY ${CMAKE_SOURCE_DIR}/.github/COPYING.md DESTINATION ${QGC_PACKAGE_ROOT}/meta/license.txt) - # file(GLOB_RECURSE FILES_TO_INSTALL RELATIVE ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_PREFIX}/**) # file(COPY ${FILES_TO_INSTALL} DESTINATION ${QGC_PACKAGE_ROOT}/data/) -# cmake_print_variables(QGC_INSTALLER_ROOT FILES_TO_INSTALL) +# ---------------------------------------------------------------------------- +# Platform-Specific Installer Names +# ---------------------------------------------------------------------------- if(WIN32) - file(DOWNLOAD https://firmware.ardupilot.org/Tools/MissionPlanner/driver.msi - ${INSTALLER_SOURCE_PACKAGES_QGC_DATA_DIR}/driver.msi - SHOW_PROGRESS - STATUS DRIVER_DOWNLOAD_STATUS - TIMEOUT 60 - ) -# file(COPY ${CMAKE_SOURCE_DIR}/deploy/windows/driver.msi DESTINATION ${INSTALLER_SOURCE_PACKAGES_QGC_DATA_DIR}/driver.msi) set(QGC_INSTALLER_NAME ${CMAKE_PROJECT_NAME}-Installer-${CMAKE_SYSTEM_PROCESSOR}.exe) -# endif() +endif() +# ---------------------------------------------------------------------------- +# Generate Installer +# ---------------------------------------------------------------------------- execute_process( COMMAND ${QT_INSTALLER_FRAMEWORK} --offline-only -c ${INSTALLER_OUTPUT_CONFIG_DIR}/config.xml -p ${INSTALLER_OUTPUT_PACKAGES_DIR} ${CMAKE_BINARY_DIR}/${QGC_INSTALLER_NAME} ) diff --git a/cmake/install/CreateWinInstaller.cmake b/cmake/install/CreateWinInstaller.cmake index 4fac604722a4..38151821188e 100644 --- a/cmake/install/CreateWinInstaller.cmake +++ b/cmake/install/CreateWinInstaller.cmake @@ -1,24 +1,36 @@ +# ============================================================================ +# CreateWinInstaller.cmake +# Windows NSIS installer creation using makensis +# ============================================================================ + message(STATUS "QGC: Creating Windows NSIS Installer") +# ---------------------------------------------------------------------------- +# Validate Required Variables +# ---------------------------------------------------------------------------- +foreach(p IN ITEMS + QGC_WINDOWS_ICON_PATH + QGC_WINDOWS_INSTALL_HEADER_PATH + QGC_WINDOWS_INSTALLER_SCRIPT + QGC_WINDOWS_OUT + CMAKE_INSTALL_PREFIX) + if(NOT DEFINED ${p}) + message(FATAL_ERROR "QGC: Missing required var: ${p}") + endif() +endforeach() + +# ---------------------------------------------------------------------------- +# Convert Paths to Native Windows Format +# ---------------------------------------------------------------------------- file(TO_NATIVE_PATH "${QGC_WINDOWS_ICON_PATH}" QGC_INSTALLER_ICON) file(TO_NATIVE_PATH "${QGC_WINDOWS_INSTALL_HEADER_PATH}" QGC_INSTALLER_HEADER_BITMAP) -file(TO_NATIVE_PATH "${QGC_WINDOWS_DRIVER_MSI}" QGC_INSTALLER_DRIVER_MSI) file(TO_NATIVE_PATH "${QGC_WINDOWS_INSTALLER_SCRIPT}" QGC_NSIS_INSTALLER_SCRIPT) file(TO_NATIVE_PATH "${QGC_WINDOWS_OUT}" QGC_INSTALLER_OUT) +file(TO_NATIVE_PATH "${CMAKE_INSTALL_PREFIX}" QGC_PAYLOAD_DIR) -set(QGC_NSIS_INSTALLER_PARAMETERS - /DDRIVER_MSI=${QGC_INSTALLER_DRIVER_MSI} - /DINSTALLER_ICON=${QGC_INSTALLER_ICON} - /DHEADER_BITMAP=${QGC_INSTALLER_HEADER_BITMAP} - /DAPPNAME=${CMAKE_PROJECT_NAME} - /DEXENAME=${CMAKE_PROJECT_NAME} - /DORGNAME=${QGC_ORG_NAME} - /DDESTDIR=${CMAKE_INSTALL_PREFIX} - /NOCD - "/XOutFile ${QGC_INSTALLER_OUT}" - ${QGC_NSIS_INSTALLER_SCRIPT} -) - +# ---------------------------------------------------------------------------- +# Locate NSIS makensis Utility +# ---------------------------------------------------------------------------- set(_pf86 "ProgramFiles(x86)") set(_PF86 "PROGRAMFILES(x86)") find_program(QGC_NSIS_INSTALLER_CMD makensis @@ -28,8 +40,41 @@ find_program(QGC_NSIS_INSTALLER_CMD makensis REQUIRED ) +# ---------------------------------------------------------------------------- +# Build NSIS Command Arguments +# ---------------------------------------------------------------------------- + +set(_nsis_args + /NOCD + /INPUTCHARSET UTF8 + /V4 + "/DAPPNAME=${CMAKE_PROJECT_NAME}" + "/DEXENAME=${CMAKE_PROJECT_NAME}" + "/DORGNAME=${QGC_ORG_NAME}" + "/DDESTDIR=${QGC_PAYLOAD_DIR}" +) + +if(EXISTS "${QGC_INSTALLER_ICON}") + list(APPEND _nsis_args "/DINSTALLER_ICON=${QGC_INSTALLER_ICON}") +endif() + +if(EXISTS "${QGC_INSTALLER_HEADER_BITMAP}") + list(APPEND _nsis_args "/DHEADER_BITMAP=${QGC_INSTALLER_HEADER_BITMAP}") +endif() + +set(_APPVER "${CMAKE_PROJECT_VERSION}") +if(_APPVER) + list(APPEND _nsis_args "/DAPPVERSION=${_APPVER}") +endif() + +list(APPEND _nsis_args "/XOutFile ${QGC_INSTALLER_OUT}") + +# ---------------------------------------------------------------------------- +# Execute NSIS Installer Creation +# ---------------------------------------------------------------------------- execute_process( - COMMAND ${QGC_NSIS_INSTALLER_CMD} ${QGC_NSIS_INSTALLER_PARAMETERS} + COMMAND "${QGC_NSIS_INSTALLER_CMD}" ${_nsis_args} "${QGC_NSIS_INSTALLER_SCRIPT}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND_ECHO STDOUT COMMAND_ERROR_IS_FATAL ANY ) diff --git a/cmake/install/Install.cmake b/cmake/install/Install.cmake index f0b3bbd8c333..3ca169a3787c 100644 --- a/cmake/install/Install.cmake +++ b/cmake/install/Install.cmake @@ -1,5 +1,16 @@ +# ============================================================================ +# QGroundControl Installation Configuration +# Handles platform-specific installation and packaging +# ============================================================================ + include(InstallRequiredSystemLibraries) +# Note: Installer generation could be conditioned on Release builds +# if(QGC_BUILD_INSTALLER AND CMAKE_INSTALL_CONFIG_NAME MATCHES "^[Rr]elease$") + +# ---------------------------------------------------------------------------- +# Main Target Installation +# ---------------------------------------------------------------------------- install( TARGETS ${CMAKE_PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -8,18 +19,15 @@ install( BUNDLE DESTINATION . ) +# ---------------------------------------------------------------------------- +# Qt Deployment Script Generation +# ---------------------------------------------------------------------------- set(deploy_tool_options_arg "") + if(MACOS OR WIN32) list(APPEND deploy_tool_options_arg "-qmldir=${CMAKE_SOURCE_DIR}") if(MACOS) - # if(DEFINED ENV{QGC_MACOS_SIGNING_IDENTITY}) - # message(STATUS "QGC: Deploy Sign For Notarization") - # list(APPEND deploy_tool_options_arg "-sign-for-notarization=\"$ENV{QGC_MACOS_SIGNING_IDENTITY}\"") - # else() - # message(STATUS "QGC: Deploy AD-HOC Codesign") - # list(APPEND deploy_tool_options_arg "-codesign=-") - # endif() - # list(APPEND deploy_tool_options_arg "-dmg" "-fs=APFS") + list(APPEND deploy_tool_options_arg "-appstore-compliant") endif() endif() @@ -31,11 +39,25 @@ qt_generate_deploy_qml_app_script( DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM DEPLOY_TOOL_OPTIONS ${deploy_tool_options_arg} ) + install(SCRIPT ${deploy_script}) +message(STATUS "QGC: Qt deployment script: ${deploy_script}") + +# ============================================================================ +# Platform-Specific Installation +# ============================================================================ +# ---------------------------------------------------------------------------- +# Android Installation +# ---------------------------------------------------------------------------- if(ANDROID) + # Android deployment handled by Qt # get_target_property(QGC_ANDROID_DEPLOY_FILE ${CMAKE_PROJECT_NAME} QT_ANDROID_DEPLOYMENT_SETTINGS_FILE) # cmake_print_variables(QGC_ANDROID_DEPLOY_FILE) + +# ---------------------------------------------------------------------------- +# Linux Installation & AppImage Creation +# ---------------------------------------------------------------------------- elseif(LINUX) configure_file( "${QGC_APPIMAGE_DESKTOP_ENTRY_PATH}" @@ -62,13 +84,14 @@ elseif(LINUX) @ONLY ) install( - PROGRAMS "${CMAKE_BINARY_DIR}/${QGC_PACKAGE_NAME}.appdata.xml" + FILES "${CMAKE_BINARY_DIR}/${QGC_PACKAGE_NAME}.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo/" ) install( FILES "${QGC_APPIMAGE_APPRUN_PATH}" DESTINATION "${CMAKE_BINARY_DIR}/" ) + # Pass variables to AppImage creation script install(CODE " set(CMAKE_PROJECT_NAME ${CMAKE_PROJECT_NAME}) set(CMAKE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION}) @@ -76,13 +99,18 @@ elseif(LINUX) set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) ") install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/install/CreateAppImage.cmake") + +# ---------------------------------------------------------------------------- +# Windows Installation & Installer Creation +# ---------------------------------------------------------------------------- elseif(WIN32) + # Pass variables to Windows installer creation script install(CODE " set(CMAKE_PROJECT_NAME ${CMAKE_PROJECT_NAME}) + set(CMAKE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION}) set(QGC_ORG_NAME ${QGC_ORG_NAME}) set(QGC_WINDOWS_ICON_PATH \"${QGC_WINDOWS_ICON_PATH}\") set(QGC_WINDOWS_INSTALL_HEADER_PATH \"${QGC_WINDOWS_INSTALL_HEADER_PATH}\") - set(QGC_WINDOWS_DRIVER_MSI \"${CMAKE_SOURCE_DIR}/deploy/windows/driver.msi\") if(CMAKE_CROSSCOMPILING) set(QGC_WINDOWS_OUT \"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-installer-${CMAKE_HOST_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_PROCESSOR}.exe\") else() @@ -91,29 +119,42 @@ elseif(WIN32) set(QGC_WINDOWS_INSTALLER_SCRIPT \"${CMAKE_SOURCE_DIR}/deploy/windows/nullsoft_installer.nsi\") ") install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/install/CreateWinInstaller.cmake") -elseif(MACOS) - install(CODE " - set(QGC_STAGING_BUNDLE_PATH \"${CMAKE_BINARY_DIR}/staging/${CMAKE_PROJECT_NAME}.app\") - message(STATUS \"QGC: Signing Bundle - \${QGC_STAGING_BUNDLE_PATH}\") - execute_process( - COMMAND codesign --force --deep -s - \"\${QGC_STAGING_BUNDLE_PATH}\" - COMMAND_ERROR_IS_FATAL ANY - ) - ") - - # include(BundleUtilities) - # fixup_bundle(\"${QGC_STAGING_BUNDLE_PATH}\" \"\" \"${CMAKE_BINARY_DIR}\") - # verify_app(\"${QGC_STAGING_BUNDLE_PATH}\") - # verify_bundle_prerequisites(\"${QGC_STAGING_BUNDLE_PATH}\" _bundle_prereqs_result _bundle_prereqs_info) - # verify_bundle_symlinks(\"${QGC_STAGING_BUNDLE_PATH}\" _bundle_symlinks_result _bundle_symlinks_info) +# ---------------------------------------------------------------------------- +# macOS Installation, Code Signing & DMG Creation +# ---------------------------------------------------------------------------- +elseif(MACOS) + # Set bundle path for subsequent operations + install(CODE "set(QGC_STAGING_BUNDLE_PATH \"${CMAKE_BINARY_DIR}/staging/${CMAKE_PROJECT_NAME}.app\")") - # include(CreateCPackDMG) + # Code signing + if(QGC_MACOS_SIGN_WITH_IDENTITY) + message(STATUS "QGC: macOS bundle will be signed with developer identity") + install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/install/SignMacBundle.cmake") + else() + message(STATUS "QGC: macOS bundle will be signed with ad-hoc signature") + install(CODE " + message(STATUS \"QGC: Signing macOS bundle (ad-hoc)\") + execute_process( + COMMAND codesign --force --deep -s - \"\${QGC_STAGING_BUNDLE_PATH}\" + COMMAND_ERROR_IS_FATAL ANY + ) + ") + endif() - # - or + # Find or fetch create-dmg tool + find_program(CREATE_DMG_PROGRAM create-dmg) + if(NOT CREATE_DMG_PROGRAM) + message(STATUS "QGC: Fetching create-dmg tool via CPM") + CPMAddPackage( + NAME create-dmg + GITHUB_REPOSITORY create-dmg/create-dmg + GIT_TAG master + DOWNLOAD_ONLY + ) + set(CREATE_DMG_PROGRAM "${create-dmg_SOURCE_DIR}/create-dmg") + endif() - install(CODE " - include(${CMAKE_SOURCE_DIR}/cmake/modules/CPM.cmake) - ") + install(CODE "set(CREATE_DMG_PROGRAM \"${CREATE_DMG_PROGRAM}\")") install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/install/CreateMacDMG.cmake") endif() diff --git a/cmake/install/SignMacBundle.cmake b/cmake/install/SignMacBundle.cmake new file mode 100644 index 000000000000..922a5f9d8149 --- /dev/null +++ b/cmake/install/SignMacBundle.cmake @@ -0,0 +1,107 @@ +# ============================================================================ +# SignMacBundle.cmake +# Code signing and notarization for macOS application bundles +# ============================================================================ + +message(STATUS "QGC: Signing Bundle using signing identity") + +# ---------------------------------------------------------------------------- +# Environment Variable Validation +# ---------------------------------------------------------------------------- +if(NOT DEFINED ENV{QGC_MACOS_SIGNING_IDENTITY} OR "$ENV{QGC_MACOS_SIGNING_IDENTITY}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_SIGNING_IDENTITY environment variable must be set to sign MacOS bundle") +endif() +if(NOT DEFINED ENV{QGC_MACOS_NOTARIZATION_USERNAME} OR "$ENV{QGC_MACOS_NOTARIZATION_USERNAME}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_NOTARIZATION_USERNAME environment variable must be set to notarize MacOS bundle") +endif() +if(NOT DEFINED ENV{QGC_MACOS_NOTARIZATION_TEAM_ID} OR "$ENV{QGC_MACOS_NOTARIZATION_TEAM_ID}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_NOTARIZATION_TEAM_ID environment variable must be set to notarize MacOS bundle") +endif() +if(NOT DEFINED ENV{QGC_MACOS_NOTARIZATION_PASSWORD} OR "$ENV{QGC_MACOS_NOTARIZATION_PASSWORD}" STREQUAL "") + message(FATAL_ERROR "QGC: QGC_MACOS_NOTARIZATION_PASSWORD environment variable must be set to notarize MacOS bundle") +endif() + +# ---------------------------------------------------------------------------- +# Clean Up GStreamer Symlinks +# ---------------------------------------------------------------------------- +file(REMOVE "${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Commands") +file(REMOVE "${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/Commands") + +# ---------------------------------------------------------------------------- +# Sign All Libraries and Executables +# ---------------------------------------------------------------------------- +# Sign all dynamic libraries +execute_process( + COMMAND find "${QGC_STAGING_BUNDLE_PATH}/Contents" -type f -name "*.dylib" -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY +) + +# Sign all shared objects +execute_process( + COMMAND find "${QGC_STAGING_BUNDLE_PATH}/Contents" -type f -name "*.so" -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY +) + +# ---------------------------------------------------------------------------- +# Sign GStreamer Framework Components +# ---------------------------------------------------------------------------- +execute_process( + COMMAND find "${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0" -type f -name "*" -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY +) +execute_process( + COMMAND codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer" + COMMAND_ERROR_IS_FATAL ANY +) +execute_process( + COMMAND codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/GStreamer.framework/Versions/1.0/GStreamer" + COMMAND_ERROR_IS_FATAL ANY +) + +# ---------------------------------------------------------------------------- +# Sign All Frameworks +# ---------------------------------------------------------------------------- +file(GLOB FRAMEWORK_DIRS "${QGC_STAGING_BUNDLE_PATH}/Contents/Frameworks/*.framework") +foreach(FRAMEWORK_DIR ${FRAMEWORK_DIRS}) + if(EXISTS "${FRAMEWORK_DIR}/Versions/1.0") + execute_process( + COMMAND find "${FRAMEWORK_DIR}/Versions/1.0" -type f -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY + ) + endif() + if(EXISTS "${FRAMEWORK_DIR}/Versions/A") + execute_process( + COMMAND find "${FRAMEWORK_DIR}/Versions/A" -type f -exec codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "{}" \\; + COMMAND_ERROR_IS_FATAL ANY + ) + endif() +endforeach() + +# ---------------------------------------------------------------------------- +# Sign Main Application Bundle +# ---------------------------------------------------------------------------- +execute_process( + COMMAND codesign --timestamp --options=runtime --force -s "$ENV{QGC_MACOS_SIGNING_IDENTITY}" "${QGC_STAGING_BUNDLE_PATH}" + COMMAND_ERROR_IS_FATAL ANY +) + +# ============================================================================ +# Notarization Process +# ============================================================================ + +message(STATUS "QGC: Archiving Bundle for Notarization upload") +file(REMOVE "qgc_notarization_upload.zip") +execute_process( + COMMAND ditto -c -k --keepParent "${QGC_STAGING_BUNDLE_PATH}" qgc_notarization_upload.zip + COMMAND_ERROR_IS_FATAL ANY +) +message(STATUS "QGC: Notarizing app bundle. This may take a while...") +execute_process( + COMMAND xcrun notarytool submit qgc_notarization_upload.zip --apple-id "$ENV{QGC_MACOS_NOTARIZATION_USERNAME}" --team-id "$ENV{QGC_MACOS_NOTARIZATION_TEAM_ID}" --password "$ENV{QGC_MACOS_NOTARIZATION_PASSWORD}" --wait + COMMAND_ERROR_IS_FATAL ANY +) +message(STATUS "QGC: Stapling notarization ticket to app bundle") +execute_process( + COMMAND xcrun stapler staple "${QGC_STAGING_BUNDLE_PATH}" + COMMAND_ERROR_IS_FATAL ANY +) diff --git a/cmake/modules/CMakeGraphVizOptions.cmake b/cmake/modules/CMakeGraphVizOptions.cmake index 2de998667403..48283f10e860 100644 --- a/cmake/modules/CMakeGraphVizOptions.cmake +++ b/cmake/modules/CMakeGraphVizOptions.cmake @@ -1,21 +1,31 @@ -# Basic appearance ------------------------------------------------ -set(GRAPHVIZ_GRAPH_NAME "QGC-deps") # title -set(GRAPHVIZ_GRAPH_HEADER "rankdir=LR;") # dot header snippet -set(GRAPHVIZ_NODE_PREFIX "node") # node IDs +# ============================================================================ +# CMakeGraphVizOptions.cmake +# Configuration for CMake's --graphviz dependency graph generation +# Usage: cmake --graphviz=deps.dot . && dot -Tpng deps.dot -o deps.png +# ============================================================================ -# What to show ---------------------------------------------------- -set(GRAPHVIZ_EXECUTABLES TRUE) # default TRUE -set(GRAPHVIZ_SHARED_LIBS TRUE) -set(GRAPHVIZ_MODULE_LIBS FALSE) -set(GRAPHVIZ_INTERFACE_LIBS FALSE) -set(GRAPHVIZ_OBJECT_LIBS FALSE) -set(GRAPHVIZ_UNKNOWN_LIBS FALSE) -set(GRAPHVIZ_EXTERNAL_LIBS FALSE) # hide Qt/system libs -set(GRAPHVIZ_CUSTOM_TARGETS TRUE) # include custom targets -set(GRAPHVIZ_IGNORE_TARGETS "test_.*;doc_.*") # regex list +# ---------------------------------------------------------------------------- +# Graph Appearance +# ---------------------------------------------------------------------------- +set(GRAPHVIZ_GRAPH_NAME "QGC-deps") # Graph title +set(GRAPHVIZ_GRAPH_HEADER "rankdir=LR;") # DOT header snippet (left-to-right) +set(GRAPHVIZ_NODE_PREFIX "node") # Node ID prefix -# File churn ------------------------------------------------------ -set(GRAPHVIZ_GENERATE_PER_TARGET FALSE) # only the master graph -set(GRAPHVIZ_GENERATE_DEPENDERS FALSE) +# ---------------------------------------------------------------------------- +# What to Include in Graph +# ---------------------------------------------------------------------------- +set(GRAPHVIZ_EXECUTABLES TRUE) # Show executables (default TRUE) +set(GRAPHVIZ_SHARED_LIBS TRUE) # Show shared libraries +set(GRAPHVIZ_MODULE_LIBS FALSE) # Hide module libraries +set(GRAPHVIZ_INTERFACE_LIBS FALSE) # Hide interface libraries +set(GRAPHVIZ_OBJECT_LIBS FALSE) # Hide object libraries +set(GRAPHVIZ_UNKNOWN_LIBS FALSE) # Hide unknown libraries +set(GRAPHVIZ_EXTERNAL_LIBS FALSE) # Hide external libraries (Qt/system libs) +set(GRAPHVIZ_CUSTOM_TARGETS TRUE) # Show custom targets +set(GRAPHVIZ_IGNORE_TARGETS "test_.*;doc_.*") # Regex list of targets to ignore -# --graphviz=foo.dot +# ---------------------------------------------------------------------------- +# Output File Generation +# ---------------------------------------------------------------------------- +set(GRAPHVIZ_GENERATE_PER_TARGET FALSE) # Only generate master graph +set(GRAPHVIZ_GENERATE_DEPENDERS FALSE) # Don't generate depender graphs diff --git a/cmake/modules/Git.cmake b/cmake/modules/Git.cmake index 2372fbf3b109..03f6e793f065 100644 --- a/cmake/modules/Git.cmake +++ b/cmake/modules/Git.cmake @@ -1,9 +1,20 @@ -find_package(Git) +# ---------------------------------------------------------------------------- +# QGroundControl Git Configuration +# Extracts version information and metadata from Git repository +# ---------------------------------------------------------------------------- +find_package(Git REQUIRED) + +# Verify we're in a Git repository +if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git") + message(WARNING "QGC: Not a Git repository. Version information may be incomplete.") +endif() + +# Optionally update submodules during configuration if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") option(GIT_SUBMODULE "Check submodules during build" OFF) if(GIT_SUBMODULE) - message(STATUS "Submodule update") + message(STATUS "Updating Git submodules...") execute_process( COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" @@ -13,62 +24,118 @@ if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT GIT_SUBMODULE_RESULT EQUAL 0) + include(CMakePrintHelpers) cmake_print_variables(GIT_SUBMODULE_RESULT GIT_SUBMODULE_OUTPUT GIT_SUBMODULE_ERROR) - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, please checkout submodules") + message(FATAL_ERROR "Git submodule update failed with code ${GIT_SUBMODULE_RESULT}") endif() + message(STATUS "Git submodules updated successfully") endif() endif() include(CMakePrintHelpers) +# ---------------------------------------------------------------------------- +# Extract Git Branch +# ---------------------------------------------------------------------------- + execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref @ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE QGC_GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) +if(NOT QGC_GIT_BRANCH) + set(QGC_GIT_BRANCH "unknown") +endif() # cmake_print_variables(QGC_GIT_BRANCH) +# ---------------------------------------------------------------------------- +# Extract Git Commit Hash +# ---------------------------------------------------------------------------- execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short @ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE QGC_GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) +if(NOT QGC_GIT_HASH) + set(QGC_GIT_HASH "0000000") +endif() # cmake_print_variables(QGC_GIT_HASH) +# ---------------------------------------------------------------------------- +# Extract Version String from Git Tags +# ---------------------------------------------------------------------------- execute_process( COMMAND ${GIT_EXECUTABLE} describe --always --tags - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE QGC_APP_VERSION_STR OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) +if(NOT QGC_APP_VERSION_STR) + set(QGC_APP_VERSION_STR "v0.0.0") +endif() # cmake_print_variables(QGC_APP_VERSION_STR) +# ---------------------------------------------------------------------------- +# Extract Clean Version Tag +# ---------------------------------------------------------------------------- execute_process( COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=0 - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE QGC_APP_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) +if(NOT QGC_APP_VERSION) + set(QGC_APP_VERSION "v0.0.0") +endif() # cmake_print_variables(QGC_APP_VERSION) +# ---------------------------------------------------------------------------- +# Extract Commit Date for Version Timestamp +# ---------------------------------------------------------------------------- + +if(QGC_STABLE_BUILD) + set(QGC_APP_DATE_VERSION "${QGC_APP_VERSION}") +else() + # Daily builds use date of last commit + set(QGC_APP_DATE_VERSION "") +endif() + execute_process( - COMMAND ${GIT_EXECUTABLE} log -1 --format=%aI ${QGC_APP_VERSION} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${GIT_EXECUTABLE} log -1 --format=%aI ${QGC_APP_DATE_VERSION} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE QGC_APP_DATE OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET ) +if(NOT QGC_APP_DATE) + string(TIMESTAMP QGC_APP_DATE "%Y-%m-%dT%H:%M:%S%z" UTC) +endif() # cmake_print_variables(QGC_APP_DATE) -string(FIND ${QGC_APP_VERSION} "v" QGC_APP_VERSION_VALID) -if(QGC_APP_VERSION_VALID GREATER -1) - string(REPLACE "v" "" QGC_APP_VERSION ${QGC_APP_VERSION}) +# ---------------------------------------------------------------------------- +# Parse Version Components (Major.Minor.Patch) +# ---------------------------------------------------------------------------- +# Strip 'v' prefix if present (e.g., v1.2.3 -> 1.2.3) +string(REGEX REPLACE "^v" "" QGC_APP_VERSION_CLEAN "${QGC_APP_VERSION}") + +# Extract version components using regex +if(QGC_APP_VERSION_CLEAN MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(QGC_APP_VERSION "${QGC_APP_VERSION_CLEAN}") + set(QGC_APP_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(QGC_APP_VERSION_MINOR "${CMAKE_MATCH_2}") + set(QGC_APP_VERSION_PATCH "${CMAKE_MATCH_3}") else() + # Fallback if version doesn't match expected format + message(WARNING "QGC: Could not parse semantic version from Git tag: '${QGC_APP_VERSION_CLEAN}'. Using fallback 0.0.0") set(QGC_APP_VERSION "0.0.0") + set(QGC_APP_VERSION_MAJOR "0") + set(QGC_APP_VERSION_MINOR "0") + set(QGC_APP_VERSION_PATCH "0") endif() -string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" QGC_APP_VERSION_MATCH ${QGC_APP_VERSION}) -set(QGC_APP_VERSION_MAJOR ${CMAKE_MATCH_1}) -set(QGC_APP_VERSION_MINOR ${CMAKE_MATCH_2}) -set(QGC_APP_VERSION_PATCH ${CMAKE_MATCH_3}) # cmake_print_variables(QGC_APP_VERSION QGC_APP_VERSION_MAJOR QGC_APP_VERSION_MINOR QGC_APP_VERSION_PATCH) diff --git a/cmake/modules/IWYU.cmake b/cmake/modules/IWYU.cmake index 0f7fdff6dfdb..c1974a737161 100644 --- a/cmake/modules/IWYU.cmake +++ b/cmake/modules/IWYU.cmake @@ -1,6 +1,14 @@ +# ============================================================================ +# IWYU.cmake +# Include-What-You-Use (IWYU) integration for header usage analysis +# Helps identify unnecessary includes and suggest proper forward declarations +# ============================================================================ + +# IWYU only works with Clang if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") find_program(INCLUDE_WHAT_YOU_USE_PROGRAM include-what-you-use) if(INCLUDE_WHAT_YOU_USE_PROGRAM) + message(STATUS "QGC: Found include-what-you-use: ${INCLUDE_WHAT_YOU_USE_PROGRAM}") set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE_PROGRAM}) set(CMAKE_C_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE_PROGRAM}) endif() diff --git a/cmake/modules/vulkan.cmake b/cmake/modules/vulkan.cmake deleted file mode 100644 index 2624f3a3c738..000000000000 --- a/cmake/modules/vulkan.cmake +++ /dev/null @@ -1,105 +0,0 @@ -add_library(mbgl-vendor-vulkan-headers INTERFACE) - -target_include_directories( - mbgl-vendor-vulkan-headers SYSTEM - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/Vulkan-Headers/include/ -) - -set_target_properties( - mbgl-vendor-vulkan-headers - PROPERTIES - INTERFACE_MAPLIBRE_NAME "Vulkan-Headers" - INTERFACE_MAPLIBRE_URL "https://github.com/KhronosGroup/Vulkan-Headers.git" - INTERFACE_MAPLIBRE_LICENSE ${CMAKE_CURRENT_LIST_DIR}/Vulkan-Headers/LICENSE.md -) - -add_library(mbgl-vendor-VulkanMemoryAllocator INTERFACE) - -target_include_directories( - mbgl-vendor-VulkanMemoryAllocator SYSTEM - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/VulkanMemoryAllocator/include/ -) - -set_target_properties( - mbgl-vendor-VulkanMemoryAllocator - PROPERTIES - INTERFACE_MAPLIBRE_NAME "VulkanMemoryAllocator" - INTERFACE_MAPLIBRE_URL "https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git" - INTERFACE_MAPLIBRE_LICENSE ${CMAKE_CURRENT_LIST_DIR}/VulkanMemoryAllocator/LICENSE.txt -) - -set(ENABLE_OPT OFF) -set(ENABLE_HLSL OFF) -set(ENABLE_GLSLANG_BINARIES OFF) -set(GLSLANG_TESTS_DEFAULT OFF) -set(ENABLE_PCH OFF) - -if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.25") - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/glslang SYSTEM) -else() - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/glslang) -endif() - -get_target_property(glslang_inc glslang INTERFACE_INCLUDE_DIRECTORIES) -set_target_properties(glslang PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES ${glslang_inc}) - -set_target_properties( - glslang - PROPERTIES - INTERFACE_MAPLIBRE_NAME "glslang" - INTERFACE_MAPLIBRE_URL "https://github.com/KhronosGroup/glslang.git" - INTERFACE_MAPLIBRE_LICENSE ${CMAKE_CURRENT_LIST_DIR}/glslang/LICENSE.txt -) - -set_target_properties( - SPIRV - PROPERTIES - INTERFACE_MAPLIBRE_NAME "SPIRV" - INTERFACE_MAPLIBRE_URL "https://github.com/KhronosGroup/glslang.git" - INTERFACE_MAPLIBRE_LICENSE ${CMAKE_CURRENT_LIST_DIR}/glslang/LICENSE.txt -) - -set_target_properties( - glslang-default-resource-limits - PROPERTIES - INTERFACE_MAPLIBRE_NAME "glslang-default-resource-limits" - INTERFACE_MAPLIBRE_URL "https://github.com/KhronosGroup/glslang.git" - INTERFACE_MAPLIBRE_LICENSE ${CMAKE_CURRENT_LIST_DIR}/glslang/LICENSE.txt -) - -export(TARGETS - mbgl-vendor-vulkan-headers - mbgl-vendor-VulkanMemoryAllocator - glslang - SPIRV - glslang-default-resource-limits - MachineIndependent - OSDependent - GenericCodeGen - - APPEND FILE MapboxCoreTargets.cmake -) - -# TODO: Build Vulkan Headers -# CPMAddPackage( -# NAME Vulkan-Headers -# GITHUB_REPOSITORY KhronosGroup/Vulkan-Headers -# GIT_TAG v1.3.269 -# ) - -# # Vulkan-Headers defines a header target but not one for the module. -# if(Vulkan-Headers_ADDED) -# add_library(Vulkan-Module) -# target_sources(Vulkan-Module -# PUBLIC FILE_SET CXX_MODULES -# BASE_DIRS "${Vulkan-Headers_SOURCE_DIR}/include" -# FILES "${Vulkan-Headers_SOURCE_DIR}/include/vulkan/vulkan.cppm" -# ) -# target_compile_definitions(Vulkan-Module PUBLIC -# # Your options here, project-dependent: -# # https://github.com/KhronosGroup/Vulkan-Hpp#configuration-options -# ) -# target_link_libraries(Vulkan-Module PUBLIC Vulkan-Headers) -# endif() diff --git a/cmake/platform/Android.cmake b/cmake/platform/Android.cmake index f531d839caba..79eb0d2c94fb 100644 --- a/cmake/platform/Android.cmake +++ b/cmake/platform/Android.cmake @@ -1,43 +1,62 @@ +# ---------------------------------------------------------------------------- +# QGroundControl Android Platform Configuration +# ---------------------------------------------------------------------------- + if(NOT ANDROID) - message(FATAL_ERROR "Invalid Platform") - return() + message(FATAL_ERROR "QGC: Invalid Platform: Android.cmake included but platform is not Android") endif() -if(${Qt6_VERSION} VERSION_EQUAL 6.8.3) - if(NOT ${CMAKE_ANDROID_NDK_VERSION} VERSION_EQUAL 26.1 AND NOT ${CMAKE_ANDROID_NDK_VERSION} VERSION_EQUAL 27.2) - message(FATAL_ERROR "Invalid NDK Version: ${CMAKE_ANDROID_NDK_VERSION}, Use Version 26B of 27C instead.") +# ---------------------------------------------------------------------------- +# Android NDK Version Validation +# ---------------------------------------------------------------------------- +if(Qt6_VERSION VERSION_EQUAL "6.10.1") + if(NOT CMAKE_ANDROID_NDK_VERSION VERSION_EQUAL "27.2") + message(FATAL_ERROR "QGC: Invalid NDK Version: ${CMAKE_ANDROID_NDK_VERSION}. Qt 6.10.1 requires NDK 27.2") endif() endif() -# Generation of android version numbers must be consistent release to release such that they are always increasing -if(${CMAKE_PROJECT_VERSION_MAJOR} GREATER 9) - message(FATAL_ERROR "Major version larger than 1 digit: ${CMAKE_PROJECT_VERSION_MAJOR}") +# ---------------------------------------------------------------------------- +# Android Version Number Validation +# ---------------------------------------------------------------------------- + +# Generation of Android version numbers must be consistent release to release +# to ensure they are always increasing for Google Play Store +if(CMAKE_PROJECT_VERSION_MAJOR GREATER 9) + message(FATAL_ERROR "QGC: Major version must be single digit (0-9), got: ${CMAKE_PROJECT_VERSION_MAJOR}") endif() -if(${CMAKE_PROJECT_VERSION_MINOR} GREATER 9) - message(FATAL_ERROR "Minor version larger than 1 digit: ${CMAKE_PROJECT_VERSION_MINOR}") +if(CMAKE_PROJECT_VERSION_MINOR GREATER 9) + message(FATAL_ERROR "QGC: Minor version must be single digit (0-9), got: ${CMAKE_PROJECT_VERSION_MINOR}") endif() -if(${CMAKE_PROJECT_VERSION_PATCH} GREATER 99) - message(FATAL_ERROR "Patch version larger than 2 digits: ${CMAKE_PROJECT_VERSION_PATCH}") +if(CMAKE_PROJECT_VERSION_PATCH GREATER 99) + message(FATAL_ERROR "QGC: Patch version must be two digits (0-99), got: ${CMAKE_PROJECT_VERSION_PATCH}") endif() -# Bitness for android version number is 66/34 instead of 64/32 in because of a required version number bump screw-up ages ago +# ---------------------------------------------------------------------------- +# Android ABI to Bitness Code Mapping +# ---------------------------------------------------------------------------- +# NOTE: Bitness codes are 66/34 instead of 64/32 due to a historical +# version number bump requirement from an earlier Android release set(ANDROID_BITNESS_CODE) -if(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a" OR ${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86") +if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a" OR CMAKE_ANDROID_ARCH_ABI STREQUAL "x86") set(ANDROID_BITNESS_CODE 34) -elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a" OR ${CMAKE_ANDROID_ARCH_ABI} STREQUAL "x86_64") +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a" OR CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") set(ANDROID_BITNESS_CODE 66) else() - message(FATAL_ERROR "Unsupported Android ABI: ${CMAKE_ANDROID_ARCH_ABI}") + message(FATAL_ERROR "QGC: Unsupported Android ABI: ${CMAKE_ANDROID_ARCH_ABI}. Supported: armeabi-v7a, arm64-v8a, x86, x86_64") endif() +# ---------------------------------------------------------------------------- +# Android Version Code Generation +# ---------------------------------------------------------------------------- +# Zero-pad patch version if less than 10 set(ANDROID_PATCH_VERSION ${CMAKE_PROJECT_VERSION_PATCH}) -if(${CMAKE_PROJECT_VERSION_PATCH} LESS 10) +if(CMAKE_PROJECT_VERSION_PATCH LESS 10) set(ANDROID_PATCH_VERSION "0${CMAKE_PROJECT_VERSION_PATCH}") endif() # Version code format: BBMIPPDDD (B=Bitness, M=Major, I=Minor, P=Patch, D=Dev) - Dev not currently supported and always 000 set(ANDROID_VERSION_CODE "${ANDROID_BITNESS_CODE}${CMAKE_PROJECT_VERSION_MAJOR}${CMAKE_PROJECT_VERSION_MINOR}${ANDROID_PATCH_VERSION}000") -message(STATUS "Android version code: ${ANDROID_VERSION_CODE}") +message(STATUS "QGC: Android version code: ${ANDROID_VERSION_CODE}") set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES @@ -45,13 +64,13 @@ set_target_properties(${CMAKE_PROJECT_NAME} # QT_ANDROID_SDK_BUILD_TOOLS_REVISION QT_ANDROID_MIN_SDK_VERSION ${QGC_QT_ANDROID_MIN_SDK_VERSION} QT_ANDROID_TARGET_SDK_VERSION ${QGC_QT_ANDROID_TARGET_SDK_VERSION} - # QT_ANDROID_COMPILE_SDK_VERSION + QT_ANDROID_COMPILE_SDK_VERSION ${QGC_QT_ANDROID_COMPILE_SDK_VERSION} QT_ANDROID_PACKAGE_NAME "${QGC_ANDROID_PACKAGE_NAME}" QT_ANDROID_PACKAGE_SOURCE_DIR "${QGC_ANDROID_PACKAGE_SOURCE_DIR}" QT_ANDROID_VERSION_NAME "${CMAKE_PROJECT_VERSION}" QT_ANDROID_VERSION_CODE ${ANDROID_VERSION_CODE} - # QT_ANDROID_APP_NAME - # QT_ANDROID_APP_ICON + QT_ANDROID_APP_NAME "${CMAKE_PROJECT_NAME}" + QT_ANDROID_APP_ICON "@drawable/icon" # QT_QML_IMPORT_PATH QT_QML_ROOT_PATH "${CMAKE_SOURCE_DIR}" # QT_ANDROID_SYSTEM_LIBS_PREFIX @@ -63,9 +82,68 @@ set_target_properties(${CMAKE_PROJECT_NAME} list(APPEND QT_ANDROID_MULTI_ABI_FORWARD_VARS QGC_STABLE_BUILD QT_HOST_PATH) +# ---------------------------------------------------------------------------- +# Android OpenSSL Libraries +# ---------------------------------------------------------------------------- CPMAddPackage( NAME android_openssl URL https://github.com/KDAB/android_openssl/archive/refs/heads/master.zip ) -include(${android_openssl_SOURCE_DIR}/android_openssl.cmake) -add_android_openssl_libraries(${CMAKE_PROJECT_NAME}) + +if(android_openssl_ADDED) + include(${android_openssl_SOURCE_DIR}/android_openssl.cmake) + add_android_openssl_libraries(${CMAKE_PROJECT_NAME}) + message(STATUS "QGC: Android OpenSSL libraries added") +else() + message(WARNING "QGC: Failed to add Android OpenSSL libraries") +endif() + +# ---------------------------------------------------------------------------- +# Android Permissions +# ---------------------------------------------------------------------------- + +if(QGC_ENABLE_BLUETOOTH) + qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.BLUETOOTH_SCAN + ATTRIBUTES + minSdkVersion 31 + usesPermissionFlags neverForLocation + ) + qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.BLUETOOTH_CONNECT + ATTRIBUTES + minSdkVersion 31 + usesPermissionFlags neverForLocation + ) +endif() + +if(NOT QGC_NO_SERIAL_LINK) + qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.USB_PERMISSION + ) +endif() + +# Need MulticastLock to receive broadcast UDP packets +qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.CHANGE_WIFI_MULTICAST_STATE +) + +# Needed to keep working while 'asleep' +qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.WAKE_LOCK +) + +# Needed for read/write to SD Card Path in AppSettings +qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.WRITE_EXTERNAL_STORAGE +) +qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.READ_EXTERNAL_STORAGE + ATTRIBUTES + maxSdkVersion 33 +) +qt_add_android_permission(${CMAKE_PROJECT_NAME} + NAME android.permission.MANAGE_EXTERNAL_STORAGE +) + +message(STATUS "QGC: Android platform configuration applied") diff --git a/cmake/platform/Apple.cmake b/cmake/platform/Apple.cmake index 8660dfea9eaf..8346fa093290 100644 --- a/cmake/platform/Apple.cmake +++ b/cmake/platform/Apple.cmake @@ -1,6 +1,9 @@ +# ---------------------------------------------------------------------------- +# QGroundControl Apple Platform Configuration (macOS and iOS) +# ---------------------------------------------------------------------------- + if(NOT APPLE) - message(FATAL_ERROR "QGC: Invalid Platform") - return() + message(FATAL_ERROR "QGC: Invalid Platform: Apple.cmake included but platform is not Apple") endif() if(CMAKE_GENERATOR STREQUAL "Xcode") @@ -16,6 +19,9 @@ if(CMAKE_GENERATOR STREQUAL "Xcode") set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${QGC_MACOS_ENTITLEMENTS_PATH}") endif() +# ---------------------------------------------------------------------------- +# macOS/iOS Bundle Configuration +# ---------------------------------------------------------------------------- cmake_path(GET QGC_MACOS_ICON_PATH FILENAME MACOSX_BUNDLE_ICON_FILE) set_target_properties(${CMAKE_PROJECT_NAME} @@ -32,7 +38,11 @@ set_target_properties(${CMAKE_PROJECT_NAME} MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}" ) +# ---------------------------------------------------------------------------- +# Platform-Specific Configuration +# ---------------------------------------------------------------------------- if(MACOS) + # macOS-specific configuration set(app_icon_macos "${QGC_MACOS_ICON_PATH}") set_source_files_properties(${app_icon_macos} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") @@ -44,7 +54,10 @@ if(MACOS) "${app_entitlements_macos}" "${app_icon_macos}" ) + + message(STATUS "QGC: macOS platform configuration applied") elseif(IOS) + # iOS-specific configuration enable_language(OBJC) set(QT_IOS_LAUNCH_SCREEN "${CMAKE_SOURCE_DIR}/deploy/ios/QGCLaunchScreen.xib") @@ -70,6 +83,9 @@ elseif(IOS) XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS "YES" ) + # Add FFmpeg libraries for iOS if needed # set(QT_NO_FFMPEG_XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON) qt_add_ios_ffmpeg_libraries(${CMAKE_PROJECT_NAME}) + + message(STATUS "QGC: iOS platform configuration applied") endif() diff --git a/cmake/platform/Linux.cmake b/cmake/platform/Linux.cmake index 0a66e137d81f..b41cf3dfbc4a 100644 --- a/cmake/platform/Linux.cmake +++ b/cmake/platform/Linux.cmake @@ -1,4 +1,37 @@ +# ---------------------------------------------------------------------------- +# QGroundControl Linux Platform Configuration +# ---------------------------------------------------------------------------- + if(NOT LINUX) - message(FATAL_ERROR "Invalid Platform") - return() + message(FATAL_ERROR "QGC: Invalid Platform: Linux.cmake included but platform is not Linux") endif() + +# ---------------------------------------------------------------------------- +# Linux-Specific Compiler Flags +# ---------------------------------------------------------------------------- +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + # Enable additional warnings for better code quality + # target_compile_options(${CMAKE_PROJECT_NAME} + # PRIVATE + # -Wall + # -Wextra + # -Wpedantic + # $<$:-O3> + # ) +endif() + +# ---------------------------------------------------------------------------- +# Linux-Specific Definitions +# ---------------------------------------------------------------------------- +target_compile_definitions(${CMAKE_PROJECT_NAME} + PRIVATE + _GNU_SOURCE +) + +# ---------------------------------------------------------------------------- +# Linux Desktop Integration +# ---------------------------------------------------------------------------- +# Desktop entry and icon files are handled by the install scripts +# See cmake/install/CreateAppImage.cmake for AppImage-specific configuration + +message(STATUS "QGC: Linux platform configuration applied") diff --git a/cmake/platform/Windows.cmake b/cmake/platform/Windows.cmake index b386f4d94e43..c6d3e7554e92 100644 --- a/cmake/platform/Windows.cmake +++ b/cmake/platform/Windows.cmake @@ -1,27 +1,57 @@ +# ---------------------------------------------------------------------------- +# QGroundControl Windows Platform Configuration +# ---------------------------------------------------------------------------- + if(NOT WIN32) - message(FATAL_ERROR "Invalid Platform") - return() + message(FATAL_ERROR "QGC: Invalid Platform: Windows.cmake included but platform is not Windows") endif() -# CPMAddPackage( -# NAME windows_drivers -# URL https://firmware.ardupilot.org/Tools/MissionPlanner/driver.msi -# ) -# ${windows_drivers_SOURCE_DIR}/driver.msi - -set_target_properties(${CMAKE_PROJECT_NAME} - PROPERTIES - WIN32_EXECUTABLE TRUE - # QT_TARGET_WINDOWS_RC_FILE "${QGC_WINDOWS_RESOURCE_FILE_PATH}" - QT_TARGET_COMPANY_NAME "${QGC_ORG_NAME}" - QT_TARGET_DESCRIPTION "${CMAKE_PROJECT_DESCRIPTION}" - QT_TARGET_VERSION "${CMAKE_PROJECT_VERSION}" - QT_TARGET_COPYRIGHT "${QGC_APP_COPYRIGHT}" - QT_TARGET_PRODUCT_NAME "${CMAKE_PROJECT_NAME}" - # QT_TARGET_COMMENTS: RC Comments - # QT_TARGET_ORIGINAL_FILENAME: RC Original FileName - # QT_TARGET_TRADEMARKS: RC LegalTrademarks - # QT_TARGET_INTERNALNAME: RC InternalName - QT_TARGET_RC_ICONS "${QGC_WINDOWS_ICON_PATH}" +# ---------------------------------------------------------------------------- +# Windows-Specific Definitions +# ---------------------------------------------------------------------------- +target_compile_definitions(${CMAKE_PROJECT_NAME} + PRIVATE + _USE_MATH_DEFINES # Enable M_PI and other math constants + NOMINMAX # Prevent min/max macro conflicts + WIN32_LEAN_AND_MEAN # Reduce Windows.h bloat ) -target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE _USE_MATH_DEFINES NOMINMAX WIN32_LEAN_AND_MEAN) + +# ---------------------------------------------------------------------------- +# Windows Executable Configuration +# ---------------------------------------------------------------------------- +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE) + +if(COMMAND _qt_internal_generate_win32_rc_file) + set_target_properties(${CMAKE_PROJECT_NAME} + PROPERTIES + QT_TARGET_COMPANY_NAME "${QGC_ORG_NAME}" + QT_TARGET_DESCRIPTION "${CMAKE_PROJECT_DESCRIPTION}" + QT_TARGET_VERSION "${CMAKE_PROJECT_VERSION}" + QT_TARGET_COPYRIGHT "${QGC_APP_COPYRIGHT}" + QT_TARGET_PRODUCT_NAME "${CMAKE_PROJECT_NAME}" + # QT_TARGET_COMMENTS: ${QGC_QT_TARGET_COMMENTS} + # QT_TARGET_ORIGINAL_FILENAME: ${QGC_QT_TARGET_ORIGINAL_FILENAME} + # QT_TARGET_TRADEMARKS: ${QGC_QT_TARGET_TRADEMARKS} + # QT_TARGET_INTERNALNAME: ${QGC_QT_TARGET_INTERNALNAME} + QT_TARGET_RC_ICONS "${QGC_WINDOWS_ICON_PATH}" + ) + _qt_internal_generate_win32_rc_file(${CMAKE_PROJECT_NAME}) +elseif(EXISTS "${QGC_WINDOWS_RESOURCE_FILE_PATH}") + target_sources(${CMAKE_PROJECT_NAME} PRIVATE "${QGC_WINDOWS_RESOURCE_FILE_PATH}") + set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES QT_TARGET_WINDOWS_RC_FILE "${QGC_WINDOWS_RESOURCE_FILE_PATH}") +elseif(EXISTS "${CMAKE_SOURCE_DIR}/deploy/windows/QGroundControl.rc.in") + configure_file( + "${CMAKE_SOURCE_DIR}/deploy/windows/QGroundControl.rc.in" + "${CMAKE_BINARY_DIR}/QGroundControl.rc" + @ONLY + ) + target_sources(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/QGroundControl.rc") +else() + message(WARNING "QGC: No Windows resource file found") +endif() + +if(MSVC) + # qt_add_win_app_sdk(${CMAKE_PROJECT_NAME} PRIVATE) +endif() + +message(STATUS "QGC: Windows platform configuration applied") diff --git a/codecov.yml b/codecov.yml index 28eb813be3bf..cef4e70c46ed 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,10 +1,51 @@ codecov: + require_ci_to_pass: yes notify: - require_ci_to_pass: yes + after_n_builds: 2 # Wait for at least 2 builds (Debug + Release) + wait_for_ci: yes coverage: + precision: 2 + round: down + range: "30...90" + status: project: default: - target: 10% + target: 30% + threshold: 2% + base: auto + if_ci_failed: error + patch: + default: + target: 40% threshold: 5% + base: auto + if_ci_failed: error + +comment: + layout: "reach,diff,flags,tree,footer" + behavior: default + require_changes: no + require_base: no + require_head: yes + +ignore: + - "test/**/*" + - "tools/**/*" + - "deploy/**/*" + - "custom/**/*" + - "libs/**/*" + - "*.qml" + - "*.js" + - "android/**/*" + +flags: + debug: + paths: + - src/ + carryforward: true + release: + paths: + - src/ + carryforward: true diff --git a/custom-example/CMakeLists.txt b/custom-example/CMakeLists.txt index 67ed26473c92..3076a4013806 100644 --- a/custom-example/CMakeLists.txt +++ b/custom-example/CMakeLists.txt @@ -1,23 +1,74 @@ +# ============================================================================ +# QGroundControl Custom Plugin Example +# Template for building custom QGroundControl plugins +# ============================================================================ + message(STATUS "QGC: Adding Custom Plugin") +# ============================================================================ +# Custom Android Package Template +# ============================================================================ + +if(ANDROID) + set(CUSTOM_ANDROID_DIR "${CMAKE_SOURCE_DIR}/custom/android") + if(EXISTS "${CUSTOM_ANDROID_DIR}") + file(GLOB CUSTOM_ANDROID_FILES "${CUSTOM_ANDROID_DIR}/*") + if(CUSTOM_ANDROID_FILES) + message(STATUS "QGC: Custom Android package template found. Overlaying custom files...") + set(DEFAULT_ANDROID_DIR "${CMAKE_SOURCE_DIR}/android") + set(FINAL_ANDROID_DIR "${CMAKE_BINARY_DIR}/custom/android") + + # Copy default Android template + file(COPY "${DEFAULT_ANDROID_DIR}/." DESTINATION "${FINAL_ANDROID_DIR}") + + # Overlay custom Android files + file(COPY "${CUSTOM_ANDROID_DIR}/." DESTINATION "${FINAL_ANDROID_DIR}") + + # Track changes to Android directories + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${DEFAULT_ANDROID_DIR}/" + "${CUSTOM_ANDROID_DIR}/" + ) + + set(QGC_ANDROID_PACKAGE_SOURCE_DIR "${FINAL_ANDROID_DIR}" CACHE PATH "Path to a custom Android package template" FORCE) + message(STATUS "QGC: Android package template path will be set to: ${QGC_ANDROID_PACKAGE_SOURCE_DIR}") + else() + message(STATUS "QGC: Custom Android package template empty. Using default.") + endif() + else() + message(STATUS "QGC: No custom Android package template found. Using default.") + endif() +endif() + +# ============================================================================ +# Custom Resources and Import Paths +# ============================================================================ + +# Add custom Qt resources list(APPEND CUSTOM_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/custom.qrc ) set(QGC_RESOURCES ${QGC_RESOURCES} ${CUSTOM_RESOURCES} CACHE STRING "Paths to .qrc Resources" FORCE) +# Add custom QML import path set(QML_IMPORT_PATH ${QML_IMPORT_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/res" CACHE STRING "Extra qml import paths" FORCE) +# ============================================================================ +# Custom QML Module +# ============================================================================ + qt_add_library(CustomModule STATIC) -set_source_files_properties(res/Custom/Widgets/CustomArtificialHorizon.qml PROPERTIES QT_RESOURCE_ALIAS CustomArtificialHorizon.qml) +# Set resource aliases for custom widgets +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomArtificialHorizon.qml PROPERTIES QT_RESOURCE_ALIAS CustomArtificialHorizon.qml) qgc_set_qt_resource_alias( - res/Custom/Widgets/CustomAttitudeWidget.qml - res/Custom/Widgets/CustomIconButton.qml - res/Custom/Widgets/CustomOnOffSwitch.qml - res/Custom/Widgets/CustomQuickButton.qml - res/Custom/Widgets/CustomSignalStrength.qml - res/Custom/Widgets/CustomToolBarButton.qml - res/Custom/Widgets/CustomVehicleButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomAttitudeWidget.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomIconButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomOnOffSwitch.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomQuickButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomSignalStrength.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomToolBarButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomVehicleButton.qml ) qt_add_qml_module(CustomModule @@ -25,17 +76,21 @@ qt_add_qml_module(CustomModule VERSION 1.0 RESOURCE_PREFIX /qml QML_FILES - res/Custom/Widgets/CustomArtificialHorizon.qml - res/Custom/Widgets/CustomAttitudeWidget.qml - res/Custom/Widgets/CustomIconButton.qml - res/Custom/Widgets/CustomOnOffSwitch.qml - res/Custom/Widgets/CustomQuickButton.qml - res/Custom/Widgets/CustomSignalStrength.qml - res/Custom/Widgets/CustomToolBarButton.qml - res/Custom/Widgets/CustomVehicleButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomArtificialHorizon.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomAttitudeWidget.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomIconButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomOnOffSwitch.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomQuickButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomSignalStrength.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomToolBarButton.qml + ${CMAKE_CURRENT_SOURCE_DIR}/res/Custom/Widgets/CustomVehicleButton.qml NO_PLUGIN ) +# ============================================================================ +# Custom Plugin Sources +# ============================================================================ + set(CUSTOM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CustomPlugin.cc ${CMAKE_CURRENT_SOURCE_DIR}/src/CustomPlugin.h @@ -48,11 +103,17 @@ set(CUSTOM_SOURCES CACHE INTERNAL "" FORCE ) +# ============================================================================ +# Custom Build Configuration +# ============================================================================ + +# Custom libraries to link set(CUSTOM_LIBRARIES CustomModule CACHE INTERNAL "" FORCE ) +# Custom include directories set(CUSTOM_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/AutoPilotPlugin @@ -60,6 +121,7 @@ set(CUSTOM_INCLUDE_DIRECTORIES CACHE INTERNAL "" FORCE ) +# Custom compiler definitions set(CUSTOM_DEFINITIONS QGC_CUSTOM_BUILD CUSTOMHEADER="CustomPlugin.h" @@ -67,6 +129,7 @@ set(CUSTOM_DEFINITIONS CACHE INTERNAL "" FORCE ) +# Custom Qt components required set(CUSTOM_QT_COMPONENTS Core CACHE INTERNAL "" FORCE diff --git a/custom-example/README.jpg b/custom-example/README.jpg index ea4f0c77a8c1..19a4418b15da 100644 Binary files a/custom-example/README.jpg and b/custom-example/README.jpg differ diff --git a/custom-example/cmake/CustomOverrides.cmake b/custom-example/cmake/CustomOverrides.cmake index fa40101b2aa9..821c850da55f 100644 --- a/custom-example/cmake/CustomOverrides.cmake +++ b/custom-example/cmake/CustomOverrides.cmake @@ -1,25 +1,45 @@ +# ============================================================================ +# Custom Build Configuration Overrides +# Template for customizing QGroundControl branding and feature set +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Application Branding +# ---------------------------------------------------------------------------- set(QGC_APP_NAME "Custom-QGroundControl" CACHE STRING "App Name" FORCE) +# ---------------------------------------------------------------------------- +# Custom Icons and Graphics +# ---------------------------------------------------------------------------- + +# macOS Icon if(EXISTS "${CMAKE_SOURCE_DIR}/custom/res/icons/custom_qgroundcontrol.icns") set(QGC_MACOS_ICON_PATH "${CMAKE_SOURCE_DIR}/custom/res/icons/custom_qgroundcontrol.icns" CACHE FILEPATH "MacOS Icon Path" FORCE) endif() +# Linux AppImage Icon if(EXISTS "${CMAKE_SOURCE_DIR}/custom/res/icons/custom_qgroundcontrol.svg") set(QGC_APPIMAGE_ICON_SCALABLE_PATH "${CMAKE_SOURCE_DIR}/custom/res/icons/custom_qgroundcontrol.svg" CACHE FILEPATH "AppImage Icon SVG Path" FORCE) endif() -if(EXISTS ${CMAKE_SOURCE_DIR}/custom/deploy/windows/installheader.bmp) +# Windows Installer Header +if(EXISTS "${CMAKE_SOURCE_DIR}/custom/deploy/windows/installheader.bmp") set(QGC_WINDOWS_INSTALL_HEADER_PATH "${CMAKE_SOURCE_DIR}/custom/deploy/windows/installheader.bmp" CACHE FILEPATH "Windows Install Header Path" FORCE) endif() -if(EXISTS ${CMAKE_SOURCE_DIR}/custom/deploy/windows/WindowsQGC.ico) +# Windows Application Icon +if(EXISTS "${CMAKE_SOURCE_DIR}/custom/deploy/windows/WindowsQGC.ico") set(QGC_WINDOWS_ICON_PATH "${CMAKE_SOURCE_DIR}/custom/deploy/windows/WindowsQGC.ico" CACHE FILEPATH "Windows Icon Path" FORCE) endif() +# ---------------------------------------------------------------------------- +# Feature Set Customization +# ---------------------------------------------------------------------------- + # Build a single flight stack by disabling APM support set(QGC_DISABLE_APM_MAVLINK ON CACHE BOOL "Disable APM Dialect" FORCE) set(QGC_DISABLE_APM_PLUGIN ON CACHE BOOL "Disable APM Plugin" FORCE) set(QGC_DISABLE_APM_PLUGIN_FACTORY ON CACHE BOOL "Disable APM Plugin Factory" FORCE) -# We implement our own PX4 plugin factory +# Implement custom PX4 plugin factory set(QGC_DISABLE_PX4_PLUGIN_FACTORY ON CACHE BOOL "Disable PX4 Plugin Factory" FORCE) diff --git a/custom-example/custom.qrc b/custom-example/custom.qrc index cee01559079d..618dcad169f7 100644 --- a/custom-example/custom.qrc +++ b/custom-example/custom.qrc @@ -18,16 +18,6 @@ res/Images/CustomVehicleIcon.svg - - res/Custom/Widgets/CustomArtificialHorizon.qml - res/Custom/Widgets/CustomAttitudeWidget.qml - res/Custom/Widgets/CustomIconButton.qml - res/Custom/Widgets/CustomOnOffSwitch.qml - res/Custom/Widgets/CustomQuickButton.qml - res/Custom/Widgets/CustomSignalStrength.qml - res/Custom/Widgets/CustomToolBarButton.qml - res/Custom/Widgets/CustomVehicleButton.qml - src/CustomGuidedActionsController.qml src/FlyViewCustomLayer.qml diff --git a/custom-example/src/CustomPlugin.cc b/custom-example/src/CustomPlugin.cc index c5ef29a1615c..be2ee1b1ae21 100644 --- a/custom-example/src/CustomPlugin.cc +++ b/custom-example/src/CustomPlugin.cc @@ -19,7 +19,7 @@ #include #include -QGC_LOGGING_CATEGORY(CustomLog, "gcs.custom.customplugin") +QGC_LOGGING_CATEGORY(CustomLog, "Custom.CustomPlugin") Q_APPLICATION_STATIC(CustomPlugin, _customPluginInstance); @@ -94,23 +94,23 @@ bool CustomPlugin::overrideSettingsGroupVisibility(const QString &name) return true; } -bool CustomPlugin::adjustSettingMetaData(const QString& settingsGroup, FactMetaData& metaData) +void CustomPlugin::adjustSettingMetaData(const QString& settingsGroup, FactMetaData& metaData, bool &visible) { - const bool parentResult = QGCCorePlugin::adjustSettingMetaData(settingsGroup, metaData); + QGCCorePlugin::adjustSettingMetaData(settingsGroup, metaData, visible); if (settingsGroup == AppSettings::settingsGroup) { // This tells QGC than when you are creating Plans while not connected to a vehicle // the specific firmware/vehicle the plan is for. if (metaData.name() == AppSettings::offlineEditingFirmwareClassName) { metaData.setRawDefaultValue(QGCMAVLink::FirmwareClassPX4); - return false; + visible = false; + return; } else if (metaData.name() == AppSettings::offlineEditingVehicleClassName) { metaData.setRawDefaultValue(QGCMAVLink::VehicleClassMultiRotor); - return false; + visible = false; + return; } } - - return parentResult; } void CustomPlugin::paletteOverride(const QString &colorName, QGCPalette::PaletteColorInfo_t& colorInfo) @@ -276,7 +276,7 @@ void CustomPlugin::paletteOverride(const QString &colorName, QGCPalette::Palette QQmlApplicationEngine* CustomPlugin::createQmlApplicationEngine(QObject* parent) { _qmlEngine = QGCCorePlugin::createQmlApplicationEngine(parent); - _qmlEngine->addImportPath("qrc:/Custom/Widgets"); + _qmlEngine->addImportPath("qrc:/qml/Custom/Widgets"); // TODO: Investigate _qmlEngine->setExtraSelectors({"custom"}) _selector = new CustomOverrideInterceptor(); diff --git a/custom-example/src/CustomPlugin.h b/custom-example/src/CustomPlugin.h index cafb3a042cdf..ab821e9f8fa2 100644 --- a/custom-example/src/CustomPlugin.h +++ b/custom-example/src/CustomPlugin.h @@ -49,10 +49,6 @@ class CustomOptions : public QGCOptions // Overrides from QGCOptions - /// Normal QGC needs to work with an ESP8266 WiFi thing which is remarkably crappy. This in turns causes PX4 Pro calibration to fail - /// quite often. There is a warning in regular QGC about this. Overriding the and returning true means that your custom vehicle has - /// a reliable WiFi connection so don't show that warning. - bool wifiReliableForCalibration() const final { return true; } /// Firmware upgrade page is only shown in Advanced Mode. bool showFirmwareUpgrade() const final { return _plugin->showAdvancedUI(); } QGCFlyViewOptions *flyViewOptions() const final { return _flyViewOptions; } @@ -81,7 +77,7 @@ class CustomPlugin : public QGCCorePlugin QString brandImageOutdoor() const final { return QStringLiteral("/custom/img/dronecode-black.svg"); } bool overrideSettingsGroupVisibility(const QString &name) final; /// This allows you to override/hide QGC Application settings - bool adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData) final; + void adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData, bool &visible) final; /// This modifies QGC colors palette to match possible custom corporate branding void paletteOverride(const QString &colorName, QGCPalette::PaletteColorInfo_t &colorInfo) final; /// We override this so we can get access to QQmlApplicationEngine and use it to register our qml module diff --git a/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc index 7d7021c0f13a..e4831f3b81e4 100644 --- a/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc +++ b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc @@ -16,7 +16,7 @@ CustomFirmwarePlugin::CustomFirmwarePlugin() { for (auto &mode: _flightModeList) { //-- Narrow the flight mode options to only these - if ((mode.mode_name != _holdFlightMode) && (mode.mode_name != _rtlFlightMode) && (mode.mode_name != _missionFlightMode)) { + if ((mode.mode_name != pauseFlightMode()) && (mode.mode_name != rtlFlightMode()) && (mode.mode_name != missionFlightMode())) { // No other flight modes can be set mode.canBeSet = false; } diff --git a/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h index fbd9c0605449..83a412d8c299 100644 --- a/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h +++ b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h @@ -12,7 +12,6 @@ #include "PX4FirmwarePlugin.h" class AutoPilotPlugin; -class CustomCameraManager; class Vehicle; class CustomFirmwarePlugin : public PX4FirmwarePlugin diff --git a/deploy/docker/Dockerfile-build-android b/deploy/docker/Dockerfile-build-android index 48bb5b1a7e97..7057a6771e6c 100644 --- a/deploy/docker/Dockerfile-build-android +++ b/deploy/docker/Dockerfile-build-android @@ -27,7 +27,7 @@ RUN mkdir -p $ANDROID_SDK_ROOT/cmdline-tools/latest && \ $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "platform-tools" "platforms;android-35" "build-tools;35.0.0" "ndk;26.1.10909125" # Qt setup and environment variables -ENV QT_VERSION="6.8.3" +ENV QT_VERSION="6.10.1" ENV QT_PATH="/opt/Qt" ENV QT_HOST="linux" ENV QT_HOST_ARCH="gcc_64" diff --git a/deploy/docker/Dockerfile-build-ubuntu b/deploy/docker/Dockerfile-build-ubuntu index da79df8b22ee..3558ec1063a1 100644 --- a/deploy/docker/Dockerfile-build-ubuntu +++ b/deploy/docker/Dockerfile-build-ubuntu @@ -38,7 +38,7 @@ RUN chmod +x /tmp/qt/*.sh && \ /tmp/qt/install-qt-debian.sh && \ rm -rf /tmp/qt # keep the image slim -ENV QT_ROOT_DIR=/opt/Qt/6.8.3/gcc_64 +ENV QT_ROOT_DIR=/opt/Qt/6.10.1/gcc_64 ENV PATH=$QT_ROOT_DIR/bin:$PATH # ---------- Git safe directory (avoids “detected dubious ownership”) ---------- diff --git a/deploy/installer/packages/org.mavlink.qgroundcontrol/data/driver.msi b/deploy/installer/packages/org.mavlink.qgroundcontrol/data/driver.msi deleted file mode 100644 index 7d89feef635c..000000000000 Binary files a/deploy/installer/packages/org.mavlink.qgroundcontrol/data/driver.msi and /dev/null differ diff --git a/deploy/installer/packages/org.mavlink.qgroundcontrol/meta/installscript.js b/deploy/installer/packages/org.mavlink.qgroundcontrol/meta/installscript.js index d200491f6da8..76152b20f52e 100644 --- a/deploy/installer/packages/org.mavlink.qgroundcontrol/meta/installscript.js +++ b/deploy/installer/packages/org.mavlink.qgroundcontrol/meta/installscript.js @@ -12,8 +12,6 @@ Component.prototype.createOperations = function() } if (systemInfo.productType === "windows") { - component.addElevatedOperation("Execute", "msiexec", ["/i", "@TargetDir@/driver.msi", "/qn"]); - component.addOperation("CreateShortcut", "@TargetDir@/bin/qgroundcontrol.exe", "@StartMenuDir@/QGroundControl.lnk"); component.addOperation("CreateShortcut", "@TargetDir@/bin/qgroundcontrol.exe", "@DesktopDir@/QGroundControl.lnk"); } diff --git a/deploy/ios/Images.xcassets/AppIcon.appiconset/Contents.json b/deploy/ios/Images.xcassets/AppIcon.appiconset/Contents.json index 001862ca607d..c0ecf32e5c0e 100644 --- a/deploy/ios/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/deploy/ios/Images.xcassets/AppIcon.appiconset/Contents.json @@ -113,4 +113,4 @@ "version" : 1, "author" : "xcode" } -} \ No newline at end of file +} diff --git a/deploy/ios/Images.xcassets/Contents.json b/deploy/ios/Images.xcassets/Contents.json index da4a164c9186..2d92bd53fdb2 100644 --- a/deploy/ios/Images.xcassets/Contents.json +++ b/deploy/ios/Images.xcassets/Contents.json @@ -3,4 +3,4 @@ "version" : 1, "author" : "xcode" } -} \ No newline at end of file +} diff --git a/deploy/ios/Info.plist.app.in b/deploy/ios/Info.plist.app.in index c6b7804db84c..01d07915eba3 100644 --- a/deploy/ios/Info.plist.app.in +++ b/deploy/ios/Info.plist.app.in @@ -47,4 +47,3 @@ Qt Multimedia Example - diff --git a/deploy/ios/iOSForAppStore-Info-Source.plist b/deploy/ios/iOSForAppStore-Info-Source.plist index a4733d71c976..1374c48e30ed 100644 --- a/deploy/ios/iOSForAppStore-Info-Source.plist +++ b/deploy/ios/iOSForAppStore-Info-Source.plist @@ -51,7 +51,7 @@ NSLocationAlwaysUsageDescription Ground Station Location NSBluetoothPeripheralUsageDescription - QGroundControl would like to use bluetooth. + QGroundControl would like to use bluetooth. UIRequiresFullScreen UISupportedInterfaceOrientations diff --git a/deploy/linux/appimagecraft.yml b/deploy/linux/appimagecraft.yml index 72429ca25f20..b8b2c5397ba3 100644 --- a/deploy/linux/appimagecraft.yml +++ b/deploy/linux/appimagecraft.yml @@ -8,7 +8,7 @@ build: cmake: source_dir: src/ extra_variables: - - Qt6_ROOT=/home/runner/work/_temp/Qt/6.8.3/gcc_64 + - Qt6_ROOT=/home/runner/work/_temp/Qt/6.10.1/gcc_64 environment: BUILD_TYPE: Release diff --git a/deploy/vagrant/.vagrantconfig.yml b/deploy/vagrant/.vagrantconfig.yml index b8f881a44736..ed7ecd7541bb 100644 --- a/deploy/vagrant/.vagrantconfig.yml +++ b/deploy/vagrant/.vagrantconfig.yml @@ -3,10 +3,10 @@ configs: 'qt_deps_unpack_parent_dir': '/home/vagrant' 'qt_deps_unpack_dir': '/home/vagrant/Qt' - 'qt_deps_bin_unpack_dir': '/home/vagrant/Qt/6.8.3/gcc_64/bin' - 'qt_deps_lib_unpack_dir': '/home/vagrant/Qt/6.8.3/gcc_64/lib' - 'qt_deps_plugins_unpack_dir': '/home/vagrant/Qt/6.8.3/gcc_64/plugins' - 'qt_deps_qml_unpack_dir': '/home/vagrant/Qt/6.8.3/gcc_64/qml' + 'qt_deps_bin_unpack_dir': '/home/vagrant/Qt/6.10.1/gcc_64/bin' + 'qt_deps_lib_unpack_dir': '/home/vagrant/Qt/6.10.1/gcc_64/lib' + 'qt_deps_plugins_unpack_dir': '/home/vagrant/Qt/6.10.1/gcc_64/plugins' + 'qt_deps_qml_unpack_dir': '/home/vagrant/Qt/6.10.1/gcc_64/qml' 'project_root_dir': '/vagrant' diff --git a/deploy/vagrant/Vagrantfile b/deploy/vagrant/Vagrantfile index f4904ef35078..68eb3f24a54d 100644 --- a/deploy/vagrant/Vagrantfile +++ b/deploy/vagrant/Vagrantfile @@ -91,10 +91,10 @@ Vagrant.configure(2) do |config| apt-get install -y patchelf dir="%{qt_deps_unpack_dir}" - version="6.8.3" + version="6.10.1" host="linux" target="desktop" - modules="qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors" + modules="qtcharts qtlocation qtpositioning qtspeech qt5compat qtmultimedia qtserialport qtimageformats qtshadertools qtconnectivity qtquick3d qtsensors qtscxml" su - vagrant -c "rm -rf ${dir}" su - vagrant -c "mkdir -p ${dir}" su - vagrant -c "python3 -m aqt install-qt -O ${dir} ${host} ${target} ${version} -m ${modules}" diff --git a/deploy/windows/QGroundControl.rc b/deploy/windows/QGroundControl.rc index 083429a40636..e9f8e24a88b6 100644 --- a/deploy/windows/QGroundControl.rc +++ b/deploy/windows/QGroundControl.rc @@ -1 +1 @@ -IDI_ICON1 ICON DISCARDABLE "./WindowsQGC.ico" +IDI_ICON1 ICON DISCARDABLE "./WindowsQGC.ico" diff --git a/deploy/windows/QGroundControl.rc.in b/deploy/windows/QGroundControl.rc.in index 0d9a95a72b00..b0ca503e42b6 100644 --- a/deploy/windows/QGroundControl.rc.in +++ b/deploy/windows/QGroundControl.rc.in @@ -1,32 +1,32 @@ -#include "winver.h" - -IDI_ICON1 ICON "@QGC_WINDOWS_ICON_PATH@" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @CMAKE_PROJECT_VERSION_MAJOR@,@CMAKE_PROJECT_VERSION_MINOR@,@CMAKE_PROJECT_VERSION_PATCH@,@CMAKE_PROJECT_VERSION_TWEAK@ - PRODUCTVERSION @CMAKE_PROJECT_VERSION_MAJOR@,@CMAKE_PROJECT_VERSION_MINOR@,@CMAKE_PROJECT_VERSION_PATCH@,@CMAKE_PROJECT_VERSION_TWEAK@ - FILEFLAGS 0x0L - FILEFLAGSMASK 0x3fL - FILEOS 0x00040004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000004b0" - BEGIN - VALUE "CompanyName", "@QGC_ORG_NAME@" - VALUE "FileDescription", "@CMAKE_PROJECT_NAME@" - VALUE "FileVersion", "@CMAKE_PROJECT_VERSION@" - VALUE "LegalCopyright", "@QGC_APP_COPYRIGHT@" - VALUE "InternalName", "@CMAKE_PROJECT_NAME@" - VALUE "OriginalFilename", "@CMAKE_PROJECT_NAME@.exe" - VALUE "ProductName", "@CMAKE_PROJECT_NAME@" - VALUE "ProductVersion", "@CMAKE_PROJECT_VERSION@" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 - END -END +#include "winver.h" + +IDI_ICON1 ICON "@QGC_WINDOWS_ICON_PATH@" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @CMAKE_PROJECT_VERSION_MAJOR@,@CMAKE_PROJECT_VERSION_MINOR@,@CMAKE_PROJECT_VERSION_PATCH@,@CMAKE_PROJECT_VERSION_TWEAK@ + PRODUCTVERSION @CMAKE_PROJECT_VERSION_MAJOR@,@CMAKE_PROJECT_VERSION_MINOR@,@CMAKE_PROJECT_VERSION_PATCH@,@CMAKE_PROJECT_VERSION_TWEAK@ + FILEFLAGS 0x0L + FILEFLAGSMASK 0x3fL + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "@QGC_ORG_NAME@" + VALUE "FileDescription", "@CMAKE_PROJECT_NAME@" + VALUE "FileVersion", "@CMAKE_PROJECT_VERSION@" + VALUE "LegalCopyright", "@QGC_APP_COPYRIGHT@" + VALUE "InternalName", "@CMAKE_PROJECT_NAME@" + VALUE "OriginalFilename", "@CMAKE_PROJECT_NAME@.exe" + VALUE "ProductName", "@CMAKE_PROJECT_NAME@" + VALUE "ProductVersion", "@CMAKE_PROJECT_VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/deploy/windows/driver-msi-README.md b/deploy/windows/driver-msi-README.md deleted file mode 100644 index 3c90adbbaf4d..000000000000 --- a/deploy/windows/driver-msi-README.md +++ /dev/null @@ -1 +0,0 @@ -The driver.msi file is pulled from https://firmware.ardupilot.org/Tools/MissionPlanner/driver.msi \ No newline at end of file diff --git a/deploy/windows/driver.msi b/deploy/windows/driver.msi deleted file mode 100644 index 7d89feef635c..000000000000 Binary files a/deploy/windows/driver.msi and /dev/null differ diff --git a/deploy/windows/nullsoft_installer.nsi b/deploy/windows/nullsoft_installer.nsi index 71a45c42b5cb..275f3fef489f 100644 --- a/deploy/windows/nullsoft_installer.nsi +++ b/deploy/windows/nullsoft_installer.nsi @@ -1,163 +1,160 @@ -!include "MUI2.nsh" -!include LogicLib.nsh -!include Win\COM.nsh -!include Win\Propkey.nsh -!include "FileFunc.nsh" - -!macro DemoteShortCut target - !insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 "" - ${If} $0 <> 0 - ${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)' - ${If} $1 P<> 0 - ${IPersistFile::Load} $1 '("${target}",1)' - ${IUnknown::Release} $1 "" - ${EndIf} - ${IUnknown::QueryInterface} $0 '("${IID_IPropertyStore}",.r1)' - ${If} $1 P<> 0 - System::Call '*${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_StartPinOption})p.r2' - System::Call '*${SYSSTRUCT_PROPVARIANT}(${VT_UI4},,&i4 ${APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL})p.r3' - ${IPropertyStore::SetValue} $1 '($2,$3)' - - ; Reuse the PROPERTYKEY & PROPVARIANT buffers to set another property - System::Call '*$2${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_ExcludeFromShowInNewInstall})' - System::Call '*$3${SYSSTRUCT_PROPVARIANT}(${VT_BOOL},,&i2 ${VARIANT_TRUE})' - ${IPropertyStore::SetValue} $1 '($2,$3)' - - System::Free $2 - System::Free $3 - ${IPropertyStore::Commit} $1 "" - ${IUnknown::Release} $1 "" - ${EndIf} - ${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)' - ${If} $1 P<> 0 - ${IPersistFile::Save} $1 '("${target}",1)' - ${IUnknown::Release} $1 "" - ${EndIf} - ${IUnknown::Release} $0 "" - ${EndIf} -!macroend - -Name "${APPNAME}" -Var StartMenuFolder - -InstallDir "$PROGRAMFILES64\${APPNAME}" - -SetCompressor /SOLID /FINAL lzma - -!define MUI_HEADERIMAGE -!define MUI_HEADERIMAGE_BITMAP "${HEADER_BITMAP}"; -!define MUI_ICON "${INSTALLER_ICON}"; -!define MUI_UNICON "${INSTALLER_ICON}"; - -!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES - -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -!insertmacro MUI_LANGUAGE "English" - -Section - DetailPrint "Checking for 32 bit uninstaller" - SetRegView 32 - ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" - StrCmp $R0 "" check64BitUninstall doUninstall - -check64BitUninstall: - DetailPrint "Checking for 64 bit uninstaller" - SetRegView 64 - ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" - StrCmp $R0 "" doInstall - -doUninstall: - DetailPrint "Uninstalling previous version..." - ExecWait "$R0 /S -LEAVE_DATA=1 _?=$INSTDIR" - IntCmp $0 0 doInstall - - MessageBox MB_OK|MB_ICONEXCLAMATION \ - "Could not remove a previously installed ${APPNAME} version.$\n$\nPlease remove it before continuing." - Abort - -doInstall: - SetRegView 64 - SetOutPath $INSTDIR - File /r /x ${EXENAME}.pdb /x ${EXENAME}.lib /x ${EXENAME}.exp ${DESTDIR}\*.* - - ; Driver location is http://firmware.ardupilot.org/Tools/MissionPlanner/driver.msi - ; Whenever this driver is updated in the repo QGCCURRENTDRIVERVERSION must be bumped by 1 - File ${DRIVER_MSI} - - WriteUninstaller $INSTDIR\${EXENAME}-Uninstall.exe - WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" - WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$\"$INSTDIR\${EXENAME}-Uninstall.exe$\"" - WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" "DumpCount" 5 - WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" "DumpType" 1 - WriteRegExpandStr HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" "DumpFolder" "%LOCALAPPDATA%\QGCCrashDumps" - - ; QGC stores its own driver version key to prevent installation if already up to date - ; This prevents running the driver install a second time which will start up in repair mode which is confusing - !define QGCDRIVERVERSIONKEY "SOFTWARE\QGroundControlUAVDrivers" - !define QGCCURRENTDRIVERVERSION 3 - - ; If the drivers are already installed the key "HKCU/SOFTWARE\MichaelOborne\driver\installed" will be present and set to 1 - SetRegView 64 - !define DRIVERKEY "SOFTWARE\MichaelOborne\driver" - ReadRegDWORD $0 HKCU "${DRIVERKEY}" "installed" - IntCmp $0 1 driversInstalled driversNotInstalled driversNotInstalled - -driversInstalled: - DetailPrint "UAV Drivers already installed. Checking version..." - ; Check if the installed drivers are out of date. - ; Missing key also indicates out of date driver install. - ReadRegDWORD $0 HKCU "${QGCDRIVERVERSIONKEY}" "version" - IntCmp $0 ${QGCCURRENTDRIVERVERSION} done driversOutOfDate done - -driversOutOfDate: - DetailPrint "UAV Drivers out of date." - goto installDrivers - -driversNotInstalled: - DetailPrint "UAV Drivers not installed." - ; Delete abandoned possibly out of date version key - DeleteRegKey HKCU "SOFTWARE\QGroundControlUAVDrivers" - -installDrivers: - DetailPrint "Installing UAV Drivers..." - ExecWait '"msiexec" /i "driver.msi"' - ; Set current driver version value - WriteRegDWORD HKCU "${QGCDRIVERVERSIONKEY}" "version" ${QGCCURRENTDRIVERVERSION} - goto done - -done: - SetRegView lastused -SectionEnd - -Section "Uninstall" - SetRegView 64 - ${GetParameters} $R0 - ${GetOptions} $R0 "-LEAVE_DATA=" $R1 - !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder - SetShellVarContext all - RMDir /r /REBOOTOK $INSTDIR - RMDir /r /REBOOTOK "$SMPROGRAMS\$StartMenuFolder\" - SetShellVarContext current - ${If} $R1 != 1 - RMDir /r /REBOOTOK "$APPDATA\${ORGNAME}\" - ${Endif} - DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" - DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" - ; NOTE: We specifically do not delete the driver version key since we need it to persist around uninstalls -SectionEnd - -Section "create Start Menu Shortcuts" - SetRegView 64 - SetShellVarContext all - CreateDirectory "$SMPROGRAMS\$StartMenuFolder" - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME}.lnk" "$INSTDIR\bin\${EXENAME}.exe" "" "$INSTDIR\bin\${EXENAME}.exe" 0 - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Compatibility Mode).lnk" "$INSTDIR\bin\${EXENAME}.exe" "-desktop" "$INSTDIR\bin\${EXENAME}.exe" 0 - !insertmacro DemoteShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Compatibility Mode).lnk" - CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Safe Mode).lnk" "$INSTDIR\bin\${EXENAME}.exe" "-swrast" "$INSTDIR\bin\${EXENAME}.exe" 0 - !insertmacro DemoteShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Safe Mode).lnk" -SectionEnd - +!include "MUI2.nsh" +!include "LogicLib.nsh" +!include "Win\COM.nsh" +!include "Win\Propkey.nsh" +!include "FileFunc.nsh" + +RequestExecutionLevel admin + +!macro DemoteShortCut target + !insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 "" + ${If} $0 <> 0 + ${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)' + ${If} $1 P<> 0 + ${IPersistFile::Load} $1 '("${target}",1)' + ${IUnknown::Release} $1 "" + ${EndIf} + ${IUnknown::QueryInterface} $0 '("${IID_IPropertyStore}",.r1)' + ${If} $1 P<> 0 + System::Call '*${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_StartPinOption})p.r2' + System::Call '*${SYSSTRUCT_PROPVARIANT}(${VT_UI4},,&i4 ${APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL})p.r3' + ${IPropertyStore::SetValue} $1 '($2,$3)' + + System::Call '*$2${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_ExcludeFromShowInNewInstall})' + System::Call '*$3${SYSSTRUCT_PROPVARIANT}(${VT_BOOL},,&i2 ${VARIANT_TRUE})' + ${IPropertyStore::SetValue} $1 '($2,$3)' + + System::Free $2 + System::Free $3 + ${IPropertyStore::Commit} $1 "" + ${IUnknown::Release} $1 "" + ${EndIf} + ${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)' + ${If} $1 P<> 0 + ${IPersistFile::Save} $1 '("${target}",1)' + ${IUnknown::Release} $1 "" + ${EndIf} + ${IUnknown::Release} $0 "" + ${EndIf} +!macroend + +Name "${APPNAME}" +Var StartMenuFolder + +InstallDir "$PROGRAMFILES64\${APPNAME}" +SetCompressor /SOLID /FINAL lzma + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${HEADER_BITMAP}" +!define MUI_ICON "${INSTALLER_ICON}" +!define MUI_UNICON "${INSTALLER_ICON}" + +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +Section "Install" SecMain + SectionIn RO + DetailPrint "Checking for 64 bit uninstaller" + SetRegView 64 + ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" + StrCmp $R0 "" doInstall checkUninstaller + +checkUninstaller: + ; Remove quotes from uninstaller path to check if file exists + StrCpy $R1 $R0 "" 1 ; Skip first quote + StrLen $R2 $R1 + IntOp $R2 $R2 - 1 ; Remove last quote + StrCpy $R1 $R1 $R2 + + DetailPrint "Checking if uninstaller exists: $R1" + IfFileExists "$R1" doUninstall cleanupOrphanedRegistry + +cleanupOrphanedRegistry: + DetailPrint "Previous uninstaller not found, cleaning up orphaned registry keys..." + SetRegView 64 + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" + Goto doInstall + +doUninstall: + DetailPrint "Uninstalling previous version..." + ClearErrors + ExecWait "$R0 /S -LEAVE_DATA=1 _?=$INSTDIR" + ${If} ${Errors} + MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to start previous uninstaller." + Abort + ${EndIf} + IntCmp $0 0 doInstall + MessageBox MB_OK|MB_ICONEXCLAMATION "Could not remove a previously installed ${APPNAME} version.$\n$\nPlease remove it before continuing." + Abort + +doInstall: + SetRegView 64 + SetOutPath $INSTDIR + ; Install payload + File /r /x ${EXENAME}.pdb /x ${EXENAME}.lib /x ${EXENAME}.exp ${DESTDIR}\*.* + + ; Create uninstaller and ARP data + WriteUninstaller "$INSTDIR\${EXENAME}-Uninstall.exe" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$\"$INSTDIR\${EXENAME}-Uninstall.exe$\"" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\bin\${EXENAME}.exe" + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "InstallLocation" "$INSTDIR" + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" 1 + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" 1 + !ifdef ORGNAME + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${ORGNAME}" + !endif + !ifdef APPVERSION + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${APPVERSION}" + !endif + + ; WER dumps for crash triage + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" "DumpCount" 5 + WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" "DumpType" 1 + WriteRegExpandStr HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" "DumpFolder" "%LOCALAPPDATA%\QGCCrashDumps" + +done: + SetRegView lastused +SectionEnd + +Section "Uninstall" + SetRegView 64 + ${GetParameters} $R0 + ${GetOptions} $R0 "-LEAVE_DATA=" $R1 + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder + + ; Remove files and shortcuts + SetShellVarContext all + RMDir /r /REBOOTOK "$INSTDIR" + RMDir /r /REBOOTOK "$SMPROGRAMS\$StartMenuFolder\" + SetShellVarContext current + + ${If} $R1 != 1 + RMDir /r /REBOOTOK "$APPDATA\${ORGNAME}\" + ${EndIf} + + ; Remove ARP + WER + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\${EXENAME}.exe" +SectionEnd + +Section "Create Start Menu Shortcuts" + SetRegView 64 + SetShellVarContext all + CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME}.lnk" "$INSTDIR\bin\${EXENAME}.exe" "" "$INSTDIR\bin\${EXENAME}.exe" 0 + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Compatibility Mode).lnk" "$INSTDIR\bin\${EXENAME}.exe" "-desktop" "$INSTDIR\bin\${EXENAME}.exe" 0 + !insertmacro DemoteShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Compatibility Mode).lnk" + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Safe Mode).lnk" "$INSTDIR\bin\${EXENAME}.exe" "-swrast" "$INSTDIR\bin\${EXENAME}.exe" 0 + !insertmacro DemoteShortCut "$SMPROGRAMS\$StartMenuFolder\${APPNAME} (GPU Safe Mode).lnk" + !insertmacro MUI_STARTMENU_WRITE_END +SectionEnd diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index 5e340471ee71..f45ee74f24be 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -3,7 +3,7 @@ import { defineConfig } from "vitepress"; // https://vitepress.dev/reference/site-config export default defineConfig({ - title: "QGC Guide (master)", + title: "QGC Guide", description: "How to use and develop QGroundControl for PX4 or ArduPilot powered vehicles.", ignoreDeadLinks: true, // Do this for stable, where we don't yet have all translations diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css index 1bd56ff29161..7a3c337f2aa5 100644 --- a/docs/.vitepress/theme/style.css +++ b/docs/.vitepress/theme/style.css @@ -8,7 +8,7 @@ * * Each colors have exact same color scale system with 3 levels of solid * colors with different brightness, and 1 soft color. - * + * * - `XXX-1`: The most solid color used mainly for colored text. It must * satisfy the contrast ratio against when used on top of `XXX-soft`. * diff --git a/docs/en/qgc-dev-guide/contribute/pull_requests.md b/docs/en/qgc-dev-guide/contribute/pull_requests.md index fe120dcf7dca..e4bb4b9f2456 100644 --- a/docs/en/qgc-dev-guide/contribute/pull_requests.md +++ b/docs/en/qgc-dev-guide/contribute/pull_requests.md @@ -1,3 +1,16 @@ # Pull Requests All pull requests go through the QGC CI build system which builds release and debug version. Builds will fail if there are compiler warnings. Also unit tests are run against supported OS debug builds. + +## Automatic Release Note Generation + +Releases notes are generated from the following GitHub labels qhich should be set on Pull Requests as appropriate: + +* "RN: MAJOR FEATURE" +* "RN: MINOR FEATURE" +* "RN: IMPROVEMENT" +* "RN: REFACTORING" +* "RN: BUGFIX" +* "RN: NEW BOARD SUPPORT" + +There are also a set of the above labels which end in "- CUSTOM BUILD" which indicate the changes is associated with the custom build architecture. diff --git a/docs/en/qgc-dev-guide/contribute/unit_tests.md b/docs/en/qgc-dev-guide/contribute/unit_tests.md index b76a38e17eec..b126b7a383c3 100644 --- a/docs/en/qgc-dev-guide/contribute/unit_tests.md +++ b/docs/en/qgc-dev-guide/contribute/unit_tests.md @@ -2,7 +2,7 @@ _QGroundControl_ (QGC) contains a set of unit tests that must pass before a pull request will be accepted. The addition of new complex subsystems to QGC should include a corresponding new unit test to test it. -The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/src/qgcunittest/UnitTestList.cc). +The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/test/UnitTestList.cc). To run unit tests: diff --git a/docs/en/qgc-dev-guide/custom_build/fork_repo.md b/docs/en/qgc-dev-guide/custom_build/fork_repo.md index 22a4cddef91d..d36348dff658 100644 --- a/docs/en/qgc-dev-guide/custom_build/fork_repo.md +++ b/docs/en/qgc-dev-guide/custom_build/fork_repo.md @@ -4,7 +4,7 @@ * Copy the `custom_example` directory to a new `custom` directory at the root of the repo. * Tweak the source in `custom` directory as needed. -You can also rename the `custom_example` directory to `custom` but that can lead to merge problems when you bring your fork up to date with newer upstream version of regular QGC. +You can also rename the `custom-example` directory to `custom` but that can lead to merge problems when you bring your fork up to date with newer upstream version of regular QGC. ## Modifying Mainline QGC Source Code diff --git a/docs/en/qgc-dev-guide/custom_build/mavlink.md b/docs/en/qgc-dev-guide/custom_build/mavlink.md index 2c38063b59c2..f64ed354253e 100644 --- a/docs/en/qgc-dev-guide/custom_build/mavlink.md +++ b/docs/en/qgc-dev-guide/custom_build/mavlink.md @@ -19,4 +19,4 @@ To modify the version of MAVLink used by QGC: Just add to [/qgroundcontrol/cmake/CustomOptions.cmake](../../../../cmake/CustomOptions.cmake): ```cmake set(CPM_mavlink_SOURCE "/path/to/your/custom/mavlink") - ``` \ No newline at end of file + ``` diff --git a/docs/en/qgc-dev-guide/custom_build/resource_override.md b/docs/en/qgc-dev-guide/custom_build/resource_override.md index 572c1ad03026..097776f34d54 100644 --- a/docs/en/qgc-dev-guide/custom_build/resource_override.md +++ b/docs/en/qgc-dev-guide/custom_build/resource_override.md @@ -11,4 +11,4 @@ By overriding a resource you can replace it with your own version of it. This co Resource overrides work by using `QQmlEngine::addUrlInterceptor` to intercept requests for resources and re-route the request to available custom resources instead of the normal resource. Look at [custom_example/CustomPlugin.cc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CustomPlugin.cc) for how it's done and replicate that in your own custom build source. -Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. \ No newline at end of file +Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. diff --git a/docs/en/qgc-dev-guide/custom_build/toolbar.md b/docs/en/qgc-dev-guide/custom_build/toolbar.md index 662cb8db4ae9..e4dae8b2607c 100644 --- a/docs/en/qgc-dev-guide/custom_build/toolbar.md +++ b/docs/en/qgc-dev-guide/custom_build/toolbar.md @@ -30,10 +30,6 @@ These provide information to the user which is not associated with a vehicle. Fo These are indicators which are associated with information about the vehicle. They are only available when a vehicle is connected. To manipulate the list of vehicle indicators you override `FirmwarePlugin::toolIndicators`. -#### Vehicle Mode Indicators - -These are indicators which are associated with information about the vehicle. They require additional UI provided by the Fly View to complete their actions. An example is Arming and Disarming. They are only available when a vehicle is connected. To manipulate the list of vehicle mode indicators you override `FirmwarePlugin::modeIndicators`. - ### Modifying the toolbar UI itself This is accomplished by using resource overrides on the qml files associated with the toolbar. This provides a high level of customization but also a higher level of complexity. The primary ui for the toolbar is in `MainToolBar.qml`. The main window code in `MainRootWindow.qml` interacts with the toolbar to show different indicator sections based on current view as well as whether the mode indicators show or not also based on current view. diff --git a/docs/en/qgc-dev-guide/getting_started/index.md b/docs/en/qgc-dev-guide/getting_started/index.md index f30fa5d05d1b..fba801e7b888 100644 --- a/docs/en/qgc-dev-guide/getting_started/index.md +++ b/docs/en/qgc-dev-guide/getting_started/index.md @@ -1,5 +1,5 @@ --- -qt_version: 6.8.3 +qt_version: 6.10.1 --- # Getting Started with Source and Builds @@ -47,9 +47,9 @@ We support Linux builds using a container found on the source tree of the reposi ### Native Builds _QGroundControl_ builds are supported for macOS, Linux, Windows, and Android. Creating a version of QGC for iOS is theoretically possible but is no longer supported as a standard build. -_QGroundControl_ uses [Qt](http://www.qt.io) as its cross-platform support library. +_QGroundControl_ uses [Qt](http://www.qt.io) as its cross-platform support library. -The required version of Qt is {{ $frontmatter.qt_version }} **(only)**. +The required version of Qt is {{ $frontmatter.qt_version }} **(only)**. ::: warning **Do not use any other version of Qt!** @@ -70,7 +70,7 @@ To install Qt: - Set the downloaded file to executable using: `chmod +x`. - You may also need to install libxcb-cursor0 -1. On the _Installation Folder_ page select "Custom Installation" +1. On the _Installation Folder_ page select "Custom Installation" 1. On the _Select Components_ page: @@ -88,10 +88,9 @@ To install Qt: - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` - - **Mac** `sh qgroundcontrol/tools/setup/macos-dependencies.sh` - - **Android** [Setup](https://doc.qt.io/qt-6/android-getting-started.html). JDK17 is required for the latest updated versions. NDK Version: 25.1.8937393 - You can confirm it is being used by reviewing the project setting: **Projects > Manage Kits > Devices > Android (tab) > Android Settings > _JDK location_**. - Note: Visit here for more detailed configurations [android.yml](.github/workflows/android.yml) + - **Mac** `sh qgroundcontrol/tools/setup/install-dependencies-osx.sh` + - **Windows** `qgroundcontrol/tools/setup/install-depedencies-windows.ps1` + - **Android** Installing dependencies for android is quite involved. You are better off using Qt documentation for android setup instructions. Search for "Qt 6.10 android" on the internet to find the correct "Gettting Started with Qt for Android" page. Read it full and carefully! An example of what you are looking for is [here](https://doc.qt.io/qt-6/android-getting-started.html). 1. Install Optional/OS-Specific Functionality @@ -137,11 +136,18 @@ Example commands to build a default QGC and run it afterwards: 1. Configure: ```sh - ~/Qt/6.8.3/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + ~/Qt/{{ $frontmatter.qt_version }}/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug ``` Change the directory for qt-cmake to match your install location for Qt and the kit you want to use. + **Mac**: To Sign/Notarize/Staple the QGC app bundle, add `-DQGC_MACOS_SIGN_WITH_IDENTITY=ON` to the configure command line. During the `install` phase the following environment variables will need to be available: + + * `QGC_MACOS_SIGNING_IDENTITY` - Signing identity for your Developer ID certificate which must be in the keychain + * `QGC_MACOS_NOTARIZATION_USERNAME` - Username for your Apple Developer Account + * `QGC_MACOS_NOTARIZATION_PASSWORD` - App specific password for Notarization from your Apple Developer Account + * `QGC_MACOS_NOTARIZATION_TEAM_ID` - Apple Developer Account Team ID + 1. Build ```sh diff --git a/docs/en/qgc-user-guide/fly_view/camera_tools.md b/docs/en/qgc-user-guide/fly_view/camera_tools.md index 69dc11226735..a1cd5980e3c4 100644 --- a/docs/en/qgc-user-guide/fly_view/camera_tools.md +++ b/docs/en/qgc-user-guide/fly_view/camera_tools.md @@ -26,4 +26,4 @@ Most of the settings that are displayed depend on the camera (they are defined i The video page is used to enable/disable video streaming. When enabled, you can start/stop the video stream, enable a grid overlay, change how the image fits the screen, and record the video locally with QGC. -![Instrument Page - Video Stream](../../../assets/fly/instrument_page_video_stream.jpg) \ No newline at end of file +![Instrument Page - Video Stream](../../../assets/fly/instrument_page_video_stream.jpg) diff --git a/docs/en/qgc-user-guide/fly_view/fly_tools.md b/docs/en/qgc-user-guide/fly_view/fly_tools.md index d3c298f4d673..292d05ee1ea3 100644 --- a/docs/en/qgc-user-guide/fly_view/fly_tools.md +++ b/docs/en/qgc-user-guide/fly_view/fly_tools.md @@ -55,4 +55,4 @@ You can change altitude while flying, except when in a mission: 1. Press the **Actions** button on the _Fly Tools_ 1. Select the _Change Altitude_ button 2. Select the new altitude from the vertical slider -3. Confirm the action \ No newline at end of file +3. Confirm the action diff --git a/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md b/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md index 35568c04e9b3..50457b484cbe 100644 --- a/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md +++ b/docs/en/qgc-user-guide/fly_view/fly_view_toolbar.md @@ -55,7 +55,7 @@ The Vehicle Messages indicator dropdown shows you messages which come from the v ## GPS -The GPS indicator shows you the satellite count and the HDOP in the toolbar icon. The dropdown shows you additional GPS status. The expanded page give you access to RTK settings. +The GPS indicator shows you the satellite count and the HDOP in the toolbar icon. The dropdown shows you additional GPS status. The expanded page give you access to RTK settings. ## Battery @@ -73,6 +73,6 @@ There are other indicators which only show in certain situations: * Telemetry RSSI * RC RSSI -* Gimbal +* Gimbal - Only displayed if the vehicle supports the [Mavlink Gimbal Protocol](https://mavlink.io/en/services/gimbal_v2.html) * VTOL transitions * Select from multiple connected vehicles diff --git a/docs/en/qgc-user-guide/fly_view/hud.md b/docs/en/qgc-user-guide/fly_view/hud.md index b367c54d8ecf..feb14f251f40 100644 --- a/docs/en/qgc-user-guide/fly_view/hud.md +++ b/docs/en/qgc-user-guide/fly_view/hud.md @@ -118,7 +118,7 @@ The Vehicle Messages Indicator dropdown shows you messages which come from the v ![Vehicle state - ready to fly green/ready background](../../../assets/fly/toolbar/gps_indicator.png) -The GPS Indicator shows you the satellite count and the HDOP in the toolbar icon. The dropdown shows you additional GPS status. The expanded page give you access to RTK settings. +The GPS Indicator shows you the satellite count and the HDOP in the toolbar icon. The dropdown shows you additional GPS status. The expanded page give you access to RTK settings. ### Battery Indicator ![Vehicle state - ready to fly green/ready background](../../../assets/fly/toolbar/battery_indicator.png) diff --git a/docs/en/qgc-user-guide/fly_view/instrument_panel.md b/docs/en/qgc-user-guide/fly_view/instrument_panel.md index b7b70ead3729..b0b52575b26a 100644 --- a/docs/en/qgc-user-guide/fly_view/instrument_panel.md +++ b/docs/en/qgc-user-guide/fly_view/instrument_panel.md @@ -29,4 +29,4 @@ By default this is the vehicle, but you can use the selector to choose a particu The selection list on the top right is used to select a particular telemetry value for the vehicle or sensor. -![Instrument Panel - value options](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) \ No newline at end of file +![Instrument Panel - value options](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) diff --git a/docs/en/qgc-user-guide/getting_started/download_and_install.md b/docs/en/qgc-user-guide/getting_started/download_and_install.md index ba5e3959d9d5..e87dd2077cfe 100644 --- a/docs/en/qgc-user-guide/getting_started/download_and_install.md +++ b/docs/en/qgc-user-guide/getting_started/download_and_install.md @@ -16,7 +16,7 @@ For the best experience and compatibility, we recommend you the newest version o ## Windows {#windows} -_QGroundControl_ can be installed on 64 bit versions of Windows 10 (1809 or later) or Windows 11: +Supported versions: Windows 10 (1809 or later), Windows 11: 1. Download [QGroundControl-installer.exe](https://d176tv9ibo4jno.cloudfront.net/latest/QGroundControl-installer.exe). 1. Double click the executable to launch the installer. @@ -27,9 +27,9 @@ Use the first shortcut unless you experience startup or video rendering issues. For more information see [Troubleshooting QGC Setup > Windows: UI Rendering/Video Driver Issues](../troubleshooting/qgc_setup.md#opengl_troubleshooting). ::: -## Mac OS X {#macOS} +## Mac OS {#macOS} -_QGroundControl_ can be installed on macOS 12 (Monterey) or later: +Supported versions: macOS 12 (Monterey) or later: @@ -38,12 +38,12 @@ _QGroundControl_ can be installed on macOS 12 (Monterey) or later: 1. Double-click the .dmg file to mount it, then drag the _QGroundControl_ application to your _Application_ folder. ::: info -QGroundControl continues to not be signed. You will not to allow permission for it to install based on you macOS version. +QGroundControl continues to not be signed. You will not to allow permission for it to install based on your macOS version. ::: ## Ubuntu Linux {#ubuntu} -_QGroundControl_ can be installed/run on Ubuntu LTS 22.04 (and later): +Supported versions: Ubuntu 22.04, 24.04: Ubuntu comes with a serial modem manager that interferes with any robotics related use of a serial port (or USB serial). Before installing _QGroundControl_ you should remove the modem manager and grant yourself permissions to access the serial port. @@ -97,7 +97,7 @@ Either double-click the AppImage in your file manager or launch it from a termin ## Android {#android} -_QGroundControl_ can be installed/run on Android 9 or later: +Supported versions: Android 9 to 15 (arm 32/64): - [Android 32/64 bit APK](https://qgroundcontrol.s3-us-west-2.amazonaws.com/latest/QGroundControl.apk) diff --git a/docs/en/qgc-user-guide/index.md b/docs/en/qgc-user-guide/index.md index 23cf76ec2502..4216df80bc2f 100644 --- a/docs/en/qgc-user-guide/index.md +++ b/docs/en/qgc-user-guide/index.md @@ -1,6 +1,6 @@ # QGroundControl Guide (Daily Builds) -[![Discuss](https://img.shields.io/badge/discuss-px4-ff69b4.svg)](http://discuss.px4.io/c/qgroundcontrol/qgroundcontrol-usage) +[![Discuss](https://img.shields.io/badge/discuss-px4-ff69b4.svg)](http://discuss.px4.io/c/qgroundcontrol/qgroundcontrol-usage) [![Discuss](https://img.shields.io/badge/discuss-ardupilot-ff69b4.svg)](http://discuss.ardupilot.org/c/ground-control-software/qgroundcontrol) _QGroundControl_ provides full flight control and vehicle setup for PX4 or ArduPilot powered vehicles. diff --git a/docs/en/qgc-user-guide/releases/daily_build_new_features.md b/docs/en/qgc-user-guide/releases/daily_build_new_features.md index abd9ddd0395d..415ca8d8dfae 100644 --- a/docs/en/qgc-user-guide/releases/daily_build_new_features.md +++ b/docs/en/qgc-user-guide/releases/daily_build_new_features.md @@ -39,5 +39,5 @@ There is also a [Change Log](https://github.com/mavlink/qgroundcontrol/blob/mast * Developer changes * Build system fully converted to cmake * qmake no longer supported - * Source updated to use Qt 6.8.3 - * GStreamer support updated to 1.22 \ No newline at end of file + * Source updated to use Qt 6.10.1 + * GStreamer support updated to 1.22 diff --git a/docs/en/qgc-user-guide/releases/daily_builds.md b/docs/en/qgc-user-guide/releases/daily_builds.md index c189b2025f30..ad7601c3f176 100644 --- a/docs/en/qgc-user-guide/releases/daily_builds.md +++ b/docs/en/qgc-user-guide/releases/daily_builds.md @@ -9,7 +9,9 @@ Use at your own risk. These can be downloaded from the links below (install as described in [Download and Install](../getting_started/download_and_install.md)): -- [Windows](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer.exe) +- Windows + - [x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-AMD64.exe) + - [Arm_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-ARM64.exe) - [OS X](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl.dmg) - Linux - (See installation instructions below) - [Linux x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-x86_64.AppImage) diff --git a/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md b/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md index af04540116b3..d6b33e9d8699 100644 --- a/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md +++ b/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md @@ -1,6 +1,6 @@ # 3D View -The 3D View is used to visualize and monitor the vehicle, the environment, and the planned mission in 3D. Most of the capabilities available in the [Fly View](../fly_view/fly_view.md) is also available in the 3D View. +The 3D View is used to visualize and monitor the vehicle, the environment, and the planned mission in 3D. Most of the capabilities available in the [Fly View](../fly_view/fly_view.md) is also available in the 3D View. You can use it to: - To import and display the 3D map for any region of interest downloaded from the OpenStreetMap website (.osm file). @@ -16,7 +16,7 @@ You can use it to: ![3D View](../../../assets/viewer_3d/viewer_3d_overview.jpg) # UI Overview -The screenshot above shows the main elements of the 3D View. +The screenshot above shows the main elements of the 3D View. **Enabling the 3D View:** The 3D View is disabled by default. To enable it, go to **Application Settings** ->**Fly View** tab, and under the **3D View** settings group, toggle the **Enabled** switch as shown below: @@ -44,7 +44,5 @@ The following properties can be modified in the 3D View settings group: - **Enabled**: To enable or disable the 3D View. - **3D Map File**: The path to the .osm file of a region of interest to be visualized in the QGC. The .osm file can be uploaded by clicking on the **Select File** button. To clear the 3D View from the previously loaded .osm file, you can click on the **Clear** button. -- **Average Building Level Height**: This parameter determines the height of each storey of the buildings, as in .osm file sometimes the height of the buildings is specified in terms of the level/storey. +- **Average Building Level Height**: This parameter determines the height of each storey of the buildings, as in .osm file sometimes the height of the buildings is specified in terms of the level/storey. - **Vehicle Altitude Bias**: This refers to the bias in the altitude of vehicles and their missions with respect to the ground level. It is helpful in cases where the estimated altitude of the vehicle by its flight control is biased, as the relative altitude is currently used in the 3D View. - - diff --git a/docs/ko/qgc-dev-guide/contribute/pull_requests.md b/docs/ko/qgc-dev-guide/contribute/pull_requests.md index fe120dcf7dca..b5891b9fcea6 100644 --- a/docs/ko/qgc-dev-guide/contribute/pull_requests.md +++ b/docs/ko/qgc-dev-guide/contribute/pull_requests.md @@ -1,3 +1,16 @@ # Pull Requests All pull requests go through the QGC CI build system which builds release and debug version. Builds will fail if there are compiler warnings. Also unit tests are run against supported OS debug builds. + +## Automatic Release Note Generation + +Releases notes are generated from the following GitHub labels qhich should be set on Pull Requests as appropriate: + +- "RN: MAJOR FEATURE" +- "RN: MINOR FEATURE" +- "RN: IMPROVEMENT" +- "RN: REFACTORING" +- "RN: BUGFIX" +- "RN: NEW BOARD SUPPORT" + +There are also a set of the above labels which end in "- CUSTOM BUILD" which indicate the changes is associated with the custom build architecture. diff --git a/docs/ko/qgc-dev-guide/contribute/unit_tests.md b/docs/ko/qgc-dev-guide/contribute/unit_tests.md index 023595d60160..f77eac271297 100644 --- a/docs/ko/qgc-dev-guide/contribute/unit_tests.md +++ b/docs/ko/qgc-dev-guide/contribute/unit_tests.md @@ -2,7 +2,7 @@ _QGroundControl_ (QGC) contains a set of unit tests that must pass before a pull request will be accepted. The addition of new complex subsystems to QGC should include a corresponding new unit test to test it. -The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/src/qgcunittest/UnitTestList.cc). +The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/test/UnitTestList.cc). To run unit tests: diff --git a/docs/ko/qgc-dev-guide/custom_build/mavlink.md b/docs/ko/qgc-dev-guide/custom_build/mavlink.md index bbdb3addd0ee..cf4cb272acb7 100644 --- a/docs/ko/qgc-dev-guide/custom_build/mavlink.md +++ b/docs/ko/qgc-dev-guide/custom_build/mavlink.md @@ -20,4 +20,4 @@ To modify the version of MAVLink used by QGC: Just add to [/qgroundcontrol/cmake/CustomOptions.cmake](../../../../cmake/CustomOptions.cmake): ```cmake set(CPM_mavlink_SOURCE "/path/to/your/custom/mavlink") - ``` \ No newline at end of file + ``` diff --git a/docs/ko/qgc-dev-guide/custom_build/resource_override.md b/docs/ko/qgc-dev-guide/custom_build/resource_override.md index 7667557aac28..897c3a6c5b3f 100644 --- a/docs/ko/qgc-dev-guide/custom_build/resource_override.md +++ b/docs/ko/qgc-dev-guide/custom_build/resource_override.md @@ -11,4 +11,4 @@ By overriding a resource you can replace it with your own version of it. This co Resource overrides work by using `QQmlEngine::addUrlInterceptor` to intercept requests for resources and re-route the request to available custom resources instead of the normal resource. Look at [custom_example/CustomPlugin.cc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CustomPlugin.cc) for how it's done and replicate that in your own custom build source. -Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. \ No newline at end of file +Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. diff --git a/docs/ko/qgc-dev-guide/custom_build/toolbar.md b/docs/ko/qgc-dev-guide/custom_build/toolbar.md index 662cb8db4ae9..e4dae8b2607c 100644 --- a/docs/ko/qgc-dev-guide/custom_build/toolbar.md +++ b/docs/ko/qgc-dev-guide/custom_build/toolbar.md @@ -30,10 +30,6 @@ These provide information to the user which is not associated with a vehicle. Fo These are indicators which are associated with information about the vehicle. They are only available when a vehicle is connected. To manipulate the list of vehicle indicators you override `FirmwarePlugin::toolIndicators`. -#### Vehicle Mode Indicators - -These are indicators which are associated with information about the vehicle. They require additional UI provided by the Fly View to complete their actions. An example is Arming and Disarming. They are only available when a vehicle is connected. To manipulate the list of vehicle mode indicators you override `FirmwarePlugin::modeIndicators`. - ### Modifying the toolbar UI itself This is accomplished by using resource overrides on the qml files associated with the toolbar. This provides a high level of customization but also a higher level of complexity. The primary ui for the toolbar is in `MainToolBar.qml`. The main window code in `MainRootWindow.qml` interacts with the toolbar to show different indicator sections based on current view as well as whether the mode indicators show or not also based on current view. diff --git a/docs/ko/qgc-dev-guide/getting_started/index.md b/docs/ko/qgc-dev-guide/getting_started/index.md index 7262c1a5c986..fd7ff25f7e9f 100644 --- a/docs/ko/qgc-dev-guide/getting_started/index.md +++ b/docs/ko/qgc-dev-guide/getting_started/index.md @@ -1,5 +1,5 @@ --- -qt_version: 6.8.3 +qt_version: 6.10.0 --- # Getting Started with Source and Builds @@ -21,15 +21,15 @@ To get the source files: 1. Clone the repo (or your fork) including submodules: - ```sh - git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git - ``` + ```sh + git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git + ``` 2. Update submodules (required each time you pull new source code): - ```sh - git submodule update --recursive - ``` + ```sh + git submodule update --recursive + ``` :::tip Github source-code zip files cannot be used because these do not contain the appropriate submodule source code. @@ -66,15 +66,15 @@ You **must install Qt as described below** instead of using pre-built packages f To install Qt: 1. Download and run the [Qt Online Installer](https://www.qt.io/download-qt-installer-oss) - - **Ubuntu:** - - Set the downloaded file to executable using: `chmod +x`. - - You may also need to install libxcb-cursor0 + - **Ubuntu:** + - Set the downloaded file to executable using: `chmod +x`. + - You may also need to install libxcb-cursor0 2. On the _Installation Folder_ page select "Custom Installation" 3. On the _Select Components_ page: - - I you don't see _Qt {{ $frontmatter.qt_version }}_ listed check the _Archive_ checkbox and click _Filter_. + - I you don't see _Qt {{ $frontmatter.qt_version }}_ listed check the _Archive_ checkbox and click _Filter_. - Under Qt -> _Qt {{ $frontmatter.qt_version }}_ select: - **Windows**: MSVC 2022 _arch_ - where _arch_ is the architecture of your machine @@ -86,22 +86,21 @@ To install Qt: 1. Install Additional Packages (Platform Specific) - - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` - - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` - - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` - - **Mac** `sh qgroundcontrol/tools/setup/macos-dependencies.sh` - - **Android** [Setup](https://doc.qt.io/qt-6/android-getting-started.html). JDK17 is required for the latest updated versions. NDK Version: 25.1.8937393 - You can confirm it is being used by reviewing the project setting: **Projects > Manage Kits > Devices > Android (tab) > Android Settings > _JDK location_**. - Note: Visit here for more detailed configurations [android.yml](.github/workflows/android.yml) + - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` + - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` + - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` + - **Mac** `sh qgroundcontrol/tools/setup/install-dependencies-osx.sh` + - **Windows** `qgroundcontrol/tools/setup/install-depedencies-windows.ps1` + - **Android** Installing dependencies for android is quite involved. You are better off using Qt documentation for android setup instructions. Search for "Qt 6.10 android" on the internet to find the correct "Gettting Started with Qt for Android" page. Read it full and carefully! An example of what you are looking for is [here](https://doc.qt.io/qt-6/android-getting-started.html). 2. Install Optional/OS-Specific Functionality - ::: info - Optional features that are dependent on the operating system and user-installed libraries are linked/described below. - These features can be forcibly enabled/disabled by specifying additional values to qmake. - ::: + ::: info + Optional features that are dependent on the operating system and user-installed libraries are linked/described below. + These features can be forcibly enabled/disabled by specifying additional values to qmake. + ::: - - **Video Streaming/Gstreamer:** - see [Video Streaming](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) + - **Video Streaming/Gstreamer:** - see [Video Streaming](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) #### Install Visual Studio (Windows Only) {#vs} @@ -123,7 +122,7 @@ Visual Studio is ONLY used to get the compiler. Building _QGroundControl_ is don 3. Build using the "hammer" (or "play") icons or the menus: - ![QtCreator Build Button](../../../assets/dev_getting_started/qt_creator_build_qgc.png) + ![QtCreator Build Button](../../../assets/dev_getting_started/qt_creator_build_qgc.png) #### Build using CMake on CLI {#cmake} @@ -131,29 +130,36 @@ Example commands to build a default QGC and run it afterwards: 1. Make sure you cloned the repository and updated the submodules before, see chapter _Source Code_ above and switch into the repository folder: - ```sh - cd qgroundcontrol - ``` + ```sh + cd qgroundcontrol + ``` 2. Configure: - ```sh - ~/Qt/6.8.3/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug - ``` + ```sh + ~/Qt/{{ $frontmatter.qt_version }}/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + ``` - Change the directory for qt-cmake to match your install location for Qt and the kit you want to use. + Change the directory for qt-cmake to match your install location for Qt and the kit you want to use. + + **Mac**: To Sign/Notarize/Staple the QGC app bundle, add `-DQGC_MACOS_SIGN_WITH_IDENTITY=ON` to the configure command line. During the `install` phase the following environment variables will need to be available: + + - `QGC_MACOS_SIGNING_IDENTITY` - Signing identity for your Developer ID certificate which must be in the keychain + - `QGC_MACOS_NOTARIZATION_USERNAME` - Username for your Apple Developer Account + - `QGC_MACOS_NOTARIZATION_PASSWORD` - App specific password for Notarization from your Apple Developer Account + - `QGC_MACOS_NOTARIZATION_TEAM_ID` - Apple Developer Account Team ID 3. Build - ```sh - cmake --build build --config Debug - ``` + ```sh + cmake --build build --config Debug + ``` 4. Run the QGroundcontrol binary that was just built: - ```sh - ./build/Debug/QGroundControl - ``` + ```sh + ./build/Debug/QGroundControl + ``` ### Vagrant diff --git a/docs/ko/qgc-user-guide/fly_view/camera_tools.md b/docs/ko/qgc-user-guide/fly_view/camera_tools.md index 69dc11226735..a1cd5980e3c4 100644 --- a/docs/ko/qgc-user-guide/fly_view/camera_tools.md +++ b/docs/ko/qgc-user-guide/fly_view/camera_tools.md @@ -26,4 +26,4 @@ Most of the settings that are displayed depend on the camera (they are defined i The video page is used to enable/disable video streaming. When enabled, you can start/stop the video stream, enable a grid overlay, change how the image fits the screen, and record the video locally with QGC. -![Instrument Page - Video Stream](../../../assets/fly/instrument_page_video_stream.jpg) \ No newline at end of file +![Instrument Page - Video Stream](../../../assets/fly/instrument_page_video_stream.jpg) diff --git a/docs/ko/qgc-user-guide/fly_view/fly_tools.md b/docs/ko/qgc-user-guide/fly_view/fly_tools.md index c1c8d2aba11f..dff94728e21a 100644 --- a/docs/ko/qgc-user-guide/fly_view/fly_tools.md +++ b/docs/ko/qgc-user-guide/fly_view/fly_tools.md @@ -57,4 +57,4 @@ You can change altitude while flying, except when in a mission: 1. Press the **Actions** button on the _Fly Tools_ 2. Select the _Change Altitude_ button 3. Select the new altitude from the vertical slider -4. Confirm the action \ No newline at end of file +4. Confirm the action diff --git a/docs/ko/qgc-user-guide/fly_view/fly_view_toolbar.md b/docs/ko/qgc-user-guide/fly_view/fly_view_toolbar.md index 1895c883cfde..d4a3ea9ed6d4 100644 --- a/docs/ko/qgc-user-guide/fly_view/fly_view_toolbar.md +++ b/docs/ko/qgc-user-guide/fly_view/fly_view_toolbar.md @@ -73,6 +73,6 @@ There are other indicators which only show in certain situations: - Telemetry RSSI - RC RSSI -- Gimbal +- Gimbal - Only displayed if the vehicle supports the [Mavlink Gimbal Protocol](https://mavlink.io/en/services/gimbal_v2.html) - VTOL transitions - Select from multiple connected vehicles diff --git a/docs/ko/qgc-user-guide/fly_view/hud.md b/docs/ko/qgc-user-guide/fly_view/hud.md index cc6ac3bae629..6ec8c28be747 100644 --- a/docs/ko/qgc-user-guide/fly_view/hud.md +++ b/docs/ko/qgc-user-guide/fly_view/hud.md @@ -309,13 +309,13 @@ To start a mission from landed: 2. Select the _Start Mission_ action from the dialog. - ![Start mission action](../../../assets/fly/start_mission_action.jpg) + ![Start mission action](../../../assets/fly/start_mission_action.jpg) - (to display the confirmation slider) + (to display the confirmation slider) 3. When the confirmation slider appears, drag it to start the mission. - ![Start mission](../../../assets/fly/start_mission.jpg) + ![Start mission](../../../assets/fly/start_mission.jpg) #### Continue Mission {#continue_mission} @@ -333,11 +333,11 @@ You can continue the current mission while (unless already in a mission!): 2. Select the _Continue Mission_ action from the dialog. - ![Continue Mission/Change Altitude action](../../../assets/fly/continue_mission_change_altitude_action.jpg) + ![Continue Mission/Change Altitude action](../../../assets/fly/continue_mission_change_altitude_action.jpg) 3. Drag the confirmation slider to continue the mission. - ![Continue Mission](../../../assets/fly/continue_mission.jpg) + ![Continue Mission](../../../assets/fly/continue_mission.jpg) #### Resume Mission {#resume_mission} diff --git a/docs/ko/qgc-user-guide/fly_view/instrument_panel.md b/docs/ko/qgc-user-guide/fly_view/instrument_panel.md index 90ee0c777ad9..5f4deed8d8c9 100644 --- a/docs/ko/qgc-user-guide/fly_view/instrument_panel.md +++ b/docs/ko/qgc-user-guide/fly_view/instrument_panel.md @@ -29,4 +29,4 @@ By default this is the vehicle, but you can use the selector to choose a particu The selection list on the top right is used to select a particular telemetry value for the vehicle or sensor. -![Instrument Panel - value options](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) \ No newline at end of file +![Instrument Panel - value options](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) diff --git a/docs/ko/qgc-user-guide/getting_started/download_and_install.md b/docs/ko/qgc-user-guide/getting_started/download_and_install.md index 27ac4d88f53f..f82f600f1eb3 100644 --- a/docs/ko/qgc-user-guide/getting_started/download_and_install.md +++ b/docs/ko/qgc-user-guide/getting_started/download_and_install.md @@ -16,7 +16,7 @@ More capable hardware will provide a better experience. ## 윈도우 {#windows} -_QGroundControl_ can be installed on 64 bit versions of Windows 10 (1809 or later) or Windows 11: +Supported versions: Windows 10 (1809 or later), Windows 11: 1. [QGroundControl-installer.exe](https://d176tv9ibo4jno.cloudfront.net/latest/QGroundControl-installer.exe)을 다운로드합니다. 2. 다운로드한 설치 파일을 더블 클릭하여 프로그램을 실행합니다. @@ -27,9 +27,9 @@ _QGroundControl_ can be installed on 64 bit versions of Windows 10 (1809 or late 자세한 내용은 [QGroundControl 설정 문제 해결 > 윈도우: UI 렌더링/비디오 드라이버 문제](../troubleshooting/qgc_setup.md#opengl_troubleshooting)를 참고하십시오. ::: -## Mac OS X {#macOS} +## Mac OS {#macOS} -_QGroundControl_ can be installed on macOS 12 (Monterey) or later: +Supported versions: macOS 12 (Monterey) or later: @@ -39,12 +39,12 @@ _QGroundControl_ can be installed on macOS 12 (Monterey) or later: 2. 다운로드한 dmg 파일을 더블 클릭하여 마운트하여, _QGroundControl_ 애플리케이션을 _Application_ 폴더로 드래그합니다. :::info -QGroundControl continues to not be signed. You will not to allow permission for it to install based on you macOS version. +QGroundControl continues to not be signed. You will not to allow permission for it to install based on your macOS version. ::: ## 우분투 리눅스 {#ubuntu} -_QGroundControl_ can be installed/run on Ubuntu LTS 22.04 (and later): +Supported versions: Ubuntu 22.04, 24.04: Ubuntu comes with a serial modem manager that interferes with any robotics related use of a serial port (or USB serial). _QGroundControl_을 설치 전에 모뎀 관리자를 제거하고, 직렬 포트 접근 권한을 부여합니다. @@ -53,7 +53,7 @@ _QGroundControl_을 설치 전에 모뎀 관리자를 제거하고, 직렬 포 **Before installing _QGroundControl_ for the first time:** 1. Enable serial-port access - Add your user to the dialout group so you can talk to USB devices without root: + Add your user to the dialout group so you can talk to USB devices without root: ``` sudo usermod -aG dialout "$(id -un)" @@ -64,7 +64,7 @@ At login, your shell takes a snapshot of your user and group memberships. Becaus ::: 1. (Optional) Disable ModemManager - On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. + On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. ``` # preferred: stop and mask the service @@ -93,7 +93,7 @@ chmod +x QGroundControl-.AppImage ``` 1. Run QGroundControl - Either double-click the AppImage in your file manager or launch it from a terminal: + Either double-click the AppImage in your file manager or launch it from a terminal: ``` ./QGroundControl-.AppImage @@ -101,7 +101,7 @@ chmod +x QGroundControl-.AppImage ## 안드로이드 {#android} -_QGroundControl_ can be installed/run on Android 9 or later: +Supported versions: Android 9 to 15 (arm 32/64): - [Android 32/64 bit APK](https://qgroundcontrol.s3-us-west-2.amazonaws.com/latest/QGroundControl.apk) diff --git a/docs/ko/qgc-user-guide/releases/daily_build_new_features.md b/docs/ko/qgc-user-guide/releases/daily_build_new_features.md index dfb5757419f5..ed4736776c19 100644 --- a/docs/ko/qgc-user-guide/releases/daily_build_new_features.md +++ b/docs/ko/qgc-user-guide/releases/daily_build_new_features.md @@ -39,5 +39,5 @@ - Developer changes - Build system fully converted to cmake - qmake no longer supported - - Source updated to use Qt 6.8.3 - - GStreamer support updated to 1.22 \ No newline at end of file + - Source updated to use Qt 6.10.0 + - GStreamer support updated to 1.22 diff --git a/docs/ko/qgc-user-guide/releases/daily_builds.md b/docs/ko/qgc-user-guide/releases/daily_builds.md index b02898fe798d..9a1c588d79e9 100644 --- a/docs/ko/qgc-user-guide/releases/daily_builds.md +++ b/docs/ko/qgc-user-guide/releases/daily_builds.md @@ -9,7 +9,9 @@ Use at your own risk. 아래 링크에서 다운로드할 수 있습니다([다운로드 및 설치](../getting_started/download_and_install.md)에 설명된 대로 설치). -- [윈도우](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer.exe) +- Windows + - [x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-AMD64.exe) + - [Arm_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-ARM64.exe) - [OS X](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl.dmg) - Linux - (See installation instructions below) - [Linux x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-x86_64.AppImage) @@ -26,7 +28,7 @@ chmod +x QGroundControl-.AppImage ``` 2. Enable serial-port access - Add your user to the dialout group so you can talk to USB devices without root: + Add your user to the dialout group so you can talk to USB devices without root: ``` sudo usermod -aG dialout "$(id -un)" @@ -37,7 +39,7 @@ At login, your shell takes a snapshot of your user and group memberships. Becaus ::: 3. (Optional) Disable ModemManager - On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. + On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. ``` # preferred: stop and mask the service @@ -48,7 +50,7 @@ sudo apt remove --purge modemmanager ``` 4. Run QGroundControl - Either double-click the AppImage in your file manager or launch it from a terminal: + Either double-click the AppImage in your file manager or launch it from a terminal: ``` ./QGroundControl-.AppImage diff --git a/docs/ko/qgc-user-guide/viewer_3d/viewer_3d.md b/docs/ko/qgc-user-guide/viewer_3d/viewer_3d.md index dbc2415ffcef..f90339efe72a 100644 --- a/docs/ko/qgc-user-guide/viewer_3d/viewer_3d.md +++ b/docs/ko/qgc-user-guide/viewer_3d/viewer_3d.md @@ -7,12 +7,12 @@ You can use it to: - To import and display the 3D map for any region of interest downloaded from the OpenStreetMap website (.osm file). - Display the vehicle along with its mission in 3D. - And most of the capabilities of the [Fly View](../fly_view/fly_view.md), including: - - Run an automated [pre-flight checklist](#preflight_checklist). - - Arm the vehicle (or check why it won't arm). - - Control missions: [start](#start_mission), [continue](#continue_mission), [pause](#pause), and [resume](#resume_mission). - - Guide the vehicle to [arm](#arm)/[disarm](#disarm)/[emergency stop](#emergency_stop), [takeoff](#takeoff)/[land](#land), [change altitude](#change_altitude), and [return/RTL](#rtl). - - Switch between a map view and a video view (if available) - - Display video, mission, telemetry, and other information for the current vehicle, and also switch between connected vehicles. + - Run an automated [pre-flight checklist](#preflight_checklist). + - Arm the vehicle (or check why it won't arm). + - Control missions: [start](#start_mission), [continue](#continue_mission), [pause](#pause), and [resume](#resume_mission). + - Guide the vehicle to [arm](#arm)/[disarm](#disarm)/[emergency stop](#emergency_stop), [takeoff](#takeoff)/[land](#land), [change altitude](#change_altitude), and [return/RTL](#rtl). + - Switch between a map view and a video view (if available) + - Display video, mission, telemetry, and other information for the current vehicle, and also switch between connected vehicles. ![3D View](../../../assets/viewer_3d/viewer_3d_overview.jpg) @@ -31,14 +31,14 @@ To open the 3D View, when you are in the [Fly View](../fly_view/fly_view.md), fr Once the 3D View is opened, you can navigate through the 3D environment by using either a mouse or a touchscreen as follows: - **Mouse:** - - **To move horizontally and vertically**: Press and hold the mouse left-click, then move the cursor. - - **To rotate**: Press and hold the mouse right-click, then move the cursor. - - **To zoom**: Use the mouse wheel\middle button. + - **To move horizontally and vertically**: Press and hold the mouse left-click, then move the cursor. + - **To rotate**: Press and hold the mouse right-click, then move the cursor. + - **To zoom**: Use the mouse wheel\middle button. - **Touchscreen:** - - **To move horizontally and vertically**: Use a single finger, then tap and move your finger. - - **To rotate**: Use two fingers, then tap and move your fingers while keeping them together. - - **To zoom**: Use a pinch with two fingers and move them together or apart to zoom in or out. + - **To move horizontally and vertically**: Use a single finger, then tap and move your finger. + - **To rotate**: Use two fingers, then tap and move your fingers while keeping them together. + - **To zoom**: Use a pinch with two fingers and move them together or apart to zoom in or out. To visualize the 3D map of a particular area in the 3D viewer, you have to download the .osm file of that area from the [OpenStreetMap](https://www.openstreetmap.org/#map=16/47.3964/8.5498) website and then import it through the **3D View** settings. More details on the **3D View** settings can be found in the next section. @@ -51,5 +51,3 @@ The following properties can be modified in the 3D View settings group: - **3D Map File**: The path to the .osm file of a region of interest to be visualized in the QGC. The .osm file can be uploaded by clicking on the **Select File** button. To clear the 3D View from the previously loaded .osm file, you can click on the **Clear** button. - **Average Building Level Height**: This parameter determines the height of each storey of the buildings, as in .osm file sometimes the height of the buildings is specified in terms of the level/storey. - **Vehicle Altitude Bias**: This refers to the bias in the altitude of vehicles and their missions with respect to the ground level. It is helpful in cases where the estimated altitude of the vehicle by its flight control is biased, as the relative altitude is currently used in the 3D View. - - diff --git a/docs/tr/qgc-dev-guide/contribute/pull_requests.md b/docs/tr/qgc-dev-guide/contribute/pull_requests.md index fe120dcf7dca..b5891b9fcea6 100644 --- a/docs/tr/qgc-dev-guide/contribute/pull_requests.md +++ b/docs/tr/qgc-dev-guide/contribute/pull_requests.md @@ -1,3 +1,16 @@ # Pull Requests All pull requests go through the QGC CI build system which builds release and debug version. Builds will fail if there are compiler warnings. Also unit tests are run against supported OS debug builds. + +## Automatic Release Note Generation + +Releases notes are generated from the following GitHub labels qhich should be set on Pull Requests as appropriate: + +- "RN: MAJOR FEATURE" +- "RN: MINOR FEATURE" +- "RN: IMPROVEMENT" +- "RN: REFACTORING" +- "RN: BUGFIX" +- "RN: NEW BOARD SUPPORT" + +There are also a set of the above labels which end in "- CUSTOM BUILD" which indicate the changes is associated with the custom build architecture. diff --git a/docs/tr/qgc-dev-guide/contribute/unit_tests.md b/docs/tr/qgc-dev-guide/contribute/unit_tests.md index 023595d60160..f77eac271297 100644 --- a/docs/tr/qgc-dev-guide/contribute/unit_tests.md +++ b/docs/tr/qgc-dev-guide/contribute/unit_tests.md @@ -2,7 +2,7 @@ _QGroundControl_ (QGC) contains a set of unit tests that must pass before a pull request will be accepted. The addition of new complex subsystems to QGC should include a corresponding new unit test to test it. -The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/src/qgcunittest/UnitTestList.cc). +The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/test/UnitTestList.cc). To run unit tests: diff --git a/docs/tr/qgc-dev-guide/custom_build/mavlink.md b/docs/tr/qgc-dev-guide/custom_build/mavlink.md index bbdb3addd0ee..cf4cb272acb7 100644 --- a/docs/tr/qgc-dev-guide/custom_build/mavlink.md +++ b/docs/tr/qgc-dev-guide/custom_build/mavlink.md @@ -20,4 +20,4 @@ To modify the version of MAVLink used by QGC: Just add to [/qgroundcontrol/cmake/CustomOptions.cmake](../../../../cmake/CustomOptions.cmake): ```cmake set(CPM_mavlink_SOURCE "/path/to/your/custom/mavlink") - ``` \ No newline at end of file + ``` diff --git a/docs/tr/qgc-dev-guide/custom_build/resource_override.md b/docs/tr/qgc-dev-guide/custom_build/resource_override.md index 7667557aac28..897c3a6c5b3f 100644 --- a/docs/tr/qgc-dev-guide/custom_build/resource_override.md +++ b/docs/tr/qgc-dev-guide/custom_build/resource_override.md @@ -11,4 +11,4 @@ By overriding a resource you can replace it with your own version of it. This co Resource overrides work by using `QQmlEngine::addUrlInterceptor` to intercept requests for resources and re-route the request to available custom resources instead of the normal resource. Look at [custom_example/CustomPlugin.cc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CustomPlugin.cc) for how it's done and replicate that in your own custom build source. -Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. \ No newline at end of file +Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. diff --git a/docs/tr/qgc-dev-guide/custom_build/toolbar.md b/docs/tr/qgc-dev-guide/custom_build/toolbar.md index 662cb8db4ae9..e4dae8b2607c 100644 --- a/docs/tr/qgc-dev-guide/custom_build/toolbar.md +++ b/docs/tr/qgc-dev-guide/custom_build/toolbar.md @@ -30,10 +30,6 @@ These provide information to the user which is not associated with a vehicle. Fo These are indicators which are associated with information about the vehicle. They are only available when a vehicle is connected. To manipulate the list of vehicle indicators you override `FirmwarePlugin::toolIndicators`. -#### Vehicle Mode Indicators - -These are indicators which are associated with information about the vehicle. They require additional UI provided by the Fly View to complete their actions. An example is Arming and Disarming. They are only available when a vehicle is connected. To manipulate the list of vehicle mode indicators you override `FirmwarePlugin::modeIndicators`. - ### Modifying the toolbar UI itself This is accomplished by using resource overrides on the qml files associated with the toolbar. This provides a high level of customization but also a higher level of complexity. The primary ui for the toolbar is in `MainToolBar.qml`. The main window code in `MainRootWindow.qml` interacts with the toolbar to show different indicator sections based on current view as well as whether the mode indicators show or not also based on current view. diff --git a/docs/tr/qgc-dev-guide/getting_started/index.md b/docs/tr/qgc-dev-guide/getting_started/index.md index 7262c1a5c986..fd7ff25f7e9f 100644 --- a/docs/tr/qgc-dev-guide/getting_started/index.md +++ b/docs/tr/qgc-dev-guide/getting_started/index.md @@ -1,5 +1,5 @@ --- -qt_version: 6.8.3 +qt_version: 6.10.0 --- # Getting Started with Source and Builds @@ -21,15 +21,15 @@ To get the source files: 1. Clone the repo (or your fork) including submodules: - ```sh - git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git - ``` + ```sh + git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git + ``` 2. Update submodules (required each time you pull new source code): - ```sh - git submodule update --recursive - ``` + ```sh + git submodule update --recursive + ``` :::tip Github source-code zip files cannot be used because these do not contain the appropriate submodule source code. @@ -66,15 +66,15 @@ You **must install Qt as described below** instead of using pre-built packages f To install Qt: 1. Download and run the [Qt Online Installer](https://www.qt.io/download-qt-installer-oss) - - **Ubuntu:** - - Set the downloaded file to executable using: `chmod +x`. - - You may also need to install libxcb-cursor0 + - **Ubuntu:** + - Set the downloaded file to executable using: `chmod +x`. + - You may also need to install libxcb-cursor0 2. On the _Installation Folder_ page select "Custom Installation" 3. On the _Select Components_ page: - - I you don't see _Qt {{ $frontmatter.qt_version }}_ listed check the _Archive_ checkbox and click _Filter_. + - I you don't see _Qt {{ $frontmatter.qt_version }}_ listed check the _Archive_ checkbox and click _Filter_. - Under Qt -> _Qt {{ $frontmatter.qt_version }}_ select: - **Windows**: MSVC 2022 _arch_ - where _arch_ is the architecture of your machine @@ -86,22 +86,21 @@ To install Qt: 1. Install Additional Packages (Platform Specific) - - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` - - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` - - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` - - **Mac** `sh qgroundcontrol/tools/setup/macos-dependencies.sh` - - **Android** [Setup](https://doc.qt.io/qt-6/android-getting-started.html). JDK17 is required for the latest updated versions. NDK Version: 25.1.8937393 - You can confirm it is being used by reviewing the project setting: **Projects > Manage Kits > Devices > Android (tab) > Android Settings > _JDK location_**. - Note: Visit here for more detailed configurations [android.yml](.github/workflows/android.yml) + - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` + - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` + - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` + - **Mac** `sh qgroundcontrol/tools/setup/install-dependencies-osx.sh` + - **Windows** `qgroundcontrol/tools/setup/install-depedencies-windows.ps1` + - **Android** Installing dependencies for android is quite involved. You are better off using Qt documentation for android setup instructions. Search for "Qt 6.10 android" on the internet to find the correct "Gettting Started with Qt for Android" page. Read it full and carefully! An example of what you are looking for is [here](https://doc.qt.io/qt-6/android-getting-started.html). 2. Install Optional/OS-Specific Functionality - ::: info - Optional features that are dependent on the operating system and user-installed libraries are linked/described below. - These features can be forcibly enabled/disabled by specifying additional values to qmake. - ::: + ::: info + Optional features that are dependent on the operating system and user-installed libraries are linked/described below. + These features can be forcibly enabled/disabled by specifying additional values to qmake. + ::: - - **Video Streaming/Gstreamer:** - see [Video Streaming](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) + - **Video Streaming/Gstreamer:** - see [Video Streaming](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) #### Install Visual Studio (Windows Only) {#vs} @@ -123,7 +122,7 @@ Visual Studio is ONLY used to get the compiler. Building _QGroundControl_ is don 3. Build using the "hammer" (or "play") icons or the menus: - ![QtCreator Build Button](../../../assets/dev_getting_started/qt_creator_build_qgc.png) + ![QtCreator Build Button](../../../assets/dev_getting_started/qt_creator_build_qgc.png) #### Build using CMake on CLI {#cmake} @@ -131,29 +130,36 @@ Example commands to build a default QGC and run it afterwards: 1. Make sure you cloned the repository and updated the submodules before, see chapter _Source Code_ above and switch into the repository folder: - ```sh - cd qgroundcontrol - ``` + ```sh + cd qgroundcontrol + ``` 2. Configure: - ```sh - ~/Qt/6.8.3/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug - ``` + ```sh + ~/Qt/{{ $frontmatter.qt_version }}/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + ``` - Change the directory for qt-cmake to match your install location for Qt and the kit you want to use. + Change the directory for qt-cmake to match your install location for Qt and the kit you want to use. + + **Mac**: To Sign/Notarize/Staple the QGC app bundle, add `-DQGC_MACOS_SIGN_WITH_IDENTITY=ON` to the configure command line. During the `install` phase the following environment variables will need to be available: + + - `QGC_MACOS_SIGNING_IDENTITY` - Signing identity for your Developer ID certificate which must be in the keychain + - `QGC_MACOS_NOTARIZATION_USERNAME` - Username for your Apple Developer Account + - `QGC_MACOS_NOTARIZATION_PASSWORD` - App specific password for Notarization from your Apple Developer Account + - `QGC_MACOS_NOTARIZATION_TEAM_ID` - Apple Developer Account Team ID 3. Build - ```sh - cmake --build build --config Debug - ``` + ```sh + cmake --build build --config Debug + ``` 4. Run the QGroundcontrol binary that was just built: - ```sh - ./build/Debug/QGroundControl - ``` + ```sh + ./build/Debug/QGroundControl + ``` ### Vagrant diff --git a/docs/tr/qgc-user-guide/fly_view/camera_tools.md b/docs/tr/qgc-user-guide/fly_view/camera_tools.md index 69dc11226735..a1cd5980e3c4 100644 --- a/docs/tr/qgc-user-guide/fly_view/camera_tools.md +++ b/docs/tr/qgc-user-guide/fly_view/camera_tools.md @@ -26,4 +26,4 @@ Most of the settings that are displayed depend on the camera (they are defined i The video page is used to enable/disable video streaming. When enabled, you can start/stop the video stream, enable a grid overlay, change how the image fits the screen, and record the video locally with QGC. -![Instrument Page - Video Stream](../../../assets/fly/instrument_page_video_stream.jpg) \ No newline at end of file +![Instrument Page - Video Stream](../../../assets/fly/instrument_page_video_stream.jpg) diff --git a/docs/tr/qgc-user-guide/fly_view/fly_tools.md b/docs/tr/qgc-user-guide/fly_view/fly_tools.md index c1c8d2aba11f..dff94728e21a 100644 --- a/docs/tr/qgc-user-guide/fly_view/fly_tools.md +++ b/docs/tr/qgc-user-guide/fly_view/fly_tools.md @@ -57,4 +57,4 @@ You can change altitude while flying, except when in a mission: 1. Press the **Actions** button on the _Fly Tools_ 2. Select the _Change Altitude_ button 3. Select the new altitude from the vertical slider -4. Confirm the action \ No newline at end of file +4. Confirm the action diff --git a/docs/tr/qgc-user-guide/fly_view/fly_view_toolbar.md b/docs/tr/qgc-user-guide/fly_view/fly_view_toolbar.md index 1895c883cfde..d4a3ea9ed6d4 100644 --- a/docs/tr/qgc-user-guide/fly_view/fly_view_toolbar.md +++ b/docs/tr/qgc-user-guide/fly_view/fly_view_toolbar.md @@ -73,6 +73,6 @@ There are other indicators which only show in certain situations: - Telemetry RSSI - RC RSSI -- Gimbal +- Gimbal - Only displayed if the vehicle supports the [Mavlink Gimbal Protocol](https://mavlink.io/en/services/gimbal_v2.html) - VTOL transitions - Select from multiple connected vehicles diff --git a/docs/tr/qgc-user-guide/fly_view/hud.md b/docs/tr/qgc-user-guide/fly_view/hud.md index cc6ac3bae629..6ec8c28be747 100644 --- a/docs/tr/qgc-user-guide/fly_view/hud.md +++ b/docs/tr/qgc-user-guide/fly_view/hud.md @@ -309,13 +309,13 @@ To start a mission from landed: 2. Select the _Start Mission_ action from the dialog. - ![Start mission action](../../../assets/fly/start_mission_action.jpg) + ![Start mission action](../../../assets/fly/start_mission_action.jpg) - (to display the confirmation slider) + (to display the confirmation slider) 3. When the confirmation slider appears, drag it to start the mission. - ![Start mission](../../../assets/fly/start_mission.jpg) + ![Start mission](../../../assets/fly/start_mission.jpg) #### Continue Mission {#continue_mission} @@ -333,11 +333,11 @@ You can continue the current mission while (unless already in a mission!): 2. Select the _Continue Mission_ action from the dialog. - ![Continue Mission/Change Altitude action](../../../assets/fly/continue_mission_change_altitude_action.jpg) + ![Continue Mission/Change Altitude action](../../../assets/fly/continue_mission_change_altitude_action.jpg) 3. Drag the confirmation slider to continue the mission. - ![Continue Mission](../../../assets/fly/continue_mission.jpg) + ![Continue Mission](../../../assets/fly/continue_mission.jpg) #### Resume Mission {#resume_mission} diff --git a/docs/tr/qgc-user-guide/fly_view/instrument_panel.md b/docs/tr/qgc-user-guide/fly_view/instrument_panel.md index 90ee0c777ad9..5f4deed8d8c9 100644 --- a/docs/tr/qgc-user-guide/fly_view/instrument_panel.md +++ b/docs/tr/qgc-user-guide/fly_view/instrument_panel.md @@ -29,4 +29,4 @@ By default this is the vehicle, but you can use the selector to choose a particu The selection list on the top right is used to select a particular telemetry value for the vehicle or sensor. -![Instrument Panel - value options](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) \ No newline at end of file +![Instrument Panel - value options](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) diff --git a/docs/tr/qgc-user-guide/getting_started/download_and_install.md b/docs/tr/qgc-user-guide/getting_started/download_and_install.md index 7028d35000ae..7ca6f7148394 100644 --- a/docs/tr/qgc-user-guide/getting_started/download_and_install.md +++ b/docs/tr/qgc-user-guide/getting_started/download_and_install.md @@ -16,7 +16,7 @@ En iyi deneyim ve uyumluluk için size işletim sisteminizin en yeni sürümün ## Windows {#windows} -_QGroundControl_ can be installed on 64 bit versions of Windows 10 (1809 or later) or Windows 11: +Supported versions: Windows 10 (1809 or later), Windows 11: 1. Download [QGroundControl-installer.exe](https://d176tv9ibo4jno.cloudfront.net/latest/QGroundControl-installer.exe). 2. exe'ye çift tıklayın. @@ -27,9 +27,9 @@ Eğer başlatma veya video işleme sorunları yaşamıyorsanız ilk kısayolu ku For more information see [Troubleshooting QGC Setup > Windows: UI Rendering/Video Driver Issues](../troubleshooting/qgc_setup.md#opengl_troubleshooting). ::: -## Mac OS X {#macOS} +## Mac OS {#macOS} -_QGroundControl_ can be installed on macOS 12 (Monterey) or later: +Supported versions: macOS 12 (Monterey) or later: @@ -39,12 +39,12 @@ _QGroundControl_ can be installed on macOS 12 (Monterey) or later: 2. .dmg dosyasına çift tıklayın, ardından çıkan ekranda _QGroundControl_'ü _Application_ dosyasına sürükleyin. :::info -QGroundControl continues to not be signed. You will not to allow permission for it to install based on you macOS version. +QGroundControl continues to not be signed. You will not to allow permission for it to install based on your macOS version. ::: ## Ubuntu Linux {#ubuntu} -_QGroundControl_ can be installed/run on Ubuntu LTS 22.04 (and later): +Supported versions: Ubuntu 22.04, 24.04: Ubuntu, bir seri bağlantı noktasının (veya USB serisinin) robotikle ilgili kullanımına müdahale eden bir seri modem yöneticisi ile birlikte gelir. _ QGroundControl _ 'ü kurmadan önce modem yöneticisini kaldırmalı ve seri bağlantı noktasına erişim için kendinize izin vermelisiniz. @@ -53,7 +53,7 @@ Ayrıca video akışını desteklemek için _ GStreamer _ 'ı da yüklemeniz ger **Before installing _QGroundControl_ for the first time:** 1. Enable serial-port access - Add your user to the dialout group so you can talk to USB devices without root: + Add your user to the dialout group so you can talk to USB devices without root: ``` sudo usermod -aG dialout "$(id -un)" @@ -64,7 +64,7 @@ At login, your shell takes a snapshot of your user and group memberships. Becaus ::: 1. (Optional) Disable ModemManager - On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. + On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. ``` # preferred: stop and mask the service @@ -93,7 +93,7 @@ chmod +x QGroundControl-.AppImage ``` 1. Run QGroundControl - Either double-click the AppImage in your file manager or launch it from a terminal: + Either double-click the AppImage in your file manager or launch it from a terminal: ``` ./QGroundControl-.AppImage @@ -101,7 +101,7 @@ chmod +x QGroundControl-.AppImage ## Android {#android} -_QGroundControl_ can be installed/run on Android 9 or later: +Supported versions: Android 9 to 15 (arm 32/64): - [Android 32/64 bit APK](https://qgroundcontrol.s3-us-west-2.amazonaws.com/latest/QGroundControl.apk) diff --git a/docs/tr/qgc-user-guide/releases/daily_build_new_features.md b/docs/tr/qgc-user-guide/releases/daily_build_new_features.md index ae8567b43834..8e64414ec619 100644 --- a/docs/tr/qgc-user-guide/releases/daily_build_new_features.md +++ b/docs/tr/qgc-user-guide/releases/daily_build_new_features.md @@ -39,5 +39,5 @@ There is also a [Change Log](https://github.com/mavlink/qgroundcontrol/blob/mast - Developer changes - Build system fully converted to cmake - qmake no longer supported - - Source updated to use Qt 6.8.3 - - GStreamer support updated to 1.22 \ No newline at end of file + - Source updated to use Qt 6.10.0 + - GStreamer support updated to 1.22 diff --git a/docs/tr/qgc-user-guide/releases/daily_builds.md b/docs/tr/qgc-user-guide/releases/daily_builds.md index 66d5092279f4..e4e12e0e67e1 100644 --- a/docs/tr/qgc-user-guide/releases/daily_builds.md +++ b/docs/tr/qgc-user-guide/releases/daily_builds.md @@ -9,7 +9,9 @@ Use at your own risk. These can be downloaded from the links below (install as described in [Download and Install](../getting_started/download_and_install.md)): -- [Windows](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer.exe) +- Windows + - [x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-AMD64.exe) + - [Arm_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-ARM64.exe) - [OS X](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl.dmg) - Linux - (See installation instructions below) - [Linux x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-x86_64.AppImage) @@ -26,7 +28,7 @@ chmod +x QGroundControl-.AppImage ``` 2. Enable serial-port access - Add your user to the dialout group so you can talk to USB devices without root: + Add your user to the dialout group so you can talk to USB devices without root: ``` sudo usermod -aG dialout "$(id -un)" @@ -37,7 +39,7 @@ At login, your shell takes a snapshot of your user and group memberships. Becaus ::: 3. (Optional) Disable ModemManager - On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. + On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. ``` # preferred: stop and mask the service @@ -48,7 +50,7 @@ sudo apt remove --purge modemmanager ``` 4. Run QGroundControl - Either double-click the AppImage in your file manager or launch it from a terminal: + Either double-click the AppImage in your file manager or launch it from a terminal: ``` ./QGroundControl-.AppImage diff --git a/docs/tr/qgc-user-guide/viewer_3d/viewer_3d.md b/docs/tr/qgc-user-guide/viewer_3d/viewer_3d.md index dbc2415ffcef..f90339efe72a 100644 --- a/docs/tr/qgc-user-guide/viewer_3d/viewer_3d.md +++ b/docs/tr/qgc-user-guide/viewer_3d/viewer_3d.md @@ -7,12 +7,12 @@ You can use it to: - To import and display the 3D map for any region of interest downloaded from the OpenStreetMap website (.osm file). - Display the vehicle along with its mission in 3D. - And most of the capabilities of the [Fly View](../fly_view/fly_view.md), including: - - Run an automated [pre-flight checklist](#preflight_checklist). - - Arm the vehicle (or check why it won't arm). - - Control missions: [start](#start_mission), [continue](#continue_mission), [pause](#pause), and [resume](#resume_mission). - - Guide the vehicle to [arm](#arm)/[disarm](#disarm)/[emergency stop](#emergency_stop), [takeoff](#takeoff)/[land](#land), [change altitude](#change_altitude), and [return/RTL](#rtl). - - Switch between a map view and a video view (if available) - - Display video, mission, telemetry, and other information for the current vehicle, and also switch between connected vehicles. + - Run an automated [pre-flight checklist](#preflight_checklist). + - Arm the vehicle (or check why it won't arm). + - Control missions: [start](#start_mission), [continue](#continue_mission), [pause](#pause), and [resume](#resume_mission). + - Guide the vehicle to [arm](#arm)/[disarm](#disarm)/[emergency stop](#emergency_stop), [takeoff](#takeoff)/[land](#land), [change altitude](#change_altitude), and [return/RTL](#rtl). + - Switch between a map view and a video view (if available) + - Display video, mission, telemetry, and other information for the current vehicle, and also switch between connected vehicles. ![3D View](../../../assets/viewer_3d/viewer_3d_overview.jpg) @@ -31,14 +31,14 @@ To open the 3D View, when you are in the [Fly View](../fly_view/fly_view.md), fr Once the 3D View is opened, you can navigate through the 3D environment by using either a mouse or a touchscreen as follows: - **Mouse:** - - **To move horizontally and vertically**: Press and hold the mouse left-click, then move the cursor. - - **To rotate**: Press and hold the mouse right-click, then move the cursor. - - **To zoom**: Use the mouse wheel\middle button. + - **To move horizontally and vertically**: Press and hold the mouse left-click, then move the cursor. + - **To rotate**: Press and hold the mouse right-click, then move the cursor. + - **To zoom**: Use the mouse wheel\middle button. - **Touchscreen:** - - **To move horizontally and vertically**: Use a single finger, then tap and move your finger. - - **To rotate**: Use two fingers, then tap and move your fingers while keeping them together. - - **To zoom**: Use a pinch with two fingers and move them together or apart to zoom in or out. + - **To move horizontally and vertically**: Use a single finger, then tap and move your finger. + - **To rotate**: Use two fingers, then tap and move your fingers while keeping them together. + - **To zoom**: Use a pinch with two fingers and move them together or apart to zoom in or out. To visualize the 3D map of a particular area in the 3D viewer, you have to download the .osm file of that area from the [OpenStreetMap](https://www.openstreetmap.org/#map=16/47.3964/8.5498) website and then import it through the **3D View** settings. More details on the **3D View** settings can be found in the next section. @@ -51,5 +51,3 @@ The following properties can be modified in the 3D View settings group: - **3D Map File**: The path to the .osm file of a region of interest to be visualized in the QGC. The .osm file can be uploaded by clicking on the **Select File** button. To clear the 3D View from the previously loaded .osm file, you can click on the **Clear** button. - **Average Building Level Height**: This parameter determines the height of each storey of the buildings, as in .osm file sometimes the height of the buildings is specified in terms of the level/storey. - **Vehicle Altitude Bias**: This refers to the bias in the altitude of vehicles and their missions with respect to the ground level. It is helpful in cases where the estimated altitude of the vehicle by its flight control is biased, as the relative altitude is currently used in the 3D View. - - diff --git a/docs/zh/qgc-dev-guide/contribute/pull_requests.md b/docs/zh/qgc-dev-guide/contribute/pull_requests.md index 27c68c28ec00..12cb36cedb32 100644 --- a/docs/zh/qgc-dev-guide/contribute/pull_requests.md +++ b/docs/zh/qgc-dev-guide/contribute/pull_requests.md @@ -1,3 +1,16 @@ # Pull Requests 所有拉取请求都通过QGC CI构建系统,该系统构建版本和调试版本。 如果存在编译器警告,则构建将失败。 还针对支持的OS调试版本运行单元测试。 如果存在编译器警告,则构建将失败。 设备测试也是根据支持的操作系统调试构建运行的。 + +## Automatic Release Note Generation + +Releases notes are generated from the following GitHub labels qhich should be set on Pull Requests as appropriate: + +- "RN: MAJOR FEATURE" +- "RN: MINOR FEATURE" +- "RN: IMPROVEMENT" +- "RN: REFACTORING" +- "RN: BUGFIX" +- "RN: NEW BOARD SUPPORT" + +There are also a set of the above labels which end in "- CUSTOM BUILD" which indicate the changes is associated with the custom build architecture. diff --git a/docs/zh/qgc-dev-guide/contribute/unit_tests.md b/docs/zh/qgc-dev-guide/contribute/unit_tests.md index 42e3904b0204..00ebd2826e05 100644 --- a/docs/zh/qgc-dev-guide/contribute/unit_tests.md +++ b/docs/zh/qgc-dev-guide/contribute/unit_tests.md @@ -2,7 +2,7 @@ _QGroundControl_ (QGC) contains a set of unit tests that must pass before a pull request will be accepted. 向QGC添加新的复杂子系统应该有相应的新单元测试来测试它。 -单元测试的完整列表可以在UnitTestList.cc中找到。 +The full list of unit tests can be found in [UnitTestList.cc](https://github.com/mavlink/qgroundcontrol/blob/master/test/UnitTestList.cc). 要运行单元测试: diff --git a/docs/zh/qgc-dev-guide/custom_build/mavlink.md b/docs/zh/qgc-dev-guide/custom_build/mavlink.md index bbdb3addd0ee..cf4cb272acb7 100644 --- a/docs/zh/qgc-dev-guide/custom_build/mavlink.md +++ b/docs/zh/qgc-dev-guide/custom_build/mavlink.md @@ -20,4 +20,4 @@ To modify the version of MAVLink used by QGC: Just add to [/qgroundcontrol/cmake/CustomOptions.cmake](../../../../cmake/CustomOptions.cmake): ```cmake set(CPM_mavlink_SOURCE "/path/to/your/custom/mavlink") - ``` \ No newline at end of file + ``` diff --git a/docs/zh/qgc-dev-guide/custom_build/resource_override.md b/docs/zh/qgc-dev-guide/custom_build/resource_override.md index 9c1b8930a7eb..f1c6c2461b26 100644 --- a/docs/zh/qgc-dev-guide/custom_build/resource_override.md +++ b/docs/zh/qgc-dev-guide/custom_build/resource_override.md @@ -11,4 +11,4 @@ By overriding a resource you can replace it with your own version of it. 这可 Resource overrides work by using `QQmlEngine::addUrlInterceptor` to intercept requests for resources and re-route the request to available custom resources instead of the normal resource. Look at [custom_example/CustomPlugin.cc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CustomPlugin.cc) for how it's done and replicate that in your own custom build source. -Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. \ No newline at end of file +Custom resources that are meant for override are prepended with `/Custom` to the resource prefix in the custom resource file. The file alias for the resouce should be exactly the same as the normal QGC resource. Take a look at [custom_example/custom.qrc](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/custom.qrc) and [custom_example/CMakeLists.txt](https://github.com/mavlink/qgroundcontrol/blob/master/custom-example/CMakeLists.txt) for examples of how to do all of this. diff --git a/docs/zh/qgc-dev-guide/custom_build/toolbar.md b/docs/zh/qgc-dev-guide/custom_build/toolbar.md index 885086fd01cf..e4dae8b2607c 100644 --- a/docs/zh/qgc-dev-guide/custom_build/toolbar.md +++ b/docs/zh/qgc-dev-guide/custom_build/toolbar.md @@ -30,10 +30,6 @@ These provide information to the user which is not associated with a vehicle. Fo These are indicators which are associated with information about the vehicle. They are only available when a vehicle is connected. To manipulate the list of vehicle indicators you override `FirmwarePlugin::toolIndicators`. -#### Vehicle Mode Indicators - -These are indicators which are associated with information about the vehicle. They require additional UI provided by the Fly View to complete their actions. 一个例子是“解锁与上锁” 。 They are only available when a vehicle is connected. To manipulate the list of vehicle mode indicators you override `FirmwarePlugin::modeIndicators`. - ### Modifying the toolbar UI itself This is accomplished by using resource overrides on the qml files associated with the toolbar. This provides a high level of customization but also a higher level of complexity. The primary ui for the toolbar is in `MainToolBar.qml`. The main window code in `MainRootWindow.qml` interacts with the toolbar to show different indicator sections based on current view as well as whether the mode indicators show or not also based on current view. diff --git a/docs/zh/qgc-dev-guide/getting_started/index.md b/docs/zh/qgc-dev-guide/getting_started/index.md index 71e25502e397..aa5904783af4 100644 --- a/docs/zh/qgc-dev-guide/getting_started/index.md +++ b/docs/zh/qgc-dev-guide/getting_started/index.md @@ -1,5 +1,5 @@ --- -qt_version: 6.8.3 +qt_version: 6.10.0 --- # 从源码开始并构建 @@ -21,15 +21,15 @@ _QGroundControl_ 的源代码保存在GitHub 上:https://github.com/mavlink/qg 1. 克隆存储库 (或您的分叉), 包括子模块: - ```sh - git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git - ``` + ```sh + git clone --recursive -j8 https://github.com/mavlink/qgroundcontrol.git + ``` 2. 2.更新子模块(每次拉新源代码时都这样做): - ```sh - git submodule update --recursive - ``` + ```sh + git submodule update --recursive + ``` :::tip 提示:不能使用Github以zip形式下载源文件,因为zip压缩包中不包含相应的子模块源代码。 你必须使用git工具! @@ -66,15 +66,15 @@ QGC 已通过指定 Qt 版本({{ $frontmatter.qt_version }})的全面测试 如何安装Qt: 1. 下载并运行[Qt Online Installer](https://www.qt.io/download-qt-installer-oss) - - **Ubuntu:** - - 使用以下命令将下载的文件设置为可执行文件:`chmod + x` - - 您可能还需要安装 libxcb-cursor0 + - **Ubuntu:** + - 使用以下命令将下载的文件设置为可执行文件:`chmod + x` + - 您可能还需要安装 libxcb-cursor0 2. 在 _Installation 文件夹页面选择"自定义安装" 3. 在 _选择组件_ 页面: - - 如果您没有看到 {{ $frontmatter.qt_version }}_ 列出_Qt选中 _Archive_ 复选框并单击 _Filter_。 + - 如果您没有看到 {{ $frontmatter.qt_version }}_ 列出_Qt选中 _Archive_ 复选框并单击 _Filter_。 - 在 Qt -> _Qt {{ $frontmatter.qt_version }}_ 下选择: - **Windows**: MSVC 2022 _arch_ - 其中 “arch” 指的是您机器的架构 @@ -86,22 +86,21 @@ QGC 已通过指定 Qt 版本({{ $frontmatter.qt_version }})的全面测试 1. 安装附加软件包(特殊平台) - - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` - - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` - - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` - - **Mac** `sh qgroundcontrol/tools/setup/macos-dependencies.sh` - - **Android** [Setup](https://doc.qt.io/qt-6/android-getting-started.html)。 最新版本需要 JDK17 。 NDK 版本:25.1.8937393 - 您可以通过审查项目设置确认它正在使用:**Projects > Managing Kits > Devices > Android (tab) > Android Settings > _JDK location_**。 - 注意:访问此处查看更详细的配置 [android.yml](.github/workflows/android.yml) + - **Ubuntu:** `sudo bash ./qgroundcontrol/tools/setup/install-dependencies-debian.sh` + - **Fedora:** `sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel patchelf` + - **Arch Linux:** `pacman -Sy speech-dispatcher patchelf` + - **Mac** `sh qgroundcontrol/tools/setup/install-dependencies-osx.sh` + - **Windows** `qgroundcontrol/tools/setup/install-depedencies-windows.ps1` + - **Android** Installing dependencies for android is quite involved. You are better off using Qt documentation for android setup instructions. Search for "Qt 6.10 android" on the internet to find the correct "Gettting Started with Qt for Android" page. Read it full and carefully! An example of what you are looking for is [here](https://doc.qt.io/qt-6/android-getting-started.html). 2. 安装可选/特定操作功能 - ::: info - 依赖操作系统和用户安装的库的可选功能在下面链接/描述。 - 这些功能可以被强制启用/禁用,为qmake指定额外的值。 - ::: + ::: info + 依赖操作系统和用户安装的库的可选功能在下面链接/描述。 + 这些功能可以被强制启用/禁用,为qmake指定额外的值。 + ::: - - **视频流/Gstream:** - 查看 [视频流](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) + - **视频流/Gstream:** - 查看 [视频流](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) #### 安装 Visual Studio (仅限Windows) {#vs} @@ -123,7 +122,7 @@ Visual Studio 仅用于获取编译器。 构建 _QGroundControl_ 可直接按 3. 使用"hammer" (或"play") 图标或菜单构建: - ![QtCreator Build Button](../../../assets/dev_getting_started/qt_creator_build_qgc.png) + ![QtCreator Build Button](../../../assets/dev_getting_started/qt_creator_build_qgc.png) #### 在CLI(命令行界面)使用 CMake {#cmake} 进行构建 @@ -131,29 +130,36 @@ Visual Studio 仅用于获取编译器。 构建 _QGroundControl_ 可直接按 1. 请确保您克隆了仓库并先更新子模块,见上文 _源代码_ 章节并切换到仓库文件夹: - ```sh - cd qgroundcontrol - ``` + ```sh + cd qgroundcontrol + ``` 2. 配置: - ```sh - ~/Qt/6.8.3/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug - ``` + ```sh + ~/Qt/{{ $frontmatter.qt_version }}/gcc_64/bin/qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug + ``` - 修改 qt-cmake 的目录,使其与你安装 Qt 的位置以及你想要使用的工具包相匹配。 + 修改 qt-cmake 的目录,使其与你安装 Qt 的位置以及你想要使用的工具包相匹配。 + + **Mac**: To Sign/Notarize/Staple the QGC app bundle, add `-DQGC_MACOS_SIGN_WITH_IDENTITY=ON` to the configure command line. During the `install` phase the following environment variables will need to be available: + + - `QGC_MACOS_SIGNING_IDENTITY` - Signing identity for your Developer ID certificate which must be in the keychain + - `QGC_MACOS_NOTARIZATION_USERNAME` - Username for your Apple Developer Account + - `QGC_MACOS_NOTARIZATION_PASSWORD` - App specific password for Notarization from your Apple Developer Account + - `QGC_MACOS_NOTARIZATION_TEAM_ID` - Apple Developer Account Team ID 3. 构建 - ```sh - cmake --build build --config Debug - ``` + ```sh + cmake --build build --config Debug + ``` 4. Run the QGroundcontrol binary that was just built: `./staging/QGroundControl` - ```sh - ./build/Debug/QGroundControl - ``` + ```sh + ./build/Debug/QGroundControl + ``` ### Vagrant diff --git a/docs/zh/qgc-user-guide/fly_view/camera_tools.md b/docs/zh/qgc-user-guide/fly_view/camera_tools.md index db0fb030b714..252efed38f42 100644 --- a/docs/zh/qgc-user-guide/fly_view/camera_tools.md +++ b/docs/zh/qgc-user-guide/fly_view/camera_tools.md @@ -26,4 +26,4 @@ 视频页面用于启用/禁用视频流。 启用后,您可以开始/停止视频流,启用网格叠加层, 更改图像如何适合屏幕,并用QGC 在本地录制视频。 -![仪表页面 - 视频流](../../../assets/fly/instrument_page_video_stream.jpg) \ No newline at end of file +![仪表页面 - 视频流](../../../assets/fly/instrument_page_video_stream.jpg) diff --git a/docs/zh/qgc-user-guide/fly_view/fly_tools.md b/docs/zh/qgc-user-guide/fly_view/fly_tools.md index 32a7ecce3ab1..b16740190851 100644 --- a/docs/zh/qgc-user-guide/fly_view/fly_tools.md +++ b/docs/zh/qgc-user-guide/fly_view/fly_tools.md @@ -57,4 +57,4 @@ 1. 按 _飞行工具_ 上的 **操作** 按钮 2. 选择 _改变高度_ 按钮 3. 从垂直滑块选择新高度 -4. 确认操作 \ No newline at end of file +4. 确认操作 diff --git a/docs/zh/qgc-user-guide/fly_view/fly_view_toolbar.md b/docs/zh/qgc-user-guide/fly_view/fly_view_toolbar.md index 48816f399a36..ba1854dcfc8a 100644 --- a/docs/zh/qgc-user-guide/fly_view/fly_view_toolbar.md +++ b/docs/zh/qgc-user-guide/fly_view/fly_view_toolbar.md @@ -73,6 +73,6 @@ GPS指示器在工具栏图标中向您显示卫星计数和HDOP。 下拉菜单 - 遥测信号强度指示 - 遥控信号强度指示 -- 云台 +- Gimbal - Only displayed if the vehicle supports the [Mavlink Gimbal Protocol](https://mavlink.io/en/services/gimbal_v2.html) - 垂直起降转换 - 从多个连接的载具中选择 diff --git a/docs/zh/qgc-user-guide/fly_view/hud.md b/docs/zh/qgc-user-guide/fly_view/hud.md index e7e521a370cc..6e00af93d9aa 100644 --- a/docs/zh/qgc-user-guide/fly_view/hud.md +++ b/docs/zh/qgc-user-guide/fly_view/hud.md @@ -309,13 +309,13 @@ To start a mission from landed: 2. Select the _Start Mission_ action from the dialog. - ![Start mission action](../../../assets/fly/start_mission_action.jpg) + ![Start mission action](../../../assets/fly/start_mission_action.jpg) - (to display the confirmation slider) + (to display the confirmation slider) 3. When the confirmation slider appears, drag it to start the mission. - ![Start mission](../../../assets/fly/start_mission.jpg) + ![Start mission](../../../assets/fly/start_mission.jpg) #### Continue Mission {#continue_mission} @@ -333,11 +333,11 @@ You can continue the current mission while (unless already in a mission!): 2. 在对话框中选择 _继续任务_ 操作。 - ![继续任务/改变高度](../../../assets/fly/continue_mission_change_altitude_action.jpg) + ![继续任务/改变高度](../../../assets/fly/continue_mission_change_altitude_action.jpg) 3. 拖动滑块以确认继续执行任务。 - ![继续任务](../../../assets/fly/continue_mission.jpg) + ![继续任务](../../../assets/fly/continue_mission.jpg) #### 恢复任务{#resume_mission} diff --git a/docs/zh/qgc-user-guide/fly_view/instrument_panel.md b/docs/zh/qgc-user-guide/fly_view/instrument_panel.md index a696b73ab7f0..6c27daea3ada 100644 --- a/docs/zh/qgc-user-guide/fly_view/instrument_panel.md +++ b/docs/zh/qgc-user-guide/fly_view/instrument_panel.md @@ -29,4 +29,4 @@ 右上方的选择列表用于为载具或传感器选择一个特定的遥测值。 -![仪表板 - 值选项](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) \ No newline at end of file +![仪表板 - 值选项](../../../assets/fly/instrument_panel/instrument_panel_edit_value_options.png) diff --git a/docs/zh/qgc-user-guide/getting_started/download_and_install.md b/docs/zh/qgc-user-guide/getting_started/download_and_install.md index b6029e821c55..392a49da155b 100644 --- a/docs/zh/qgc-user-guide/getting_started/download_and_install.md +++ b/docs/zh/qgc-user-guide/getting_started/download_and_install.md @@ -16,7 +16,7 @@ QGC可以在任何当下流行的计算机或移动设备上正常运行。 性 ## Windows 系统 {#windows} -_QGroundControl_ 可以安装在 Windows 10 (1809 或更高) 或 Windows 11的64 位版本上: +Supported versions: Windows 10 (1809 or later), Windows 11: 1. 下载 [QGroundControl-installer.exe](https://d176tv9ibo4jno.cloudfront.net/latest/QGroundControl-installer.exe)。 2. 双击可执行文件来启动安装程序。 @@ -27,9 +27,9 @@ Windows 安装程序创建 3 个快捷方式:**QGroundControl**,**GPU 兼容 更多信息请见[QGC 设置故障排查 > Windows:用户界面渲染 / 视频驱动问题](../troubleshooting/qgc_setup.md#opengl_troubleshooting)。 ::: -## Mac OS X 系统 {#macOS} +## Mac OS {#macOS} -_QGroundControl_ 可以安装在 macOS 12 (Montreey) 上或以后: +Supported versions: macOS 12 (Monterey) or later: @@ -39,12 +39,12 @@ _QGroundControl_ 可以安装在 macOS 12 (Montreey) 上或以后: 2. 双击 .dmg 文件以挂载它,然后将 _QGroundControl_ 应用程序拖动到您的 _Application_ 文件夹。 :::info -QGroundControl continues to not be signed. You will not to allow permission for it to install based on you macOS version. +QGroundControl continues to not be signed. You will not to allow permission for it to install based on your macOS version. ::: ## Ubuntu Linux 系统 {#ubuntu} -_QGroundControl_ 可以在 Ubuntu LTS 22.04 (及以后) 安装/运行: +Supported versions: Ubuntu 22.04, 24.04: Ubuntu 自带一个串口调制解调器管理器,它会干扰串口(或 USB 转串口)在任何与机器人相关方面的使用。 在安装 _QGroundControl_ 之前,您应该删除调制解调器管理器并授予自己访问串行端口的权限。 @@ -53,7 +53,7 @@ Ubuntu 自带一个串口调制解调器管理器,它会干扰串口(或 USB **Before installing _QGroundControl_ for the first time:** 1. Enable serial-port access - Add your user to the dialout group so you can talk to USB devices without root: + Add your user to the dialout group so you can talk to USB devices without root: ``` sudo usermod -aG dialout "$(id -un)" @@ -64,7 +64,7 @@ At login, your shell takes a snapshot of your user and group memberships. Becaus ::: 1. (Optional) Disable ModemManager - On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. + On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. ``` # preferred: stop and mask the service @@ -93,7 +93,7 @@ chmod +x QGroundControl-.AppImage ``` 1. Run QGroundControl - Either double-click the AppImage in your file manager or launch it from a terminal: + Either double-click the AppImage in your file manager or launch it from a terminal: ``` ./QGroundControl-.AppImage @@ -101,7 +101,7 @@ chmod +x QGroundControl-.AppImage ## Android {#android} -_QGroundControl_ 可以在 Android 9 上安装/运行: +Supported versions: Android 9 to 15 (arm 32/64): - [Android 32/64 bit APK](https://qgroundcontrol.s3-us-west-2.amazonaws.com/latest/QGroundControl.apk) diff --git a/docs/zh/qgc-user-guide/releases/daily_build_new_features.md b/docs/zh/qgc-user-guide/releases/daily_build_new_features.md index 26d22a496859..d1b6467eaf5a 100644 --- a/docs/zh/qgc-user-guide/releases/daily_build_new_features.md +++ b/docs/zh/qgc-user-guide/releases/daily_build_new_features.md @@ -39,5 +39,5 @@ - 开发者更改 - 构建系统完全转换为 cmake - qmake 不再支持 - - 源代码已更新为使用Qt 6.8.3 - - GStreamer 支持已更新到 1.22 \ No newline at end of file + - Source updated to use Qt 6.10.0 + - GStreamer 支持已更新到 1.22 diff --git a/docs/zh/qgc-user-guide/releases/daily_builds.md b/docs/zh/qgc-user-guide/releases/daily_builds.md index 64aa8d9a3754..16262f6d4644 100644 --- a/docs/zh/qgc-user-guide/releases/daily_builds.md +++ b/docs/zh/qgc-user-guide/releases/daily_builds.md @@ -9,7 +9,9 @@ Use at your own risk. 这些可从以下链接下载(按照 [下载与安装](../getting_started/download_and_install.md) 中所述进行安装): -- [Windows](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer.exe) +- Windows + - [x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-AMD64.exe) + - [Arm_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-installer-ARM64.exe) - [OS X](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl.dmg) - Linux - (See installation instructions below) - [Linux x86_64](https://d176tv9ibo4jno.cloudfront.net/builds/master/QGroundControl-x86_64.AppImage) @@ -26,7 +28,7 @@ chmod +x QGroundControl-.AppImage ``` 2. Enable serial-port access - Add your user to the dialout group so you can talk to USB devices without root: + Add your user to the dialout group so you can talk to USB devices without root: ``` sudo usermod -aG dialout "$(id -un)" @@ -37,7 +39,7 @@ At login, your shell takes a snapshot of your user and group memberships. Becaus ::: 3. (Optional) Disable ModemManager - On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. + On some Ubuntu-based systems, ModemManager can claim serial ports that QGC needs. If you don't use it elsewhere, mask or remove it. ``` # preferred: stop and mask the service @@ -48,7 +50,7 @@ sudo apt remove --purge modemmanager ``` 4. Run QGroundControl - Either double-click the AppImage in your file manager or launch it from a terminal: + Either double-click the AppImage in your file manager or launch it from a terminal: ``` ./QGroundControl-.AppImage diff --git a/docs/zh/qgc-user-guide/viewer_3d/viewer_3d.md b/docs/zh/qgc-user-guide/viewer_3d/viewer_3d.md index 996ebdd55414..d879a11f6b9e 100644 --- a/docs/zh/qgc-user-guide/viewer_3d/viewer_3d.md +++ b/docs/zh/qgc-user-guide/viewer_3d/viewer_3d.md @@ -7,12 +7,12 @@ - 从OpenStreetMap 网站(.osm 文件)下载的任何感兴趣区域导入和显示3D映射图。 - 在 3D 中显示车辆和它的任务。 - 以及[飞行视图](../fly_view/fly_view.md)的大多数功能,包括: - - 运行一个自动的[飞行前检查列表](#preflight_checklist)。 - - 把载具解锁(或检查它为什么不会解锁)。 - - 控制任务: [开始](#start_mission)、 [继续](#continue_mission)、 [暂停](#pause)和 [恢复](#resume_mission)。 - - 引导飞行器进行[解锁](#arm)/[上锁](#disarm)/[紧急停止](#emergency_stop)、[起飞](#takeoff)/[降落](#land)、[改变高度](#change_altitude) 以及 [返航/一键返航](#rtl)。 - - 在地图视图和视频视图之间切换(如果可用) - - 显示当前载具的视频、飞行任务、遥测和其他信息,同时在已连接的载具之间切换。 + - 运行一个自动的[飞行前检查列表](#preflight_checklist)。 + - 把载具解锁(或检查它为什么不会解锁)。 + - 控制任务: [开始](#start_mission)、 [继续](#continue_mission)、 [暂停](#pause)和 [恢复](#resume_mission)。 + - 引导飞行器进行[解锁](#arm)/[上锁](#disarm)/[紧急停止](#emergency_stop)、[起飞](#takeoff)/[降落](#land)、[改变高度](#change_altitude) 以及 [返航/一键返航](#rtl)。 + - 在地图视图和视频视图之间切换(如果可用) + - 显示当前载具的视频、飞行任务、遥测和其他信息,同时在已连接的载具之间切换。 ![3D 视图](../../../assets/viewer_3d/viewer_3d_overview.jpg) @@ -31,14 +31,14 @@ 打开3D视图后,您可以使用鼠标或触摸屏来导航3D环境: - **鼠标:** - - **要水平和垂直移动**:按住鼠标左键,然后移动光标。 - - **要旋转**:按住鼠标右键,然后移动光标。 - - **放大**:使用鼠标滚轮\中键。 + - **要水平和垂直移动**:按住鼠标左键,然后移动光标。 + - **要旋转**:按住鼠标右键,然后移动光标。 + - **放大**:使用鼠标滚轮\中键。 - **触摸屏:** - - **要水平和垂直移动**:使用单指,然后点击并移动您的手指。 - - **要旋转**:使用两个手指,然后点击并移动你的手指,同时将它们保持在一起。 - - **要缩放**:使用双指的粉丝并移动到一起或分别缩放。 + - **要水平和垂直移动**:使用单指,然后点击并移动您的手指。 + - **要旋转**:使用两个手指,然后点击并移动你的手指,同时将它们保持在一起。 + - **要缩放**:使用双指的粉丝并移动到一起或分别缩放。 若要在三维查看器中可视化特定区域的3D地图,您必须下载。 从 [OpenStreetMap](https://www.openstreetmap.org/#map=16/47.3964/8.5498)网站上的sm 文件,然后通过 **3D View** 的设置导入。 更多关于**3D View** 设置的详细信息可在下一节找到。 @@ -51,5 +51,3 @@ - **3D地图文件**:在QGC中可视化感兴趣区域的.osm文件的路径。 可以点击 **选择文件** 按钮上传.osm 文件。 要清除以前加载的 .osm 文件的3D 视图,您可以点击**清除** 按钮。 - **建筑物平均楼层高度**:此参数决定建筑物每层的高度,因为在.osm文件中,建筑物高度有时是按楼层来指定的。 - **载具高度偏差**:这是指载具及其任务的高度相对于地面的偏差。 在飞行器飞控估算的高度存在偏差的情况下,这很有用,因为3D视图目前使用的是相对高度。 - - diff --git a/package-lock.json b/package-lock.json index c26574e517e9..1da0bd9e8444 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.1", "license": "CC-BY-4.0", "dependencies": { - "vitepress": "^1.6.3" + "vitepress": "^1.6.4" } }, "node_modules/@algolia/autocomplete-core": { @@ -2106,10 +2106,9 @@ } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", - "license": "MIT", + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -2165,10 +2164,9 @@ } }, "node_modules/vitepress": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz", - "integrity": "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==", - "license": "MIT", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", @@ -3471,9 +3469,9 @@ } }, "vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "requires": { "esbuild": "^0.21.3", "fsevents": "~2.3.3", @@ -3482,9 +3480,9 @@ } }, "vitepress": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz", - "integrity": "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", "requires": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", diff --git a/package.json b/package.json index c9c8d8099611..d8fd0bb6446e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "yarn docs:dev" }, "dependencies": { - "vitepress": "^1.6.3" + "vitepress": "^1.6.4" }, "license": "CC-BY-4.0", "repository": { diff --git a/qgcimages.qrc b/qgcimages.qrc index 60fabeb58894..5c995eac1753 100644 --- a/qgcimages.qrc +++ b/qgcimages.qrc @@ -25,6 +25,7 @@ src/AutoPilotPlugins/Common/Images/QuadRotorWide.svg src/AutoPilotPlugins/Common/Images/QuadRotorX.svg src/AutoPilotPlugins/Common/Images/Rover.svg + src/AutoPilotPlugins/Common/Images/FreeFlyer.svg src/AutoPilotPlugins/Common/Images/Vectored6DofUUV.svg src/AutoPilotPlugins/Common/Images/VTOLDuoRotorTailSitter.svg src/AutoPilotPlugins/Common/Images/VTOLPlane.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 221737f1f1cf..37d88c0533c2 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -108,11 +108,18 @@ resources/opengl/buglist.json - resources/gimbal/payload.svg + resources/gimbal/payload.png resources/gcscontrolIndicator/gcscontrol_device.svg resources/gcscontrolIndicator/gcscontrol_gcs.svg resources/gcscontrolIndicator/gcscontrol_line.svg + + src/UI/AppSettings/NTRIPSettings.qml + src/UI/AppSettings/NTRIPSettings.qml + + + src/Settings/NTRIP.SettingsGroup.json + diff --git a/resources/InstrumentValueIcons/UpdateResourceFile.py b/resources/InstrumentValueIcons/UpdateResourceFile.py index 4ec3c13aa8a3..9c29eeee4e39 100644 --- a/resources/InstrumentValueIcons/UpdateResourceFile.py +++ b/resources/InstrumentValueIcons/UpdateResourceFile.py @@ -1,21 +1,23 @@ #!/usr/bin/env python import os + def main(): - qrcFile = open("InstrumentValueIcons.qrc", 'wt') + qrcFile = open("InstrumentValueIcons.qrc", "wt") qrcFile.write("\n") - qrcFile.write("\t\n") + qrcFile.write('\t\n') files = os.listdir(".") for filename in files: if filename.endswith(".svg"): - qrcFile.write("\t\t%s\n" % (filename, filename)) + qrcFile.write('\t\t%s\n' % (filename, filename)) qrcFile.write("\t\n") qrcFile.write("\n") qrcFile.close() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/resources/gimbal/payload.png b/resources/gimbal/payload.png new file mode 100644 index 000000000000..fc490674bb3c Binary files /dev/null and b/resources/gimbal/payload.png differ diff --git a/resources/gimbal/payload.svg b/resources/gimbal/payload.svg deleted file mode 100644 index d3563162cb97..000000000000 --- a/resources/gimbal/payload.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/ADSB/ADSBTCPLink.cc b/src/ADSB/ADSBTCPLink.cc index 936612f41de3..765d4e25c021 100644 --- a/src/ADSB/ADSBTCPLink.cc +++ b/src/ADSB/ADSBTCPLink.cc @@ -14,7 +14,7 @@ #include #include -QGC_LOGGING_CATEGORY(ADSBTCPLinkLog, "qgc.adsb.adsbtcplink") +QGC_LOGGING_CATEGORY(ADSBTCPLinkLog, "ADSB.ADSBTCPLink") ADSBTCPLink::ADSBTCPLink(const QHostAddress &hostAddress, quint16 port, QObject *parent) : QObject(parent) diff --git a/src/ADSB/ADSBVehicle.cc b/src/ADSB/ADSBVehicle.cc index 115fea20196e..9c402b06e425 100644 --- a/src/ADSB/ADSBVehicle.cc +++ b/src/ADSB/ADSBVehicle.cc @@ -13,7 +13,7 @@ #include -QGC_LOGGING_CATEGORY(ADSBVehicleLog, "qgc.adsb.adsbvehicle") +QGC_LOGGING_CATEGORY(ADSBVehicleLog, "ADSB.ADSBVehicle") ADSBVehicle::ADSBVehicle(const ADSB::VehicleInfo_t &vehicleInfo, QObject *parent) : QObject(parent) @@ -36,7 +36,7 @@ void ADSBVehicle::update(const ADSB::VehicleInfo_t &vehicleInfo) return; } - qCDebug(ADSBVehicleLog) << "Updating" << QStringLiteral("%1 Flags: %2").arg(vehicleInfo.icaoAddress, 0, 16).arg(vehicleInfo.availableFlags, 0, 2); + qCDebug(ADSBVehicleLog) << "Updating" << QStringLiteral("%1 Flags: %2").arg(vehicleInfo.icaoAddress, 0, 16).arg(vehicleInfo.availableFlags.toInt(), 0, 2); if (vehicleInfo.availableFlags & ADSB::LocationAvailable) { if (!QGC::fuzzyCompare(vehicleInfo.location.latitude(), coordinate().latitude()) || !QGC::fuzzyCompare(vehicleInfo.location.longitude(), coordinate().longitude())) { diff --git a/src/ADSB/ADSBVehicleManager.cc b/src/ADSB/ADSBVehicleManager.cc index 3c8dabad5ecd..a8eaf850ace4 100644 --- a/src/ADSB/ADSBVehicleManager.cc +++ b/src/ADSB/ADSBVehicleManager.cc @@ -20,7 +20,7 @@ #include #include -QGC_LOGGING_CATEGORY(ADSBVehicleManagerLog, "qgc.adsb.adsbvehiclemanager") +QGC_LOGGING_CATEGORY(ADSBVehicleManagerLog, "ADSB.ADSBVehicleManager") Q_APPLICATION_STATIC(ADSBVehicleManager, _adsbVehicleManager, SettingsManager::instance()->adsbVehicleManagerSettings()); diff --git a/src/ADSB/CMakeLists.txt b/src/ADSB/CMakeLists.txt index 4cfa8c870d57..10695141faf2 100644 --- a/src/ADSB/CMakeLists.txt +++ b/src/ADSB/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# ADS-B Module +# Automatic Dependent Surveillance-Broadcast for air traffic awareness +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE ADSBTCPLink.cc diff --git a/src/API/CMakeLists.txt b/src/API/CMakeLists.txt index ab2df327ad49..65786f431911 100644 --- a/src/API/CMakeLists.txt +++ b/src/API/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# API Module +# Core plugin architecture and QML component interfaces +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE QGCCorePlugin.cc diff --git a/src/API/QGCCorePlugin.cc b/src/API/QGCCorePlugin.cc index ae2ee81461af..7194d4a8f7d5 100644 --- a/src/API/QGCCorePlugin.cc +++ b/src/API/QGCCorePlugin.cc @@ -40,7 +40,7 @@ #include #include -QGC_LOGGING_CATEGORY(QGCCorePluginLog, "qgc.api.qgccoreplugin"); +QGC_LOGGING_CATEGORY(QGCCorePluginLog, "API.QGCCorePlugin"); #ifndef QGC_CUSTOM_BUILD Q_APPLICATION_STATIC(QGCCorePlugin, _qgcCorePluginInstance); @@ -110,7 +110,7 @@ const QmlObjectListModel *QGCCorePlugin::customMapItems() return _emptyCustomMapItems; } -bool QGCCorePlugin::adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData) +void QGCCorePlugin::adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData, bool &visible) { if (settingsGroup == AppSettings::settingsGroup) { if (metaData.name() == AppSettings::indoorPaletteName) { @@ -121,22 +121,21 @@ bool QGCCorePlugin::adjustSettingMetaData(const QString &settingsGroup, FactMeta outdoorPalette = 1; #endif metaData.setRawDefaultValue(outdoorPalette); - return true; + return; } #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) else if (metaData.name() == MavlinkSettings::telemetrySaveName) { metaData.setRawDefaultValue(false); - return true; + return; } #endif #ifndef Q_OS_ANDROID - else if (metaData.name() == AppSettings::androidSaveToSDCardName) { - return false; + else if (metaData.name() == AppSettings::androidDontSaveToSDCardName) { + visible = false; + return; } #endif } - - return true; } QString QGCCorePlugin::showAdvancedUIMessage() const @@ -149,10 +148,16 @@ QString QGCCorePlugin::showAdvancedUIMessage() const void QGCCorePlugin::factValueGridCreateDefaultSettings(FactValueGrid* factValueGrid) { +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + FactValueGrid::FontSize defaultFontSize = FactValueGrid::DefaultFontSize; +#else + FactValueGrid::FontSize defaultFontSize = FactValueGrid::MediumFontSize; +#endif + if (factValueGrid->specificVehicleForCard()) { bool includeFWValues = factValueGrid->vehicleClass() == QGCMAVLink::VehicleClassFixedWing || factValueGrid->vehicleClass() == QGCMAVLink::VehicleClassVTOL || factValueGrid->vehicleClass() == QGCMAVLink::VehicleClassAirship; - factValueGrid->setFontSize(FactValueGrid::LargeFontSize); + factValueGrid->setFontSize(defaultFontSize); factValueGrid->appendColumn(); factValueGrid->appendColumn(); @@ -183,7 +188,7 @@ void QGCCorePlugin::factValueGridCreateDefaultSettings(FactValueGrid* factValueG } else { const bool includeFWValues = ((factValueGrid->vehicleClass() == QGCMAVLink::VehicleClassFixedWing) || (factValueGrid->vehicleClass() == QGCMAVLink::VehicleClassVTOL) || (factValueGrid->vehicleClass() == QGCMAVLink::VehicleClassAirship)); - factValueGrid->setFontSize(FactValueGrid::LargeFontSize); + factValueGrid->setFontSize(defaultFontSize); (void) factValueGrid->appendColumn(); (void) factValueGrid->appendColumn(); diff --git a/src/API/QGCCorePlugin.h b/src/API/QGCCorePlugin.h index 6e86af66b738..7b81a80d45b1 100644 --- a/src/API/QGCCorePlugin.h +++ b/src/API/QGCCorePlugin.h @@ -78,11 +78,12 @@ class QGCCorePlugin : public QObject /// @return true: Show settings ui, false: Hide settings ui virtual bool overrideSettingsGroupVisibility(const QString &name) { Q_UNUSED(name); return true; } - /// Allows the core plugin to override the setting meta data before the setting fact is created. + /// Allows the core plugin to override the meta data before the fact is created. /// @param settingsGroup - QSettings group which contains this item /// @param metaData - MetaData for setting fact - /// @return true: Setting should be visible in ui, false: Setting should not be shown in ui - virtual bool adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData); + /// @param visible - true: Setting should be visible in ui, false: Setting should not be shown in ui (default value will be used as value) + /// If not overridden, metaData and visible are left unchanged. + virtual void adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData, bool &visible); /// Return the resource file which contains the brand image for for Indoor theme. virtual QString brandImageIndoor() const { return QString(); } diff --git a/src/API/QGCOptions.cc b/src/API/QGCOptions.cc index b334d48c7d84..a49fc4db259a 100644 --- a/src/API/QGCOptions.cc +++ b/src/API/QGCOptions.cc @@ -10,7 +10,7 @@ #include "QGCOptions.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(QGCFlyViewOptionsLog, "qgc.api.qgcflyviewoptions"); +QGC_LOGGING_CATEGORY(QGCFlyViewOptionsLog, "API.QGCFlyViewOptions"); QGCFlyViewOptions::QGCFlyViewOptions(QGCOptions *options, QObject *parent) : QObject(parent) @@ -26,7 +26,7 @@ QGCFlyViewOptions::~QGCFlyViewOptions() /*===========================================================================*/ -QGC_LOGGING_CATEGORY(QGCOptionsLog, "qgc.api.qgcoptions"); +QGC_LOGGING_CATEGORY(QGCOptionsLog, "API.QGCOptions"); QGCOptions::QGCOptions(QObject *parent) : QObject(parent) diff --git a/src/API/QGCOptions.h b/src/API/QGCOptions.h index fed55fe2ca08..e475aa6f9617 100644 --- a/src/API/QGCOptions.h +++ b/src/API/QGCOptions.h @@ -84,7 +84,6 @@ class QGCOptions : public QObject Q_PROPERTY(bool showSensorCalibrationLevel READ showSensorCalibrationLevel NOTIFY showSensorCalibrationLevelChanged) Q_PROPERTY(bool showSimpleMissionStart READ showSimpleMissionStart NOTIFY showSimpleMissionStartChanged) Q_PROPERTY(bool useMobileFileDialog READ useMobileFileDialog CONSTANT) - Q_PROPERTY(bool wifiReliableForCalibration READ wifiReliableForCalibration CONSTANT) Q_PROPERTY(double toolbarHeightMultiplier READ toolbarHeightMultiplier CONSTANT) Q_PROPERTY(float devicePixelDensity READ devicePixelDensity NOTIFY devicePixelDensityChanged) Q_PROPERTY(float devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged) @@ -155,8 +154,6 @@ class QGCOptions : public QObject virtual bool showPX4LogTransferOptions() const { return true; } virtual bool showSimpleMissionStart() const { return false; } - virtual bool wifiReliableForCalibration() const { return false; } - /// Desktop builds save the main application size and position on close (and restore it on open) virtual bool enableSaveMainWindowPosition() const { return true; } diff --git a/src/API/QmlComponentInfo.cc b/src/API/QmlComponentInfo.cc index c8a9a6e55c83..d264d798357a 100644 --- a/src/API/QmlComponentInfo.cc +++ b/src/API/QmlComponentInfo.cc @@ -10,7 +10,7 @@ #include "QmlComponentInfo.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(QmlComponentInfoLog, "qgc.api.qmlcomponentinfo"); +QGC_LOGGING_CATEGORY(QmlComponentInfoLog, "API.QmlComponentInfo"); QmlComponentInfo::QmlComponentInfo(const QString &title, QUrl url, QUrl icon, QObject *parent) : QObject(parent) diff --git a/src/AnalyzeView/AnalyzeView.qml b/src/AnalyzeView/AnalyzeView.qml index 3f32d2fb3f0c..8b5390bb96e2 100644 --- a/src/AnalyzeView/AnalyzeView.qml +++ b/src/AnalyzeView/AnalyzeView.qml @@ -30,6 +30,11 @@ Rectangle { readonly property real _verticalMargin: _defaultTextHeight / 2 readonly property real _buttonWidth: _defaultTextWidth * 18 + // This need to block click event leakage to underlying map. + DeadMouseArea { + anchors.fill: parent + } + GeoTagController { id: geoController } diff --git a/src/AnalyzeView/CMakeLists.txt b/src/AnalyzeView/CMakeLists.txt index 25bc1bc3b5f7..202de77e55c6 100644 --- a/src/AnalyzeView/CMakeLists.txt +++ b/src/AnalyzeView/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Analyze View Module +# Flight log analysis, MAVLink inspection, and telemetry visualization +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE ExifParser.cc @@ -30,6 +35,9 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# QML Module for Analyze View UI +# ---------------------------------------------------------------------------- qt_add_library(AnalyzeViewModule STATIC) qt_add_qml_module(AnalyzeViewModule @@ -46,7 +54,9 @@ qt_add_qml_module(AnalyzeViewModule NO_PLUGIN ) -#===========================================================================# +# ============================================================================ +# ULog Parser Integration +# ============================================================================ CPMAddPackage( NAME ulog_cpp @@ -54,6 +64,7 @@ CPMAddPackage( GIT_TAG main ) +# Suppress Clang warnings for ulog_cpp if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options(ulog_cpp PRIVATE -Wno-unknown-warning-option) endif() diff --git a/src/AnalyzeView/ExifParser.cc b/src/AnalyzeView/ExifParser.cc index 0e9edc74ba64..af0ae559c240 100644 --- a/src/AnalyzeView/ExifParser.cc +++ b/src/AnalyzeView/ExifParser.cc @@ -14,7 +14,7 @@ #include #include -QGC_LOGGING_CATEGORY(ExifParserLog, "qgc.analyzeview.exifparser") +QGC_LOGGING_CATEGORY(ExifParserLog, "AnalyzeView.ExifParser") namespace ExifParser { diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index fbad97313639..792ad7bc61ef 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -16,7 +16,7 @@ #include #include -QGC_LOGGING_CATEGORY(GeoTagControllerLog, "qgc.analyzeview.geotagcontroller") +QGC_LOGGING_CATEGORY(GeoTagControllerLog, "AnalyzeView.GeoTagController") GeoTagController::GeoTagController(QObject *parent) : QObject(parent) diff --git a/src/AnalyzeView/GeoTagWorker.cc b/src/AnalyzeView/GeoTagWorker.cc index 234ae6f470b2..38352e5508da 100644 --- a/src/AnalyzeView/GeoTagWorker.cc +++ b/src/AnalyzeView/GeoTagWorker.cc @@ -17,7 +17,7 @@ #include -QGC_LOGGING_CATEGORY(GeoTagWorkerLog, "qgc.analyzeview.geotagworker") +QGC_LOGGING_CATEGORY(GeoTagWorkerLog, "AnalyzeView.GeoTagWorker") GeoTagWorker::GeoTagWorker(QObject *parent) : QObject(parent) diff --git a/src/AnalyzeView/LogDownloadController.cc b/src/AnalyzeView/LogDownloadController.cc index 1ae5f6ce6fb0..ccf020f9c2c2 100644 --- a/src/AnalyzeView/LogDownloadController.cc +++ b/src/AnalyzeView/LogDownloadController.cc @@ -22,7 +22,7 @@ #include #include -QGC_LOGGING_CATEGORY(LogDownloadControllerLog, "qgc.analyzeview.logdownloadcontroller") +QGC_LOGGING_CATEGORY(LogDownloadControllerLog, "AnalyzeView.LogDownloadController") LogDownloadController::LogDownloadController(QObject *parent) : QObject(parent) diff --git a/src/AnalyzeView/LogEntry.cc b/src/AnalyzeView/LogEntry.cc index e03a7964b7f0..f933d8214549 100644 --- a/src/AnalyzeView/LogEntry.cc +++ b/src/AnalyzeView/LogEntry.cc @@ -13,7 +13,7 @@ #include -QGC_LOGGING_CATEGORY(LogEntryLog, "test.analyzeview.logentry") +QGC_LOGGING_CATEGORY(LogEntryLog, "AnalyzeView.QGCLogEntry") LogDownloadData::LogDownloadData(QGCLogEntry * const entry) : ID(entry->id()) diff --git a/src/AnalyzeView/MAVLinkChartController.cc b/src/AnalyzeView/MAVLinkChartController.cc index d944914bd808..f4f64563810f 100644 --- a/src/AnalyzeView/MAVLinkChartController.cc +++ b/src/AnalyzeView/MAVLinkChartController.cc @@ -18,12 +18,10 @@ Q_DECLARE_METATYPE(QAbstractSeries*) -QGC_LOGGING_CATEGORY(MAVLinkChartControllerLog, "qgc.analyzeview.mavlinkchartcontroller") +QGC_LOGGING_CATEGORY(MAVLinkChartControllerLog, "AnalyzeView.MAVLinkChartController") -MAVLinkChartController::MAVLinkChartController(MAVLinkInspectorController *controller, int index, QObject *parent) +MAVLinkChartController::MAVLinkChartController(QObject *parent) : QObject(parent) - , _index(index) - , _controller(controller) , _updateSeriesTimer(new QTimer(this)) { // qCDebug(MAVLinkChartControllerLog) << Q_FUNC_INFO << this; @@ -31,8 +29,6 @@ MAVLinkChartController::MAVLinkChartController(MAVLinkInspectorController *contr (void) qRegisterMetaType("QAbstractSeries*"); (void) connect(_updateSeriesTimer, &QTimer::timeout, this, &MAVLinkChartController::_refreshSeries); - - updateXRange(); } MAVLinkChartController::~MAVLinkChartController() @@ -40,13 +36,23 @@ MAVLinkChartController::~MAVLinkChartController() // qCDebug(MAVLinkChartControllerLog) << Q_FUNC_INFO << this; } +void MAVLinkChartController::setInspectorController(MAVLinkInspectorController *controller) +{ + if (_inspectorController == controller) { + return; + } + + _inspectorController = controller; + updateXRange(); +} + void MAVLinkChartController::setRangeYIndex(quint32 index) { if (index == _rangeYIndex) { return; } - if (index >= static_cast(_controller->rangeSt().count())) { + if (index >= static_cast(_inspectorController->rangeSt().count())) { return; } @@ -54,7 +60,7 @@ void MAVLinkChartController::setRangeYIndex(quint32 index) emit rangeYIndexChanged(); // If not Auto, use defined range - const qreal range = _controller->rangeSt()[static_cast(index)]->range; + const qreal range = _inspectorController->rangeSt()[static_cast(index)]->range; if (_rangeYIndex > 0) { _rangeYMin = -range; emit rangeYMinChanged(); @@ -78,7 +84,7 @@ void MAVLinkChartController::setRangeXIndex(quint32 index) void MAVLinkChartController::updateXRange() { - if (_rangeXIndex >= static_cast(_controller->timeScaleSt().count())) { + if (_rangeXIndex >= static_cast(_inspectorController->timeScaleSt().count())) { return; } @@ -86,7 +92,7 @@ void MAVLinkChartController::updateXRange() _rangeXMax = QDateTime::fromMSecsSinceEpoch(bootTime); emit rangeXMaxChanged(); - _rangeXMin = QDateTime::fromMSecsSinceEpoch(bootTime - _controller->timeScaleSt()[static_cast(_rangeXIndex)]->timeScale); + _rangeXMin = QDateTime::fromMSecsSinceEpoch(bootTime - _inspectorController->timeScaleSt()[static_cast(_rangeXIndex)]->timeScale); emit rangeXMinChanged(); } diff --git a/src/AnalyzeView/MAVLinkChartController.h b/src/AnalyzeView/MAVLinkChartController.h index a77b45433800..d71ea5b3a151 100644 --- a/src/AnalyzeView/MAVLinkChartController.h +++ b/src/AnalyzeView/MAVLinkChartController.h @@ -7,10 +7,6 @@ * ****************************************************************************/ -/// @file -/// @brief MAVLink message inspector and charting controller -/// @author Gus Grubba - #pragma once #include @@ -30,27 +26,30 @@ class MAVLinkChartController : public QObject { Q_OBJECT QML_ELEMENT - QML_UNCREATABLE("") Q_MOC_INCLUDE("MAVLinkInspectorController.h") Q_MOC_INCLUDE("MAVLinkMessageField.h") Q_MOC_INCLUDE("QtCharts/qabstractseries.h") + + Q_PROPERTY(MAVLinkInspectorController* inspectorController READ inspectorController WRITE setInspectorController REQUIRED) + Q_PROPERTY(int chartIndex MEMBER _chartIndex REQUIRED) Q_PROPERTY(QVariantList chartFields READ chartFields NOTIFY chartFieldsChanged) Q_PROPERTY(QDateTime rangeXMin READ rangeXMin NOTIFY rangeXMinChanged) Q_PROPERTY(QDateTime rangeXMax READ rangeXMax NOTIFY rangeXMaxChanged) Q_PROPERTY(qreal rangeYMin READ rangeYMin NOTIFY rangeYMinChanged) Q_PROPERTY(qreal rangeYMax READ rangeYMax NOTIFY rangeYMaxChanged) - Q_PROPERTY(int chartIndex READ chartIndex CONSTANT) Q_PROPERTY(quint32 rangeYIndex READ rangeYIndex WRITE setRangeYIndex NOTIFY rangeYIndexChanged) Q_PROPERTY(quint32 rangeXIndex READ rangeXIndex WRITE setRangeXIndex NOTIFY rangeXIndexChanged) + public: - explicit MAVLinkChartController(MAVLinkInspectorController *controller, int index, QObject *parent = nullptr); + explicit MAVLinkChartController(QObject *parent = nullptr); ~MAVLinkChartController(); Q_INVOKABLE void addSeries(QGCMAVLinkMessageField *field, QAbstractSeries *series); Q_INVOKABLE void delSeries(QGCMAVLinkMessageField *field); - Q_INVOKABLE MAVLinkInspectorController *controller() const { return _controller; } + void setInspectorController(MAVLinkInspectorController *inspectorController); + MAVLinkInspectorController *inspectorController() const { return _inspectorController; } QVariantList chartFields() const { return _chartFields; } QDateTime rangeXMin() const { return _rangeXMin; } QDateTime rangeXMax() const { return _rangeXMax; } @@ -58,7 +57,7 @@ class MAVLinkChartController : public QObject qreal rangeYMax() const { return _rangeYMax; } quint32 rangeXIndex() const { return _rangeXIndex; } quint32 rangeYIndex() const { return _rangeYIndex; } - int chartIndex() const { return _index; } + int chartIndex() const { return _chartIndex; } void setRangeXIndex(quint32 index); void setRangeYIndex(quint32 index); @@ -78,8 +77,8 @@ private slots: void _refreshSeries(); private: - int _index = 0; - MAVLinkInspectorController *_controller = nullptr; + int _chartIndex = 0; + MAVLinkInspectorController *_inspectorController = nullptr; QTimer *_updateSeriesTimer = nullptr; QDateTime _rangeXMin; diff --git a/src/AnalyzeView/MAVLinkConsoleController.cc b/src/AnalyzeView/MAVLinkConsoleController.cc index eba4c7b60a98..dce87d2e3888 100644 --- a/src/AnalyzeView/MAVLinkConsoleController.cc +++ b/src/AnalyzeView/MAVLinkConsoleController.cc @@ -17,7 +17,7 @@ #include #include -QGC_LOGGING_CATEGORY(MAVLinkConsoleControllerLog, "qgc.analyzeview.mavlinkconsolecontroller") +QGC_LOGGING_CATEGORY(MAVLinkConsoleControllerLog, "AnalyzeView.MAVLinkConsoleController") MAVLinkConsoleController::MAVLinkConsoleController(QObject *parent) : QStringListModel(parent) diff --git a/src/AnalyzeView/MAVLinkInspectorController.cc b/src/AnalyzeView/MAVLinkInspectorController.cc index e9fddbc57651..bf5fa7f6f890 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.cc +++ b/src/AnalyzeView/MAVLinkInspectorController.cc @@ -20,7 +20,7 @@ #include -QGC_LOGGING_CATEGORY(MAVLinkInspectorControllerLog, "qgc.analyzeview.mavlinkinspectorcontroller") +QGC_LOGGING_CATEGORY(MAVLinkInspectorControllerLog, "AnalyzeView.MAVLinkInspectorController") MAVLinkInspectorController::TimeScale_st::TimeScale_st(const QString &label_, uint32_t timeScale_) : label(label_) @@ -44,7 +44,6 @@ MAVLinkInspectorController::MAVLinkInspectorController(QObject *parent) : QObject(parent) , _updateFrequencyTimer(new QTimer(this)) , _systems(new QmlObjectListModel(this)) - , _charts(new QmlObjectListModel(this)) { // qCDebug(MAVLinkInspectorControllerLog) << Q_FUNC_INFO << this; @@ -84,7 +83,6 @@ MAVLinkInspectorController::~MAVLinkInspectorController() { qDeleteAll(_timeScaleSt); qDeleteAll(_rangeSt); - _charts->clearAndDeleteContents(); _systems->clearAndDeleteContents(); // qCDebug(MAVLinkInspectorControllerLog) << Q_FUNC_INFO << this; @@ -227,39 +225,6 @@ void MAVLinkInspectorController::_receiveMessage(LinkInterface *link, const mavl } } -MAVLinkChartController *MAVLinkInspectorController::createChart() -{ - MAVLinkChartController *const pChart = new MAVLinkChartController(this, _charts->count()); - QQmlEngine::setObjectOwnership(pChart, QQmlEngine::CppOwnership); - - _charts->append(pChart); - emit chartsChanged(); - - return pChart; -} - -void MAVLinkInspectorController::deleteChart(MAVLinkChartController *chart) -{ - if (!chart) { - return; - } - - bool found = false; - for (int i = 0; i < _charts->count(); i++) { - MAVLinkChartController *const controller = qobject_cast(_charts->get(i)); - if (controller && (controller == chart)) { - found = true; - _charts->removeOne(controller); - delete controller; - break; - } - } - - if (found) { - emit chartsChanged(); - } -} - void MAVLinkInspectorController::setActiveSystem(int systemId) { QGCMAVLinkSystem *const system = _findVehicle(systemId); diff --git a/src/AnalyzeView/MAVLinkInspectorController.h b/src/AnalyzeView/MAVLinkInspectorController.h index 92b70630c32f..5e5d022c1c83 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.h +++ b/src/AnalyzeView/MAVLinkInspectorController.h @@ -36,7 +36,6 @@ class MAVLinkInspectorController : public QObject Q_MOC_INCLUDE("MAVLinkChartController.h") Q_MOC_INCLUDE("QmlObjectListModel.h") Q_PROPERTY(QmlObjectListModel *systems READ systems NOTIFY systemsChanged) - Q_PROPERTY(QmlObjectListModel *charts READ charts NOTIFY chartsChanged) Q_PROPERTY(QGCMAVLinkSystem *activeSystem READ activeSystem NOTIFY activeSystemChanged) Q_PROPERTY(QStringList timeScales READ timeScales NOTIFY timeScalesChanged) Q_PROPERTY(QStringList rangeList READ rangeList NOTIFY rangeListChanged) @@ -62,13 +61,10 @@ class MAVLinkInspectorController : public QObject explicit MAVLinkInspectorController(QObject *parent = nullptr); ~MAVLinkInspectorController(); - Q_INVOKABLE MAVLinkChartController *createChart(); - Q_INVOKABLE void deleteChart(MAVLinkChartController *chart); Q_INVOKABLE void setActiveSystem(int systemId); Q_INVOKABLE void setMessageInterval(int32_t rate) const; QmlObjectListModel *systems() const { return _systems; } - QmlObjectListModel *charts() const { return _charts; } QGCMAVLinkSystem *activeSystem() const { return _activeSystem; } QStringList systemNames() const { return _systemNames; } QStringList timeScales(); @@ -79,7 +75,6 @@ class MAVLinkInspectorController : public QObject signals: void activeSystemChanged(); - void chartsChanged(); void rangeListChanged(); void systemsChanged(); void timeScalesChanged(); @@ -104,5 +99,4 @@ private slots: QGCMAVLinkSystem *_activeSystem = nullptr; QTimer *_updateFrequencyTimer = nullptr; QmlObjectListModel *_systems = nullptr; ///< List of QGCMAVLinkSystem - QmlObjectListModel *_charts = nullptr; ///< List of MAVLinkCharts }; diff --git a/src/AnalyzeView/MAVLinkInspectorPage.qml b/src/AnalyzeView/MAVLinkInspectorPage.qml index 0a810272b319..2445beaeed5b 100644 --- a/src/AnalyzeView/MAVLinkInspectorPage.qml +++ b/src/AnalyzeView/MAVLinkInspectorPage.qml @@ -18,8 +18,6 @@ import QGroundControl import QGroundControl.Controls - - AnalyzePage { id: root headerComponent: headerComponent @@ -45,27 +43,22 @@ AnalyzePage { if(!checkBox) { continue } - const object = message.fields.get(i) - checkBox.enabled = isCheckboxEnabled(checkBox, object, chart) + const messageField = message.fields.get(i) + checkBox.enabled = isCheckboxEnabled(checkBox, messageField, chart) } } - function isCheckboxEnabled(checkBox, object, chart) { + function isCheckboxEnabled(checkBox, messageField, chart) { if(checkBox.checkState === Qt.Checked) { return true } - if(!object.selectable) { + if(!messageField.selectable) { return false } - if(object.series !== null) { + if(messageField.series !== null) { return false } - if(chart.chartController !== null) { - if (chart.chartController.chartFields.length >= chart.seriesColors.length) { - return false - } - } - return true + return chart.roomForNewDimension() } Component { @@ -338,14 +331,18 @@ AnalyzePage { } Item { height: ScreenTools.defaultFontPixelHeight * 0.25; width: 1 } MAVLinkChart { - id: chart1 - height: ScreenTools.defaultFontPixelHeight * 20 - width: parent.width + id: chart1 + height: ScreenTools.defaultFontPixelHeight * 20 + width: parent.width + inspectorController: controller + chartIndex: 0 } MAVLinkChart { - id: chart2 - height: ScreenTools.defaultFontPixelHeight * 20 - width: parent.width + id: chart2 + height: ScreenTools.defaultFontPixelHeight * 20 + width: parent.width + inspectorController: controller + chartIndex: 1 } } } diff --git a/src/AnalyzeView/MAVLinkMessage.cc b/src/AnalyzeView/MAVLinkMessage.cc index 0771fab06b4c..52b2cf1a8038 100644 --- a/src/AnalyzeView/MAVLinkMessage.cc +++ b/src/AnalyzeView/MAVLinkMessage.cc @@ -14,7 +14,7 @@ #include -QGC_LOGGING_CATEGORY(MAVLinkMessageLog, "qgc.analyzeview.mavlinkmessage") +QGC_LOGGING_CATEGORY(MAVLinkMessageLog, "AnalyzeView.MAVLinkMessage") QGCMAVLinkMessage::QGCMAVLinkMessage(const mavlink_message_t &message, QObject *parent) : QObject(parent) diff --git a/src/AnalyzeView/MAVLinkMessageField.cc b/src/AnalyzeView/MAVLinkMessageField.cc index bfe73677b8fc..f09c0a541e87 100644 --- a/src/AnalyzeView/MAVLinkMessageField.cc +++ b/src/AnalyzeView/MAVLinkMessageField.cc @@ -16,7 +16,7 @@ #include #include -QGC_LOGGING_CATEGORY(MAVLinkMessageFieldLog, "qgc.analyzeview.mavlinkmessagefield") +QGC_LOGGING_CATEGORY(MAVLinkMessageFieldLog, "AnalyzeView.MAVLinkMessageField") QGCMAVLinkMessageField::QGCMAVLinkMessageField(const QString &name, const QString &type, QGCMAVLinkMessage *parent) : QObject(parent) @@ -34,13 +34,13 @@ QGCMAVLinkMessageField::~QGCMAVLinkMessageField() // qCDebug(MAVLinkMessageFieldLog) << Q_FUNC_INFO << this; } -void QGCMAVLinkMessageField::addSeries(MAVLinkChartController *chart, QAbstractSeries *series) +void QGCMAVLinkMessageField::addSeries(MAVLinkChartController *chartController, QAbstractSeries *series) { if (_pSeries) { return; } - _chart = chart; + _chartController = chartController; _pSeries = series; emit seriesChanged(); @@ -58,7 +58,7 @@ void QGCMAVLinkMessageField::delSeries() QLineSeries *const lineSeries = static_cast(_pSeries); lineSeries->replace(_values); _pSeries = nullptr; - _chart = nullptr; + _chartController = nullptr; emit seriesChanged(); _msg->updateFieldSelection(); } @@ -78,8 +78,8 @@ void QGCMAVLinkMessageField::setSelectable(bool sel) int QGCMAVLinkMessageField::chartIndex() const { - if (_chart) { - return _chart->chartIndex(); + if (_chartController) { + return _chartController->chartIndex(); } return 0; @@ -92,7 +92,7 @@ void QGCMAVLinkMessageField::updateValue(const QString &newValue, qreal v) emit valueChanged(); } - if (!_pSeries || !_chart) { + if (!_pSeries || !_chartController) { return; } @@ -109,7 +109,7 @@ void QGCMAVLinkMessageField::updateValue(const QString &newValue, qreal v) _dataIndex++; } - if (_chart->rangeYIndex() != 0) { + if (_chartController->rangeYIndex() != 0) { return; } @@ -137,7 +137,7 @@ void QGCMAVLinkMessageField::updateValue(const QString &newValue, qreal v) } if (changed) { - _chart->updateYRange(); + _chartController->updateYRange(); } } diff --git a/src/AnalyzeView/MAVLinkMessageField.h b/src/AnalyzeView/MAVLinkMessageField.h index 66745cb71314..f459e8faa34d 100644 --- a/src/AnalyzeView/MAVLinkMessageField.h +++ b/src/AnalyzeView/MAVLinkMessageField.h @@ -53,7 +53,7 @@ class QGCMAVLinkMessageField : public QObject void setSelectable(bool sel); void updateValue(const QString &newValue, qreal v); - void addSeries(MAVLinkChartController *chart, QAbstractSeries *series); + void addSeries(MAVLinkChartController *chartController, QAbstractSeries *series); void delSeries(); void updateSeries(); @@ -75,5 +75,5 @@ class QGCMAVLinkMessageField : public QObject QList _values; QAbstractSeries *_pSeries = nullptr; - MAVLinkChartController *_chart = nullptr; + MAVLinkChartController *_chartController = nullptr; }; diff --git a/src/AnalyzeView/MAVLinkSystem.cc b/src/AnalyzeView/MAVLinkSystem.cc index 2ad489afa407..63788e833e40 100644 --- a/src/AnalyzeView/MAVLinkSystem.cc +++ b/src/AnalyzeView/MAVLinkSystem.cc @@ -11,7 +11,7 @@ #include "MAVLinkMessage.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(MAVLinkSystemLog, "qgc.analyzeview.mavlinksystem") +QGC_LOGGING_CATEGORY(MAVLinkSystemLog, "AnalyzeView.MAVLinkSystem") QGCMAVLinkSystem::QGCMAVLinkSystem(quint8 id, QObject *parent) : QObject(parent) diff --git a/src/AnalyzeView/PX4LogParser.cc b/src/AnalyzeView/PX4LogParser.cc index 21f9e240e89c..95c7562f9564 100644 --- a/src/AnalyzeView/PX4LogParser.cc +++ b/src/AnalyzeView/PX4LogParser.cc @@ -12,7 +12,7 @@ #include -QGC_LOGGING_CATEGORY(PX4LogParserLog, "qgc.analyzeview.px4logparser") +QGC_LOGGING_CATEGORY(PX4LogParserLog, "AnalyzeView.PX4LogParser") // general message header static constexpr const char header[3] = {static_cast(0xA3), static_cast(0x95), static_cast(0x00)}; diff --git a/src/AnalyzeView/ULogParser.cc b/src/AnalyzeView/ULogParser.cc index 4897f2c0731e..b9836dcf829f 100644 --- a/src/AnalyzeView/ULogParser.cc +++ b/src/AnalyzeView/ULogParser.cc @@ -18,7 +18,7 @@ using namespace ulog_cpp; -QGC_LOGGING_CATEGORY(ULogParserLog, "qgc.analyzeview.ulogparser") +QGC_LOGGING_CATEGORY(ULogParserLog, "AnalyzeView.ULogParser") namespace ULogParser { diff --git a/src/Android/AndroidInterface.cc b/src/Android/AndroidInterface.cc index e63b95f2d2a7..675fede6e0bb 100644 --- a/src/Android/AndroidInterface.cc +++ b/src/Android/AndroidInterface.cc @@ -12,7 +12,6 @@ #include #include -#include QGC_LOGGING_CATEGORY(AndroidInterfaceLog, "qgc.android.src.androidinterface") @@ -107,23 +106,20 @@ void jniLogWarning(JNIEnv *envA, jobject thizA, jstring messageA) bool checkStoragePermissions() { - const QString readPermission("android.permission.READ_EXTERNAL_STORAGE"); - const QString writePermission("android.permission.WRITE_EXTERNAL_STORAGE"); - - const QStringList permissions = { readPermission, writePermission }; - for (const auto& permission: permissions) { - QFuture futurePermissionResult = QtAndroidPrivate::checkPermission(permission); - QtAndroidPrivate::PermissionResult permissionResult = futurePermissionResult.result(); - if (permissionResult == QtAndroidPrivate::PermissionResult::Denied) { - futurePermissionResult = QtAndroidPrivate::requestPermission(permission); - permissionResult = futurePermissionResult.result(); - if (permissionResult == QtAndroidPrivate::PermissionResult::Denied) { - return false; - } - } + // Call the Java method to check and request storage permissions + const bool hasPermission = QJniObject::callStaticMethod( + kJniQGCActivityClassName, + "checkStoragePermissions", + "()Z" + ); + + if (hasPermission) { + qCDebug(AndroidInterfaceLog) << "Storage permissions granted"; + } else { + qCWarning(AndroidInterfaceLog) << "Storage permissions not granted"; } - return true; + return hasPermission; } QString getSDCardPath() diff --git a/src/Android/CMakeLists.txt b/src/Android/CMakeLists.txt index de495aab6718..88adb80b1c3d 100644 --- a/src/Android/CMakeLists.txt +++ b/src/Android/CMakeLists.txt @@ -1,3 +1,9 @@ +# ============================================================================ +# Android Module +# Android platform-specific initialization and interfaces +# ============================================================================ + +# Only build on Android platforms if(NOT ANDROID) return() endif() @@ -13,4 +19,7 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# Android Serial Port Support +# ---------------------------------------------------------------------------- add_subdirectory(qtandroidserialport) diff --git a/src/Android/qtandroidserialport/CMakeLists.txt b/src/Android/qtandroidserialport/CMakeLists.txt index 16dc23a16007..ba3f06258e6c 100644 --- a/src/Android/qtandroidserialport/CMakeLists.txt +++ b/src/Android/qtandroidserialport/CMakeLists.txt @@ -1,3 +1,9 @@ +# ============================================================================ +# Qt Android Serial Port +# Android-specific serial port implementation +# ============================================================================ + +# Only build on Android platforms if(NOT ANDROID) return() endif() @@ -19,4 +25,5 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# TODO: Enable I/O device debugging # target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QIODEVICE_DEBUG) diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc index 8829e3ac88b4..dde7ac4ecfb8 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc @@ -23,7 +23,7 @@ #include #include -QGC_LOGGING_CATEGORY(APMAirframeComponentControllerLog, "qgc.autopilotplugins.apm.apmairframecomponentcontroller") +QGC_LOGGING_CATEGORY(APMAirframeComponentControllerLog, "AutoPilotPlugins.APMAirframeComponentController") /*===========================================================================*/ diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 0bd9f0e5cc3b..ae23fb638798 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -37,7 +37,7 @@ #include "SerialLink.h" #endif -QGC_LOGGING_CATEGORY(APMAutoPilotPluginLog, "qgc.autopilotplugins.apm.apmautopilotplugin") +QGC_LOGGING_CATEGORY(APMAutoPilotPluginLog, "AutoPilotPlugins.APM.apmautopilotplugin") APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle *vehicle, QObject *parent) : AutoPilotPlugin(vehicle, parent) diff --git a/src/AutoPilotPlugins/APM/APMRemoteSupportComponent.qml b/src/AutoPilotPlugins/APM/APMRemoteSupportComponent.qml index 3f43e5e539f7..7a368b06019e 100644 --- a/src/AutoPilotPlugins/APM/APMRemoteSupportComponent.qml +++ b/src/AutoPilotPlugins/APM/APMRemoteSupportComponent.qml @@ -33,7 +33,7 @@ SetupPage { GridLayout { id: elementsRow columns: 2 - + anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter anchors.margins: ScreenTools.defaultFontPixelWidth diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index b63397d6a317..ee6f585ddee2 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -18,8 +18,8 @@ #include -QGC_LOGGING_CATEGORY(APMSensorsComponentControllerLog, "qgc.autopilotplugins.apm.apmsensorscomponentcontroller") -QGC_LOGGING_CATEGORY(APMSensorsComponentControllerVerboseLog, "qgc.autopilotplugins.apm.apmsensorscomponentcontroller:verbose") +QGC_LOGGING_CATEGORY(APMSensorsComponentControllerLog, "AutoPilotPlugins.APMSensorsComponentController") +QGC_LOGGING_CATEGORY(APMSensorsComponentControllerVerboseLog, "AutoPilotPlugins.APMSensorsComponentController:verbose") APMSensorsComponentController::APMSensorsComponentController(QObject *parent) : FactPanelController(parent) diff --git a/src/AutoPilotPlugins/APM/APMSubMotorComponentController.h b/src/AutoPilotPlugins/APM/APMSubMotorComponentController.h index 97d7386ee520..5387a50702c2 100644 --- a/src/AutoPilotPlugins/APM/APMSubMotorComponentController.h +++ b/src/AutoPilotPlugins/APM/APMSubMotorComponentController.h @@ -34,4 +34,3 @@ private slots: private: QString _motorDetectionMessages; }; - diff --git a/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml b/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml index 1f5636cd4a49..f24fedef7ece 100644 --- a/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml +++ b/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml @@ -27,9 +27,8 @@ SetupPage { Component { id: tuningPageComponent - Column { - width: availableWidth - height: availableHeight + ColumnLayout { + width: availableWidth FactPanelController { id: controller; } @@ -65,19 +64,7 @@ SetupPage { Component.onCompleted: { // We use QtCharts only on Desktop platforms showAdvanced = !ScreenTools.isMobile - - // Qml Sliders have a strange behavior in which they first set Slider::value to some internal - // setting and then set Slider::value to the bound properties value. If you have an onValueChanged - // handler which updates your property with the new value, this first value change will trash - // your bound values. In order to work around this we don't set the values into the Sliders until - // after Qml load is done. We also don't track value changes until Qml load completes. - rollPitch.value = _rateRollP.value - climb.value = _rateClimbP.value - if (_atcInputTCAvailable) { - atcInputTC.value = _atcInputTC.value - } _loadComplete = true - calcAutoTuneChannel() } @@ -118,341 +105,171 @@ SetupPage { Connections { target: _ch11Opt; function onValueChanged(value) { calcAutoTuneChannel() } } Connections { target: _ch12Opt; function onValueChanged(value) { calcAutoTuneChannel() } } - Column { - anchors.left: parent.left - anchors.right: parent.right + ColumnLayout { + Layout.fillWidth: true spacing: _margins visible: !advanced - QGCLabel { - text: qsTr("Basic Tuning") - font.bold: true - } - - Rectangle { - id: basicTuningRect - anchors.left: parent.left - anchors.right: parent.right - height: basicTuningColumn.y + basicTuningColumn.height + _margins - color: qgcPal.windowShade - - Column { - id: basicTuningColumn - anchors.margins: _margins - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: _margins - - Column { - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - text: qsTr("Roll/Pitch Sensitivity") - font.bold: true - } - - QGCLabel { - text: qsTr("Slide to the right if the copter is sluggish or slide to the left if the copter is twitchy") - } - - QGCSlider { - id: rollPitch - anchors.left: parent.left - anchors.right: parent.right - from: 0.08 - to: 0.4 - stepSize: 0.01 - tickmarksEnabled: true - - onValueChanged: { - if (_loadComplete) { - _rateRollP.value = value - _rateRollI.value = value - _ratePitchP.value = value - _ratePitchI.value = value - } - } - } - } - - Column { - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - text: qsTr("Climb Sensitivity") - font.bold: true - } - - QGCLabel { - text: qsTr("Slide to the right to climb more aggressively or slide to the left to climb more gently") - } - - QGCSlider { - id: climb - anchors.left: parent.left - anchors.right: parent.right - from: 0.3 - to: 1.0 - stepSize: 0.02 - tickmarksEnabled: true - value: _rateClimbP.value - - onValueChanged: { - if (_loadComplete) { - _rateClimbP.value = value - _rateClimbI.value = value * 2 - } - } - } - } - - Column { - anchors.left: parent.left - anchors.right: parent.right - visible: _atcInputTCAvailable - - QGCLabel { - text: qsTr("RC Roll/Pitch Feel") - font.bold: true - } - - QGCLabel { - text: qsTr("Slide to the left for soft control, slide to the right for crisp control") - } - - QGCSlider { - id: atcInputTC - anchors.left: parent.left - anchors.right: parent.right - from: _atcInputTC.min - to: _atcInputTC.max - stepSize: _atcInputTC.increment - tickmarksEnabled: true - - onValueChanged: { - if (_loadComplete) { - _atcInputTC.value = value - } - } - } - } - - Column { - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - text: qsTr("Spin While Armed") - font.bold: true - } - - QGCLabel { - text: qsTr("Adjust the amount the motors spin to indicate armed") - } - - QGCSlider { - anchors.left: parent.left - anchors.right: parent.right - from: 0 - to: Math.max(0.3, _motSpinArm.rawValue) - stepSize: 0.01 - tickmarksEnabled: true - value: _motSpinArm.rawValue - - onValueChanged: { - if (_loadComplete) { - _motSpinArm.rawValue = value - } - } - } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Roll/Pitch Sensitivity") + headingDescription: qsTr("Slide to the right if the copter is sluggish or slide to the left if the copter is twitchy") + + FactSlider { + id: rollPitch + Layout.fillWidth: true + from: 0.08 + to: 0.4 + majorTickStepSize: 0.02 + decimalPlaces: 3 + fact: _rateRollP + + onValueChanged: { + _rateRollI.rawValue = value + _ratePitchP.rawValue = value + _ratePitchI.rawValue = value } + } + } - Column { - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - text: qsTr("Minimum Thrust") - font.bold: true - } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Climb Sensitivity") + headingDescription: qsTr("Slide to the right to climb more aggressively or slide to the left to climb more gently") + + FactSlider { + id: climb + Layout.fillWidth: true + from: 0.03 + to: 1.0 + majorTickStepSize: 0.05 + decimalPlaces: 3 + fact: _rateClimbP + onValueChanged: _rateClimbI.rawValue = value * 2 + } + } - QGCLabel { - text: qsTr("Adjust the minimum amount of thrust require for the vehicle to move") - } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("RC Roll/Pitch Feel") + headingDescription: qsTr("Slide to the left for soft control, slide to the right for crisp control") + + FactSlider { + id: atcInputTC + Layout.fillWidth: true + from: _atcInputTC.min + to: _atcInputTC.max + majorTickStepSize: _atcInputTC.increment + decimalPlaces: 2 + unitsString: "" + fact: _atcInputTC + } + } - QGCLabel { - text: qsTr("Warning: This setting should be higher than 'Spin While Armed'") - color: qgcPal.warningText - visible: _motSpinMin.rawValue < _motSpinArm.rawValue - } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Spin While Armed") + headingDescription: qsTr("Adjust the amount the motors spin to indicate armed. Should be lower than 'Minimum Thrust'") + + FactSlider { + Layout.fillWidth: true + from: 0 + to: 0.3 + majorTickStepSize: 0.1 + decimalPlaces: 1 + fact: _motSpinArm + } + } - QGCSlider { - anchors.left: parent.left - anchors.right: parent.right - from: 0 - to: Math.max(0.3, _motSpinMin.rawValue) - stepSize: 0.01 - tickmarksEnabled: true - value: _motSpinMin.rawValue - - onValueChanged: { - if (_loadComplete) { - _motSpinMin.rawValue = value - } - } - } - } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Minimum Thrust") + headingDescription: qsTr("Adjust the minimum amount of thrust require for the vehicle to move. Should be higher than 'Spin While Armed") + + FactSlider { + Layout.fillWidth: true + from: 0 + to: 0.3 + majorTickStepSize: 0.1 + decimalPlaces: 1 + fact: _motSpinArm } - } // Rectangle - Basic tuning + } Flow { id: flowLayout Layout.fillWidth: true spacing: _margins - Rectangle { - height: autoTuneLabel.height + autoTuneRect.height - width: autoTuneRect.width - color: qgcPal.window + SettingsGroupLayout { + heading: qsTr("AutoTune") - QGCLabel { - id: autoTuneLabel - text: qsTr("AutoTune") - font.bold: true - } - - Rectangle { - id: autoTuneRect - width: autoTuneColumn.x + autoTuneColumn.width + _margins - height: autoTuneColumn.y + autoTuneColumn.height + _margins - anchors.top: autoTuneLabel.bottom - color: qgcPal.windowShade - - Column { - id: autoTuneColumn - anchors.margins: _margins - anchors.left: parent.left - anchors.top: parent.top - spacing: _margins - - Row { - spacing: _margins - - QGCLabel { text: qsTr("Axes to AutoTune:") } - FactBitmask { fact: _autoTuneAxes } - } + ColumnLayout { + RowLayout { + spacing: _margins - Row { - spacing: _margins - - QGCLabel { - anchors.baseline: autoTuneChannelCombo.baseline - text: qsTr("Channel for AutoTune switch:") - } + QGCLabel { text: qsTr("Axes to AutoTune:") } + FactBitmask { fact: _autoTuneAxes } + } - QGCComboBox { - id: autoTuneChannelCombo - width: ScreenTools.defaultFontPixelWidth * 14 - model: [qsTr("None"), qsTr("Channel 7"), qsTr("Channel 8"), qsTr("Channel 9"), qsTr("Channel 10"), qsTr("Channel 11"), qsTr("Channel 12") ] - currentIndex: _autoTuneSwitchChannelIndex + LabelledComboBox { + id: autoTuneChannelCombo + width: ScreenTools.defaultFontPixelWidth * 14 + label: qsTr("Channel for AutoTune switch:") + model: [qsTr("None"), qsTr("Channel 7"), qsTr("Channel 8"), qsTr("Channel 9"), qsTr("Channel 10"), qsTr("Channel 11"), qsTr("Channel 12") ] + currentIndex: _autoTuneSwitchChannelIndex - onActivated: (index) => { - var channel = index + onActivated: (index) => { + var channel = index - if (channel > 0) { - channel += 6 - } - setChannelAutoTuneOption(channel) - } + if (channel > 0) { + channel += 6 } + setChannelAutoTuneOption(channel) } } - } // Rectangle - AutoTune - } // Rectangle - AutoTuneWrap - - Rectangle { - height: inFlightTuneLabel.height + channel6TuningOption.height - width: channel6TuningOption.width - color: qgcPal.window - - QGCLabel { - id: inFlightTuneLabel - text: qsTr("In Flight Tuning") - font.bold: true } + } - Rectangle { - id: channel6TuningOption - width: channel6TuningOptColumn.width + (_margins * 2) - height: channel6TuningOptColumn.height + ScreenTools.defaultFontPixelHeight - anchors.top: inFlightTuneLabel.bottom - color: qgcPal.windowShade - - Column { - id: channel6TuningOptColumn - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.left: parent.left - anchors.top: parent.top - spacing: ScreenTools.defaultFontPixelHeight - - Row { - spacing: ScreenTools.defaultFontPixelWidth - property Fact nullFact: Fact { } - - QGCLabel { - anchors.baseline: optCombo.baseline - text: qsTr("RC Channel 6 Option (Tuning):") - //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text - } - - FactComboBox { - id: optCombo - width: ScreenTools.defaultFontPixelWidth * 15 - fact: controller.getParameterFact(-1, "TUNE") - indexModel: false - } - } + SettingsGroupLayout { + heading: qsTr("In Flight Tuning") - Row { - spacing: ScreenTools.defaultFontPixelWidth - property Fact nullFact: Fact { } + ColumnLayout { + id: channel6TuningOptColumn + spacing: ScreenTools.defaultFontPixelHeight - QGCLabel { - anchors.baseline: tuneMinField.baseline - text: qsTr("Min:") - //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text - } + LabelledFactComboBox { + id: optCombo + width: ScreenTools.defaultFontPixelWidth * 15 + label: qsTr("RC Channel 6 Option (Tuning):") + fact: controller.getParameterFact(-1, "TUNE") + indexModel: false + } - FactTextField { - id: tuneMinField - validator: DoubleValidator {bottom: 0; top: 32767;} - fact: controller.getParameterFact(-1, "r.TUNE_MAX") - } + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - anchors.baseline: tuneMaxField.baseline - text: qsTr("Max:") - //color: controller.channelOptionEnabled[modelData] ? "yellow" : qgcPal.text - } + LabelledFactTextField { + id: tuneMinField + textField.validator: DoubleValidator {bottom: 0; top: 32767;} + label: qsTr("Min:") + fact: controller.getParameterFact(-1, "r.TUNE_MIN") + } - FactTextField { - id: tuneMaxField - validator: DoubleValidator {bottom: 0; top: 32767;} - fact: controller.getParameterFact(-1, "r.TUNE_MIN") - } + LabelledFactTextField { + id: tuneMaxField + textField.validator: DoubleValidator {bottom: 0; top: 32767;} + label: qsTr("Max:") + fact: controller.getParameterFact(-1, "r.TUNE_MAX") } - } // Column - Channel 6 Tuning option - } // Rectangle - Channel 6 Tuning options - } // Rectangle - Channel 6 Tuning options wrap - } // Flow - Tune + } + } + } + } } Loader { - anchors.left: parent.left - anchors.right: parent.right + Layout.fillWidth: true sourceComponent: advanced ? advancePageComponent : undefined } @@ -460,9 +277,7 @@ SetupPage { id: advancePageComponent PIDTuning { - anchors.left: parent.left - anchors.right: parent.right - height: availableHeight + property bool useAutoTuning: false property var roll: QtObject { property string name: qsTr("Roll") diff --git a/src/AutoPilotPlugins/APM/CMakeLists.txt b/src/AutoPilotPlugins/APM/CMakeLists.txt index d1e69b582dda..ea86c4ae8ed6 100644 --- a/src/AutoPilotPlugins/APM/CMakeLists.txt +++ b/src/AutoPilotPlugins/APM/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# APM AutoPilot Plugin +# ArduPilot-specific vehicle configuration and setup +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE APMAirframeComponent.cc @@ -44,6 +49,9 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# APM QML Module +# ---------------------------------------------------------------------------- qt_add_library(AutoPilotPluginsAPMModule STATIC) qt_add_qml_module(AutoPilotPluginsAPMModule @@ -91,7 +99,9 @@ qt_add_qml_module(AutoPilotPluginsAPMModule NO_PLUGIN ) -# Add json file +# ---------------------------------------------------------------------------- +# APM Component Metadata +# ---------------------------------------------------------------------------- qt_add_resources(${CMAKE_PROJECT_NAME} autopilot_plugin_apm_resource PREFIX "/json" FILES APMFollowComponent.FactMetaData.json diff --git a/src/AutoPilotPlugins/AutoPilotPlugin.cc b/src/AutoPilotPlugins/AutoPilotPlugin.cc index dc65ee2b11a5..8cfec9aeeec1 100644 --- a/src/AutoPilotPlugins/AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/AutoPilotPlugin.cc @@ -16,7 +16,7 @@ #include -QGC_LOGGING_CATEGORY(AutoPilotPluginLog, "qgc.autopilotplugin.autopilotplugin"); +QGC_LOGGING_CATEGORY(AutoPilotPluginLog, "AutoPilotPlugins.AutoPilotPlugin"); AutoPilotPlugin::AutoPilotPlugin(Vehicle *vehicle, QObject *parent) : QObject(parent) diff --git a/src/AutoPilotPlugins/CMakeLists.txt b/src/AutoPilotPlugins/CMakeLists.txt index 04f26cb8de01..16f48fdeacd4 100644 --- a/src/AutoPilotPlugins/CMakeLists.txt +++ b/src/AutoPilotPlugins/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# AutoPilot Plugins Module +# Provides autopilot-specific UI and configuration handling +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE AutoPilotPlugin.cc @@ -14,10 +19,15 @@ target_include_directories(${CMAKE_PROJECT_NAME} Generic ) +# ---------------------------------------------------------------------------- +# Autopilot-Specific Plugins +# ---------------------------------------------------------------------------- add_subdirectory(Common) + if(NOT QGC_DISABLE_APM_PLUGIN) add_subdirectory(APM) endif() + if(NOT QGC_DISABLE_PX4_PLUGIN) add_subdirectory(PX4) endif() diff --git a/src/AutoPilotPlugins/Common/CMakeLists.txt b/src/AutoPilotPlugins/Common/CMakeLists.txt index 38993b427d2c..c2c02f80c90e 100644 --- a/src/AutoPilotPlugins/Common/CMakeLists.txt +++ b/src/AutoPilotPlugins/Common/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Common AutoPilot Plugins +# Components shared across different autopilot types +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE ESP8266Component.cc @@ -16,6 +21,9 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# Common AutoPilot Plugins QML Module +# ---------------------------------------------------------------------------- qt_add_library(AutoPilotPluginsCommonModule STATIC) qt_add_qml_module(AutoPilotPluginsCommonModule diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc index 76de21bb4ccc..9e3e1c01b5c3 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc @@ -14,7 +14,7 @@ #include -QGC_LOGGING_CATEGORY(ESP8266ComponentControllerLog, "qgc.autopilotplugins.common.esp8266componentcontroller") +QGC_LOGGING_CATEGORY(ESP8266ComponentControllerLog, "AutoPilotPlugins.ESP8266ComponentController") #define MAX_RETRIES 5 diff --git a/src/AutoPilotPlugins/Common/Images/FreeFlyer.svg b/src/AutoPilotPlugins/Common/Images/FreeFlyer.svg new file mode 100644 index 000000000000..aba991c424ef --- /dev/null +++ b/src/AutoPilotPlugins/Common/Images/FreeFlyer.svg @@ -0,0 +1,197 @@ + + + +image/svg+xmlQuadRotorXQuadRotorX + 12345678 diff --git a/src/AutoPilotPlugins/Common/MotorComponent.qml b/src/AutoPilotPlugins/Common/MotorComponent.qml index e1c5b5be7a41..fe382c9a840c 100644 --- a/src/AutoPilotPlugins/Common/MotorComponent.qml +++ b/src/AutoPilotPlugins/Common/MotorComponent.qml @@ -56,7 +56,7 @@ SetupPage { id: motorSlider enabled: safetySwitch.checked spacing: ScreenTools.defaultFontPixelWidth * 4 - + ValueSlider { id: sliderThrottle width: motorButtons.width diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.cc b/src/AutoPilotPlugins/Common/RadioComponentController.cc index dbf6d7de3aa0..8d8497f7fd9c 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.cc +++ b/src/AutoPilotPlugins/Common/RadioComponentController.cc @@ -16,8 +16,8 @@ #include -QGC_LOGGING_CATEGORY(RadioComponentControllerLog, "qgc.autopilotplugins.common.radiocomponentcontroller") -QGC_LOGGING_CATEGORY(RadioComponentControllerVerboseLog, "qgc.autopilotplugins.common.radiocomponentcontroller:verbose") +QGC_LOGGING_CATEGORY(RadioComponentControllerLog, "AutoPilotPlugins.RadioComponentController") +QGC_LOGGING_CATEGORY(RadioComponentControllerVerboseLog, "AutoPilotPlugins.RadioComponentController:verbose") #ifdef QGC_UNITTEST_BUILD RadioComponentController* RadioComponentController::_unitTestController = nullptr; diff --git a/src/AutoPilotPlugins/Common/SyslinkComponentController.cc b/src/AutoPilotPlugins/Common/SyslinkComponentController.cc index 0ea2e15edfe4..b4927d7c1e28 100644 --- a/src/AutoPilotPlugins/Common/SyslinkComponentController.cc +++ b/src/AutoPilotPlugins/Common/SyslinkComponentController.cc @@ -11,7 +11,7 @@ #include "Vehicle.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(SyslinkComponentControllerLog, "qgc.autopilotplugins.common.syslinkcomponentcontroller") +QGC_LOGGING_CATEGORY(SyslinkComponentControllerLog, "AutoPilotPlugins.SyslinkComponentController") SyslinkComponentController::SyslinkComponentController(QObject *parent) : FactPanelController(parent) diff --git a/src/AutoPilotPlugins/PX4/ActuatorComponent.cc b/src/AutoPilotPlugins/PX4/ActuatorComponent.cc index d2ccc780a375..533f729b885c 100644 --- a/src/AutoPilotPlugins/PX4/ActuatorComponent.cc +++ b/src/AutoPilotPlugins/PX4/ActuatorComponent.cc @@ -16,7 +16,7 @@ static bool imageProviderAdded{false}; -ActuatorComponent::ActuatorComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) +ActuatorComponent::ActuatorComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) : VehicleComponent(vehicle, autopilot, AutoPilotPlugin::UnknownVehicleComponent, parent) , _name(tr("Actuators")) , _actuators(*vehicle->actuators()) diff --git a/src/AutoPilotPlugins/PX4/ActuatorComponent.h b/src/AutoPilotPlugins/PX4/ActuatorComponent.h index bf4b35b2c6c7..e3ad0dec593f 100644 --- a/src/AutoPilotPlugins/PX4/ActuatorComponent.h +++ b/src/AutoPilotPlugins/PX4/ActuatorComponent.h @@ -17,13 +17,13 @@ class Actuators; class ActuatorComponent : public VehicleComponent { Q_OBJECT - + public: ActuatorComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); // Virtuals from VehicleComponent QStringList setupCompleteChangedTriggerList(void) const final; - + // Virtuals from VehicleComponent QString name(void) const final; QString description(void) const final; diff --git a/src/AutoPilotPlugins/PX4/ActuatorFact.qml b/src/AutoPilotPlugins/PX4/ActuatorFact.qml index 910bb58a7901..eecae1c8ae8d 100644 --- a/src/AutoPilotPlugins/PX4/ActuatorFact.qml +++ b/src/AutoPilotPlugins/PX4/ActuatorFact.qml @@ -46,9 +46,8 @@ Loader { text: qsTr("(Param not available)") } } - sourceComponent: fact ? - (fact.enumStrings.length > 0 ? factComboBox : + sourceComponent: fact ? + (fact.enumStrings.length > 0 ? factComboBox : (fact.readOnly ? factReadOnly : (fact.typeIsBool ? factCheckbox : factTextField)) ) : notAvailable } - diff --git a/src/AutoPilotPlugins/PX4/AirframeComponent.h b/src/AutoPilotPlugins/PX4/AirframeComponent.h index 122eb8182c86..e2853b9889a5 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponent.h +++ b/src/AutoPilotPlugins/PX4/AirframeComponent.h @@ -19,10 +19,10 @@ class AirframeComponent : public VehicleComponent { Q_OBJECT - + public: AirframeComponent(Vehicle* vehicles, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Virtuals from VehicleComponent virtual QStringList setupCompleteChangedTriggerList(void) const; @@ -33,7 +33,7 @@ class AirframeComponent : public VehicleComponent virtual bool requiresSetup(void) const; virtual bool setupComplete(void) const; virtual QUrl setupSource(void) const; - virtual QUrl summaryQmlSource(void) const; + virtual QUrl summaryQmlSource(void) const; private: const QString _name; diff --git a/src/AutoPilotPlugins/PX4/AirframeComponent.qml b/src/AutoPilotPlugins/PX4/AirframeComponent.qml index 3e0a107b66a9..4390eadc4a71 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponent.qml +++ b/src/AutoPilotPlugins/PX4/AirframeComponent.qml @@ -109,7 +109,7 @@ SetupPage { text: (controller.currentVehicleName != "" ? qsTr("You've connected a %1.").arg(controller.currentVehicleName) : qsTr("Airframe is not set.")) + - qsTr("To change this configuration, select the desired airframe below then click 'Apply and Restart'.") + qsTr(" To change this configuration, select the desired airframe below then click 'Apply and Restart'.") font.bold: true wrapMode: Text.WordWrap } diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h b/src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h index 17748b623fa5..10a7fb5da4df 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h +++ b/src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h @@ -24,7 +24,7 @@ class AirframeComponentAirframes QString name; int autostartId; } AirframeInfo_t; - + typedef struct { QString name; QString imageResource; @@ -34,9 +34,9 @@ class AirframeComponentAirframes static QMap& get(); static void clear(); static void insert(QString& group, QString& image, QString& name, int id); - + protected: static QMap rgAirframeTypes; - + private: }; diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index 0fb2fe8d09c0..9b1527f47fa5 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -30,19 +30,19 @@ AirframeComponentController::AirframeComponentController(void) : if (!_typesRegistered) { _typesRegistered = true; } - + QStringList usedParams; usedParams << "SYS_AUTOSTART" << "SYS_AUTOCONFIG"; if (!_allParametersExists(ParameterManager::defaultComponentId, usedParams)) { return; } - + // Load up member variables - + bool autostartFound = false; _autostartId = getParameterFact(ParameterManager::defaultComponentId, "SYS_AUTOSTART")->rawValue().toInt(); + _currentVehicleName = QString::number(_autostartId); // Temp val. Replaced with actual vehicle name if found - for (int tindex = 0; tindex < AirframeComponentAirframes::get().count(); tindex++) { const AirframeComponentAirframes::AirframeType_t* pType = AirframeComponentAirframes::get().values().at(tindex); @@ -65,10 +65,10 @@ AirframeComponentController::AirframeComponentController(void) : } airframeType->addAirframe(pInfo->name, pInfo->autostartId); } - + _airframeTypes.append(QVariant::fromValue(airframeType)); } - + if (_autostartId != 0 && !autostartFound) { _showCustomConfigPanel = true; emit showCustomConfigPanelChanged(true); @@ -88,15 +88,15 @@ void AirframeComponentController::changeAutostart(void) } QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - + Fact* sysAutoStartFact = getParameterFact(-1, "SYS_AUTOSTART"); Fact* sysAutoConfigFact = getParameterFact(-1, "SYS_AUTOCONFIG"); - + // We need to wait for the vehicleUpdated signals to come back before we reboot _waitParamWriteSignalCount = 0; connect(sysAutoStartFact, &Fact::vehicleUpdated, this, &AirframeComponentController::_waitParamWriteSignal); connect(sysAutoConfigFact, &Fact::vehicleUpdated, this, &AirframeComponentController::_waitParamWriteSignal); - + // We use forceSetValue to ensure params are sent even if the previous value is that same as the new value sysAutoStartFact->forceSetRawValue(_autostartId); sysAutoConfigFact->forceSetRawValue(1); @@ -105,7 +105,7 @@ void AirframeComponentController::changeAutostart(void) void AirframeComponentController::_waitParamWriteSignal(QVariant value) { Q_UNUSED(value); - + _waitParamWriteSignalCount++; if (_waitParamWriteSignalCount == 2) { // Now that both params have made it to the vehicle we can reboot it. All these signals are flying @@ -116,7 +116,7 @@ void AirframeComponentController::_waitParamWriteSignal(QVariant value) } void AirframeComponentController::_rebootAfterStackUnwind(void) -{ +{ _vehicle->sendMavCommand(_vehicle->defaultComponentId(), MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, true /* showError */, 1.0f); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); for (unsigned i = 0; i < 2000; i++) { @@ -132,19 +132,19 @@ AirframeType::AirframeType(const QString& name, const QString& imageResource, QO _name(name), _imageResource(imageResource) { - + } AirframeType::~AirframeType() { - + } void AirframeType::addAirframe(const QString& name, int autostartId) { Airframe* airframe = new Airframe(name, autostartId); Q_CHECK_PTR(airframe); - + _airframes.append(QVariant::fromValue(airframe)); } @@ -153,10 +153,10 @@ Airframe::Airframe(const QString& name, int autostartId, QObject* parent) : _name(name), _autostartId(autostartId) { - + } Airframe::~Airframe() { - + } diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.h b/src/AutoPilotPlugins/PX4/AirframeComponentController.h index 54f81f5d6c1a..f2ac08b45733 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.h +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.h @@ -24,37 +24,37 @@ class AirframeComponentController : public FactPanelController { Q_OBJECT QML_ELEMENT - + public: AirframeComponentController(void); ~AirframeComponentController(); - + Q_PROPERTY(bool showCustomConfigPanel MEMBER _showCustomConfigPanel NOTIFY showCustomConfigPanelChanged) - + Q_PROPERTY(QVariantList airframeTypes MEMBER _airframeTypes CONSTANT) - + Q_PROPERTY(QString currentAirframeType MEMBER _currentAirframeType CONSTANT) Q_PROPERTY(QString currentVehicleName MEMBER _currentVehicleName CONSTANT) Q_PROPERTY(int currentVehicleIndex MEMBER _currentVehicleIndex CONSTANT) - + Q_PROPERTY(int autostartId MEMBER _autostartId NOTIFY autostartIdChanged) - + Q_INVOKABLE void changeAutostart(void); - + int currentAirframeIndex(void); void setCurrentAirframeIndex(int newIndex); - + signals: void autostartIdChanged(int newAutostartId); void showCustomConfigPanelChanged(bool show); - + private slots: void _waitParamWriteSignal(QVariant value); void _rebootAfterStackUnwind(void); - + private: static bool _typesRegistered; - + QVariantList _airframeTypes; QString _currentAirframeType; QString _currentVehicleName; @@ -69,14 +69,14 @@ class Airframe : public QObject Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") - + public: Airframe(const QString& name, int autostartId, QObject* parent = nullptr); ~Airframe(); - + Q_PROPERTY(QString text MEMBER _name CONSTANT) Q_PROPERTY(int autostartId MEMBER _autostartId CONSTANT) - + private: QString _name; int _autostartId; @@ -87,17 +87,17 @@ class AirframeType : public QObject Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") - + public: AirframeType(const QString& name, const QString& imageResource, QObject* parent = nullptr); ~AirframeType(); - + Q_PROPERTY(QString name MEMBER _name CONSTANT) Q_PROPERTY(QString imageResource MEMBER _imageResource CONSTANT) Q_PROPERTY(QVariantList airframes MEMBER _airframes CONSTANT) - + void addAirframe(const QString& name, int autostartId); - + private: QString _name; QString _imageResource; diff --git a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml index 5a9dad533369..d91b37a0046e 100644 --- a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml +++ b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml @@ -372,4 +372,10 @@ VTOL Tiltrotor + + + Spacecraft + Free-Flyer + + diff --git a/src/AutoPilotPlugins/PX4/CMakeLists.txt b/src/AutoPilotPlugins/PX4/CMakeLists.txt index 0f4afdd3fe8a..3b3093257cb2 100644 --- a/src/AutoPilotPlugins/PX4/CMakeLists.txt +++ b/src/AutoPilotPlugins/PX4/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# PX4 AutoPilot Plugin +# PX4-specific vehicle configuration and setup +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE ActuatorComponent.cc @@ -34,7 +39,9 @@ target_sources(${CMAKE_PROJECT_NAME} SensorsComponentController.h ) -# Add metadata file +# ---------------------------------------------------------------------------- +# PX4 Airframe Metadata +# ---------------------------------------------------------------------------- qt_add_resources(${CMAKE_PROJECT_NAME} autopilot_plugin_px4_resource PREFIX "/AutoPilotPlugins/PX4" FILES AirframeFactMetaData.xml @@ -42,6 +49,9 @@ qt_add_resources(${CMAKE_PROJECT_NAME} autopilot_plugin_px4_resource target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# PX4 QML Module +# ---------------------------------------------------------------------------- qt_add_library(AutoPilotPluginsPX4Module STATIC) qt_add_qml_module(AutoPilotPluginsPX4Module @@ -74,6 +84,12 @@ qt_add_qml_module(AutoPilotPluginsPX4Module PX4TuningComponentPlaneAttitude.qml PX4TuningComponentPlaneRate.qml PX4TuningComponentPlaneTECS.qml + PX4TuningComponentSpacecraft.qml + PX4TuningComponentSpacecraftAll.qml + PX4TuningComponentSpacecraftAttitude.qml + PX4TuningComponentSpacecraftPosition.qml + PX4TuningComponentSpacecraftRate.qml + PX4TuningComponentSpacecraftVelocity.qml PX4TuningComponentVTOL.qml SafetyComponent.qml SafetyComponentSummary.qml diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc index ac7b252cb461..dc7f1dbb3b93 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc +++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc @@ -20,7 +20,7 @@ struct SwitchListItem { const char* name; }; -FlightModesComponent::FlightModesComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) +FlightModesComponent::FlightModesComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) : VehicleComponent(vehicle, autopilot, AutoPilotPlugin::KnownFlightModesVehicleComponent, parent) , _name(tr("Flight Modes")) { diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.h b/src/AutoPilotPlugins/PX4/FlightModesComponent.h index 2cd98d043a27..db306efeb3f4 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponent.h +++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.h @@ -19,10 +19,10 @@ class FlightModesComponent : public VehicleComponent { Q_OBJECT - + public: FlightModesComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Overrides from VehicleComponent QString name(void) const final; QString description(void) const final; @@ -32,7 +32,7 @@ class FlightModesComponent : public VehicleComponent bool requiresSetup() const final { return false; } bool setupComplete() const final { return true; } QStringList setupCompleteChangedTriggerList() const final { return QStringList(); } - + private: const QString _name; QVariantList _summaryItems; diff --git a/src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc b/src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc index 05a495968a7b..472911d6ad2a 100644 --- a/src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc +++ b/src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc @@ -24,7 +24,7 @@ #include #include -QGC_LOGGING_CATEGORY(PX4AirframeLoaderLog, "PX4AirframeLoaderLog") +QGC_LOGGING_CATEGORY(PX4AirframeLoaderLog, "AutoPilotPlugins.PX4AirframeLoader") bool PX4AirframeLoader::_airframeMetaDataLoaded = false; diff --git a/src/AutoPilotPlugins/PX4/PX4FlightBehavior.h b/src/AutoPilotPlugins/PX4/PX4FlightBehavior.h index 264f7fb25b7a..cdc1d8332dd0 100644 --- a/src/AutoPilotPlugins/PX4/PX4FlightBehavior.h +++ b/src/AutoPilotPlugins/PX4/PX4FlightBehavior.h @@ -15,13 +15,13 @@ class PX4FlightBehavior : public VehicleComponent { Q_OBJECT - + public: PX4FlightBehavior(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Virtuals from VehicleComponent QStringList setupCompleteChangedTriggerList() const final; - + // Virtuals from VehicleComponent QString name() const final; QString description() const final; diff --git a/src/AutoPilotPlugins/PX4/PX4FlightBehaviorCopter.qml b/src/AutoPilotPlugins/PX4/PX4FlightBehaviorCopter.qml index 8c51f801aba7..9d0d4107fcc8 100644 --- a/src/AutoPilotPlugins/PX4/PX4FlightBehaviorCopter.qml +++ b/src/AutoPilotPlugins/PX4/PX4FlightBehaviorCopter.qml @@ -20,14 +20,15 @@ import QGroundControl.Controls SetupPage { id: flightBehavior pageComponent: pageComponent + property real _margins: ScreenTools.defaultFontPixelHeight FactPanelController { - id: controller + id: controller } QGCPalette { - id: qgcPal + id: qgcPal } property Fact _sys_vehicle_resp: controller.getParameterFact(-1, "SYS_VEHICLE_RESP", false) @@ -37,135 +38,153 @@ SetupPage { Component { id: pageComponent - Column { - - spacing: _margins - - Column { - visible: _sys_vehicle_resp + ColumnLayout { + width: Math.max(availableWidth, ScreenTools.defaultFontPixelWidth * 40) + spacing: _margins + + ColumnLayout { + Layout.fillWidth: true + visible: _sys_vehicle_resp + + SettingsGroupLayout { + Layout.fillWidth: true + enabled: responsivenessCheckbox.checked + heading: qsTr("Responsiveness") + headingDescription: qsTr("A higher value makes the vehicle react faster. Be aware that this affects braking as well, and a combination of slow responsiveness with high maximum velocity will lead to long braking distances.") + + // It's a bit tricky to handle the fact that the parameter goes negative to signal disabled. + // We can't allow the slider to go negative, hence all the hoops to jump through with value and loadComplete. + ValueSlider { + Layout.fillWidth: true + from: 0.0 + to: 1 + majorTickStepSize: 0.01 + value: { value = _sys_vehicle_resp ? Math.abs(_sys_vehicle_resp.value) : (from + to) / 2 } + decimalPlaces: _sys_vehicle_resp ? _sys_vehicle_resp.decimalPlaces : 0 + + property bool loadComplete: false + + Component.onCompleted: loadComplete = true + + onValueChanged: { + if (loadComplete && enabled) { + _sys_vehicle_resp.value = value + } + } + } + } QGCCheckBox { id: responsivenessCheckbox + Layout.fillWidth: true text: qsTr("Enable responsiveness slider (if enabled, acceleration limit parameters and others are automatically set)") checked: _sys_vehicle_resp && _sys_vehicle_resp.value >= 0 - onClicked: { - if (checked) { - _sys_vehicle_resp.value = Math.abs(_sys_vehicle_resp.value) - } else { - _sys_vehicle_resp.value = -Math.abs(_sys_vehicle_resp.value) - } - } + onClicked: _sys_vehicle_resp.value = checked ? Math.abs(_sys_vehicle_resp.value) : -Math.abs(_sys_vehicle_resp.value) } - FactSliderPanel { - width: availableWidth - enabled: responsivenessCheckbox.checked - - sliderModel: ListModel { - id: responsivenessSlider - - ListElement { - title: qsTr("Responsiveness") - description: qsTr("A higher value makes the vehicle react faster. Be aware that this affects braking as well, and a combination of slow responsiveness with high maximum velocity will lead to long braking distances.") - param: "SYS_VEHICLE_RESP" - min: 0.01 - max: 1 - step: 0.01 - } - } - } QGCLabel { + Layout.fillWidth: true visible: _sys_vehicle_resp && _sys_vehicle_resp.value > 0.8 color: qgcPal.warningText - text: qsTr("Warning: a high responsiveness requires a vehicle with large thrust-to-weight ratio. The vehicle might lose altitude otherwise.") + text: qsTr("Warning: a high responsiveness requires a vehicle with large thrust-to-weight ratio. The vehicle might lose altitude otherwise.") } } - Column { - visible: _mpc_xy_vel_all + ColumnLayout { + Layout.fillWidth: true + visible: _mpc_xy_vel_all + + SettingsGroupLayout { + Layout.fillWidth: true + enabled: xyVelCheckbox.checked + heading: qsTr("Horizontal velocity (m/s)") + headingDescription: qsTr("Limit the horizonal velocity (applies to all modes).") + + // It's a bit tricky to handle the fact that the parameter goes negative to signal disabled. + // We can't allow the slider to go negative, hence all the hoops to jump through with value and loadComplete. + ValueSlider { + Layout.fillWidth: true + from: 0.5 + to: 20 + majorTickStepSize: 0.6 + value: { value = _mpc_xy_vel_all ? Math.abs(_mpc_xy_vel_all.value) : (from + to) / 2 } + decimalPlaces: _mpc_xy_vel_all ? _mpc_xy_vel_all.decimalPlaces : 0 + + property bool loadComplete: false + + Component.onCompleted: loadComplete = true + + onValueChanged: { + if (loadComplete && enabled) { + _mpc_xy_vel_all.value = value + } + } + } + } QGCCheckBox { id: xyVelCheckbox + Layout.fillWidth: true text: qsTr("Enable horizontal velocity slider (if enabled, individual velocity limit parameters are automatically set)") checked: _mpc_xy_vel_all ? (_mpc_xy_vel_all.value >= 0) : false - onClicked: { - if (checked) { - _mpc_xy_vel_all.value = Math.abs(_mpc_xy_vel_all.value) - } else { - _mpc_xy_vel_all.value = -Math.abs(_mpc_xy_vel_all.value) - } - } + onClicked: mpc_xy_vel_all.value = checked ? Math.abs(_mpc_xy_vel_all.value) : -Math.abs(_mpc_xy_vel_all.value) } + } - FactSliderPanel { - width: availableWidth - enabled: xyVelCheckbox.checked - - sliderModel: ListModel { - id: xyVelSlider - - ListElement { - title: qsTr("Horizontal velocity (m/s)") - description: qsTr("Limit the horizonal velocity (applies to all modes).") - param: "MPC_XY_VEL_ALL" - min: 0.5 - max: 20 - step: 0.5 + ColumnLayout { + Layout.fillWidth: true + visible: _mpc_z_vel_all + + SettingsGroupLayout { + Layout.fillWidth: true + enabled: zVelCheckbox.checked + heading: qsTr("Vertical velocity (m/s)") + headingDescription: qsTr("Limit the vertical velocity (applies to all modes).") + + // It's a bit tricky to handle the fact that the parameter goes negative to signal disabled. + // We can't allow the slider to go negative, hence all the hoops to jump through with value and loadComplete. + ValueSlider { + Layout.fillWidth: true + from: 0.2 + to: 8 + majorTickStepSize: 0.2 + value: { value = _mpc_z_vel_all ? Math.abs(_mpc_z_vel_all.value) : (from + to) / 2 } + decimalPlaces: _mpc_z_vel_all ? _mpc_z_vel_all.decimalPlaces : 0 + + property bool loadComplete: false + + Component.onCompleted: loadComplete = true + + onValueChanged: { + if (loadComplete && enabled) { + _mpc_z_vel_all.value = value + } } } } - } - - Column { - visible: _mpc_z_vel_all QGCCheckBox { id: zVelCheckbox + Layout.fillWidth: true text: qsTr("Enable vertical velocity slider (if enabled, individual velocity limit parameters are automatically set)") checked: _mpc_z_vel_all && _mpc_z_vel_all.value >= 0 - onClicked: { - if (checked) { - _mpc_z_vel_all.value = Math.abs(_mpc_z_vel_all.value) - } else { - _mpc_z_vel_all.value = -Math.abs(_mpc_z_vel_all.value) - } - } - } - - FactSliderPanel { - width: availableWidth - enabled: zVelCheckbox.checked - - sliderModel: ListModel { - id: zVelSlider - - ListElement { - title: qsTr("Vertical velocity (m/s)") - description: qsTr("Limit the vertical velocity (applies to all modes).") - param: "MPC_Z_VEL_ALL" - min: 0.2 - max: 8 - step: 0.2 - } - } + onClicked: mpc_z_vel_all.value = checked ? Math.abs(_mpc_z_vel_all.value) : -Math.abs(_mpc_z_vel_all.value) } } - FactSliderPanel { - width: availableWidth - - sliderModel: ListModel { - ListElement { - title: qsTr("Mission Turning Radius") - description: qsTr("Increasing this leads to rounder turns in missions (corner cutting). Use the minimum value for accurate corner tracking.") - param: "NAV_ACC_RAD" - min: 2 - max: 16 - step: 0.5 - } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Mission Turning Radius") + headingDescription: qsTr("Increasing this leads to rounder turns in missions (corner cutting). Use the minimum value for accurate corner tracking.") + + FactSlider { + Layout.fillWidth: true + from: QGroundControl.unitsConversion.metersToAppSettingsHorizontalDistanceUnits(2) + to: QGroundControl.unitsConversion.metersToAppSettingsHorizontalDistanceUnits(16) + majorTickStepSize: 0.5 + fact: controller.getParameterFact(-1, "NAV_ACC_RAD") } } - - } // Column - } // Component - pageComponent -} // SetupPage + } + } +} diff --git a/src/AutoPilotPlugins/PX4/PX4RadioComponent.h b/src/AutoPilotPlugins/PX4/PX4RadioComponent.h index e7d69e55c5df..ae15a7285c9b 100644 --- a/src/AutoPilotPlugins/PX4/PX4RadioComponent.h +++ b/src/AutoPilotPlugins/PX4/PX4RadioComponent.h @@ -15,13 +15,13 @@ class PX4RadioComponent : public VehicleComponent { Q_OBJECT - + public: PX4RadioComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Virtuals from VehicleComponent virtual QStringList setupCompleteChangedTriggerList(void) const; - + // Virtuals from VehicleComponent virtual QString name(void) const; virtual QString description(void) const; @@ -30,7 +30,7 @@ class PX4RadioComponent : public VehicleComponent virtual bool setupComplete(void) const; virtual QUrl setupSource(void) const; virtual QUrl summaryQmlSource(void) const; - + private: const QString _name; QVariantList _summaryItems; diff --git a/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h b/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h index 27a748d3e677..586be6139c5c 100644 --- a/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h +++ b/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h @@ -23,7 +23,7 @@ class PX4SimpleFlightModesController : public FactPanelController QML_ELEMENT public: PX4SimpleFlightModesController(void); - + Q_PROPERTY(int activeFlightMode READ activeFlightMode NOTIFY activeFlightModeChanged) Q_PROPERTY(int channelCount MEMBER _channelCount CONSTANT) Q_PROPERTY(QVariantList rcChannelValues MEMBER _rcChannelValues NOTIFY rcChannelValuesChanged) @@ -34,10 +34,10 @@ class PX4SimpleFlightModesController : public FactPanelController void activeFlightModeChanged(int activeFlightMode); void channelOptionEnabledChanged(void); void rcChannelValuesChanged(void); - + private slots: void _rcChannelsChanged(int channelCount, int pwmValues[QGCMAVLink::maxRcChannels]); - + private: int _activeFlightMode; int _channelCount; diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponent.cc b/src/AutoPilotPlugins/PX4/PX4TuningComponent.cc index ca0842df36c3..b3ebed2f213c 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponent.cc +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponent.cc @@ -73,6 +73,10 @@ QUrl PX4TuningComponent::setupSource(void) const case MAV_TYPE_VTOL_RESERVED5: qmlFile = "qrc:/qml/QGroundControl/AutoPilotPlugins/PX4/PX4TuningComponentVTOL.qml"; break; + case MAV_TYPE_SPACECRAFT_ORBITER: + // Spacecraft Type + qmlFile = "qrc:/qml/QGroundControl/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraft.qml"; + break; default: break; } diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponent.h b/src/AutoPilotPlugins/PX4/PX4TuningComponent.h index e620ea8c7d6a..d5a2cefd603f 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponent.h +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponent.h @@ -15,13 +15,13 @@ class PX4TuningComponent : public VehicleComponent { Q_OBJECT - + public: PX4TuningComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Virtuals from VehicleComponent QStringList setupCompleteChangedTriggerList(void) const final; - + // Virtuals from VehicleComponent QString name(void) const final; QString description(void) const final; diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAll.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAll.qml index c296d084b654..096e82937553 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAll.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAll.qml @@ -18,19 +18,19 @@ import QGroundControl.Controls PX4TuningComponent { model: ListModel { - ListElement { + ListElement { buttonText: qsTr("Rate Controller") tuningPage: "PX4TuningComponentCopterRate.qml" } - ListElement { + ListElement { buttonText: qsTr("Attitude Controller") tuningPage: "PX4TuningComponentCopterAttitude.qml" } - ListElement { + ListElement { buttonText: qsTr("Velocity Controller") tuningPage: "PX4TuningComponentCopterVelocity.qml" } - ListElement { + ListElement { buttonText: qsTr("Position Controller") tuningPage: "PX4TuningComponentCopterPosition.qml" } diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAttitude.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAttitude.qml index 933fc08fe03d..0635096b581f 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAttitude.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterAttitude.qml @@ -86,4 +86,3 @@ ColumnLayout { showAutoTuning: true } } - diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterPosition.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterPosition.qml index 7d724414d7ba..ed00610a0818 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterPosition.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterPosition.qml @@ -84,5 +84,3 @@ ColumnLayout { chartDisplaySec: 50 } } - - diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterRate.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterRate.qml index 1e1d83d6b63c..df2868960a10 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterRate.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterRate.qml @@ -161,4 +161,3 @@ ColumnLayout { } } } - diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterVelocity.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterVelocity.qml index 01bc8fe01f9a..8c31652cc868 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterVelocity.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentCopterVelocity.qml @@ -115,5 +115,3 @@ ColumnLayout { axis: [ horizontal, vertical ] } } - - diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneAll.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneAll.qml index 4e224b381c8e..5c853ac98637 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneAll.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneAll.qml @@ -18,13 +18,13 @@ import QGroundControl.Controls PX4TuningComponent { model: ListModel { - ListElement { + ListElement { buttonText: qsTr("Rate Controller") - tuningPage: "PX4TuningComponentPlaneRate.qml" + tuningPage: "PX4TuningComponentPlaneRate.qml" } - ListElement { + ListElement { buttonText: qsTr("Rate Controller") - tuningPage: "PX4TuningComponentPlaneAttitude.qml" + tuningPage: "PX4TuningComponentPlaneAttitude.qml" } } } diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneRate.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneRate.qml index a04d3606a8d2..3781ff517561 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneRate.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneRate.qml @@ -167,4 +167,3 @@ ColumnLayout { showAutoTuning: true } } - diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneTECS.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneTECS.qml index 8cb6fc770cd8..4c020f980f08 100644 --- a/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneTECS.qml +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentPlaneTECS.qml @@ -53,6 +53,3 @@ ColumnLayout { axis: [ data ] } } - - - diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraft.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraft.qml new file mode 100644 index 000000000000..79da78d045b9 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraft.qml @@ -0,0 +1,27 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +SetupPage { + id: tuningPage + pageComponent: pageComponent + + Component { + id: pageComponent + + PX4TuningComponentSpacecraftAll { + } + } // Component - pageComponent +} // SetupPage diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftAll.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftAll.qml new file mode 100644 index 000000000000..2b18c741663b --- /dev/null +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftAll.qml @@ -0,0 +1,36 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +PX4TuningComponent { + model: ListModel { + ListElement { + buttonText: qsTr("Rate Controller") + tuningPage: "PX4TuningComponentSpacecraftRate.qml" + } + ListElement { + buttonText: qsTr("Attitude Controller") + tuningPage: "PX4TuningComponentSpacecraftAttitude.qml" + } + ListElement { + buttonText: qsTr("Velocity Controller") + tuningPage: "PX4TuningComponentSpacecraftVelocity.qml" + } + ListElement { + buttonText: qsTr("Position Controller") + tuningPage: "PX4TuningComponentSpacecraftPosition.qml" + } + } +} diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftAttitude.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftAttitude.qml new file mode 100644 index 000000000000..e7bdbac6b370 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftAttitude.qml @@ -0,0 +1,86 @@ +/**************************************************************************** + * + * (c) 2021 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +import QGroundControl.FactControls + +ColumnLayout { + property real _availableHeight: availableHeight + property real _availableWidth: availableWidth + + PIDTuning { + id: pidTuning + availableWidth: _availableWidth + availableHeight: _availableHeight - pidTuning.y + + property var roll: QtObject { + property string name: qsTr("Roll") + property var plot: [ + { name: "Response", value: globals.activeVehicle.roll.value }, + { name: "Setpoint", value: globals.activeVehicle.setpoint.roll.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Proportional Gain (SC_ROLL_P)") + description: qsTr("Increase for more responsiveness, reduce if the attitude overshoots.") + param: "SC_ROLL_P" + min: 1 + max: 14 + step: 0.5 + } + } + } + property var pitch: QtObject { + property string name: qsTr("Pitch") + property var plot: [ + { name: "Response", value: globals.activeVehicle.pitch.value }, + { name: "Setpoint", value: globals.activeVehicle.setpoint.pitch.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Proportional Gain (SC_PITCH_P)") + description: qsTr("Increase for more responsiveness, reduce if the attitude overshoots.") + param: "SC_PITCH_P" + min: 1 + max: 14 + step: 0.5 + } + } + } + property var yaw: QtObject { + property string name: qsTr("Yaw") + property var plot: [ + { name: "Response", value: globals.activeVehicle.heading.value }, + { name: "Setpoint", value: globals.activeVehicle.setpoint.yaw.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Proportional Gain (SC_YAW_P)") + description: qsTr("Increase for more responsiveness, reduce if the attitude overshoots (there is only a setpoint when yaw is fixed, i.e. when centering the stick).") + param: "SC_YAW_P" + min: 1 + max: 5 + step: 0.1 + } + } + } + title: "Attitude" + tuningMode: Vehicle.ModeRateAndAttitude + unit: "deg" + axis: [ roll, pitch, yaw ] + showAutoModeChange: true + showAutoTuning: false + } +} diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftPosition.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftPosition.qml new file mode 100644 index 000000000000..20276a3e0bd4 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftPosition.qml @@ -0,0 +1,76 @@ +/**************************************************************************** + * + * (c) 2021 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +import QGroundControl.FactControls + +ColumnLayout { + property real _availableHeight: availableHeight + property real _availableWidth: availableWidth + + PIDTuning { + id: pidTuning + availableWidth: _availableWidth + availableHeight: _availableHeight - pidTuning.y + + property var position: QtObject { + property string name: qsTr("Position") + property string plotTitle: qsTr("Position (X direction)") + property var plot: [ + { name: "Response", value: globals.activeVehicle.localPosition.x.value }, + { name: "Setpoint", value: globals.activeVehicle.localPositionSetpoint.x.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Proportional gain (SPC_POS_P)") + description: qsTr("Increase for more responsiveness, reduce if the position overshoots.") + param: "SPC_POS_P" + min: 0 + max: 20 + step: 0.05 + } + ListElement { + title: qsTr("Integral gain (SPC_POS_I)") + description: qsTr("Increase for faster convergence to zero steady-state error.") + param: "SPC_POS_I" + min: 0 + max: 2 + step: 0.001 + } + ListElement { + title: qsTr("Integral gain limit (SPC_POS_I_LIM)") + description: qsTr("Anti-windup limit for the position controller integral component.") + param: "SPC_POS_I_LIM" + min: 0 + max: 2 + step: 0.001 + } + ListElement { + title: qsTr("Position Error Limit (SPC_VEL_MAX)") + description: qsTr("Increase for more responsiveness, reduce if the position overshoots.") + param: "SPC_VEL_MAX" + min: 0 + max: 20 + step: 0.05 + } + } + } + title: "Position" + tuningMode: Vehicle.ModeVelocityAndPosition + unit: "m" + axis: [ position ] + chartDisplaySec: 50 + } +} diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftRate.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftRate.qml new file mode 100644 index 000000000000..12dd4587748e --- /dev/null +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftRate.qml @@ -0,0 +1,145 @@ +/**************************************************************************** + * + * (c) 2021 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +import QGroundControl.FactControls + +ColumnLayout { + property real _availableHeight: availableHeight + property real _availableWidth: availableWidth + property Fact _airmode: controller.getParameterFact(-1, "MC_AIRMODE", false) + property Fact _thrustModelFactor: controller.getParameterFact(-1, "THR_MDL_FAC", false) + + PIDTuning { + id: pidTuning + availableWidth: _availableWidth + availableHeight: _availableHeight - pidTuning.y + title: qsTr("Rate") + tuningMode: Vehicle.ModeRateAndAttitude + unit: qsTr("deg/s") + axis: [ roll, pitch, yaw ] + chartDisplaySec: 3 + showAutoModeChange: false + showAutoTuning: false + + property var roll: QtObject { + property string name: qsTr("Roll") + property var plot: [ + { name: "Response", value: globals.activeVehicle.rollRate.value }, + { name: "Setpoint", value: globals.activeVehicle.setpoint.rollRate.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Overall Multiplier (SC_ROLLRATE_K)") + description: qsTr("Multiplier for P, I and D gains: increase for more responsiveness, reduce if the rates overshoot (and increasing D does not help).") + param: "SC_ROLLRATE_K" + min: 0.0 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Differential Gain (SC_ROLLRATE_D)") + description: qsTr("Damping: increase to reduce overshoots and oscillations, but not higher than really needed.") + param: "SC_ROLLRATE_D" + min: 0.0000 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Integral Gain (SC_ROLLRATE_I)") + description: qsTr("Generally does not need much adjustment, reduce this when seeing slow oscillations.") + param: "SC_ROLLRATE_I" + min: 0.0 + max: 10.0 + step: 0.005 + } + } + } + property var pitch: QtObject { + property string name: qsTr("Pitch") + property var plot: [ + { name: "Response", value: globals.activeVehicle.pitchRate.value }, + { name: "Setpoint", value: globals.activeVehicle.setpoint.pitchRate.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Overall Multiplier (SC_PITCHRATE_K)") + description: qsTr("Multiplier for P, I and D gains: increase for more responsiveness, reduce if the rates overshoot (and increasing D does not help).") + param: "SC_PITCHRATE_K" + min: 0.0 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Differential Gain (SC_PITCHRATE_D)") + description: qsTr("Damping: increase to reduce overshoots and oscillations, but not higher than really needed.") + param: "SC_PITCHRATE_D" + min: 0.0 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Integral Gain (SC_PITCHRATE_I)") + description: qsTr("Generally does not need much adjustment, reduce this when seeing slow oscillations.") + param: "SC_PITCHRATE_I" + min: 0.0 + max: 10.0 + step: 0.005 + } + } + } + property var yaw: QtObject { + property string name: qsTr("Yaw") + property var plot: [ + { name: "Response", value: globals.activeVehicle.yawRate.value }, + { name: "Setpoint", value: globals.activeVehicle.setpoint.yawRate.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Overall Multiplier (SC_YAWRATE_K)") + description: qsTr("Multiplier for P, I and D gains: increase for more responsiveness, reduce if the rates overshoot (and increasing D does not help).") + param: "SC_YAWRATE_K" + min: 0.0 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Derivative Gain (SC_YAWRATE_D)") + description: qsTr("Damping: increase to reduce overshoots and oscillations, but not higher than really needed.") + param: "SC_YAWRATE_D" + min: 0.0 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Integral Gain (SC_YAWRATE_I)") + description: qsTr("Generally does not need much adjustment, reduce this when seeing slow oscillations.") + param: "SC_YAWRATE_I" + min: 0.0 + max: 10.0 + step: 0.005 + } + ListElement { + title: qsTr("Integral Limit (SC_YR_INT_LIM)") + description: qsTr("Increase if the robot still has a steady-state error at maximum integral gain.") + param: "SC_YR_INT_LIM" + min: 0.0 + max: 5.0 + step: 0.005 + } + } + } + } +} diff --git a/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftVelocity.qml b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftVelocity.qml new file mode 100644 index 000000000000..da27af403cbf --- /dev/null +++ b/src/AutoPilotPlugins/PX4/PX4TuningComponentSpacecraftVelocity.qml @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * (c) 2021 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +import QGroundControl.FactControls + +ColumnLayout { + property real _availableHeight: availableHeight + property real _availableWidth: availableWidth + + PIDTuning { + id: pidTuning + availableWidth: _availableWidth + availableHeight: _availableHeight - pidTuning.y + + property var position: QtObject { + property string name: qsTr("Velocity") + property string plotTitle: qsTr("Velocity (X axis)") + property var plot: [ + { name: "Response", value: globals.activeVehicle.localPosition.vx.value }, + { name: "Setpoint", value: globals.activeVehicle.localPositionSetpoint.vx.value } + ] + property var params: ListModel { + ListElement { + title: qsTr("Proportional gain (SPC_VEL_P)") + description: qsTr("Increase for more responsiveness, reduce if the velocity overshoots (and increasing D does not help).") + param: "SPC_VEL_P" + min: 0.0 + max: 20 + step: 0.05 + } + ListElement { + title: qsTr("Differential gain (SPC_VEL_D)") + description: qsTr("Damping: increase to reduce overshoots and oscillations, but not higher than really needed.") + param: "SPC_VEL_D" + min: 0.0 + max: 3 + step: 0.05 + } + ListElement { + title: qsTr("Integral gain (SPC_VEL_I)") + description: qsTr("Increase to reduce steady-state error (e.g. wind)") + param: "SPC_VEL_I" + min: 0.0 + max: 10 + step: 0.2 + } + ListElement { + title: qsTr("Integral gain Limiter (SPC_VEL_I_LIM)") + description: qsTr("Increase to enlarge the allowed integral compensation.") + param: "SPC_VEL_I_LIM" + min: 0.0 + max: 5 + step: 0.2 + } + } + } + title: "Velocity" + tuningMode: Vehicle.ModeVelocityAndPosition + unit: "m/s" + axis: [ position] + } +} diff --git a/src/AutoPilotPlugins/PX4/PowerComponent.h b/src/AutoPilotPlugins/PX4/PowerComponent.h index 0647d69af068..0083aac89eb7 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponent.h +++ b/src/AutoPilotPlugins/PX4/PowerComponent.h @@ -19,13 +19,13 @@ class PowerComponent : public VehicleComponent { Q_OBJECT - + public: PowerComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Overrides from VehicleComponent QStringList setupCompleteChangedTriggerList(void) const override; - + // Overrides from VehicleComponent QString name (void) const override; QString description (void) const override; diff --git a/src/AutoPilotPlugins/PX4/PowerComponent.qml b/src/AutoPilotPlugins/PX4/PowerComponent.qml index abd0befa3c1f..2bb56f4483a7 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponent.qml +++ b/src/AutoPilotPlugins/PX4/PowerComponent.qml @@ -289,14 +289,14 @@ SetupPage { visible: battNumCellsAvailable && battLowVoltAvailable && battHighVoltAvailable } - Item { + Item { width: 1 height: 1 Layout.columnSpan: battImage.visible ? 2 : 3 } - QGCLabel { - text: qsTr("Number of Cells (in Series)") + QGCLabel { + text: qsTr("Number of Cells (in Series)") visible: battNumCellsAvailable } FactTextField { @@ -305,23 +305,23 @@ SetupPage { showUnits: true visible: battNumCellsAvailable } - QGCLabel { + QGCLabel { text: qsTr("Battery Max:") - visible: battImage.visible + visible: battImage.visible } - QGCLabel { + QGCLabel { text: visible ? (battNumCells.value * battHighVolt.value).toFixed(1) + ' V' : "" - visible: battImage.visible + visible: battImage.visible } - Item { + Item { width: 1 height: 1 Layout.columnSpan: 3 visible: !battImage.visible } - QGCLabel { - text: qsTr("Empty Voltage (per cell)") + QGCLabel { + text: qsTr("Empty Voltage (per cell)") visible: battLowVoltAvailable } FactTextField { @@ -330,23 +330,23 @@ SetupPage { showUnits: true visible: battLowVoltAvailable } - QGCLabel { - text: qsTr("Battery Min:") + QGCLabel { + text: qsTr("Battery Min:") visible: battImage.visible } - QGCLabel { + QGCLabel { text: visible ? (battNumCells.value * battLowVolt.value).toFixed(1) + ' V' : "" visible: battImage.visible } - Item { + Item { width: 1 height: 1 Layout.columnSpan: 3 visible: battLowVoltAvailable && !battImage.visible } - QGCLabel { - text: qsTr("Full Voltage (per cell)") + QGCLabel { + text: qsTr("Full Voltage (per cell)") visible: battHighVoltAvailable } FactTextField { @@ -355,7 +355,7 @@ SetupPage { showUnits: true visible: battHighVoltAvailable } - Item { + Item { width: 1 height: 1 Layout.columnSpan: battImage.visible ? 2 : 3 diff --git a/src/AutoPilotPlugins/PX4/PowerComponentController.cc b/src/AutoPilotPlugins/PX4/PowerComponentController.cc index f9de761c187c..2daac1060f24 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponentController.cc +++ b/src/AutoPilotPlugins/PX4/PowerComponentController.cc @@ -64,17 +64,17 @@ void PowerComponentController::_handleVehicleTextMessage(int vehicleId, int /* c QString calStartPrefix("calibration started: "); if (text.startsWith(calStartPrefix)) { text = text.right(text.length() - calStartPrefix.length()); - + // Split version number and cal type QStringList parts = text.split(" "); if (parts.count() != 2) { emit incorrectFirmwareRevReporting(); return; } - + #if 0 // FIXME: Cal version check is not working. Needs to be able to cancel, calibration - + int firmwareRev = parts[0].toInt(); if (firmwareRev < _neededFirmwareRev) { emit oldFirmware(); @@ -91,13 +91,13 @@ void PowerComponentController::_handleVehicleTextMessage(int vehicleId, int /* c emit connectBattery(); return; } - + if (text == "Battery connected") { emit batteryConnected(); return; } - + QString failedPrefix("calibration failed: "); if (text.startsWith(failedPrefix)) { QString failureText = text.right(text.length() - failedPrefix.length()); @@ -106,18 +106,18 @@ void PowerComponentController::_handleVehicleTextMessage(int vehicleId, int /* c emit disconnectBattery(); return; } - + emit calibrationFailed(text.right(text.length() - failedPrefix.length())); return; } - + QString calCompletePrefix("calibration done:"); if (text.startsWith(calCompletePrefix)) { _stopCalibration(); emit calibrationSuccess(_warningMessages); return; } - + QString warningPrefix("config warning: "); if (text.startsWith(warningPrefix)) { _warningMessages << text.right(text.length() - warningPrefix.length()); diff --git a/src/AutoPilotPlugins/PX4/PowerComponentController.h b/src/AutoPilotPlugins/PX4/PowerComponentController.h index 4172bd82e2ac..2cbf9eb8e152 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponentController.h +++ b/src/AutoPilotPlugins/PX4/PowerComponentController.h @@ -24,11 +24,11 @@ class PowerComponentController : public FactPanelController QML_ELEMENT public: PowerComponentController(void); - + Q_INVOKABLE void calibrateEsc(void); Q_INVOKABLE void startBusConfigureActuators(void); Q_INVOKABLE void stopBusConfigureActuators(void); - + signals: void oldFirmware(void); void newerFirmware(void); @@ -38,14 +38,14 @@ class PowerComponentController : public FactPanelController void batteryConnected(void); void calibrationFailed(const QString& errorMessage); void calibrationSuccess(const QStringList& warningMessages); - + private slots: void _handleVehicleTextMessage(int vehicleId, int compId, int severity, QString text, const QString &description); - + private: void _stopCalibration(void); void _stopBusConfig(void); - + QStringList _warningMessages; static const int _neededFirmwareRev = 1; }; diff --git a/src/AutoPilotPlugins/PX4/SafetyComponent.h b/src/AutoPilotPlugins/PX4/SafetyComponent.h index 226ff0cdb4b7..57a26685725d 100644 --- a/src/AutoPilotPlugins/PX4/SafetyComponent.h +++ b/src/AutoPilotPlugins/PX4/SafetyComponent.h @@ -20,13 +20,13 @@ class SafetyComponent : public VehicleComponent { Q_OBJECT - + public: SafetyComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + // Virtuals from VehicleComponent QStringList setupCompleteChangedTriggerList(void) const override; - + // Virtuals from VehicleComponent QString name (void) const override; QString description (void) const override; diff --git a/src/AutoPilotPlugins/PX4/SensorsComponent.cc b/src/AutoPilotPlugins/PX4/SensorsComponent.cc index b849238417d8..d2c585062430 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponent.cc +++ b/src/AutoPilotPlugins/PX4/SensorsComponent.cc @@ -84,12 +84,12 @@ bool SensorsComponent::setupComplete(void) const QStringList SensorsComponent::setupCompleteChangedTriggerList(void) const { QStringList triggers; - + triggers << _deviceIds << _magCalParam << _magEnabledParam; if (_vehicle->fixedWing() || _vehicle->vtol() || _vehicle->airship()) { triggers << _airspeedCalTriggerParams; } - + return triggers; } @@ -101,13 +101,13 @@ QUrl SensorsComponent::setupSource(void) const QUrl SensorsComponent::summaryQmlSource(void) const { QString summaryQml; - + if (_vehicle->fixedWing() || _vehicle->vtol() || _vehicle->airship()) { summaryQml = "qrc:/qml/QGroundControl/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml"; } else { summaryQml = "qrc:/qml/QGroundControl/AutoPilotPlugins/PX4/SensorsComponentSummary.qml"; } - + return QUrl::fromUserInput(summaryQml); } diff --git a/src/AutoPilotPlugins/PX4/SensorsComponent.h b/src/AutoPilotPlugins/PX4/SensorsComponent.h index 2f5329d00fc4..6dc9e8decca5 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponent.h +++ b/src/AutoPilotPlugins/PX4/SensorsComponent.h @@ -19,16 +19,16 @@ class SensorsComponent : public VehicleComponent { Q_OBJECT - + public: SensorsComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); - + Q_PROPERTY(bool airspeedCalSupported READ _airspeedCalSupported STORED false NOTIFY setupCompleteChanged) Q_PROPERTY(bool airspeedCalRequired READ _airspeedCalRequired STORED false NOTIFY setupCompleteChanged) // Virtuals from VehicleComponent QStringList setupCompleteChangedTriggerList(void) const override; - + // Virtuals from VehicleComponent virtual QString name(void) const override; virtual QString description(void) const override; @@ -37,7 +37,7 @@ class SensorsComponent : public VehicleComponent virtual bool setupComplete(void) const override; virtual QUrl setupSource(void) const override; virtual QUrl summaryQmlSource(void) const override; - + private: bool _airspeedCalSupported (void) const; bool _airspeedCalRequired (void) const; diff --git a/src/AutoPilotPlugins/PX4/SensorsComponentController.cc b/src/AutoPilotPlugins/PX4/SensorsComponentController.cc index fa26007f9ce5..a82bed0024b5 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponentController.cc +++ b/src/AutoPilotPlugins/PX4/SensorsComponentController.cc @@ -13,7 +13,7 @@ #include "Vehicle.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(SensorsComponentControllerLog, "SensorsComponentControllerLog") +QGC_LOGGING_CATEGORY(SensorsComponentControllerLog, "AutoPilotPlugins.SensorsComponentController") SensorsComponentController::SensorsComponentController(void) : _statusLog (nullptr) @@ -79,7 +79,7 @@ void SensorsComponentController::_appendStatusLog(const QString& text) qWarning() << "Internal error"; return; } - + QString varText = text; QMetaObject::invokeMethod(_statusLog, "append", @@ -90,9 +90,9 @@ void SensorsComponentController::_startLogCalibration(void) { _unknownFirmwareVersion = false; _hideAllCalAreas(); - + connect(_vehicle, &Vehicle::textMessageReceived, this, &SensorsComponentController::_handleUASTextMessage); - + _cancelButton->setEnabled(false); } @@ -107,7 +107,7 @@ void SensorsComponentController::_startVisualCalibration(void) _cancelButton->setEnabled(true); _resetInternalState(); - + _progressBar->setProperty("value", 0); } @@ -140,7 +140,7 @@ void SensorsComponentController::_resetInternalState(void) void SensorsComponentController::_stopCalibration(SensorsComponentController::StopCalibrationCode code) { disconnect(_vehicle, &Vehicle::textMessageReceived, this, &SensorsComponentController::_handleUASTextMessage); - + _compassButton->setEnabled(true); _gyroButton->setEnabled(true); _accelButton->setEnabled(true); @@ -148,20 +148,20 @@ void SensorsComponentController::_stopCalibration(SensorsComponentController::St _levelButton->setEnabled(true); _setOrientationsButton->setEnabled(true); _cancelButton->setEnabled(false); - + if (code == StopCalibrationSuccess) { _resetInternalState(); - + _progressBar->setProperty("value", 1); } else { _progressBar->setProperty("value", 0); } - + _waitingForCancel = false; emit waitingForCancelChanged(); _refreshParams(); - + switch (code) { case StopCalibrationSuccess: _orientationCalAreaHelpText->setProperty("text", tr("Calibration complete")); @@ -172,19 +172,19 @@ void SensorsComponentController::_stopCalibration(SensorsComponentController::St emit magCalComplete(); } break; - + case StopCalibrationCancelled: emit resetStatusTextArea(); _hideAllCalAreas(); break; - + default: // Assume failed _hideAllCalAreas(); qgcApp()->showAppMessage(tr("Calibration failed. Calibration log will be displayed.")); break; } - + _magCalInProgress = false; _accelCalInProgress = false; _gyroCalInProgress = false; @@ -226,7 +226,7 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in Q_UNUSED(compId); Q_UNUSED(severity); Q_UNUSED(description); - + if (uasId != _vehicle->id()) { return; } @@ -234,7 +234,7 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in // Needed for level horizon calibration text.replace("<", "<"); text.replace(">", ">"); - + if (text.contains("progress <")) { QString percent = text.split("<").last().split(">").first(); bool ok; @@ -251,12 +251,12 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in _appendStatusLog(text); qCDebug(SensorsComponentControllerLog) << text; - + if (_unknownFirmwareVersion) { // We don't know how to do visual cal with the version of firwmare return; } - + // All calibration messages start with [cal] QString calPrefix("[cal] "); if (!text.startsWith(calPrefix)) { @@ -267,7 +267,7 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in QString calStartPrefix("calibration started: "); if (text.startsWith(calStartPrefix)) { text = text.right(text.length() - calStartPrefix.length()); - + // Split version number and cal type QStringList parts = text.split(" "); if (parts.count() != 2 && parts[0].toInt() != _supportedFirmwareCalVersion) { @@ -277,9 +277,9 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in qDebug() << msg; return; } - + _startVisualCalibration(); - + text = parts[1]; if (text == "accel" || text == "mag" || text == "gyro") { // Reset all progress indication @@ -295,7 +295,7 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in _orientationCalRightSideInProgress = false; _orientationCalNoseDownSideInProgress = false; _orientationCalTailDownSideInProgress = false; - + // Reset all visibility _orientationCalDownSideVisible = false; _orientationCalUpsideDownSideVisible = false; @@ -303,9 +303,9 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in _orientationCalRightSideVisible = false; _orientationCalTailDownSideVisible = false; _orientationCalNoseDownSideVisible = false; - + _orientationCalAreaHelpText->setProperty("text", tr("Place your vehicle into one of the Incomplete orientations shown below and hold it still")); - + if (text == "accel") { _accelCalInProgress = true; _orientationCalDownSideVisible = true; @@ -351,11 +351,11 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in } return; } - + if (text.endsWith("orientation detected")) { QString side = text.section(" ", 0, 0); qCDebug(SensorsComponentControllerLog) << "Side started" << side; - + if (side == "down") { _orientationCalDownSideInProgress = true; if (_magCalInProgress) { @@ -387,22 +387,22 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in _orientationCalTailDownSideRotate = true; } } - + if (_magCalInProgress) { _orientationCalAreaHelpText->setProperty("text", tr("Rotate the vehicle continuously as shown in the diagram until marked as Completed")); } else { _orientationCalAreaHelpText->setProperty("text", tr("Hold still in the current orientation")); } - + emit orientationCalSidesInProgressChanged(); emit orientationCalSidesRotateChanged(); return; } - + if (text.endsWith("side done, rotate to a different side")) { QString side = text.section(" ", 0, 0); qCDebug(SensorsComponentControllerLog) << "Side finished" << side; - + if (side == "down") { _orientationCalDownSideInProgress = false; _orientationCalDownSideDone = true; @@ -428,7 +428,7 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in _orientationCalTailDownSideDone = true; _orientationCalTailDownSideRotate = false; } - + _orientationCalAreaHelpText->setProperty("text", tr("Place you vehicle into one of the orientations shown below and hold it still")); emit orientationCalSidesInProgressChanged(); @@ -441,18 +441,18 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in _orientationCalAreaHelpText->setProperty("text", tr("Orientation already completed, place you vehicle into one of the incomplete orientations shown below and hold it still")); return; } - + QString calCompletePrefix("calibration done:"); if (text.startsWith(calCompletePrefix)) { _stopCalibration(StopCalibrationSuccess); return; } - + if (text.startsWith("calibration cancelled")) { _stopCalibration(_waitingForCancel ? StopCalibrationCancelled : StopCalibrationFailed); return; } - + if (text.startsWith("calibration failed")) { _stopCalibration(StopCalibrationFailed); return; @@ -462,13 +462,13 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in void SensorsComponentController::_refreshParams(void) { QStringList fastRefreshList; - + // We ask for a refresh on these first so that the rotation combo show up as fast as possible fastRefreshList << "CAL_MAG0_ID" << "CAL_MAG1_ID" << "CAL_MAG2_ID" << "CAL_MAG0_ROT" << "CAL_MAG1_ROT" << "CAL_MAG2_ROT"; for (const QString ¶mName : std::as_const(fastRefreshList)) { _vehicle->parameterManager()->refreshParameter(ParameterManager::defaultComponentId, paramName); } - + // Now ask for all to refresh _vehicle->parameterManager()->refreshParametersPrefix(ParameterManager::defaultComponentId, "CAL_"); _vehicle->parameterManager()->refreshParametersPrefix(ParameterManager::defaultComponentId, "SENS_"); diff --git a/src/AutoPilotPlugins/PX4/SensorsComponentController.h b/src/AutoPilotPlugins/PX4/SensorsComponentController.h index bf42aae872e0..99d5622c309d 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponentController.h +++ b/src/AutoPilotPlugins/PX4/SensorsComponentController.h @@ -28,10 +28,10 @@ class SensorsComponentController : public FactPanelController QML_ELEMENT public: SensorsComponentController(void); - + Q_PROPERTY(QQuickItem* statusLog MEMBER _statusLog) Q_PROPERTY(QQuickItem* progressBar MEMBER _progressBar) - + Q_PROPERTY(QQuickItem* compassButton MEMBER _compassButton) Q_PROPERTY(QQuickItem* gyroButton MEMBER _gyroButton) Q_PROPERTY(QQuickItem* accelButton MEMBER _accelButton) @@ -40,39 +40,39 @@ class SensorsComponentController : public FactPanelController Q_PROPERTY(QQuickItem* cancelButton MEMBER _cancelButton) Q_PROPERTY(QQuickItem* setOrientationsButton MEMBER _setOrientationsButton) Q_PROPERTY(QQuickItem* orientationCalAreaHelpText MEMBER _orientationCalAreaHelpText) - + Q_PROPERTY(bool showOrientationCalArea MEMBER _showOrientationCalArea NOTIFY showOrientationCalAreaChanged) - + Q_PROPERTY(bool orientationCalDownSideDone MEMBER _orientationCalDownSideDone NOTIFY orientationCalSidesDoneChanged) Q_PROPERTY(bool orientationCalUpsideDownSideDone MEMBER _orientationCalUpsideDownSideDone NOTIFY orientationCalSidesDoneChanged) Q_PROPERTY(bool orientationCalLeftSideDone MEMBER _orientationCalLeftSideDone NOTIFY orientationCalSidesDoneChanged) Q_PROPERTY(bool orientationCalRightSideDone MEMBER _orientationCalRightSideDone NOTIFY orientationCalSidesDoneChanged) Q_PROPERTY(bool orientationCalNoseDownSideDone MEMBER _orientationCalNoseDownSideDone NOTIFY orientationCalSidesDoneChanged) Q_PROPERTY(bool orientationCalTailDownSideDone MEMBER _orientationCalTailDownSideDone NOTIFY orientationCalSidesDoneChanged) - + Q_PROPERTY(bool orientationCalDownSideVisible MEMBER _orientationCalDownSideVisible NOTIFY orientationCalSidesVisibleChanged) Q_PROPERTY(bool orientationCalUpsideDownSideVisible MEMBER _orientationCalUpsideDownSideVisible NOTIFY orientationCalSidesVisibleChanged) Q_PROPERTY(bool orientationCalLeftSideVisible MEMBER _orientationCalLeftSideVisible NOTIFY orientationCalSidesVisibleChanged) Q_PROPERTY(bool orientationCalRightSideVisible MEMBER _orientationCalRightSideVisible NOTIFY orientationCalSidesVisibleChanged) Q_PROPERTY(bool orientationCalNoseDownSideVisible MEMBER _orientationCalNoseDownSideVisible NOTIFY orientationCalSidesVisibleChanged) Q_PROPERTY(bool orientationCalTailDownSideVisible MEMBER _orientationCalTailDownSideVisible NOTIFY orientationCalSidesVisibleChanged) - + Q_PROPERTY(bool orientationCalDownSideInProgress MEMBER _orientationCalDownSideInProgress NOTIFY orientationCalSidesInProgressChanged) Q_PROPERTY(bool orientationCalUpsideDownSideInProgress MEMBER _orientationCalUpsideDownSideInProgress NOTIFY orientationCalSidesInProgressChanged) Q_PROPERTY(bool orientationCalLeftSideInProgress MEMBER _orientationCalLeftSideInProgress NOTIFY orientationCalSidesInProgressChanged) Q_PROPERTY(bool orientationCalRightSideInProgress MEMBER _orientationCalRightSideInProgress NOTIFY orientationCalSidesInProgressChanged) Q_PROPERTY(bool orientationCalNoseDownSideInProgress MEMBER _orientationCalNoseDownSideInProgress NOTIFY orientationCalSidesInProgressChanged) Q_PROPERTY(bool orientationCalTailDownSideInProgress MEMBER _orientationCalTailDownSideInProgress NOTIFY orientationCalSidesInProgressChanged) - + Q_PROPERTY(bool orientationCalDownSideRotate MEMBER _orientationCalDownSideRotate NOTIFY orientationCalSidesRotateChanged) Q_PROPERTY(bool orientationCalUpsideDownSideRotate MEMBER _orientationCalUpsideDownSideRotate NOTIFY orientationCalSidesRotateChanged) Q_PROPERTY(bool orientationCalLeftSideRotate MEMBER _orientationCalLeftSideRotate NOTIFY orientationCalSidesRotateChanged) Q_PROPERTY(bool orientationCalRightSideRotate MEMBER _orientationCalRightSideRotate NOTIFY orientationCalSidesRotateChanged) Q_PROPERTY(bool orientationCalNoseDownSideRotate MEMBER _orientationCalNoseDownSideRotate NOTIFY orientationCalSidesRotateChanged) Q_PROPERTY(bool orientationCalTailDownSideRotate MEMBER _orientationCalTailDownSideRotate NOTIFY orientationCalSidesRotateChanged) - + Q_PROPERTY(bool waitingForCancel MEMBER _waitingForCancel NOTIFY waitingForCancelChanged) - + Q_INVOKABLE void calibrateCompass(void); Q_INVOKABLE void calibrateGyro(void); Q_INVOKABLE void calibrateAccel(void); @@ -81,7 +81,7 @@ class SensorsComponentController : public FactPanelController Q_INVOKABLE void cancelCalibration(void); Q_INVOKABLE bool usingUDPLink(void); Q_INVOKABLE void resetFactoryParameters(); - + signals: void showGyroCalAreaChanged(void); void showOrientationCalAreaChanged(void); @@ -96,7 +96,7 @@ class SensorsComponentController : public FactPanelController private slots: void _handleUASTextMessage(int uasId, int compId, int severity, QString text, const QString &description); void _handleParametersReset(bool success); - + private: void _startLogCalibration(void); void _startVisualCalibration(void); @@ -104,14 +104,14 @@ private slots: void _refreshParams(void); void _hideAllCalAreas(void); void _resetInternalState(void); - + enum StopCalibrationCode { StopCalibrationSuccess, StopCalibrationFailed, StopCalibrationCancelled }; void _stopCalibration(StopCalibrationCode code); - + void _updateAndEmitShowOrientationCalArea(bool show); QQuickItem* _statusLog; @@ -124,10 +124,10 @@ private slots: QQuickItem* _cancelButton; QQuickItem* _setOrientationsButton; QQuickItem* _orientationCalAreaHelpText; - + bool _showGyroCalArea; bool _showOrientationCalArea; - + bool _gyroCalInProgress; bool _magCalInProgress; bool _accelCalInProgress; @@ -140,30 +140,30 @@ private slots: bool _orientationCalRightSideDone; bool _orientationCalNoseDownSideDone; bool _orientationCalTailDownSideDone; - + bool _orientationCalDownSideVisible; bool _orientationCalUpsideDownSideVisible; bool _orientationCalLeftSideVisible; bool _orientationCalRightSideVisible; bool _orientationCalNoseDownSideVisible; bool _orientationCalTailDownSideVisible; - + bool _orientationCalDownSideInProgress; bool _orientationCalUpsideDownSideInProgress; bool _orientationCalLeftSideInProgress; bool _orientationCalRightSideInProgress; bool _orientationCalNoseDownSideInProgress; bool _orientationCalTailDownSideInProgress; - + bool _orientationCalDownSideRotate; bool _orientationCalUpsideDownSideRotate; bool _orientationCalLeftSideRotate; bool _orientationCalRightSideRotate; bool _orientationCalNoseDownSideRotate; bool _orientationCalTailDownSideRotate; - + bool _unknownFirmwareVersion; bool _waitingForCancel; - + static const int _supportedFirmwareCalVersion = 2; }; diff --git a/src/AutoPilotPlugins/PX4/SensorsSetup.qml b/src/AutoPilotPlugins/PX4/SensorsSetup.qml index 0b5b792ba143..cb6dceb80004 100644 --- a/src/AutoPilotPlugins/PX4/SensorsSetup.qml +++ b/src/AutoPilotPlugins/PX4/SensorsSetup.qml @@ -74,7 +74,6 @@ Item { property bool showCompass2Rot: cal_mag2_id.value > 0 && cal_mag2_rot.value >= 0 property bool _sensorsHaveFixedOrientation: QGroundControl.corePlugin.options.sensorsHaveFixedOrientation - property bool _wifiReliableForCalibration: QGroundControl.corePlugin.options.wifiReliableForCalibration property int _buttonWidth: ScreenTools.defaultFontPixelWidth * 15 property string _calMagIdParamFormat: "CAL_MAG#_ID" property string _calMagRotParamFormat: "CAL_MAG#_ROT" @@ -173,13 +172,6 @@ Item { } } - Component.onCompleted: { - var usingUDP = controller.usingUDPLink() - if (usingUDP && !_wifiReliableForCalibration) { - mainWindow.showMessageDialog(qsTr("Sensor Calibration"), qsTr("Performing sensor calibration over a WiFi connection is known to be unreliable. You should disconnect and perform calibration using a direct USB connection instead.")) - } - } - Component { id: waitForCancelDialogComponent @@ -403,7 +395,7 @@ Item { id: airspeedButton width: _buttonWidth text: qsTr("Airspeed") - visible: vehicleComponent.airspeedCalSupported && + visible: vehicleComponent.airspeedCalSupported && QGroundControl.corePlugin.options.showSensorCalibrationAirspeed && showSensorCalibrationAirspeed indicatorGreen: sens_dpres_off.value !== 0 diff --git a/src/AutoPilotPlugins/VehicleComponent.cc b/src/AutoPilotPlugins/VehicleComponent.cc index 9af8d8037627..2636b1e11553 100644 --- a/src/AutoPilotPlugins/VehicleComponent.cc +++ b/src/AutoPilotPlugins/VehicleComponent.cc @@ -15,7 +15,7 @@ #include #include -QGC_LOGGING_CATEGORY(VehicleComponentLog, "qgc.autopilotplugin.vehiclecomponent"); +QGC_LOGGING_CATEGORY(VehicleComponentLog, "AutoPilotPlugins.VehicleComponent"); VehicleComponent::VehicleComponent(Vehicle *vehicle, AutoPilotPlugin *autopilot, AutoPilotPlugin::KnownVehicleComponent KnownVehicleComponent, QObject *parent) : QObject(parent) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 222325c005ba..d3d9507df8ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,16 @@ +# ============================================================================ +# QGroundControl Module Library +# ============================================================================ + qt_add_library(QGroundControlModule STATIC) -set_source_files_properties(UI/MainWindow.qml PROPERTIES QT_RESOURCE_ALIAS MainWindow.qml) +# ---------------------------------------------------------------------------- +# QML Resources +# ---------------------------------------------------------------------------- +set_source_files_properties(UI/MainWindow.qml + PROPERTIES + QT_RESOURCE_ALIAS MainWindow.qml +) qt_add_qml_module(QGroundControlModule URI QGroundControl @@ -13,9 +23,13 @@ qt_add_qml_module(QGroundControlModule NO_PLUGIN ) +# ============================================================================ +# Subdirectories +# ============================================================================ + +# Core subsystems add_subdirectory(ADSB) add_subdirectory(AnalyzeView) -add_subdirectory(Android) add_subdirectory(API) add_subdirectory(AutoPilotPlugins) add_subdirectory(Camera) @@ -41,18 +55,25 @@ add_subdirectory(Vehicle) add_subdirectory(VideoManager) add_subdirectory(Viewer3D) +# Platform-specific +add_subdirectory(Android) + +# Qt plugins add_subdirectory(QtLocationPlugin) +# ============================================================================ +# Main Application Sources +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE main.cc - CmdLineOptParser.cc - CmdLineOptParser.h pch.h QGCApplication.cc QGCApplication.h ) +# Platform-specific sources if(NOT ANDROID AND NOT IOS) target_sources(${CMAKE_PROJECT_NAME} PRIVATE @@ -61,32 +82,53 @@ if(NOT ANDROID AND NOT IOS) ) endif() -target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# Include Directories +# ---------------------------------------------------------------------------- +target_include_directories(${CMAKE_PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# ============================================================================ +# Link Libraries +# ============================================================================ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE + # Qt6 Core Qt6::Charts Qt6::Concurrent Qt6::Core Qt6::Core5Compat Qt6::CorePrivate Qt6::Gui - Qt6::Location - Qt6::LocationPrivate - Qt6::Multimedia - Qt6::MultimediaQuickPrivate Qt6::Network - Qt6::Positioning - Qt6::PositioningPrivate - Qt6::Qml - Qt6::QmlIntegration - Qt6::Quick - Qt6::QuickControls2 Qt6::Sensors Qt6::Svg Qt6::TextToSpeech Qt6::Widgets Qt6::Xml + Qt6::StateMachine + + # Qt6 QML/Quick + Qt6::Qml + Qt6::QmlIntegration + Qt6::Quick + Qt6::QuickControls2 + + # Qt6 Location + Qt6::Location + Qt6::LocationPrivate + Qt6::Positioning + Qt6::PositioningPrivate + Qt6::Sql + + # Qt6 Multimedia + Qt6::Multimedia + Qt6::MultimediaQuickPrivate + + # QGC Modules AnalyzeViewModule AppSettingsModule AutoPilotPluginsCommonModule @@ -101,30 +143,50 @@ target_link_libraries(${CMAKE_PROJECT_NAME} VehicleSetupModule ) -if(NOT QGC_DISABLE_APM_PLUGIN AND NOT QGC_DISABLE_APM_PLUGIN_FACTORY) +# ---------------------------------------------------------------------------- +# Optional/Conditional Modules +# ---------------------------------------------------------------------------- +if(NOT QGC_DISABLE_APM_PLUGIN) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE AutoPilotPluginsAPMModule) endif() -if(NOT QGC_DISABLE_PX4_PLUGIN AND NOT QGC_DISABLE_PX4_PLUGIN_FACTORY) + +if(NOT QGC_DISABLE_PX4_PLUGIN) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE AutoPilotPluginsPX4Module) endif() + if(QGC_VIEWER3D) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Viewer3DModule) endif() + if(NOT QGC_AIRLINK_DISABLED) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE AirLinkModule) endif() +# ============================================================================ +# Compile Definitions +# ============================================================================ + +# Application metadata target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_APP_NAME="${QGC_APP_NAME}" QGC_ORG_NAME="${QGC_ORG_NAME}" QGC_ORG_DOMAIN="${QGC_ORG_DOMAIN}" QGC_APP_VERSION_STR="${QGC_APP_VERSION_STR}" + QGC_APP_DATE="${QGC_APP_DATE}" + QGC_APP_DESCRIPTION="${QGC_APP_DESCRIPTION}" QGC_SETTINGS_VERSION=${QGC_SETTINGS_VERSION} $<$>:QGC_DAILY_BUILD> $<$:QGC_NO_ARDUPILOT_DIALECT> ) +# Build configuration-specific definitions +target_compile_definitions(${CMAKE_PROJECT_NAME} + PRIVATE + QT_DISABLE_DEPRECATED_UP_TO=${QGC_QT_DISABLE_DEPRECATED_UP_TO} + QT_ENABLE_STRICT_MODE_UP_TO=${QGC_QT_ENABLE_STRICT_MODE_UP_TO} +) + if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE @@ -135,24 +197,55 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release") else() target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE - QT_DISABLE_DEPRECATED_UP_TO=0x060800 - QT_ENABLE_STRICT_MODE_UP_TO=0x060800 $<$:QT_QML_DEBUG> ) endif() +# ============================================================================ +# Code Coverage Configuration +# ============================================================================ + +include(Coverage) + +# ============================================================================ +# Target Properties +# ============================================================================ + set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES QT_RESOURCE_PREFIX "/qml" OUTPUT_NAME ${CMAKE_PROJECT_NAME} ) +# ---------------------------------------------------------------------------- +# Precompiled Headers +# ---------------------------------------------------------------------------- target_precompile_headers(${CMAKE_PROJECT_NAME} PRIVATE pch.h) +# ============================================================================ +# Custom Build Configuration +# ============================================================================ + if(QGC_CUSTOM_BUILD) - find_package(Qt6 REQUIRED COMPONENTS ${CUSTOM_QT_COMPONENTS}) - target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_SOURCES}) - target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_LIBRARIES}) - target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_INCLUDE_DIRECTORIES}) - target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_DEFINITIONS}) + if(CUSTOM_QT_COMPONENTS) + find_package(Qt6 REQUIRED COMPONENTS ${CUSTOM_QT_COMPONENTS}) + endif() + + if(CUSTOM_SOURCES) + target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_SOURCES}) + endif() + + if(CUSTOM_LIBRARIES) + target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_LIBRARIES}) + endif() + + if(CUSTOM_INCLUDE_DIRECTORIES) + target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_INCLUDE_DIRECTORIES}) + endif() + + if(CUSTOM_DEFINITIONS) + target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE ${CUSTOM_DEFINITIONS}) + endif() + + message(STATUS "QGC: Custom build configuration applied") endif() diff --git a/src/Camera/CMakeLists.txt b/src/Camera/CMakeLists.txt index 5b494eafb376..8e973259bf1f 100644 --- a/src/Camera/CMakeLists.txt +++ b/src/Camera/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Camera Module +# MAVLink camera control and video stream management +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE CameraMetaData.cc @@ -18,7 +23,9 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -# Add JSON files +# ---------------------------------------------------------------------------- +# Camera Metadata Resources +# ---------------------------------------------------------------------------- qt_add_resources(${CMAKE_PROJECT_NAME} json_mavlink_camera PREFIX "/json" FILES CameraMetaData.json diff --git a/src/Camera/CameraMetaData.cc b/src/Camera/CameraMetaData.cc index 745187d3f925..ccc7e4bb5021 100644 --- a/src/Camera/CameraMetaData.cc +++ b/src/Camera/CameraMetaData.cc @@ -8,9 +8,15 @@ ****************************************************************************/ #include "CameraMetaData.h" + +#include +#include +#include + +#include "JsonHelper.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(CameraMetaDataLog, "qgc.camera.camerametadata") +QGC_LOGGING_CATEGORY(CameraMetaDataLog, "Camera.CameraMetaData") CameraMetaData::CameraMetaData(const QString &canonicalName, const QString &brand, @@ -44,3 +50,73 @@ CameraMetaData::~CameraMetaData() { qCDebug(CameraMetaDataLog) << this; } + +QList CameraMetaData::parseCameraMetaData() +{ + QList cameraList; + + QString errorString; + int version = 0; + const QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(QStringLiteral(":/json/CameraMetaData.json"), "CameraMetaData", 1, 1, version, errorString); + if (!errorString.isEmpty()) { + qCWarning(CameraMetaDataLog) << "Internal Error:" << errorString; + return cameraList; + } + + static const QList rootKeyInfoList = { + { "cameraMetaData", QJsonValue::Array, true } + }; + if (!JsonHelper::validateKeys(jsonObject, rootKeyInfoList, errorString)) { + qCWarning(CameraMetaDataLog) << errorString; + return cameraList; + } + + static const QList cameraKeyInfoList = { + { "canonicalName", QJsonValue::String, true }, + { "brand", QJsonValue::String, true }, + { "model", QJsonValue::String, true }, + { "sensorWidth", QJsonValue::Double, true }, + { "sensorHeight", QJsonValue::Double, true }, + { "imageWidth", QJsonValue::Double, true }, + { "imageHeight", QJsonValue::Double, true }, + { "focalLength", QJsonValue::Double, true }, + { "landscape", QJsonValue::Bool, true }, + { "fixedOrientation", QJsonValue::Bool, true }, + { "minTriggerInterval", QJsonValue::Double, true }, + { "deprecatedTranslatedName", QJsonValue::String, true }, + }; + const QJsonArray cameraInfo = jsonObject["cameraMetaData"].toArray(); + for (const QJsonValue &jsonValue : cameraInfo) { + if (!jsonValue.isObject()) { + qCWarning(CameraMetaDataLog) << "Entry in CameraMetaData array is not object"; + return cameraList; + } + + const QJsonObject obj = jsonValue.toObject(); + if (!JsonHelper::validateKeys(obj, cameraKeyInfoList, errorString)) { + qCWarning(CameraMetaDataLog) << errorString; + return cameraList; + } + + const QString canonicalName = obj["canonicalName"].toString(); + const QString brand = obj["brand"].toString(); + const QString model = obj["model"].toString(); + const double sensorWidth = obj["sensorWidth"].toDouble(); + const double sensorHeight = obj["sensorHeight"].toDouble(); + const double imageWidth = obj["imageWidth"].toDouble(); + const double imageHeight = obj["imageHeight"].toDouble(); + const double focalLength = obj["focalLength"].toDouble(); + const bool landscape = obj["landscape"].toBool(); + const bool fixedOrientation = obj["fixedOrientation"].toBool(); + const double minTriggerInterval = obj["minTriggerInterval"].toDouble(); + const QString deprecatedTranslatedName = obj["deprecatedTranslatedName"].toString(); + + CameraMetaData *metaData = new CameraMetaData( + canonicalName, brand, model, sensorWidth, sensorHeight, + imageWidth, imageHeight, focalLength, landscape, + fixedOrientation, minTriggerInterval, deprecatedTranslatedName); + cameraList.append(metaData); + } + + return cameraList; +} diff --git a/src/Camera/CameraMetaData.h b/src/Camera/CameraMetaData.h index f9729856c9a2..5fb99fcc9c3a 100644 --- a/src/Camera/CameraMetaData.h +++ b/src/Camera/CameraMetaData.h @@ -10,6 +10,7 @@ #pragma once #include +#include Q_DECLARE_LOGGING_CATEGORY(CameraMetaDataLog) @@ -45,6 +46,8 @@ class CameraMetaData const QString &deprecatedTranslatedName); ~CameraMetaData(); + static QList parseCameraMetaData(); + const QString canonicalName; ///< Canonical name saved in plan files. Not translated. const QString brand; ///< Camera brand. Used for grouping. const QString model; ///< Camerar model @@ -63,4 +66,4 @@ class CameraMetaData /// Newly added CameraMetaData entries should leave this value empty. const QString deprecatedTranslatedName; }; -Q_DECLARE_METATYPE(CameraMetaData) +Q_DECLARE_METATYPE(CameraMetaData*) diff --git a/src/Camera/MavlinkCameraControl.cc b/src/Camera/MavlinkCameraControl.cc index 6871bae01d9c..e4dd89a82d1e 100644 --- a/src/Camera/MavlinkCameraControl.cc +++ b/src/Camera/MavlinkCameraControl.cc @@ -10,8 +10,8 @@ #include "MavlinkCameraControl.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(CameraControlLog, "qgc.camera.mavlinkcameracontrol") -QGC_LOGGING_CATEGORY(CameraControlVerboseLog, "qgc.camera.mavlinkcameracontrol") +QGC_LOGGING_CATEGORY(CameraControlLog, "Camera.MavlinkCameraControl") +QGC_LOGGING_CATEGORY(CameraControlVerboseLog, "Camera.MavlinkCameraControl:verbose") MavlinkCameraControl::MavlinkCameraControl(Vehicle *vehicle, QObject *parent) : FactGroup(0, parent, true /* ignore camel case */) diff --git a/src/Camera/QGCCameraIO.cc b/src/Camera/QGCCameraIO.cc index 7858688caa34..653eb0aaadc2 100644 --- a/src/Camera/QGCCameraIO.cc +++ b/src/Camera/QGCCameraIO.cc @@ -14,8 +14,8 @@ #include "QGCLoggingCategory.h" #include "Vehicle.h" -QGC_LOGGING_CATEGORY(QGCCameraParamIOLog, "qgc.camera.qgccameraparamio") -QGC_LOGGING_CATEGORY(QGCCameraParamIOVerbose, "qgc.camera.qgccameraparamio:verbose") +QGC_LOGGING_CATEGORY(QGCCameraParamIOLog, "Camera.QGCCameraParamIO") +QGC_LOGGING_CATEGORY(QGCCameraParamIOVerbose, "Camera.QGCCameraParamIO:verbose") namespace { constexpr int kMaxRetries = 3; diff --git a/src/Camera/QGCCameraManager.cc b/src/Camera/QGCCameraManager.cc index 16a625681386..0c570230dc45 100644 --- a/src/Camera/QGCCameraManager.cc +++ b/src/Camera/QGCCameraManager.cc @@ -8,78 +8,84 @@ ****************************************************************************/ #include "QGCCameraManager.h" +#include "CameraMetaData.h" +#include "FirmwarePlugin.h" +#include "Joystick.h" #include "JoystickManager.h" -#include "SimulatedCameraControl.h" +#include "MavlinkCameraControl.h" #include "MultiVehicleManager.h" -#include "Vehicle.h" -#include "FirmwarePlugin.h" #include "QGCLoggingCategory.h" -#include "Joystick.h" -#include "CameraMetaData.h" -#include "JsonHelper.h" #include "QGCVideoStreamInfo.h" +#include "SimulatedCameraControl.h" -#include -#include -#include -#include +QGC_LOGGING_CATEGORY(CameraManagerLog, "Camera.QGCCameraManager") -QGC_LOGGING_CATEGORY(CameraManagerLog, "qgc.camera.qgccameramanager") +namespace { + constexpr int kHeartbeatTickMs = 500; + constexpr int kSilentTimeoutMs = 5000; + constexpr int kMaxRetryCount = 10; +} QVariantList QGCCameraManager::_cameraList; -//----------------------------------------------------------------------------- -QGCCameraManager::CameraStruct::CameraStruct(QObject* parent, uint8_t compID_, Vehicle* vehicle_) - : QObject (parent) - , compID (compID_) - , vehicle (vehicle_) +/*===========================================================================*/ + +QGCCameraManager::CameraStruct::CameraStruct(QGCCameraManager *manager_, uint8_t compID_, Vehicle *vehicle_) + : manager(manager_) + , compID(compID_) + , vehicle(vehicle_) { - backoffTimer = new QTimer(this); - backoffTimer->setSingleShot(true); + qCDebug(CameraManagerLog) << this; + backoffTimer.setSingleShot(true); } -//----------------------------------------------------------------------------- -QGCCameraManager::QGCCameraManager(Vehicle *vehicle) - : _vehicle (vehicle) - , _simulatedCameraControl (new SimulatedCameraControl(vehicle, this)) +QGCCameraManager::CameraStruct::~CameraStruct() { - qCDebug(CameraManagerLog) << "QGCCameraManager Created"; + qCDebug(CameraManagerLog) << this; +} - (void) qRegisterMetaType("CameraMetaData"); +/*===========================================================================*/ - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +QGCCameraManager::QGCCameraManager(Vehicle *vehicle) + : QObject(vehicle) + , _vehicle(vehicle) + , _simulatedCameraControl(new SimulatedCameraControl(vehicle, this)) +{ + qCDebug(CameraManagerLog) << this; + + (void) qRegisterMetaType("CameraMetaData*"); _addCameraControlToLists(_simulatedCameraControl); - connect(MultiVehicleManager::instance(), &MultiVehicleManager::parameterReadyVehicleAvailableChanged, this, &QGCCameraManager::_vehicleReady); - connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &QGCCameraManager::_mavlinkMessageReceived); - connect(&_camerasLostHeartbeatTimer, &QTimer::timeout, this, &QGCCameraManager::_checkForLostCameras); + (void) connect(MultiVehicleManager::instance(), &MultiVehicleManager::parameterReadyVehicleAvailableChanged, this, &QGCCameraManager::_vehicleReady); + (void) connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &QGCCameraManager::_mavlinkMessageReceived); + (void) connect(&_camerasLostHeartbeatTimer, &QTimer::timeout, this, &QGCCameraManager::_checkForLostCameras); _camerasLostHeartbeatTimer.setSingleShot(false); _lastZoomChange.start(); _lastCameraChange.start(); - _camerasLostHeartbeatTimer.start(500); + _camerasLostHeartbeatTimer.start(kHeartbeatTickMs); } QGCCameraManager::~QGCCameraManager() { // Stop all camera info request timers and clean up for (auto* cameraInfo : _cameraInfoRequest) { - if (cameraInfo->backoffTimer) { - cameraInfo->backoffTimer->stop(); - QObject::disconnect(cameraInfo->backoffTimer, nullptr, nullptr, nullptr); - } - delete cameraInfo; + cameraInfo->backoffTimer.stop(); + QObject::disconnect(&cameraInfo->backoffTimer, nullptr, nullptr, nullptr); } + qDeleteAll(_cameraInfoRequest); _cameraInfoRequest.clear(); // Stop the main heartbeat timer _camerasLostHeartbeatTimer.stop(); + + qCDebug(CameraManagerLog) << this; } void QGCCameraManager::setCurrentCamera(int sel) { - if(sel != _currentCameraIndex && sel >= 0 && sel < _cameras.count()) { + if ((sel != _currentCameraIndex) && (sel >= 0) && (sel < _cameras.count())) { _currentCameraIndex = sel; emit currentCameraChanged(); emit streamChanged(); @@ -88,414 +94,391 @@ void QGCCameraManager::setCurrentCamera(int sel) void QGCCameraManager::_vehicleReady(bool ready) { - qCDebug(CameraManagerLog) << "_vehicleReady(" << ready << ")"; - if(ready) { - if(MultiVehicleManager::instance()->activeVehicle() == _vehicle) { - _vehicleReadyState = true; - _activeJoystickChanged(JoystickManager::instance()->activeJoystick()); - connect(JoystickManager::instance(), &JoystickManager::activeJoystickChanged, this, &QGCCameraManager::_activeJoystickChanged); - } + qCDebug(CameraManagerLog) << ready; + if (!ready) { + return; + } + if (MultiVehicleManager::instance()->activeVehicle() != _vehicle) { + return; } + + _vehicleReadyState = true; + _activeJoystickChanged(JoystickManager::instance()->activeJoystick()); + (void) connect(JoystickManager::instance(), &JoystickManager::activeJoystickChanged, this, &QGCCameraManager::_activeJoystickChanged, Qt::UniqueConnection); } -void QGCCameraManager::_mavlinkMessageReceived(const mavlink_message_t& message) +void QGCCameraManager::_mavlinkMessageReceived(const mavlink_message_t &message) { - //-- Only pay attention to the camera components, as identified by their compId, - // as well as the autopilot, as it might have a non-MAVLink camera connected. - if(message.sysid == _vehicle->id() && (message.compid == MAV_COMP_ID_AUTOPILOT1 || - (message.compid >= MAV_COMP_ID_CAMERA && message.compid <= MAV_COMP_ID_CAMERA6))) { + // Only pay attention to camera components (MAV_COMP_ID_CAMERA..CAMERA6) + // and the autopilot (it might proxy a non-MAVLink camera). + const bool fromAutopilot = message.compid == MAV_COMP_ID_AUTOPILOT1; + const bool fromCamera = (message.compid >= MAV_COMP_ID_CAMERA) && (message.compid <= MAV_COMP_ID_CAMERA6); + if ((message.sysid == _vehicle->id()) && (fromAutopilot || fromCamera)) { switch (message.msgid) { - case MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS: - _handleCaptureStatus(message); - break; - case MAVLINK_MSG_ID_STORAGE_INFORMATION: - _handleStorageInfo(message); - break; - case MAVLINK_MSG_ID_HEARTBEAT: - _handleHeartbeat(message); - break; - case MAVLINK_MSG_ID_CAMERA_INFORMATION: - _handleCameraInfo(message); - break; - case MAVLINK_MSG_ID_CAMERA_SETTINGS: - _handleCameraSettings(message); - break; - case MAVLINK_MSG_ID_PARAM_EXT_ACK: - _handleParamAck(message); - break; - case MAVLINK_MSG_ID_PARAM_EXT_VALUE: - _handleParamValue(message); - break; - case MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION: - _handleVideoStreamInfo(message); - break; - case MAVLINK_MSG_ID_VIDEO_STREAM_STATUS: - _handleVideoStreamStatus(message); - break; - case MAVLINK_MSG_ID_BATTERY_STATUS: - _handleBatteryStatus(message); - break; - case MAVLINK_MSG_ID_CAMERA_TRACKING_IMAGE_STATUS: - _handleTrackingImageStatus(message); - break; + case MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS: + _handleCaptureStatus(message); + break; + case MAVLINK_MSG_ID_STORAGE_INFORMATION: + _handleStorageInfo(message); + break; + case MAVLINK_MSG_ID_HEARTBEAT: + _handleHeartbeat(message); + break; + case MAVLINK_MSG_ID_CAMERA_INFORMATION: + _handleCameraInfo(message); + break; + case MAVLINK_MSG_ID_CAMERA_SETTINGS: + _handleCameraSettings(message); + break; + case MAVLINK_MSG_ID_PARAM_EXT_ACK: + _handleParamAck(message); + break; + case MAVLINK_MSG_ID_PARAM_EXT_VALUE: + _handleParamValue(message); + break; + case MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION: + _handleVideoStreamInfo(message); + break; + case MAVLINK_MSG_ID_VIDEO_STREAM_STATUS: + _handleVideoStreamStatus(message); + break; + case MAVLINK_MSG_ID_BATTERY_STATUS: + _handleBatteryStatus(message); + break; + case MAVLINK_MSG_ID_CAMERA_TRACKING_IMAGE_STATUS: + _handleTrackingImageStatus(message); + break; + default: + break; } } } void QGCCameraManager::_handleHeartbeat(const mavlink_message_t &message) { - QString sCompID = QString::number(message.compid); + const QString sCompID = QString::number(message.compid); if (!_cameraInfoRequest.contains(sCompID)) { - // This is the first time we are heading from this camera - qCDebug(CameraManagerLog) << "Hearbeat from " << message.compid; - CameraStruct* pInfo = new CameraStruct(this, message.compid, _vehicle); + qCDebug(CameraManagerLog) << "Heartbeat from" << QGCMAVLink::compIdToString(message.compid); + CameraStruct *pInfo = new CameraStruct(this, message.compid, _vehicle); pInfo->lastHeartbeat.start(); _cameraInfoRequest[sCompID] = pInfo; _requestCameraInfo(pInfo); - } else { - if (_cameraInfoRequest[sCompID]) { - CameraStruct* pInfo = _cameraInfoRequest[sCompID]; - //-- Check if we have indeed received the camera info - if (pInfo->infoReceived) { - //-- We have it. Just update the heartbeat timeout - pInfo->lastHeartbeat.start(); - } else { - //-- Camera info not received yet. Check if camera was silent and is now back - if (pInfo->lastHeartbeat.elapsed() > 5000) { - qCDebug(CameraManagerLog) << "Camera" << message.compid << "reappeared after being silent. Resetting retry count and requesting info."; - pInfo->retryCount = 0; // Reset retry count for fresh attempts - pInfo->backoffTimer->stop(); // Stop any pending backoff timer - pInfo->lastHeartbeat.start(); - _requestCameraInfo(pInfo); - } else { - //-- Just update heartbeat - pInfo->lastHeartbeat.start(); - } - } - } else { - qWarning() << Q_FUNC_INFO << "_cameraInfoRequest[" << sCompID << "] is null"; - } + return; + } + + CameraStruct *pInfo = _cameraInfoRequest[sCompID]; + if (!pInfo) { + qCWarning(CameraManagerLog) << sCompID << "is null"; + return; } + + if (pInfo->infoReceived) { + pInfo->lastHeartbeat.start(); + return; + } + + if (pInfo->lastHeartbeat.elapsed() > kSilentTimeoutMs) { + qCDebug(CameraManagerLog) << "Camera" << QGCMAVLink::compIdToString(message.compid) << "reappeared after being silent. Resetting retry count and requesting info."; + pInfo->retryCount = 0; + pInfo->backoffTimer.stop(); + pInfo->lastHeartbeat.start(); + _requestCameraInfo(pInfo); + return; + } + + pInfo->lastHeartbeat.start(); } -MavlinkCameraControl* QGCCameraManager::currentCameraInstance() +MavlinkCameraControl *QGCCameraManager::currentCameraInstance() { - if(_currentCameraIndex < _cameras.count() && _cameras.count()) { - auto pCamera = qobject_cast(_cameras[_currentCameraIndex]); + if ((_currentCameraIndex < _cameras.count()) && !_cameras.isEmpty()) { + MavlinkCameraControl *pCamera = qobject_cast(_cameras[_currentCameraIndex]); return pCamera; } return nullptr; } -QGCVideoStreamInfo* QGCCameraManager::currentStreamInstance() +QGCVideoStreamInfo *QGCCameraManager::currentStreamInstance() { - auto pCamera = currentCameraInstance(); - if(pCamera) { - QGCVideoStreamInfo* pInfo = pCamera->currentStreamInstance(); + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { + QGCVideoStreamInfo *pInfo = pCamera->currentStreamInstance(); return pInfo; } return nullptr; } -QGCVideoStreamInfo* QGCCameraManager::thermalStreamInstance() +QGCVideoStreamInfo *QGCCameraManager::thermalStreamInstance() { - auto pCamera = currentCameraInstance(); - if(pCamera) { - QGCVideoStreamInfo* pInfo = pCamera->thermalStreamInstance(); + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { + QGCVideoStreamInfo *pInfo = pCamera->thermalStreamInstance(); return pInfo; } return nullptr; } -MavlinkCameraControl* QGCCameraManager::_findCamera(int id) +MavlinkCameraControl *QGCCameraManager::_findCamera(int id) { - for(int i = 0; i < _cameras.count(); i++) { - if(_cameras[i]) { - auto pCamera = qobject_cast(_cameras[i]); - if(pCamera) { - if(pCamera->compID() == id) { - return pCamera; - } - } else { - qCritical() << "Null MavlinkCameraControl instance"; - } + for (int i = 0; i < _cameras.count(); i++) { + if (!_cameras[i]) { + continue; + } + MavlinkCameraControl *pCamera = qobject_cast(_cameras[i]); + if (!pCamera) { + qCCritical(CameraManagerLog) << "Invalid MavlinkCameraControl instance"; + continue; + } + if (pCamera->compID() == id) { + return pCamera; } } - //qWarning() << "Camera component id not found:" << id; + + // qCWarning(CameraManagerLog) << "Camera component id not found:" << id; return nullptr; } -void QGCCameraManager::_addCameraControlToLists(MavlinkCameraControl* cameraControl) +void QGCCameraManager::_addCameraControlToLists(MavlinkCameraControl *cameraControl) { - QQmlEngine::setObjectOwnership(cameraControl, QQmlEngine::CppOwnership); + if (qobject_cast(cameraControl)) { + qCDebug(CameraManagerLog) << "Adding simulated camera to list"; + } else { + qCDebug(CameraManagerLog) << "Adding real camera to list - simulated camera will be removed if present"; + } + _cameras.append(cameraControl); _cameraLabels.append(cameraControl->modelName()); emit camerasChanged(); emit cameraLabelsChanged(); - // If the simulated camera is already in the list, remove it since we have a real camera now - if (_cameras.count() == 2 && _cameras[0] == _simulatedCameraControl) { - _cameras.removeAt(0); - _cameraLabels.removeAt(0); + // If simulated camera is in list, remove it when a real camera appears + if ((_cameras.count() == 2) && (_cameras[0] == _simulatedCameraControl)) { + (void) _cameras.removeAt(0); + (void) _cameraLabels.removeAt(0); emit camerasChanged(); emit cameraLabelsChanged(); emit currentCameraChanged(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleCameraInfo(const mavlink_message_t& message) +void QGCCameraManager::_handleCameraInfo(const mavlink_message_t& message) { - qCDebug(CameraManagerLog) << "_handleCameraInfo"; - //-- Have we requested it? - QString sCompID = QString::number(message.compid); - if(_cameraInfoRequest.contains(sCompID) && !_cameraInfoRequest[sCompID]->infoReceived) { - //-- Flag it as done + const QString sCompID = QString::number(message.compid); + if (!_cameraInfoRequest.contains(sCompID)) { + qCDebug(CameraManagerLog) << "Ignoring - Camera info not requested for component" << QGCMAVLink::compIdToString(message.compid); + return; + } + if (_cameraInfoRequest[sCompID]->infoReceived) { + qCDebug(CameraManagerLog) << "Ignoring - Already received camera info for component" << QGCMAVLink::compIdToString(message.compid); + return; + } + + mavlink_camera_information_t info{}; + mavlink_msg_camera_information_decode(&message, &info); + qCDebug(CameraManagerLog) << "Camera information received from" << QGCMAVLink::compIdToString(message.compid) + << "Model:" << reinterpret_cast(info.model_name); + qCDebug(CameraManagerLog) << "Creating MavlinkCameraControl for camera"; + + MavlinkCameraControl *pCamera = _vehicle->firmwarePlugin()->createCameraControl(&info, _vehicle, message.compid, this); + if (pCamera) { + _addCameraControlToLists(pCamera); + _cameraInfoRequest[sCompID]->infoReceived = true; - _cameraInfoRequest[sCompID]->retryCount = 0; // Reset retry counter on success - _cameraInfoRequest[sCompID]->backoffTimer->stop(); // Stop any pending backoff timer - qCDebug(CameraManagerLog) << "_handleCameraInfo: Success for compId" << message.compid << "- reset retry counter"; - mavlink_camera_information_t info; - mavlink_msg_camera_information_decode(&message, &info); - qCDebug(CameraManagerLog) << "_handleCameraInfo:" << reinterpret_cast(info.model_name) << reinterpret_cast(info.vendor_name) << "Comp ID:" << message.compid; - auto pCamera = _vehicle->firmwarePlugin()->createCameraControl(&info, _vehicle, message.compid, this); - if(pCamera) { - _addCameraControlToLists(pCamera); - } + _cameraInfoRequest[sCompID]->retryCount = 0; + _cameraInfoRequest[sCompID]->backoffTimer.stop(); + qCDebug(CameraManagerLog) << "Success for compId" << QGCMAVLink::compIdToString(message.compid) << "- reset retry counter"; } } -/// Called to check for cameras which are no longer sending a heartbeat void QGCCameraManager::_checkForLostCameras() { - //-- Iterate cameras - for (auto it = _cameraInfoRequest.constBegin(); it != _cameraInfoRequest.constEnd(); ++it) { - const QString &sCompID = it.key(); - if (_cameraInfoRequest[sCompID]) { - CameraStruct* pInfo = _cameraInfoRequest[sCompID]; - //-- Have we received a camera info message? - if (pInfo->infoReceived) { - //-- Has the camera stopped talking to us? - if (pInfo->lastHeartbeat.elapsed() > 5000) { - auto pCamera = _findCamera(pInfo->compID); - - if (pCamera) { - // Before removing the current camera from the list add the simulated camera back into the list if thera are no other cameras. - // This way we smaoothly transition from a real camera to the simulated camera. - if (_cameras.count() == 1) { - qCDebug(CameraManagerLog) << "Adding simulated camera back to list."; - _addCameraControlToLists(_simulatedCameraControl); - } - - qWarning() << "Camera" << pCamera->modelName() << "stopped transmitting. Removing from list."; - _cameraLabels.removeOne(pCamera->modelName()); - _cameras.removeOne(pCamera); - emit cameraLabelsChanged(); - emit camerasChanged(); - - pCamera->deleteLater(); - delete pInfo; - - // There will always be at least one camera in the list, so we don't need to check if the list is empty. - // We specifically don't use setCurrentCamera since that checks for a index change. But in this case we may be using the same index. - _currentCameraIndex = 0; - emit currentCameraChanged(); - emit streamChanged(); - } - - _cameraInfoRequest.remove(sCompID); - - //-- Exit loop. - return; - } + QList stale; + for (auto it = _cameraInfoRequest.cbegin(), end = _cameraInfoRequest.cend(); it != end; ++it) { + const auto *info = it.value(); + if (info && info->infoReceived && (info->lastHeartbeat.elapsed() > kSilentTimeoutMs)) { + stale.push_back(it.key()); + } + } + if (stale.isEmpty()) { + return; + } + + bool removedAny = false; + for (const QString& key : std::as_const(stale)) { + CameraStruct* pInfo = _cameraInfoRequest.take(key); + if (!pInfo) { + continue; + } + + MavlinkCameraControl* pCamera = _findCamera(pInfo->compID); + if (pCamera) { + const int idx = _cameras.indexOf(pCamera); + if (idx >= 0) { + qCDebug(CameraManagerLog) << "Removing lost camera" << QGCMAVLink::compIdToString(pInfo->compID); + removedAny = true; + (void) _cameraLabels.removeAt(idx); + (void) _cameras.removeAt(idx); + pCamera->deleteLater(); } } + + delete pInfo; } + + if (!removedAny) { + return; + } + + if (_cameras.isEmpty()) { + _addCameraControlToLists(_simulatedCameraControl); + } + + emit cameraLabelsChanged(); + emit camerasChanged(); + + if (_currentCameraIndex != 0) { + _currentCameraIndex = 0; + emit currentCameraChanged(); + } + emit streamChanged(); } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleCaptureStatus(const mavlink_message_t &message) +void QGCCameraManager::_handleCaptureStatus(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_camera_capture_status_t cap; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_camera_capture_status_t cap{}; mavlink_msg_camera_capture_status_decode(&message, &cap); pCamera->handleCaptureStatus(cap); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleStorageInfo(const mavlink_message_t& message) +void QGCCameraManager::_handleStorageInfo(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_storage_information_t st; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_storage_information_t st{}; mavlink_msg_storage_information_decode(&message, &st); pCamera->handleStorageInfo(st); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleCameraSettings(const mavlink_message_t& message) +void QGCCameraManager::_handleCameraSettings(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_camera_settings_t settings; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_camera_settings_t settings{}; mavlink_msg_camera_settings_decode(&message, &settings); pCamera->handleSettings(settings); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleParamAck(const mavlink_message_t& message) +void QGCCameraManager::_handleParamAck(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_param_ext_ack_t ack; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_param_ext_ack_t ack{}; mavlink_msg_param_ext_ack_decode(&message, &ack); pCamera->handleParamAck(ack); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleParamValue(const mavlink_message_t& message) +void QGCCameraManager::_handleParamValue(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_param_ext_value_t value; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_param_ext_value_t value{}; mavlink_msg_param_ext_value_decode(&message, &value); pCamera->handleParamValue(value); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleVideoStreamInfo(const mavlink_message_t& message) +void QGCCameraManager::_handleVideoStreamInfo(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_video_stream_information_t streamInfo; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_video_stream_information_t streamInfo{}; mavlink_msg_video_stream_information_decode(&message, &streamInfo); pCamera->handleVideoInfo(&streamInfo); emit streamChanged(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleVideoStreamStatus(const mavlink_message_t& message) +void QGCCameraManager::_handleVideoStreamStatus(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_video_stream_status_t streamStatus; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_video_stream_status_t streamStatus{}; mavlink_msg_video_stream_status_decode(&message, &streamStatus); pCamera->handleVideoStatus(&streamStatus); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleBatteryStatus(const mavlink_message_t& message) +void QGCCameraManager::_handleBatteryStatus(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_battery_status_t batteryStatus; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_battery_status_t batteryStatus{}; mavlink_msg_battery_status_decode(&message, &batteryStatus); pCamera->handleBatteryStatus(batteryStatus); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_handleTrackingImageStatus(const mavlink_message_t& message) +void QGCCameraManager::_handleTrackingImageStatus(const mavlink_message_t &message) { - auto pCamera = _findCamera(message.compid); - if(pCamera) { - mavlink_camera_tracking_image_status_t tis; + MavlinkCameraControl *pCamera = _findCamera(message.compid); + if (pCamera) { + mavlink_camera_tracking_image_status_t tis{}; mavlink_msg_camera_tracking_image_status_decode(&message, &tis); pCamera->handleTrackingImageStatus(&tis); } } -// Forward declarations for mutually recursive handler functions -static void _requestCameraInfoCommandResultHandler(void* resultHandlerData, int compId, const mavlink_command_ack_t& ack, Vehicle::MavCmdResultFailureCode_t failureCode); -static void _requestCameraInfoMessageResultHandler(void* resultHandlerData, MAV_RESULT result, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t& message); -static void _requestCameraInfoHelper(QGCCameraManager* manager, QGCCameraManager::CameraStruct* pInfo); - +static void _handleCameraInfoRetry(QGCCameraManager::CameraStruct *cameraInfo); -static void _handleCameraInfoRetry(QGCCameraManager::CameraStruct* cameraInfo) +static void _requestCameraInfoCommandResultHandler(void *resultHandlerData, int /*compId*/, const mavlink_command_ack_t &ack, Vehicle::MavCmdResultFailureCode_t failureCode) { - cameraInfo->retryCount++; - auto manager = static_cast(cameraInfo->parent()); - - // For even attempts >= 2, use exponential backoff - if (cameraInfo->retryCount >= 2 && cameraInfo->retryCount % 2 == 0) { - // Calculate delay: 2^(retryCount/2) seconds - int delaySeconds = 1 << (cameraInfo->retryCount / 2); - int delayMs = delaySeconds * 1000; - - qCDebug(CameraManagerLog) << "Waiting" << delaySeconds << "seconds before retry for compId" << cameraInfo->compID; - - // Stop any existing timer and set up new one - cameraInfo->backoffTimer->stop(); - QObject::disconnect(cameraInfo->backoffTimer, nullptr, nullptr, nullptr); - QObject::connect(cameraInfo->backoffTimer, &QTimer::timeout, manager, [=]() { - _requestCameraInfoHelper(manager, cameraInfo); - }); - - cameraInfo->backoffTimer->start(delayMs); - } else { - // Make immediate retry - _requestCameraInfoHelper(manager, cameraInfo); - } -} - -static void _requestCameraInfoCommandResultHandler(void* resultHandlerData, int compId, const mavlink_command_ack_t& ack, Vehicle::MavCmdResultFailureCode_t failureCode) -{ - auto cameraInfo = static_cast(resultHandlerData); + auto *cameraInfo = static_cast(resultHandlerData); if (ack.result != MAV_RESULT_ACCEPTED) { - qCDebug(CameraManagerLog) << "MAV_CMD_REQUEST_CAMERA_INFORMATION failed. compId" << cameraInfo->compID << "Result:" << ack.result << "FailureCode:" << failureCode << "retryCount:" << cameraInfo->retryCount; - + qCDebug(CameraManagerLog) << "MAV_CMD_REQUEST_CAMERA_INFORMATION failed. compId" << QGCMAVLink::compIdToString(cameraInfo->compID) + << "Result:" << QGCMAVLink::mavResultToString(ack.result) + << "FailureCode:" << Vehicle::mavCmdResultFailureCodeToString(failureCode) + << "retryCount:" << cameraInfo->retryCount; _handleCameraInfoRetry(cameraInfo); } } -static void _requestCameraInfoMessageResultHandler(void* resultHandlerData, MAV_RESULT result, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, [[maybe_unused]] const mavlink_message_t& message) +static void _requestCameraInfoMessageResultHandler(void *resultHandlerData, MAV_RESULT result, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, [[maybe_unused]] const mavlink_message_t &message) { - auto cameraInfo = static_cast(resultHandlerData); + auto *cameraInfo = static_cast(resultHandlerData); if (result != MAV_RESULT_ACCEPTED) { - qCDebug(CameraManagerLog) << "MAV_CMD_REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_INFORMATION failed. compId" << cameraInfo->compID << "Result:" << result << "FailureCode:" << failureCode << "retryCount:" << cameraInfo->retryCount; - + qCDebug(CameraManagerLog) << "MAV_CMD_REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_INFORMATION failed. compId" << QGCMAVLink::compIdToString(cameraInfo->compID) + << "Result:" << QGCMAVLink::mavResultToString(result) + << "FailureCode:" << Vehicle::requestMessageResultHandlerFailureCodeToString(failureCode) + << "retryCount:" << cameraInfo->retryCount; _handleCameraInfoRetry(cameraInfo); } } -//----------------------------------------------------------------------------- -static void _requestCameraInfoHelper(QGCCameraManager* manager, QGCCameraManager::CameraStruct* pInfo) +static void _requestCameraInfoHelper(QGCCameraManager *manager, QGCCameraManager::CameraStruct *pInfo) { - // Give up after 10 attempts - if (pInfo->retryCount >= 10) { - qCWarning(CameraManagerLog) << "Giving up requesting camera info after" << pInfo->retryCount << "attempts for compId" << pInfo->compID; + // Give up after max attempts + if (pInfo->retryCount >= kMaxRetryCount) { + qCDebug(CameraManagerLog) << "Giving up requesting camera info after" << pInfo->retryCount << "attempts for compId" << QGCMAVLink::compIdToString(pInfo->compID); return; } - // Make immediate request - alternate between REQUEST_MESSAGE and REQUEST_CAMERA_INFORMATION - if (pInfo->retryCount % 2 == 0) { - qCDebug(CameraManagerLog) << "Using MAV_CMD_REQUEST_MESSAGE for compId" << pInfo->compID; + // Alternate between REQUEST_MESSAGE and REQUEST_CAMERA_INFORMATION + if ((pInfo->retryCount % 2) == 0) { + qCDebug(CameraManagerLog) << "Using MAV_CMD_REQUEST_MESSAGE:CAMERA_INFORMATION for compId" << QGCMAVLink::compIdToString(pInfo->compID); manager->vehicle()->requestMessage(_requestCameraInfoMessageResultHandler, pInfo, pInfo->compID, MAVLINK_MSG_ID_CAMERA_INFORMATION); } else { - qCDebug(CameraManagerLog) << "Using MAV_CMD_REQUEST_CAMERA_INFORMATION for compId" << pInfo->compID; + qCDebug(CameraManagerLog) << "Using MAV_CMD_REQUEST_CAMERA_INFORMATION for compId" << QGCMAVLink::compIdToString(pInfo->compID); - Vehicle::MavCmdAckHandlerInfo_t ackHandlerInfo; + Vehicle::MavCmdAckHandlerInfo_t ackHandlerInfo{}; ackHandlerInfo.resultHandler = _requestCameraInfoCommandResultHandler; ackHandlerInfo.resultHandlerData = pInfo; ackHandlerInfo.progressHandler = nullptr; @@ -505,230 +488,195 @@ static void _requestCameraInfoHelper(QGCCameraManager* manager, QGCCameraManager } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_requestCameraInfo(CameraStruct* pInfo) +static void _handleCameraInfoRetry(QGCCameraManager::CameraStruct *cameraInfo) +{ + if (!cameraInfo) { + return; + } + + QGCCameraManager *manager = cameraInfo->manager; + if (!manager) { + qCDebug(CameraManagerLog) << "manager is unavailable for compId" << QGCMAVLink::compIdToString(cameraInfo->compID); + return; + } + + cameraInfo->retryCount++; + + // For even attempts >= 2, use exponential backoff + if ((cameraInfo->retryCount >= 2) && ((cameraInfo->retryCount % 2) == 0)) { + const int delaySeconds = 1 << (cameraInfo->retryCount / 2); + const int delayMs = delaySeconds * 1000; + + qCDebug(CameraManagerLog) << "Waiting" << delaySeconds << "seconds before retry for compId" << QGCMAVLink::compIdToString(cameraInfo->compID); + + cameraInfo->backoffTimer.stop(); + (void) QObject::disconnect(&cameraInfo->backoffTimer, nullptr, nullptr, nullptr); + + // Capture compID by value and look up the struct + const uint8_t compId = cameraInfo->compID; + QPointer mgrGuard(manager); + + (void) QObject::connect(&cameraInfo->backoffTimer, &QTimer::timeout, + &cameraInfo->backoffTimer, // context ensures timer is still alive + [mgrGuard, compId]() { + if (!mgrGuard) { + return; + } + + auto* info = mgrGuard->findCameraStruct(compId); + if (info) { + _requestCameraInfoHelper(mgrGuard.data(), info); + } + }); + + cameraInfo->backoffTimer.start(delayMs); + } else { + _requestCameraInfoHelper(manager, cameraInfo); + } +} + +void QGCCameraManager::_requestCameraInfo(CameraStruct *pInfo) { + if (!pInfo) { + return; + } _requestCameraInfoHelper(this, pInfo); } -//---------------------------------------------------------------------------------------- -void -QGCCameraManager::_activeJoystickChanged(Joystick* joystick) +void QGCCameraManager::_activeJoystickChanged(Joystick *joystick) { qCDebug(CameraManagerLog) << "Joystick changed"; - if(_activeJoystick) { - disconnect(_activeJoystick, &Joystick::stepZoom, this, &QGCCameraManager::_stepZoom); - disconnect(_activeJoystick, &Joystick::startContinuousZoom, this, &QGCCameraManager::_startZoom); - disconnect(_activeJoystick, &Joystick::stopContinuousZoom, this, &QGCCameraManager::_stopZoom); - disconnect(_activeJoystick, &Joystick::stepCamera, this, &QGCCameraManager::_stepCamera); - disconnect(_activeJoystick, &Joystick::stepStream, this, &QGCCameraManager::_stepStream); - disconnect(_activeJoystick, &Joystick::triggerCamera, this, &QGCCameraManager::_triggerCamera); - disconnect(_activeJoystick, &Joystick::startVideoRecord, this, &QGCCameraManager::_startVideoRecording); - disconnect(_activeJoystick, &Joystick::stopVideoRecord, this, &QGCCameraManager::_stopVideoRecording); - disconnect(_activeJoystick, &Joystick::toggleVideoRecord, this, &QGCCameraManager::_toggleVideoRecording); + if (_activeJoystick) { + (void) disconnect(_activeJoystick, &Joystick::stepZoom, this, &QGCCameraManager::_stepZoom); + (void) disconnect(_activeJoystick, &Joystick::startContinuousZoom, this, &QGCCameraManager::_startZoom); + (void) disconnect(_activeJoystick, &Joystick::stopContinuousZoom, this, &QGCCameraManager::_stopZoom); + (void) disconnect(_activeJoystick, &Joystick::stepCamera, this, &QGCCameraManager::_stepCamera); + (void) disconnect(_activeJoystick, &Joystick::stepStream, this, &QGCCameraManager::_stepStream); + (void) disconnect(_activeJoystick, &Joystick::triggerCamera, this, &QGCCameraManager::_triggerCamera); + (void) disconnect(_activeJoystick, &Joystick::startVideoRecord, this, &QGCCameraManager::_startVideoRecording); + (void) disconnect(_activeJoystick, &Joystick::stopVideoRecord, this, &QGCCameraManager::_stopVideoRecording); + (void) disconnect(_activeJoystick, &Joystick::toggleVideoRecord, this, &QGCCameraManager::_toggleVideoRecording); } + _activeJoystick = joystick; - if(_activeJoystick) { - connect(_activeJoystick, &Joystick::stepZoom, this, &QGCCameraManager::_stepZoom); - connect(_activeJoystick, &Joystick::startContinuousZoom, this, &QGCCameraManager::_startZoom); - connect(_activeJoystick, &Joystick::stopContinuousZoom, this, &QGCCameraManager::_stopZoom); - connect(_activeJoystick, &Joystick::stepCamera, this, &QGCCameraManager::_stepCamera); - connect(_activeJoystick, &Joystick::stepStream, this, &QGCCameraManager::_stepStream); - connect(_activeJoystick, &Joystick::triggerCamera, this, &QGCCameraManager::_triggerCamera); - connect(_activeJoystick, &Joystick::startVideoRecord, this, &QGCCameraManager::_startVideoRecording); - connect(_activeJoystick, &Joystick::stopVideoRecord, this, &QGCCameraManager::_stopVideoRecording); - connect(_activeJoystick, &Joystick::toggleVideoRecord, this, &QGCCameraManager::_toggleVideoRecording); + + if (_activeJoystick) { + (void) connect(_activeJoystick, &Joystick::stepZoom, this, &QGCCameraManager::_stepZoom, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::startContinuousZoom, this, &QGCCameraManager::_startZoom, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::stopContinuousZoom, this, &QGCCameraManager::_stopZoom, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::stepCamera, this, &QGCCameraManager::_stepCamera, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::stepStream, this, &QGCCameraManager::_stepStream, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::triggerCamera, this, &QGCCameraManager::_triggerCamera, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::startVideoRecord, this, &QGCCameraManager::_startVideoRecording, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::stopVideoRecord, this, &QGCCameraManager::_stopVideoRecording, Qt::UniqueConnection); + (void) connect(_activeJoystick, &Joystick::toggleVideoRecord, this, &QGCCameraManager::_toggleVideoRecording, Qt::UniqueConnection); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_triggerCamera() +void QGCCameraManager::_triggerCamera() { - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->takePhoto(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_startVideoRecording() +void QGCCameraManager::_startVideoRecording() { - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->startVideoRecording(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_stopVideoRecording() +void QGCCameraManager::_stopVideoRecording() { - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->stopVideoRecording(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_toggleVideoRecording() +void QGCCameraManager::_toggleVideoRecording() { - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->toggleVideoRecording(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_stepZoom(int direction) +void QGCCameraManager::_stepZoom(int direction) { - if(_lastZoomChange.elapsed() > 40) { + if (_lastZoomChange.elapsed() > 40) { _lastZoomChange.start(); qCDebug(CameraManagerLog) << "Step Camera Zoom" << direction; - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->stepZoom(direction); } } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_startZoom(int direction) +void QGCCameraManager::_startZoom(int direction) { qCDebug(CameraManagerLog) << "Start Camera Zoom" << direction; - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->startZoom(direction); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_stopZoom() +void QGCCameraManager::_stopZoom() { qCDebug(CameraManagerLog) << "Stop Camera Zoom"; - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { pCamera->stopZoom(); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_stepCamera(int direction) +void QGCCameraManager::_stepCamera(int direction) { - if(_lastCameraChange.elapsed() > 1000) { + if (_lastCameraChange.elapsed() > 1000) { _lastCameraChange.start(); qCDebug(CameraManagerLog) << "Step Camera" << direction; - int c = _currentCameraIndex + direction; - if(c < 0) c = _cameras.count() - 1; - if(c >= _cameras.count()) c = 0; - setCurrentCamera(c); + int camera = _currentCameraIndex + direction; + if (camera < 0) { + camera = _cameras.count() - 1; + } else if (camera >= _cameras.count()) { + camera = 0; + } + setCurrentCamera(camera); } } -//----------------------------------------------------------------------------- -void -QGCCameraManager::_stepStream(int direction) +void QGCCameraManager::_stepStream(int direction) { - if(_lastCameraChange.elapsed() > 1000) { + if (_lastCameraChange.elapsed() > 1000) { _lastCameraChange.start(); - auto pCamera = currentCameraInstance(); - if(pCamera) { + MavlinkCameraControl *pCamera = currentCameraInstance(); + if (pCamera) { qCDebug(CameraManagerLog) << "Step Camera Stream" << direction; - int c = pCamera->currentStream() + direction; - if(c < 0) c = pCamera->streams()->count() - 1; - if(c >= pCamera->streams()->count()) c = 0; - pCamera->setCurrentStream(c); + int stream = pCamera->currentStream() + direction; + if (stream < 0) { + stream = pCamera->streams()->count() - 1; + } else if (stream >= pCamera->streams()->count()) { + stream = 0; + } + pCamera->setCurrentStream(stream); } } } -const QVariantList &QGCCameraManager::cameraList() +const QVariantList &QGCCameraManager::cameraList() const { if (_cameraList.isEmpty()) { - const QList cams = _parseCameraMetaData(QStringLiteral(":/json/CameraMetaData.json")); + const QList cams = CameraMetaData::parseCameraMetaData(); _cameraList.reserve(cams.size()); - for (CameraMetaData* cam : cams) { + for (CameraMetaData *cam : cams) { _cameraList << QVariant::fromValue(cam); } } - return _cameraList; } - -QList QGCCameraManager::_parseCameraMetaData(const QString &jsonFilePath) -{ - QList cameraList; - - QString errorString; - int version; - const QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(jsonFilePath, "CameraMetaData", 1, 1, version, errorString); - if (!errorString.isEmpty()) { - qCWarning(CameraManagerLog) << "Internal Error:" << errorString; - return cameraList; - } - - static const QList rootKeyInfoList = { - { "cameraMetaData", QJsonValue::Array, true } - }; - if (!JsonHelper::validateKeys(jsonObject, rootKeyInfoList, errorString)) { - qCWarning(CameraManagerLog) << errorString; - return cameraList; - } - - static const QList cameraKeyInfoList = { - { "canonicalName", QJsonValue::String, true }, - { "brand", QJsonValue::String, true }, - { "model", QJsonValue::String, true }, - { "sensorWidth", QJsonValue::Double, true }, - { "sensorHeight", QJsonValue::Double, true }, - { "imageWidth", QJsonValue::Double, true }, - { "imageHeight", QJsonValue::Double, true }, - { "focalLength", QJsonValue::Double, true }, - { "landscape", QJsonValue::Bool, true }, - { "fixedOrientation", QJsonValue::Bool, true }, - { "minTriggerInterval", QJsonValue::Double, true }, - { "deprecatedTranslatedName", QJsonValue::String, true }, - }; - const QJsonArray cameraInfo = jsonObject["cameraMetaData"].toArray(); - for (const QJsonValue &jsonValue : cameraInfo) { - if (!jsonValue.isObject()) { - qCWarning(CameraManagerLog) << "Entry in CameraMetaData array is not object"; - return cameraList; - } - - const QJsonObject obj = jsonValue.toObject(); - if (!JsonHelper::validateKeys(obj, cameraKeyInfoList, errorString)) { - qCWarning(CameraManagerLog) << errorString; - return cameraList; - } - - const QString canonicalName = obj["canonicalName"].toString(); - const QString brand = obj["brand"].toString(); - const QString model = obj["model"].toString(); - const double sensorWidth = obj["sensorWidth"].toDouble(); - const double sensorHeight = obj["sensorHeight"].toDouble(); - const double imageWidth = obj["imageWidth"].toDouble(); - const double imageHeight = obj["imageHeight"].toDouble(); - const double focalLength = obj["focalLength"].toDouble(); - const bool landscape = obj["landscape"].toBool(); - const bool fixedOrientation = obj["fixedOrientation"].toBool(); - const double minTriggerInterval = obj["minTriggerInterval"].toDouble(); - const QString deprecatedTranslatedName = obj["deprecatedTranslatedName"].toString(); - - CameraMetaData *const metaData = new CameraMetaData( - canonicalName, brand, model, sensorWidth, sensorHeight, - imageWidth, imageHeight, focalLength, landscape, - fixedOrientation, minTriggerInterval, deprecatedTranslatedName); - cameraList.append(metaData); - } - - return cameraList; -} diff --git a/src/Camera/QGCCameraManager.h b/src/Camera/QGCCameraManager.h index 74c658cbc567..4aa7aa1f23b1 100644 --- a/src/Camera/QGCCameraManager.h +++ b/src/Camera/QGCCameraManager.h @@ -9,27 +9,28 @@ #pragma once -#include "QmlObjectListModel.h" -#include "MavlinkCameraControl.h" - #include #include #include #include +#include #include #include #include +#include "MAVLinkLib.h" +#include "QmlObjectListModel.h" +#include "Vehicle.h" + Q_DECLARE_LOGGING_CATEGORY(CameraManagerLog) -class Joystick; -class SimulatedCameraControl; -class QGCVideoStreamInfo; -class Vehicle; class CameraMetaData; +class Joystick; +class MavlinkCameraControl; class QGCCameraManagerTest; +class QGCVideoStreamInfo; +class SimulatedCameraControl; -//----------------------------------------------------------------------------- /// Camera Manager class QGCCameraManager : public QObject { @@ -37,92 +38,98 @@ class QGCCameraManager : public QObject QML_ELEMENT QML_UNCREATABLE("") Q_MOC_INCLUDE("Joystick.h") + Q_MOC_INCLUDE("MavlinkCameraControl.h") + + Q_PROPERTY(QmlObjectListModel* cameras READ cameras NOTIFY camerasChanged) + Q_PROPERTY(QStringList cameraLabels READ cameraLabels NOTIFY cameraLabelsChanged) + Q_PROPERTY(MavlinkCameraControl* currentCameraInstance READ currentCameraInstance NOTIFY currentCameraChanged) + Q_PROPERTY(int currentCamera READ currentCamera WRITE setCurrentCamera NOTIFY currentCameraChanged) + friend class QGCCameraManagerTest; + public: - QGCCameraManager(Vehicle* vehicle); - virtual ~QGCCameraManager(); - - Q_PROPERTY(QmlObjectListModel* cameras READ cameras NOTIFY camerasChanged) - Q_PROPERTY(QStringList cameraLabels READ cameraLabels NOTIFY cameraLabelsChanged) - Q_PROPERTY(MavlinkCameraControl* currentCameraInstance READ currentCameraInstance NOTIFY currentCameraChanged) - Q_PROPERTY(int currentCamera READ currentCamera WRITE setCurrentCamera NOTIFY currentCameraChanged) - - virtual QmlObjectListModel* cameras () { return &_cameras; } ///< List of cameras provided by current vehicle - virtual QStringList cameraLabels () { return _cameraLabels; } ///< Camera names to show the user (for selection) - virtual int currentCamera () { return _currentCameraIndex; } ///< Current selected camera - virtual MavlinkCameraControl* currentCameraInstance(); - virtual void setCurrentCamera (int sel); - virtual QGCVideoStreamInfo* currentStreamInstance(); - virtual QGCVideoStreamInfo* thermalStreamInstance(); - - /// Returns a list of CameraMetaData objects for available cameras on the vehicle. - virtual const QVariantList &cameraList(); - - // Helper method for static functions to access vehicle - Vehicle* vehicle() const { return _vehicle; } + explicit QGCCameraManager(Vehicle* vehicle); + ~QGCCameraManager(); - // This is public to avoid some circular include problems caused by statics - class CameraStruct : public QObject { - public: - CameraStruct(QObject* parent, uint8_t compID_, Vehicle* vehicle); + struct CameraStruct { + CameraStruct(QGCCameraManager* manager_, uint8_t compID_, Vehicle* vehicle_); + ~CameraStruct(); + + bool infoReceived = false; + uint8_t compID = 0; + int retryCount = 0; QElapsedTimer lastHeartbeat; - bool infoReceived = false; - uint8_t compID = 0; - Vehicle* vehicle = nullptr; - int retryCount = 0; - QTimer* backoffTimer = nullptr; + QTimer backoffTimer; + QPointer vehicle; + QPointer manager; + + private: + Q_DISABLE_COPY_MOVE(CameraStruct) }; + QmlObjectListModel* cameras() { return &_cameras; } + const QmlObjectListModel* cameras() const { return &_cameras; } + QStringList cameraLabels() const { return _cameraLabels; } + int currentCamera() const { return _currentCameraIndex; } + MavlinkCameraControl* currentCameraInstance(); + void setCurrentCamera(int sel); + QGCVideoStreamInfo* currentStreamInstance(); + QGCVideoStreamInfo* thermalStreamInstance(); + + const QVariantList& cameraList() const; + + Vehicle* vehicle() const { return _vehicle; } + + CameraStruct* findCameraStruct(uint8_t compId) const { return _cameraInfoRequest.value(QString::number(compId), nullptr); } + signals: - void camerasChanged (); - void cameraLabelsChanged (); - void currentCameraChanged (); - void streamChanged (); + void camerasChanged(); + void cameraLabelsChanged(); + void currentCameraChanged(); + void streamChanged(); protected slots: - virtual void _vehicleReady (bool ready); - virtual void _mavlinkMessageReceived (const mavlink_message_t& message); - virtual void _activeJoystickChanged (Joystick* joystick); - virtual void _stepZoom (int direction); - virtual void _startZoom (int direction); - virtual void _stopZoom (); - virtual void _stepCamera (int direction); - virtual void _stepStream (int direction); - virtual void _checkForLostCameras (); - virtual void _triggerCamera (); - virtual void _startVideoRecording (); - virtual void _stopVideoRecording (); - virtual void _toggleVideoRecording (); - -protected: - virtual MavlinkCameraControl* _findCamera(int id); - virtual void _requestCameraInfo (CameraStruct* cameraInfo); - virtual void _handleHeartbeat (const mavlink_message_t& message); - virtual void _handleCameraInfo (const mavlink_message_t& message); - virtual void _handleStorageInfo (const mavlink_message_t& message); - virtual void _handleCameraSettings (const mavlink_message_t& message); - virtual void _handleParamAck (const mavlink_message_t& message); - virtual void _handleParamValue (const mavlink_message_t& message); - virtual void _handleCaptureStatus (const mavlink_message_t& message); - virtual void _handleVideoStreamInfo (const mavlink_message_t& message); - virtual void _handleVideoStreamStatus(const mavlink_message_t& message); - virtual void _handleBatteryStatus (const mavlink_message_t& message); - virtual void _handleTrackingImageStatus(const mavlink_message_t& message); - virtual void _addCameraControlToLists(MavlinkCameraControl* cameraControl); - - static QList _parseCameraMetaData(const QString &jsonFilePath); - - Vehicle* _vehicle = nullptr; - Joystick* _activeJoystick = nullptr; - bool _vehicleReadyState = false; - int _currentTask = 0; - QmlObjectListModel _cameras; - QStringList _cameraLabels; - int _currentCameraIndex = 0; - QElapsedTimer _lastZoomChange; - QElapsedTimer _lastCameraChange; - QTimer _camerasLostHeartbeatTimer; + void _vehicleReady(bool ready); + void _mavlinkMessageReceived(const mavlink_message_t& message); + void _activeJoystickChanged(Joystick* joystick); + void _stepZoom(int direction); + void _startZoom(int direction); + void _stopZoom(); + void _stepCamera(int direction); + void _stepStream(int direction); + void _checkForLostCameras(); + void _triggerCamera(); + void _startVideoRecording(); + void _stopVideoRecording(); + void _toggleVideoRecording(); + +private: + MavlinkCameraControl* _findCamera(int id); + void _requestCameraInfo(CameraStruct* cameraInfo); + void _handleHeartbeat(const mavlink_message_t& message); + void _handleCameraInfo(const mavlink_message_t& message); + void _handleStorageInfo(const mavlink_message_t& message); + void _handleCameraSettings(const mavlink_message_t& message); + void _handleParamAck(const mavlink_message_t& message); + void _handleParamValue(const mavlink_message_t& message); + void _handleCaptureStatus(const mavlink_message_t& message); + void _handleVideoStreamInfo(const mavlink_message_t& message); + void _handleVideoStreamStatus(const mavlink_message_t& message); + void _handleBatteryStatus(const mavlink_message_t& message); + void _handleTrackingImageStatus(const mavlink_message_t& message); + void _addCameraControlToLists(MavlinkCameraControl* cameraControl); + + QPointer _vehicle; + QPointer _simulatedCameraControl; + QPointer _activeJoystick; + bool _vehicleReadyState = false; + int _currentTask = 0; + QmlObjectListModel _cameras; + QStringList _cameraLabels; + int _currentCameraIndex = 0; + QElapsedTimer _lastZoomChange; + QElapsedTimer _lastCameraChange; + QTimer _camerasLostHeartbeatTimer; QMap _cameraInfoRequest; - SimulatedCameraControl* _simulatedCameraControl = nullptr; - static QVariantList _cameraList; ///< Standard QGC camera list + static QVariantList _cameraList; }; diff --git a/src/Camera/QGCVideoStreamInfo.cc b/src/Camera/QGCVideoStreamInfo.cc index ae30ac24e6fa..1cd2ce892d2f 100644 --- a/src/Camera/QGCVideoStreamInfo.cc +++ b/src/Camera/QGCVideoStreamInfo.cc @@ -10,7 +10,7 @@ #include "QGCVideoStreamInfo.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(QGCVideoStreamInfoLog, "qgc.camera.qgcvideostreaminfo") +QGC_LOGGING_CATEGORY(QGCVideoStreamInfoLog, "Camera.QGCVideoStreamInfo") QGCVideoStreamInfo::QGCVideoStreamInfo(const mavlink_video_stream_information_t &info, QObject *parent) : QObject(parent) diff --git a/src/Camera/SimulatedCameraControl.cc b/src/Camera/SimulatedCameraControl.cc index cbb3e0072991..8e6a6c39cdae 100644 --- a/src/Camera/SimulatedCameraControl.cc +++ b/src/Camera/SimulatedCameraControl.cc @@ -15,17 +15,19 @@ #include "Vehicle.h" #include "VideoManager.h" -QGC_LOGGING_CATEGORY(SimulatedCameraControlLog, "qgc.camera.simulatedcameracontrol") +QGC_LOGGING_CATEGORY(SimulatedCameraControlLog, "Camera.SimulatedCameraControl") SimulatedCameraControl::SimulatedCameraControl(Vehicle *vehicle, QObject *parent) : MavlinkCameraControl(vehicle, parent) { qCDebug(SimulatedCameraControlLog) << this; - (void) connect(VideoManager::instance(), &VideoManager::recordingChanged, this, [this](bool recording) { + auto videoManager = VideoManager::instance(); + (void) connect(videoManager, &VideoManager::recordingChanged, this, [this](bool recording) { _videoCaptureStatus = recording ? VIDEO_CAPTURE_STATUS_RUNNING : VIDEO_CAPTURE_STATUS_STOPPED; emit videoCaptureStatusChanged(); }); + connect(videoManager, &VideoManager::hasVideoChanged, this, &SimulatedCameraControl::infoChanged); (void) connect(SettingsManager::instance()->flyViewSettings()->showSimpleCameraControl(), &Fact::rawValueChanged, this, &SimulatedCameraControl::infoChanged); diff --git a/src/Camera/VehicleCameraControl.cc b/src/Camera/VehicleCameraControl.cc index 02a5851e5e47..4360aa8a672c 100644 --- a/src/Camera/VehicleCameraControl.cc +++ b/src/Camera/VehicleCameraControl.cc @@ -28,6 +28,7 @@ #include "LinkInterface.h" #include "MAVLinkProtocol.h" #include "QGCVideoStreamInfo.h" +#include "MissionCommandTree.h" #include #include @@ -636,14 +637,14 @@ VehicleCameraControl::_requestCaptureStatus() qCDebug(CameraControlLog) << "_requestCaptureStatus() - retries:" << _cameraCaptureStatusRetries; if(_cameraCaptureStatusRetries++ % 2 == 0) { - qCDebug(CameraControlLog) << "_requestCaptureStatus() - using REQUEST_MESSAGE"; + qCDebug(CameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS"; _vehicle->sendMavCommand( _compID, // target component MAV_CMD_REQUEST_MESSAGE, // command id false, // showError MAVLINK_MSG_ID_CAMERA_CAPTURE_STATUS); // msgid } else { - qCDebug(CameraControlLog) << "_requestCaptureStatus() - using legacy MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS"; + qCDebug(CameraControlLog) << " Sending MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS (legacy)"; _vehicle->sendMavCommand( _compID, // target component MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS, // command id @@ -739,7 +740,7 @@ VehicleCameraControl::_mavCommandResult(int vehicleId, int component, int comman break; } } else { - qCDebug(CameraControlLog) << "Bad response for" << command << result; + qCDebug(CameraControlLog) << "Bad response for" << MissionCommandTree::instance()->rawName(static_cast(command)) << QGCMAVLink::mavResultToString(result); } } } @@ -1429,14 +1430,14 @@ VehicleCameraControl::_requestCameraSettings() // first time and every other time after that. if(_cameraSettingsRetries % 2 == 0) { - qCDebug(CameraControlLog) << "_requestCameraSettings() - using REQUEST_MESSAGE"; + qCDebug(CameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_CAMERA_SETTINGS"; _vehicle->sendMavCommand( _compID, // target component MAV_CMD_REQUEST_MESSAGE, // command id false, // showError MAVLINK_MSG_ID_CAMERA_SETTINGS); // msgid } else { - qCDebug(CameraControlLog) << "_requestCameraSettings() - using legacy MAV_CMD_REQUEST_CAMERA_SETTINGS"; + qCDebug(CameraControlLog) << " Sending MAV_CMD_REQUEST_CAMERA_SETTINGS (legacy)"; _vehicle->sendMavCommand( _compID, // Target component MAV_CMD_REQUEST_CAMERA_SETTINGS, // command id @@ -1462,7 +1463,7 @@ VehicleCameraControl::_requestStorageInfo() // Use REQUEST_MESSAGE instead of deprecated REQUEST_STORAGE_INFORMATION // first time and every other time after that. if(_storageInfoRetries % 2 == 0) { - qCDebug(CameraControlLog) << "_requestStorageInfo() - using REQUEST_MESSAGE"; + qCDebug(CameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_STORAGE_INFORMATION"; _vehicle->sendMavCommand( _compID, // target component MAV_CMD_REQUEST_MESSAGE, // command id @@ -1470,7 +1471,7 @@ VehicleCameraControl::_requestStorageInfo() MAVLINK_MSG_ID_STORAGE_INFORMATION, // msgid 0); // storage ID } else { - qCDebug(CameraControlLog) << "_requestStorageInfo() - using legacy MAV_CMD_REQUEST_STORAGE_INFORMATION"; + qCDebug(CameraControlLog) << " Sending MAV_CMD_REQUEST_STORAGE_INFORMATION (legacy)"; _vehicle->sendMavCommand( _compID, // Target component MAV_CMD_REQUEST_STORAGE_INFORMATION, // command id @@ -1487,7 +1488,7 @@ VehicleCameraControl::_requestStorageInfo() void VehicleCameraControl::handleSettings(const mavlink_camera_settings_t& settings) { - qCDebug(CameraControlLog) << "handleSettings() Mode:" << settings.mode_id << "- stopping timer, resetting retries"; + qCDebug(CameraControlLog) << "Received CAMERA_SETTINGS Mode:" << settings.mode_id << "- stopping timer, resetting retries"; _cameraSettingsTimer.stop(); _cameraSettingsRetries = 0; _setCameraMode(static_cast(settings.mode_id)); @@ -1507,7 +1508,7 @@ VehicleCameraControl::handleSettings(const mavlink_camera_settings_t& settings) void VehicleCameraControl::handleStorageInfo(const mavlink_storage_information_t& st) { - qCDebug(CameraControlLog) << "handleStorageInfo() - stopping timer, resetting retries"; + qCDebug(CameraControlLog) << "Received STORAGE_INFORMATION - stopping timer, resetting retries"; _storageInfoTimer.stop(); _storageInfoRetries = 0; qCDebug(CameraControlLog) << "handleStorageInfo:" @@ -1540,7 +1541,7 @@ VehicleCameraControl::handleStorageInfo(const mavlink_storage_information_t& st) void VehicleCameraControl::handleBatteryStatus(const mavlink_battery_status_t& bs) { - qCDebug(CameraControlLog) << "handleBatteryStatus:" << bs.battery_remaining; + qCDebug(CameraControlLog) << "Received BATTERY_STATUS:" << bs.battery_remaining; if(bs.battery_remaining >= 0 && _batteryRemaining != static_cast(bs.battery_remaining)) { _batteryRemaining = static_cast(bs.battery_remaining); emit batteryRemainingChanged(); @@ -1552,7 +1553,7 @@ void VehicleCameraControl::handleCaptureStatus(const mavlink_camera_capture_status_t& cap) { //-- This is a response to MAV_CMD_REQUEST_CAMERA_CAPTURE_STATUS - qCDebug(CameraControlLog) << "handleCaptureStatus() - stopping timer, resetting retries"; + qCDebug(CameraControlLog) << "Received CAMERA_CAPTURE_STATUS - stopping timer, resetting retries"; _captureStatusTimer.stop(); _cameraCaptureStatusRetries = 0; qCDebug(CameraControlLog).noquote() << "handleCaptureStatus:" @@ -1600,7 +1601,7 @@ VehicleCameraControl::handleCaptureStatus(const mavlink_camera_capture_status_t& void VehicleCameraControl::handleVideoInfo(const mavlink_video_stream_information_t* vi) { - qCDebug(CameraControlLog) << "handleVideoInfo:" << vi->stream_id << vi->uri; + qCDebug(CameraControlLog) << "Received VIDEO_STREAM_INFORMATION:" << vi->stream_id << vi->uri; _expectedCount = vi->count; if(!_findStream(vi->stream_id, false)) { qCDebug(CameraControlLog) << "Create stream handler for stream ID:" << vi->stream_id; @@ -1633,7 +1634,7 @@ VehicleCameraControl::handleVideoInfo(const mavlink_video_stream_information_t* void VehicleCameraControl::handleVideoStatus(const mavlink_video_stream_status_t* vs) { - qCDebug(CameraControlLog) << "handleVideoStatus() - stopping timer, resetting retries"; + qCDebug(CameraControlLog) << "Received VIDEO_STREAM_STATUS - stopping timer, resetting retries"; _streamStatusTimer.stop(); _videoStreamStatusRetries = 0; qCDebug(CameraControlLog) << "handleVideoStatus:" << vs->stream_id; @@ -1792,7 +1793,7 @@ VehicleCameraControl::_requestStreamInfo(uint8_t streamID) // By default, try to use new REQUEST_MESSAGE command instead of // deprecated MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION. if (_videoStreamInfoRetries % 2 == 0) { - qCDebug(CameraControlLog) << "_requestStreamInfo() - using REQUEST_MESSAGE"; + qCDebug(CameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION"; _vehicle->sendMavCommand( _compID, // target component MAV_CMD_REQUEST_MESSAGE, // command id @@ -1800,7 +1801,7 @@ VehicleCameraControl::_requestStreamInfo(uint8_t streamID) MAVLINK_MSG_ID_VIDEO_STREAM_INFORMATION, // msgid streamID); // stream ID } else { - qCDebug(CameraControlLog) << "_requestStreamInfo() - using legacy MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION"; + qCDebug(CameraControlLog) << " Sending MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION (legacy)"; _vehicle->sendMavCommand( _compID, // Target component MAV_CMD_REQUEST_VIDEO_STREAM_INFORMATION, // Command id @@ -1818,7 +1819,7 @@ VehicleCameraControl::_requestStreamStatus(uint8_t streamID) // By default, try to use new REQUEST_MESSAGE command instead of // deprecated MAV_CMD_REQUEST_VIDEO_STREAM_STATUS. if (_videoStreamStatusRetries % 2 == 0) { - qCDebug(CameraControlLog) << "_requestStreamStatus() - using REQUEST_MESSAGE"; + qCDebug(CameraControlLog) << " Sending REQUEST_MESSAGE:MAVLINK_MSG_ID_VIDEO_STREAM_STATUS"; _vehicle->sendMavCommand( _compID, // target component MAV_CMD_REQUEST_MESSAGE, // command id @@ -1826,7 +1827,7 @@ VehicleCameraControl::_requestStreamStatus(uint8_t streamID) MAVLINK_MSG_ID_VIDEO_STREAM_STATUS, // msgid streamID); // stream id } else { - qCDebug(CameraControlLog) << "_requestStreamStatus() - using legacy MAV_CMD_REQUEST_VIDEO_STREAM_STATUS"; + qCDebug(CameraControlLog) << " Sending MAV_CMD_REQUEST_VIDEO_STREAM_STATUS (legacy)"; _vehicle->sendMavCommand( _compID, // Target component MAV_CMD_REQUEST_VIDEO_STREAM_STATUS, // Command id @@ -2378,6 +2379,9 @@ VehicleCameraControl::startTracking(QRectF rec) static_cast(rec.y()), static_cast(rec.x() + rec.width()), static_cast(rec.y() + rec.height())); + + // Request tracking status + _requestTrackingStatus(); } } @@ -2400,6 +2404,9 @@ VehicleCameraControl::startTracking(QPointF point, double radius) static_cast(point.x()), static_cast(point.y()), static_cast(radius)); + + // Request tracking status + _requestTrackingStatus(); } } diff --git a/src/Camera/camera_definition_example.xml b/src/Camera/camera_definition_example.xml index fa8fd9eee635..c2400f44e822 100644 --- a/src/Camera/camera_definition_example.xml +++ b/src/Camera/camera_definition_example.xml @@ -260,7 +260,7 @@ - + diff --git a/src/CmdLineOptParser.cc b/src/CmdLineOptParser.cc deleted file mode 100644 index 5e4b26a411c7..000000000000 --- a/src/CmdLineOptParser.cc +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -/// @file -/// @brief Command line option parser implementation -/// @author Don Gagne - -#include "CmdLineOptParser.h" - -/// @brief Implements a simple command line parser which sets booleans to true if the option is found. -void ParseCmdLineOptions(int& argc, ///< count of arguments in argv - char* argv[], ///< command line arguments - CmdLineOpt_t* prgOpts, ///< command line options - size_t cOpts, ///< count of command line options - bool removeParsedOptions) ///< true: remove parsed option from argc/argv -{ - // Start with all options off - for (size_t iOption=0; iOption - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -/// @file -/// @brief Command line option parser declaration -/// @author Don Gagne - -#pragma once - -#include - -/// @brief Structure used to pass command line options to the ParseCmdLineOptions function. -typedef struct { - const char* optionStr; ///< Command line option, for example "--foo" - bool* optionFound; ///< If option is found this variable will be set to true - QString* optionArg; ///< Option has additional argument, form is option:arg -} CmdLineOpt_t; - -void ParseCmdLineOptions(int& argc, - char* argv[], - CmdLineOpt_t* prgOpts, - size_t cOpts, - bool removeParsedOptions); - diff --git a/src/Comms/AirLink/AirLinkLink.cc b/src/Comms/AirLink/AirLinkLink.cc index 2a4330bc6dab..70610bd2671e 100644 --- a/src/Comms/AirLink/AirLinkLink.cc +++ b/src/Comms/AirLink/AirLinkLink.cc @@ -17,7 +17,7 @@ #include #include -QGC_LOGGING_CATEGORY(AirLinkLinkLog, "qgc.AirLink.AirLinklink"); +QGC_LOGGING_CATEGORY(AirLinkLinkLog, "AirLink.AirLinkLink"); /*===========================================================================*/ diff --git a/src/Comms/AirLink/AirLinkManager.cc b/src/Comms/AirLink/AirLinkManager.cc index a55289524ecc..bc4284fa8d07 100644 --- a/src/Comms/AirLink/AirLinkManager.cc +++ b/src/Comms/AirLink/AirLinkManager.cc @@ -18,7 +18,7 @@ #include #include -QGC_LOGGING_CATEGORY(AirLinkManagerLog, "qgc.airlink.airlinkmanager"); +QGC_LOGGING_CATEGORY(AirLinkManagerLog, "AirLink.AirLinkManager"); Q_APPLICATION_STATIC(AirLinkManager, _airLinkManager); diff --git a/src/Comms/BluetoothLink.cc b/src/Comms/BluetoothLink.cc index eff6c6c148b4..8116ff29f0aa 100644 --- a/src/Comms/BluetoothLink.cc +++ b/src/Comms/BluetoothLink.cc @@ -16,7 +16,7 @@ #include #include -QGC_LOGGING_CATEGORY(BluetoothLinkLog, "qgc.comms.bluetoothlink") +QGC_LOGGING_CATEGORY(BluetoothLinkLog, "Comms.BluetoothLink") /*===========================================================================*/ @@ -24,7 +24,7 @@ BluetoothConfiguration::BluetoothConfiguration(const QString &name, QObject *par : LinkConfiguration(name, parent) , _deviceDiscoveryAgent(new QBluetoothDeviceDiscoveryAgent(this)) { - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; _initDeviceDiscoveryAgent(); } @@ -34,7 +34,7 @@ BluetoothConfiguration::BluetoothConfiguration(const BluetoothConfiguration *cop , _device(copy->device()) , _deviceDiscoveryAgent(new QBluetoothDeviceDiscoveryAgent(this)) { - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; BluetoothConfiguration::copyFrom(copy); @@ -45,7 +45,7 @@ BluetoothConfiguration::~BluetoothConfiguration() { stopScan(); - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; } void BluetoothConfiguration::_initDeviceDiscoveryAgent() @@ -64,11 +64,9 @@ void BluetoothConfiguration::_initDeviceDiscoveryAgent() void BluetoothConfiguration::copyFrom(const LinkConfiguration *source) { - Q_ASSERT(source); LinkConfiguration::copyFrom(source); - const BluetoothConfiguration *const bluetoothSource = qobject_cast(source); - Q_ASSERT(bluetoothSource); + const BluetoothConfiguration *bluetoothSource = qobject_cast(source); _device = bluetoothSource->device(); emit deviceChanged(); @@ -177,14 +175,14 @@ BluetoothWorker::BluetoothWorker(const BluetoothConfiguration *config, QObject * : QObject(parent) , _config(config) { - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; } BluetoothWorker::~BluetoothWorker() { disconnectLink(); - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; } bool BluetoothWorker::isConnected() const @@ -194,12 +192,14 @@ bool BluetoothWorker::isConnected() const void BluetoothWorker::setupSocket() { - Q_ASSERT(!_socket); - _socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); + if (!_socket) { + _socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); + } #ifdef Q_OS_IOS - Q_ASSERT(!_serviceDiscoveryAgent); - _serviceDiscoveryAgent = new QBluetoothServiceDiscoveryAgent(this); + if (!_serviceDiscoveryAgent) { + _serviceDiscoveryAgent = new QBluetoothServiceDiscoveryAgent(this); + } #endif (void) connect(_socket, &QBluetoothSocket::connected, this, &BluetoothWorker::_onSocketConnected); @@ -256,6 +256,7 @@ void BluetoothWorker::disconnectLink() _serviceDiscoveryAgent->stop(); } #endif + _socket->disconnectFromService(); } @@ -366,7 +367,7 @@ BluetoothLink::BluetoothLink(SharedLinkConfigurationPtr &config, QObject *parent , _worker(new BluetoothWorker(_bluetoothConfig)) , _workerThread(new QThread(this)) { - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; _checkPermission(); @@ -390,19 +391,22 @@ BluetoothLink::BluetoothLink(SharedLinkConfigurationPtr &config, QObject *parent BluetoothLink::~BluetoothLink() { - BluetoothLink::disconnect(); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::BlockingQueuedConnection); + _onDisconnected(); + } _workerThread->quit(); if (!_workerThread->wait()) { qCWarning(BluetoothLinkLog) << "Failed to wait for Bluetooth Thread to close"; } - // qCDebug(BluetoothLinkLog) << Q_FUNC_INFO << this; + qCDebug(BluetoothLinkLog) << this; } bool BluetoothLink::isConnected() const { - return _worker->isConnected(); + return _worker && _worker->isConnected(); } bool BluetoothLink::_connect() @@ -412,17 +416,22 @@ bool BluetoothLink::_connect() void BluetoothLink::disconnect() { - (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::QueuedConnection); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::QueuedConnection); + } } void BluetoothLink::_onConnected() { + _disconnectedEmitted = false; emit connected(); } void BluetoothLink::_onDisconnected() { - emit disconnected(); + if (!_disconnectedEmitted.exchange(true)) { + emit disconnected(); + } } void BluetoothLink::_onErrorOccurred(const QString &errorString) diff --git a/src/Comms/BluetoothLink.h b/src/Comms/BluetoothLink.h index f72d31bfa51c..c18f690b8317 100644 --- a/src/Comms/BluetoothLink.h +++ b/src/Comms/BluetoothLink.h @@ -9,6 +9,9 @@ #pragma once +#include "LinkConfiguration.h" +#include "LinkInterface.h" + #include #include #include @@ -22,8 +25,7 @@ #include #include -#include "LinkConfiguration.h" -#include "LinkInterface.h" +#include class QThread; @@ -207,4 +209,5 @@ private slots: const BluetoothConfiguration *_bluetoothConfig = nullptr; BluetoothWorker *_worker = nullptr; QThread *_workerThread = nullptr; + std::atomic _disconnectedEmitted{false}; }; diff --git a/src/Comms/CMakeLists.txt b/src/Comms/CMakeLists.txt index 62ce9307cc1c..858a43dc06af 100644 --- a/src/Comms/CMakeLists.txt +++ b/src/Comms/CMakeLists.txt @@ -1,3 +1,11 @@ +# ============================================================================ +# Communications Module +# Handles all communication links (TCP, UDP, Serial, Bluetooth, etc.) +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Core Communication Sources +# ---------------------------------------------------------------------------- target_sources(${CMAKE_PROJECT_NAME} PRIVATE LinkConfiguration.cc @@ -18,7 +26,7 @@ target_sources(${CMAKE_PROJECT_NAME} UDPLink.h ) -# Add JSON files +# USB board information JSON data qt_add_resources(${CMAKE_PROJECT_NAME} json_comm PREFIX "/json" FILES USBBoardInfo.json @@ -26,9 +34,16 @@ qt_add_resources(${CMAKE_PROJECT_NAME} json_comm target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# Uncomment to enable Qt socket debugging # target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QABSTRACTSOCKET_DEBUG) -#===========================================================================# +# ============================================================================ +# Optional Communication Features +# ============================================================================ + +# ---------------------------------------------------------------------------- +# Serial Port Communication +# ---------------------------------------------------------------------------- if(QGC_NO_SERIAL_LINK) target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_NO_SERIAL_LINK) @@ -43,13 +58,15 @@ else() UdpIODevice.h ) + # Qt SerialPort not available on mobile platforms if(NOT ANDROID AND NOT IOS) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt6::SerialPort) endif() endif() -#===========================================================================# - +# ---------------------------------------------------------------------------- +# Bluetooth Communication +# ---------------------------------------------------------------------------- if(QGC_ENABLE_BLUETOOTH) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt6::Bluetooth) target_sources(${CMAKE_PROJECT_NAME} @@ -60,8 +77,9 @@ if(QGC_ENABLE_BLUETOOTH) target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_ENABLE_BLUETOOTH) endif() -#===========================================================================# - +# ---------------------------------------------------------------------------- +# ZeroConf/mDNS Service Discovery +# ---------------------------------------------------------------------------- if(QGC_ZEROCONF_ENABLED) CPMAddPackage( NAME qmdnsengine @@ -76,10 +94,14 @@ if(QGC_ZEROCONF_ENABLED) if(TARGET qmdnsengine) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE qmdnsengine) target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE QGC_ZEROCONF_ENABLED) + else() + message(WARNING "QGC: qmdnsengine target not created, ZeroConf will be disabled") endif() endif() -#===========================================================================# +# ============================================================================ +# Communication Link Subdirectories +# ============================================================================ add_subdirectory(AirLink) add_subdirectory(MockLink) diff --git a/src/Comms/LinkConfiguration.cc b/src/Comms/LinkConfiguration.cc index 422b5aa1817a..d36d3f964265 100644 --- a/src/Comms/LinkConfiguration.cc +++ b/src/Comms/LinkConfiguration.cc @@ -8,6 +8,7 @@ ****************************************************************************/ #include "LinkConfiguration.h" +#include "QGCLoggingCategory.h" #ifndef QGC_NO_SERIAL_LINK #include "SerialLink.h" #endif @@ -24,11 +25,13 @@ #include "AirLinkLink.h" #endif +QGC_LOGGING_CATEGORY(LinkConfigurationLog, "Comms.LinkConfiguration") + LinkConfiguration::LinkConfiguration(const QString &name, QObject *parent) : QObject(parent) , _name(name) { - // qCDebug(AudioOutputLog) << Q_FUNC_INFO << this; + qCDebug(LinkConfigurationLog) << this; } LinkConfiguration::LinkConfiguration(const LinkConfiguration *copy, QObject *parent) @@ -39,14 +42,14 @@ LinkConfiguration::LinkConfiguration(const LinkConfiguration *copy, QObject *par , _autoConnect(copy->isAutoConnect()) , _highLatency(copy->isHighLatency()) { - // qCDebug(AudioOutputLog) << Q_FUNC_INFO << this; + qCDebug(LinkConfigurationLog) << this; Q_ASSERT(!_name.isEmpty()); } LinkConfiguration::~LinkConfiguration() { - // qCDebug(AudioOutputLog) << Q_FUNC_INFO << this; + qCDebug(LinkConfigurationLog) << this; } void LinkConfiguration::copyFrom(const LinkConfiguration *source) @@ -158,7 +161,9 @@ void LinkConfiguration::setLink(const SharedLinkInterfacePtr link) _link = link; emit linkChanged(); - (void) connect(link.get(), &LinkInterface::disconnected, this, &LinkConfiguration::linkChanged, Qt::QueuedConnection); + if (link.get()) { + (void) connect(link.get(), &LinkInterface::disconnected, this, &LinkConfiguration::linkChanged, Qt::QueuedConnection); + } } } diff --git a/src/Comms/LinkConfiguration.h b/src/Comms/LinkConfiguration.h index fb122f729640..01cb1775b0c0 100644 --- a/src/Comms/LinkConfiguration.h +++ b/src/Comms/LinkConfiguration.h @@ -9,12 +9,15 @@ #pragma once +#include #include #include #include class LinkInterface; +Q_DECLARE_LOGGING_CATEGORY(LinkConfigurationLog) + /// Interface holding link specific settings. class LinkConfiguration : public QObject { diff --git a/src/Comms/LinkInterface.cc b/src/Comms/LinkInterface.cc index c75d9903fce3..7eb7e01c7dde 100644 --- a/src/Comms/LinkInterface.cc +++ b/src/Comms/LinkInterface.cc @@ -17,7 +17,7 @@ #include -QGC_LOGGING_CATEGORY(LinkInterfaceLog, "qgc.comms.linkinterface") +QGC_LOGGING_CATEGORY(LinkInterfaceLog, "Comms.LinkInterface") LinkInterface::LinkInterface(SharedLinkConfigurationPtr &config, QObject *parent) : QObject(parent) @@ -29,7 +29,7 @@ LinkInterface::LinkInterface(SharedLinkConfigurationPtr &config, QObject *parent LinkInterface::~LinkInterface() { if (_vehicleReferenceCount != 0) { - qCWarning(LinkInterfaceLog) << Q_FUNC_INFO << "still have vehicle references:" << _vehicleReferenceCount; + qCWarning(LinkInterfaceLog) << "still have vehicle references:" << _vehicleReferenceCount; } _config.reset(); @@ -38,7 +38,7 @@ LinkInterface::~LinkInterface() uint8_t LinkInterface::mavlinkChannel() const { if (!mavlinkChannelIsSet()) { - qCWarning(LinkInterfaceLog) << Q_FUNC_INFO << "mavlinkChannelIsSet() == false"; + qCWarning(LinkInterfaceLog) << "mavlinkChannelIsSet() == false"; } return _mavlinkChannel; @@ -61,7 +61,7 @@ bool LinkInterface::initMavlinkSigning() qCDebug(LinkInterfaceLog) << "Signing enabled on channel" << _mavlinkChannel; } } else { - qCWarning(LinkInterfaceLog) << Q_FUNC_INFO << "Failed To enable Signing on channel" << _mavlinkChannel; + qCWarning(LinkInterfaceLog) << "Failed To enable Signing on channel" << _mavlinkChannel; // FIXME: What should we do here? return false; } @@ -75,19 +75,20 @@ bool LinkInterface::_allocateMavlinkChannel() Q_ASSERT(!mavlinkChannelIsSet()); if (mavlinkChannelIsSet()) { - qCWarning(LinkInterfaceLog) << Q_FUNC_INFO << "already have" << _mavlinkChannel; + qCWarning(LinkInterfaceLog) << "already have" << _mavlinkChannel; return true; } _mavlinkChannel = LinkManager::instance()->allocateMavlinkChannel(); if (!mavlinkChannelIsSet()) { - qCWarning(LinkInterfaceLog) << Q_FUNC_INFO << "failed"; + qCWarning(LinkInterfaceLog) << "failed"; return false; } qCDebug(LinkInterfaceLog) << "_allocateMavlinkChannel" << _mavlinkChannel; + mavlink_set_proto_version(_mavlinkChannel, MAVLINK_VERSION); // We only support v2 protcol initMavlinkSigning(); return true; @@ -95,7 +96,7 @@ bool LinkInterface::_allocateMavlinkChannel() void LinkInterface::_freeMavlinkChannel() { - qCDebug(LinkInterfaceLog) << Q_FUNC_INFO << _mavlinkChannel; + qCDebug(LinkInterfaceLog) << _mavlinkChannel; if (!mavlinkChannelIsSet()) { return; @@ -117,7 +118,7 @@ void LinkInterface::removeVehicleReference() _vehicleReferenceCount--; _connectionRemoved(); } else { - qCWarning(LinkInterfaceLog) << Q_FUNC_INFO << "called with no vehicle references"; + qCWarning(LinkInterfaceLog) << "called with no vehicle references"; } } @@ -140,3 +141,19 @@ void LinkInterface::setSigningSignatureFailure(bool failure) } } } + +void LinkInterface::reportMavlinkV1Traffic() +{ + if (!_mavlinkV1TrafficReported) { + _mavlinkV1TrafficReported = true; + + const SharedLinkConfigurationPtr linkConfig = linkConfiguration(); + const QString linkName = linkConfig ? linkConfig->name() : QStringLiteral("unknown"); + qCWarning(LinkInterfaceLog) << "MAVLink v1 traffic detected on link" << linkName; + const QString message = tr("MAVLink v1 traffic detected on link '%1'. " + "%2 only supports MAVLink v2. " + "Please ensure your vehicle is configured to use MAVLink v2.") + .arg(linkName).arg(qgcApp()->applicationName()); + qgcApp()->showAppMessage(message); + } +} diff --git a/src/Comms/LinkInterface.h b/src/Comms/LinkInterface.h index 645f747a8d7c..1837449c8d26 100644 --- a/src/Comms/LinkInterface.h +++ b/src/Comms/LinkInterface.h @@ -29,7 +29,7 @@ class LinkInterface : public QObject public: virtual ~LinkInterface(); - Q_INVOKABLE virtual void disconnect() = 0; // FIXME: This gets called 3x when closing link + Q_INVOKABLE virtual void disconnect() = 0; // Implementations should guard against multiple calls virtual bool isConnected() const = 0; virtual bool isLogReplay() const { return false; } @@ -46,6 +46,7 @@ class LinkInterface : public QObject void removeVehicleReference(); bool initMavlinkSigning(); void setSigningSignatureFailure(bool failure); + void reportMavlinkV1Traffic(); signals: void bytesReceived(LinkInterface *link, const QByteArray &data); @@ -80,6 +81,7 @@ private slots: bool _decodedFirstMavlinkPacket = false; int _vehicleReferenceCount = 0; bool _signingSignatureFailure = false; + bool _mavlinkV1TrafficReported = false; }; typedef std::shared_ptr SharedLinkInterfacePtr; diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index d3796dbbf800..187bfe2a39c4 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -52,8 +52,8 @@ #include #include -QGC_LOGGING_CATEGORY(LinkManagerLog, "qgc.comms.linkmanager") -QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "qgc.comms.linkmanager:verbose") +QGC_LOGGING_CATEGORY(LinkManagerLog, "Comms.LinkManager") +QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "Comms.LinkManager:verbose") Q_APPLICATION_STATIC(LinkManager, _linkManagerInstance); @@ -94,6 +94,12 @@ void LinkManager::init() } } +QList LinkManager::links() +{ + QMutexLocker locker(&_linksMutex); + return _rgLinks; +} + QmlObjectListModel *LinkManager::_qmlLinkConfigurations() { return _qmlConfigurations; @@ -156,24 +162,31 @@ bool LinkManager::createConnectedLink(SharedLinkConfigurationPtr &config) return false; } - _rgLinks.append(link); - config->setLink(link); - + // Set up signal connections before adding to list, so link is fully initialized (void) connect(link.get(), &LinkInterface::communicationError, this, &LinkManager::_communicationError); (void) connect(link.get(), &LinkInterface::bytesReceived, MAVLinkProtocol::instance(), &MAVLinkProtocol::receiveBytes); (void) connect(link.get(), &LinkInterface::bytesSent, MAVLinkProtocol::instance(), &MAVLinkProtocol::logSentBytes); (void) connect(link.get(), &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected); MAVLinkProtocol::instance()->resetMetadataForLink(link.get()); - MAVLinkProtocol::instance()->setVersion(MAVLinkProtocol::instance()->getCurrentVersion()); + // Try to connect before adding to active links list if (!link->_connect()) { + (void) disconnect(link.get(), &LinkInterface::communicationError, this, &LinkManager::_communicationError); + (void) disconnect(link.get(), &LinkInterface::bytesReceived, MAVLinkProtocol::instance(), &MAVLinkProtocol::receiveBytes); + (void) disconnect(link.get(), &LinkInterface::bytesSent, MAVLinkProtocol::instance(), &MAVLinkProtocol::logSentBytes); + (void) disconnect(link.get(), &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected); link->_freeMavlinkChannel(); - _rgLinks.removeAt(_rgLinks.indexOf(link)); config->setLink(nullptr); return false; } + { + QMutexLocker locker(&_linksMutex); + _rgLinks.append(link); + } + config->setLink(link); + return true; } @@ -184,9 +197,11 @@ void LinkManager::_communicationError(const QString &title, const QString &error SharedLinkInterfacePtr LinkManager::mavlinkForwardingLink() { - for (SharedLinkInterfacePtr &link : _rgLinks) { + QMutexLocker locker(&_linksMutex); + + for (const SharedLinkInterfacePtr &link : _rgLinks) { const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); - if ((linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingLinkName)) { + if (linkConfig && (linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingLinkName)) { return link; } } @@ -196,9 +211,11 @@ SharedLinkInterfacePtr LinkManager::mavlinkForwardingLink() SharedLinkInterfacePtr LinkManager::mavlinkForwardingSupportLink() { - for (SharedLinkInterfacePtr &link : _rgLinks) { + QMutexLocker locker(&_linksMutex); + + for (const SharedLinkInterfacePtr &link : _rgLinks) { const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); - if ((linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingSupportLinkName)) { + if (linkConfig && (linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingSupportLinkName)) { return link; } } @@ -208,7 +225,12 @@ SharedLinkInterfacePtr LinkManager::mavlinkForwardingSupportLink() void LinkManager::disconnectAll() { - const QList links = _rgLinks; + QList links; + { + QMutexLocker locker(&_linksMutex); + links = _rgLinks; + } + for (const SharedLinkInterfacePtr &sharedLink: links) { sharedLink->disconnect(); } @@ -218,35 +240,57 @@ void LinkManager::_linkDisconnected() { LinkInterface* const link = qobject_cast(sender()); - if (!link || !containsLink(link)) { + if (!link) { + return; + } + + SharedLinkInterfacePtr linkToCleanup; + SharedLinkConfigurationPtr config; + { + QMutexLocker locker(&_linksMutex); + + for (auto it = _rgLinks.begin(); it != _rgLinks.end(); ++it) { + if (it->get() == link) { + config = it->get()->linkConfiguration(); + const QString linkName = config ? config->name() : QStringLiteral(""); + qCDebug(LinkManagerLog) << linkName << "use_count:" << it->use_count(); + linkToCleanup = *it; + (void) _rgLinks.erase(it); + break; + } + } + } + + if (!linkToCleanup) { + qCDebug(LinkManagerLog) << "link already removed"; return; } - (void) disconnect(link, &LinkInterface::communicationError, qgcApp(), &QGCApplication::showAppMessage); + if (config) { + config->setLink(nullptr); + } + + (void) disconnect(link, &LinkInterface::communicationError, this, &LinkManager::_communicationError); (void) disconnect(link, &LinkInterface::bytesReceived, MAVLinkProtocol::instance(), &MAVLinkProtocol::receiveBytes); (void) disconnect(link, &LinkInterface::bytesSent, MAVLinkProtocol::instance(), &MAVLinkProtocol::logSentBytes); (void) disconnect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected); link->_freeMavlinkChannel(); - - for (auto it = _rgLinks.begin(); it != _rgLinks.end(); ++it) { - if (it->get() == link) { - qCDebug(LinkManagerLog) << Q_FUNC_INFO << it->get()->linkConfiguration()->name() << it->use_count(); - (void) _rgLinks.erase(it); - return; - } - } } SharedLinkInterfacePtr LinkManager::sharedLinkInterfacePointerForLink(const LinkInterface *link) { - for (SharedLinkInterfacePtr &sharedLink: _rgLinks) { + QMutexLocker locker(&_linksMutex); + + for (const SharedLinkInterfacePtr &sharedLink: _rgLinks) { if (sharedLink.get() == link) { return sharedLink; } } - qCWarning(LinkManagerLog) << "returning nullptr"; + // Link not found - this is normal during disconnect when queued signals are still processing. + // Callers should check for nullptr return value. + qCDebug(LinkManagerLog) << "link not in list (likely disconnected)"; return SharedLinkInterfacePtr(nullptr); } @@ -377,10 +421,13 @@ void LinkManager::_addUDPAutoConnectLink() return; } - for (SharedLinkInterfacePtr &link : _rgLinks) { - const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); - if ((linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _defaultUDPLinkName)) { - return; + { + QMutexLocker locker(&_linksMutex); + for (const SharedLinkInterfacePtr &link : _rgLinks) { + const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); + if (linkConfig && (linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _defaultUDPLinkName)) { + return; + } } } @@ -398,11 +445,14 @@ void LinkManager::_addMAVLinkForwardingLink() return; } - for (const SharedLinkInterfacePtr &link : _rgLinks) { - const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); - if ((linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingLinkName)) { - // TODO: should we check if the host/port matches the mavlinkForwardHostName setting and update if it does not match? - return; + { + QMutexLocker locker(&_linksMutex); + for (const SharedLinkInterfacePtr &link : _rgLinks) { + const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); + if (linkConfig && (linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingLinkName)) { + // TODO: should we check if the host/port matches the mavlinkForwardHostName setting and update if it does not match? + return; + } } } @@ -423,9 +473,10 @@ void LinkManager::_addZeroConfAutoConnectLink() browser.reset(new QMdnsEngine::Browser(server.get(), QMdnsEngine::MdnsBrowseType)); const auto checkIfConnectionLinkExist = [this](LinkConfiguration::LinkType linkType, const QString &linkName) { + QMutexLocker locker(&_linksMutex); for (const SharedLinkInterfacePtr &link : std::as_const(_rgLinks)) { const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration(); - if ((linkConfig->type() == linkType) && (linkConfig->name() == linkName)) { + if (linkConfig && (linkConfig->type() == linkType) && (linkConfig->name() == linkName)) { return true; } } @@ -650,7 +701,7 @@ void LinkManager::_removeConfiguration(const LinkConfiguration *config) } } - qCWarning(LinkManagerLog) << Q_FUNC_INFO << "called with unknown config"; + qCWarning(LinkManagerLog) << "called with unknown config"; } bool LinkManager::isBluetoothAvailable() @@ -658,8 +709,10 @@ bool LinkManager::isBluetoothAvailable() return QGCDeviceInfo::isBluetoothAvailable(); } -bool LinkManager::containsLink(const LinkInterface *link) const +bool LinkManager::containsLink(const LinkInterface *link) { + QMutexLocker locker(&_linksMutex); + for (const SharedLinkInterfacePtr &sharedLink : _rgLinks) { if (sharedLink.get() == link) { return true; @@ -768,7 +821,14 @@ bool LinkManager::isLinkUSBDirect(const LinkInterface *link) void LinkManager::resetMavlinkSigning() { - for (const SharedLinkInterfacePtr &sharedLink: _rgLinks) { + // Make a copy under mutex protection to avoid holding lock during signing initialization + QList links; + { + QMutexLocker locker(&_linksMutex); + links = _rgLinks; + } + + for (const SharedLinkInterfacePtr &sharedLink: links) { sharedLink->initMavlinkSigning(); } } @@ -789,7 +849,7 @@ void LinkManager::_filterCompositePorts(QList &portList) // Some boards are a composite USB device, with the first port being mavlink and the second something else. We only expose to first mavlink port. // However internal NMEA devices can present like this, so dont skip anything with NMEA in description if(!portInfo.description().contains("NMEA")) { - qCDebug(LinkManagerVerboseLog) << QStringLiteral("Removing secondary port on same device - port:%1 vid:%2 pid%3 sn:%4").arg(portInfo.portName()).arg(portInfo.vendorIdentifier()).arg(portInfo.productIdentifier()).arg(portInfo.serialNumber()) << Q_FUNC_INFO; + qCDebug(LinkManagerVerboseLog) << QStringLiteral("Removing secondary port on same device - port:%1 vid:%2 pid%3 sn:%4").arg(portInfo.portName()).arg(portInfo.vendorIdentifier()).arg(portInfo.productIdentifier()).arg(portInfo.serialNumber()); it = portList.erase(it); continue; } @@ -948,8 +1008,10 @@ bool LinkManager::_allowAutoConnectToBoard(QGCSerialPortInfo::BoardType_t boardT return false; } -bool LinkManager::_portAlreadyConnected(const QString &portName) const +bool LinkManager::_portAlreadyConnected(const QString &portName) { + QMutexLocker locker(&_linksMutex); + const QString searchPort = portName.trimmed(); for (const SharedLinkInterfacePtr &linkInterface : _rgLinks) { const SharedLinkConfigurationPtr linkConfig = linkInterface->linkConfiguration(); @@ -997,8 +1059,10 @@ QStringList LinkManager::serialBaudRates() return SerialConfiguration::supportedBaudRates(); } -bool LinkManager::_isSerialPortConnected() const +bool LinkManager::_isSerialPortConnected() { + QMutexLocker locker(&_linksMutex); + for (const SharedLinkInterfacePtr &link: _rgLinks) { if (qobject_cast(link.get())) { return true; diff --git a/src/Comms/LinkManager.h b/src/Comms/LinkManager.h index 30ee45584c84..c4e621f70310 100644 --- a/src/Comms/LinkManager.h +++ b/src/Comms/LinkManager.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -72,7 +74,7 @@ class LinkManager : public QObject Q_INVOKABLE void shutdown(); Q_INVOKABLE LogReplayLink *startLogReplay(const QString &logFile); - QList links() { return _rgLinks; } + QList links(); QStringList linkTypeStrings() const; bool mavlinkSupportForwardingEnabled() const { return _mavlinkSupportForwardingEnabled; } @@ -109,7 +111,7 @@ class LinkManager : public QObject /// by using this method to get access to the shared pointer. SharedLinkInterfacePtr sharedLinkInterfacePointerForLink(const LinkInterface *link); - bool containsLink(const LinkInterface *link) const; + bool containsLink(const LinkInterface *link); SharedLinkConfigurationPtr addConfiguration(LinkConfiguration *config); @@ -153,6 +155,7 @@ private slots: uint32_t _mavlinkChannelsUsedBitMask = 1; QString _connectionsSuspendedReason; ///< User visible reason for suspension + QMutex _linksMutex; ///< Protects _rgLinks access from multiple threads QList _rgLinks; QList _rgLinkConfigs; @@ -184,11 +187,11 @@ private slots: void commPortsChanged(); private: - bool _isSerialPortConnected() const; + bool _isSerialPortConnected(); void _updateSerialPorts(); bool _allowAutoConnectToBoard(QGCSerialPortInfo::BoardType_t boardType) const; void _addSerialAutoConnectLink(); - bool _portAlreadyConnected(const QString &portName) const; + bool _portAlreadyConnected(const QString &portName); void _filterCompositePorts(QList &portList); UdpIODevice *_nmeaSocket = nullptr; diff --git a/src/Comms/LogReplayLink.cc b/src/Comms/LogReplayLink.cc index e5cd0bcfb49b..af74b1462ac4 100644 --- a/src/Comms/LogReplayLink.cc +++ b/src/Comms/LogReplayLink.cc @@ -18,35 +18,33 @@ #include #include -QGC_LOGGING_CATEGORY(LogReplayLinkLog, "qgc.comms.logreplaylink") +QGC_LOGGING_CATEGORY(LogReplayLinkLog, "Comms.LogReplayLink") /*===========================================================================*/ LogReplayConfiguration::LogReplayConfiguration(const QString &name, QObject *parent) : LinkConfiguration(name, parent) { - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; } LogReplayConfiguration::LogReplayConfiguration(const LogReplayConfiguration *copy, QObject *parent) : LinkConfiguration(copy, parent) , _logFilename(copy->logFilename()) { - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; } LogReplayConfiguration::~LogReplayConfiguration() { - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; } void LogReplayConfiguration::copyFrom(const LinkConfiguration *source) { - Q_ASSERT(source); LinkConfiguration::copyFrom(source); - const LogReplayConfiguration *const logReplaySource = qobject_cast(source); - Q_ASSERT(logReplaySource); + const LogReplayConfiguration *logReplaySource = qobject_cast(source); setLogFilename(logReplaySource->logFilename()); } @@ -88,20 +86,21 @@ LogReplayWorker::LogReplayWorker(const LogReplayConfiguration *config, QObject * : QObject(parent) , _logReplayConfig(config) { - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; } LogReplayWorker::~LogReplayWorker() { disconnectFromLog(); - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; } void LogReplayWorker::setup() { - Q_ASSERT(!_readTickTimer); - _readTickTimer = new QTimer(this); + if (!_readTickTimer) { + _readTickTimer = new QTimer(this); + } (void) connect(_readTickTimer, &QTimer::timeout, this, &LogReplayWorker::_readNextLogEntry); } @@ -136,10 +135,18 @@ void LogReplayWorker::disconnectFromLog() return; } + qCDebug(LogReplayLinkLog) << "Disconnecting from log"; + + if (_readTickTimer) { + _readTickTimer->stop(); + } + + if (_logFile.isOpen()) { + _logFile.close(); + } + _isConnected = false; emit disconnected(); - - _readTickTimer->stop(); } bool LogReplayWorker::isPlaying() const @@ -403,7 +410,7 @@ LogReplayLink::LogReplayLink(SharedLinkConfigurationPtr &config, QObject *parent , _worker(new LogReplayWorker(_logReplayConfig)) , _workerThread(new QThread(this)) { - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; _workerThread->setObjectName(QStringLiteral("LogReplay_%1").arg(_logReplayConfig->name())); @@ -429,14 +436,22 @@ LogReplayLink::LogReplayLink(SharedLinkConfigurationPtr &config, QObject *parent LogReplayLink::~LogReplayLink() { - LogReplayLink::disconnect(); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectFromLog", Qt::BlockingQueuedConnection); + _onDisconnected(); + } _workerThread->quit(); if (!_workerThread->wait()) { qCWarning(LogReplayLinkLog) << "Failed to wait for LogReplay Thread to close"; } - // qCDebug(LogReplayLinkLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkLog) << this; +} + +bool LogReplayLink::isConnected() const +{ + return _worker && _worker->isConnected(); } bool LogReplayLink::_connect() @@ -446,7 +461,22 @@ bool LogReplayLink::_connect() void LogReplayLink::disconnect() { - (void) QMetaObject::invokeMethod(_worker, "disconnectFromLog", Qt::QueuedConnection); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectFromLog", Qt::QueuedConnection); + } +} + +void LogReplayLink::_onConnected() +{ + _disconnectedEmitted = false; + emit connected(); +} + +void LogReplayLink::_onDisconnected() +{ + if (!_disconnectedEmitted.exchange(true)) { + emit disconnected(); + } } void LogReplayLink::_onErrorOccurred(const QString &errorString) @@ -460,6 +490,11 @@ void LogReplayLink::_onDataReceived(const QByteArray &data) emit bytesReceived(this, data); } +bool LogReplayLink::isPlaying() const +{ + return _worker && _worker->isPlaying(); +} + void LogReplayLink::play() { (void) QMetaObject::invokeMethod(_worker, "play", Qt::QueuedConnection); diff --git a/src/Comms/LogReplayLink.h b/src/Comms/LogReplayLink.h index cf58b780ddf4..51eb874eb45d 100644 --- a/src/Comms/LogReplayLink.h +++ b/src/Comms/LogReplayLink.h @@ -9,12 +9,14 @@ #pragma once +#include "LinkConfiguration.h" +#include "LinkInterface.h" + #include #include #include -#include "LinkConfiguration.h" -#include "LinkInterface.h" +#include class QTimer; @@ -131,11 +133,11 @@ class LogReplayLink : public LinkInterface explicit LogReplayLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); virtual ~LogReplayLink(); - bool isConnected() const override { return _worker->isConnected(); } + bool isConnected() const override; void disconnect() override; bool isLogReplay() const final { return true; } - bool isPlaying() const { return _worker->isPlaying(); } + bool isPlaying() const; void play(); void pause(); void setPlaybackSpeed(qreal playbackSpeed); @@ -151,8 +153,8 @@ class LogReplayLink : public LinkInterface private slots: void _writeBytes(const QByteArray &bytes) override { Q_UNUSED(bytes); } - void _onConnected() { emit connected(); } - void _onDisconnected() { emit disconnected(); } + void _onConnected(); + void _onDisconnected(); void _onErrorOccurred(const QString &errorString); void _onDataReceived(const QByteArray &data); @@ -162,4 +164,5 @@ private slots: const LogReplayConfiguration *_logReplayConfig = nullptr; LogReplayWorker *_worker = nullptr; QThread *_workerThread = nullptr; + std::atomic _disconnectedEmitted{false}; }; diff --git a/src/Comms/LogReplayLinkController.cc b/src/Comms/LogReplayLinkController.cc index e89ee2c92015..878720d228a8 100644 --- a/src/Comms/LogReplayLinkController.cc +++ b/src/Comms/LogReplayLinkController.cc @@ -10,17 +10,17 @@ #include "LogReplayLinkController.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(LogReplayLinkControllerLog, "qgc.comms.logreplaylink") +QGC_LOGGING_CATEGORY(LogReplayLinkControllerLog, "Comms.LogReplayLinkController") LogReplayLinkController::LogReplayLinkController(QObject *parent) : QObject(parent) { - // qCDebug(LogReplayLinkControllerLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkControllerLog) << this; } LogReplayLinkController::~LogReplayLinkController() { - // qCDebug(LogReplayLinkControllerLog) << Q_FUNC_INFO << this; + qCDebug(LogReplayLinkControllerLog) << this; } void LogReplayLinkController::setLink(LogReplayLink *link) @@ -63,6 +63,10 @@ void LogReplayLinkController::setLink(LogReplayLink *link) void LogReplayLinkController::setIsPlaying(bool isPlaying) const { + if (!_link) { + return; + } + if (isPlaying) { _link->play(); } else { @@ -72,6 +76,10 @@ void LogReplayLinkController::setIsPlaying(bool isPlaying) const void LogReplayLinkController::setPercentComplete(qreal percentComplete) const { + if (!_link) { + return; + } + _link->movePlayhead(percentComplete); } diff --git a/src/Comms/LogReplayLinkController.h b/src/Comms/LogReplayLinkController.h index 574e275f1598..18d2c5d50d8d 100644 --- a/src/Comms/LogReplayLinkController.h +++ b/src/Comms/LogReplayLinkController.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include "LogReplayLink.h" @@ -66,5 +67,5 @@ private slots: qreal _playbackSpeed = 1; QString _playheadTime; QString _totalTime; - LogReplayLink *_link = nullptr; + QPointer _link; }; diff --git a/src/Comms/MAVLinkProtocol.cc b/src/Comms/MAVLinkProtocol.cc index 560032b88290..320808f63177 100644 --- a/src/Comms/MAVLinkProtocol.cc +++ b/src/Comms/MAVLinkProtocol.cc @@ -24,8 +24,9 @@ #include #include #include +#include -QGC_LOGGING_CATEGORY(MAVLinkProtocolLog, "qgc.comms.mavlinkprotocol") +QGC_LOGGING_CATEGORY(MAVLinkProtocolLog, "Comms.MAVLinkProtocol") Q_APPLICATION_STATIC(MAVLinkProtocol, _mavlinkProtocolInstance); @@ -33,14 +34,14 @@ MAVLinkProtocol::MAVLinkProtocol(QObject *parent) : QObject(parent) , _tempLogFile(new QGCTemporaryFile(QStringLiteral("%2.%3").arg(_tempLogFileTemplate, _logFileExtension), this)) { - // qCDebug(MAVLinkProtocolLog) << Q_FUNC_INFO << this; + qCDebug(MAVLinkProtocolLog) << this; } MAVLinkProtocol::~MAVLinkProtocol() { _closeLogFile(); - // qCDebug(MAVLinkProtocolLog) << Q_FUNC_INFO << this; + qCDebug(MAVLinkProtocolLog) << this; } MAVLinkProtocol *MAVLinkProtocol::instance() @@ -59,16 +60,6 @@ void MAVLinkProtocol::init() _initialized = true; } -void MAVLinkProtocol::setVersion(unsigned version) -{ - const QList sharedLinks = LinkManager::instance()->links(); - for (const SharedLinkInterfacePtr &interface : sharedLinks) { - mavlink_set_proto_version(interface.get()->mavlinkChannel(), version / 100); - } - - _currentVersion = version; -} - void MAVLinkProtocol::resetMetadataForLink(LinkInterface *link) { const uint8_t channel = link->mavlinkChannel(); @@ -119,7 +110,11 @@ void MAVLinkProtocol::receiveBytes(LinkInterface *link, const QByteArray &data) continue; } - _updateVersion(link, mavlinkChannel); + if (status.flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) { + link->reportMavlinkV1Traffic(); + continue; + } + _updateCounters(mavlinkChannel, message); if (!linkPtr->linkConfiguration()->isForwarding()) { _forward(message); @@ -133,25 +128,6 @@ void MAVLinkProtocol::receiveBytes(LinkInterface *link, const QByteArray &data) } } -void MAVLinkProtocol::_updateVersion(LinkInterface *link, uint8_t mavlinkChannel) -{ - if (link->decodedFirstMavlinkPacket()) { - return; - } - - link->setDecodedFirstMavlinkPacket(true); - const mavlink_status_t *const mavlinkStatus = mavlink_get_channel_status(mavlinkChannel); - - if (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) { - return; - } - - if (mavlink_get_proto_version(mavlinkChannel) == 1) { - qCDebug(MAVLinkProtocolLog) << "Switching outbound to mavlink 2.0 due to incoming mavlink 2.0 packet:" << mavlinkChannel; - setVersion(200); - } -} - void MAVLinkProtocol::_updateCounters(uint8_t mavlinkChannel, const mavlink_message_t &message) { _totalReceiveCounter[mavlinkChannel]++; @@ -349,8 +325,8 @@ void MAVLinkProtocol::_stopLogging() if (_tempLogFile->isOpen() && _closeLogFile()) { auto appSettings = SettingsManager::instance()->appSettings(); auto mavlinkSettings = SettingsManager::instance()->mavlinkSettings(); - if ((_vehicleWasArmed || mavlinkSettings->telemetrySaveNotArmed()->rawValue().toBool()) && - mavlinkSettings->telemetrySave()->rawValue().toBool() && + if ((_vehicleWasArmed || mavlinkSettings->telemetrySaveNotArmed()->rawValue().toBool()) && + mavlinkSettings->telemetrySave()->rawValue().toBool() && !appSettings->disableAllPersistence()->rawValue().toBool()) { _saveTelemetryLog(_tempLogFile->fileName()); } else { @@ -404,17 +380,68 @@ void MAVLinkProtocol::_saveTelemetryLog(const QString &tempLogfile) const QString dtFormat("yyyy-MM-dd hh-mm-ss"); int tryIndex = 1; - QString saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat), QStringLiteral(""), AppSettings::telemetryFileExtension); + QString saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat), QString(), AppSettings::telemetryFileExtension); while (saveDir.exists(saveFileName)) { saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat), QStringLiteral(".%1").arg(tryIndex++), AppSettings::telemetryFileExtension); } const QString saveFilePath = saveDir.absoluteFilePath(saveFileName); - QFile tempFile(tempLogfile); - if (!tempFile.copy(saveFilePath)) { - const QString error = tr("Unable to save telemetry log. Error copying telemetry to '%1': '%2'.").arg(saveFilePath, tempFile.errorString()); + + QFile in(tempLogfile); + if (!in.open(QIODevice::ReadOnly)) { + const QString error = tr("Unable to save telemetry log. Error opening source '%1': '%2'.").arg(tempLogfile, in.errorString()); qgcApp()->showAppMessage(error); + (void) QFile::remove(tempLogfile); + return; } + + QSaveFile out(saveFilePath); + out.setDirectWriteFallback(true); // allows non-atomic fallback where rename isn’t possible + + if (!out.open(QIODevice::WriteOnly)) { + const QString error = tr("Unable to save telemetry log. Error opening destination '%1': '%2'.").arg(saveFilePath, out.errorString()); + qgcApp()->showAppMessage(error); + (void) QFile::remove(tempLogfile); + return; + } + + // Stream copy to avoid large allocations. + QByteArray buffer; + constexpr int bufferSize = 256 * 1024; // 256 KiB + buffer.resize(bufferSize); + while (true) { + const qint64 n = in.read(buffer.data(), buffer.size()); + if (n == 0) { + break; + } + if (n < 0) { + const QString error = tr("Unable to save telemetry log. Error reading source '%1': '%2'.").arg(tempLogfile, in.errorString()); + qgcApp()->showAppMessage(error); + out.cancelWriting(); + (void) QFile::remove(tempLogfile); + return; + } + if (out.write(buffer.constData(), n) != n) { + const QString error = tr("Unable to save telemetry log. Error writing destination '%1': '%2'.").arg(saveFilePath, out.errorString()); + qgcApp()->showAppMessage(error); + out.cancelWriting(); + (void) QFile::remove(tempLogfile); + return; + } + } + + if (!out.commit()) { + const QString error = tr("Unable to finalize telemetry log '%1': '%2'.").arg(saveFilePath, out.errorString()); + qgcApp()->showAppMessage(error); + (void) QFile::remove(tempLogfile); + return; + } + + constexpr QFileDevice::Permissions perms = + QFileDevice::ReadOwner | QFileDevice::WriteOwner | + QFileDevice::ReadGroup | + QFileDevice::ReadOther; + (void) out.setPermissions(perms); } (void) QFile::remove(tempLogfile); @@ -446,7 +473,7 @@ void MAVLinkProtocol::_vehicleCountChanged() } } -int MAVLinkProtocol::getSystemId() const -{ - return SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->rawValue().toInt(); +int MAVLinkProtocol::getSystemId() const +{ + return SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->rawValue().toInt(); } diff --git a/src/Comms/MAVLinkProtocol.h b/src/Comms/MAVLinkProtocol.h index 921f74086a13..b3c7fcbb101c 100644 --- a/src/Comms/MAVLinkProtocol.h +++ b/src/Comms/MAVLinkProtocol.h @@ -51,21 +51,12 @@ class MAVLinkProtocol : public QObject /// Get the component id of this application static int getComponentId() { return MAV_COMP_ID_MISSIONPLANNER; } - /// Get the protocol version - static int getVersion() { return MAVLINK_VERSION; } - - /// Get the currently configured protocol version - unsigned getCurrentVersion() const { return _currentVersion; } - /// Reset the counters for all metadata for this link. void resetMetadataForLink(LinkInterface *link); /// Suspend/Restart logging during replay. void suspendLogForReplay(bool suspend) { _logSuspendReplay = suspend; } - /// Set protocol version - void setVersion(unsigned version); - /// Checks the temp directory for log files which may have been left there. /// This could happen if QGC crashes without the temp log file being saved. /// Give the user an option to save these orphaned files. @@ -107,7 +98,6 @@ private slots: void _updateCounters(uint8_t mavlinkChannel, const mavlink_message_t &message); bool _updateStatus(LinkInterface *link, const SharedLinkInterfacePtr linkPtr, uint8_t mavlinkChannel, const mavlink_message_t &message); - void _updateVersion(LinkInterface *link, uint8_t mavlinkChannel); void _saveTelemetryLog(const QString &tempLogfile); bool _checkTelemetrySavePath(); @@ -124,7 +114,6 @@ private slots: uint64_t _totalLossCounter[MAVLINK_COMM_NUM_BUFFERS]{}; ///< Total messages lost during transmission. float _runningLossPercent[MAVLINK_COMM_NUM_BUFFERS]{}; ///< Loss rate - unsigned _currentVersion = 100; bool _initialized = false; static constexpr const char *_tempLogFileTemplate = "FlightDataXXXXXX"; ///< Template for temporary log file diff --git a/src/Comms/MockLink/MockConfiguration.cc b/src/Comms/MockLink/MockConfiguration.cc index 5dbe4377fb8b..9735a3f1333c 100644 --- a/src/Comms/MockLink/MockConfiguration.cc +++ b/src/Comms/MockLink/MockConfiguration.cc @@ -10,12 +10,12 @@ #include "MockConfiguration.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(MockConfigurationLog, "qgc.comms.mocklink.mockconfiguration") +QGC_LOGGING_CATEGORY(MockConfigurationLog, "Comms.MockLink.MockConfiguration") MockConfiguration::MockConfiguration(const QString &name, QObject *parent) : LinkConfiguration(name, parent) { - // qCDebug(MockConfigurationLog) << Q_FUNC_INFO << this; + qCDebug(MockConfigurationLog) << this; } MockConfiguration::MockConfiguration(const MockConfiguration *copy, QObject *parent) @@ -26,21 +26,19 @@ MockConfiguration::MockConfiguration(const MockConfiguration *copy, QObject *par , _incrementVehicleId(copy->incrementVehicleId()) , _failureMode(copy->failureMode()) { - // qCDebug(MockConfigurationLog) << Q_FUNC_INFO << this; + qCDebug(MockConfigurationLog) << this; } MockConfiguration::~MockConfiguration() { - // qCDebug(MockConfigurationLog) << Q_FUNC_INFO << this; + qCDebug(MockConfigurationLog) << this; } void MockConfiguration::copyFrom(const LinkConfiguration *source) { - Q_ASSERT(source); LinkConfiguration::copyFrom(source); - const MockConfiguration *const mockLinkSource = qobject_cast(source); - Q_ASSERT(mockLinkSource); + const MockConfiguration *mockLinkSource = qobject_cast(source); setFirmwareType(mockLinkSource->firmwareType()); setVehicleType(mockLinkSource->vehicleType()); diff --git a/src/Comms/MockLink/MockConfiguration.h b/src/Comms/MockLink/MockConfiguration.h index 1d3523df7195..a6ef65a786d0 100644 --- a/src/Comms/MockLink/MockConfiguration.h +++ b/src/Comms/MockLink/MockConfiguration.h @@ -59,8 +59,6 @@ class MockConfiguration : public LinkConfiguration FailMissingParamOnAllRequests, // Not all params are sent on initial request, QGC retries will fail as well FailInitialConnectRequestMessageAutopilotVersionFailure, // REQUEST_MESSAGE:AUTOPILOT_VERSION returns failure FailInitialConnectRequestMessageAutopilotVersionLost, // REQUEST_MESSAGE:AUTOPILOT_VERSION success, AUTOPILOT_VERSION never sent - FailInitialConnectRequestMessageProtocolVersionFailure, // REQUEST_MESSAGE:PROTOCOL_VERSION returns failure - FailInitialConnectRequestMessageProtocolVersionLost, // REQUEST_MESSAGE:PROTOCOL_VERSION success, PROTOCOL_VERSION never sent }; FailureMode_t failureMode() const { return _failureMode; } void setFailureMode(FailureMode_t failureMode) { _failureMode = failureMode; } diff --git a/src/Comms/MockLink/MockLink.General.MetaData.json b/src/Comms/MockLink/MockLink.General.MetaData.json index cd76f4b100da..1204020e5f1d 100644 --- a/src/Comms/MockLink/MockLink.General.MetaData.json +++ b/src/Comms/MockLink/MockLink.General.MetaData.json @@ -7,4 +7,4 @@ "metadataTypes": [ {"type": 1, "uri": "mftp://[;comp=1]parameter.json.xz", "fileCrc": 1} ] -} +} diff --git a/src/Comms/MockLink/MockLink.cc b/src/Comms/MockLink/MockLink.cc index c03ef63a47dc..2a580b070c13 100644 --- a/src/Comms/MockLink/MockLink.cc +++ b/src/Comms/MockLink/MockLink.cc @@ -22,8 +22,8 @@ #include #include -QGC_LOGGING_CATEGORY(MockLinkLog, "qgc.comms.mocklink.mocklink") -QGC_LOGGING_CATEGORY(MockLinkVerboseLog, "qgc.comms.mocklink.mocklink:verbose") +QGC_LOGGING_CATEGORY(MockLinkLog, "Comms.MockLink.MockLink") +QGC_LOGGING_CATEGORY(MockLinkVerboseLog, "Comms.MockLink.MockLink:verbose") int MockLink::_nextVehicleSystemId = 128; @@ -62,7 +62,7 @@ MockLink::MockLink(SharedLinkConfigurationPtr &config, QObject *parent) , _missionItemHandler(new MockLinkMissionItemHandler(this)) , _mockLinkFTP(new MockLinkFTP(_vehicleSystemId, _vehicleComponentId, this)) { - // qCDebug(MockLinkLog) << Q_FUNC_INFO << this; + qCDebug(MockLinkLog) << this; // Initialize 5 ADS-B vehicles with different starting conditions _numberOfVehicles _adsbVehicles.resize(_numberOfVehicles); @@ -87,6 +87,7 @@ MockLink::MockLink(SharedLinkConfigurationPtr &config, QObject *parent) _runningTime.start(); _workerThread = new QThread(this); + _workerThread->setObjectName(QStringLiteral("Mock_%1").arg(_mockConfig->name())); _worker = new MockLinkWorker(this); _worker->moveToThread(_workerThread); (void) connect(_workerThread, &QThread::started, _worker, &MockLinkWorker::startWork); @@ -107,13 +108,14 @@ MockLink::~MockLink() _workerThread->wait(); } - // qCDebug(MockLinkLog) << Q_FUNC_INFO << this; + qCDebug(MockLinkLog) << this; } bool MockLink::_connect() { if (!_connected) { _connected = true; + _disconnectedEmitted = false; mavlink_status_t *const mavlinkStatus = mavlink_get_channel_status(mavlinkChannel()); mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; mavlink_status_t *const auxStatus = mavlink_get_channel_status(_getMavlinkAuxChannel()); @@ -130,7 +132,9 @@ void MockLink::disconnect() if (_connected) { _connected = false; - emit disconnected(); + if (!_disconnectedEmitted.exchange(true)) { + emit disconnected(); + } } } @@ -149,9 +153,20 @@ void MockLink::run1HzTasks() _sendBatteryStatus(); _sendSysStatus(); _sendADSBVehicles(); - _sendRemoteIDArmStatus(); + if (_vehicleType != MAV_TYPE_SUBMARINE) { + _sendRemoteIDArmStatus(); + } _sendAvailableModesMonitor(); + + if (_sendGimbalManagerStatusNow) { + _sendGimbalManagerStatus(); + } + if (_sendGimbalDeviceAttitudeStatusNow) { + _sendGimbalDeviceAttitudeStatus(); + } + // _sendVideoInfo(); + if (!qgcApp()->runningUnitTests()) { // Sending RC Channels during unit test breaks RC tests which does it's own RC simulation _sendRCChannels(); @@ -182,8 +197,10 @@ void MockLink::run10HzTasks() // We delay gps position for better testing _sendGPSPositionDelayCount--; } else { - _sendGpsRawInt(); - _sendGlobalPositionInt(); + if (_vehicleType != MAV_TYPE_SUBMARINE) { + _sendGpsRawInt(); + _sendGlobalPositionInt(); + } _sendExtendedSysState(); } } @@ -202,6 +219,68 @@ void MockLink::run500HzTasks() } } +void MockLink::_sendGimbalManagerStatus() +{ + mavlink_message_t msg{}; + + (void) mavlink_msg_gimbal_manager_status_pack_chan( + _vehicleSystemId, + _vehicleComponentId, + mavlinkChannel(), + &msg, + 0, // time_boot_ms + 0, // flags + MAV_COMP_ID_GIMBAL, // gimbal_device_id + 0, 0, 0, 0); + respondWithMavlinkMessage(msg); + (void) mavlink_msg_gimbal_manager_status_pack_chan( + _vehicleSystemId, + _vehicleComponentId, + mavlinkChannel(), + &msg, + 0, // time_boot_ms + 0, // flags + MAV_COMP_ID_GIMBAL2, // gimbal_device_id + 0, 0, 0, 0); + respondWithMavlinkMessage(msg); +} + +void MockLink::_sendGimbalDeviceAttitudeStatus() +{ + mavlink_message_t msg{}; + + float q[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + + (void) mavlink_msg_gimbal_device_attitude_status_pack_chan( + _vehicleSystemId, + MAV_COMP_ID_GIMBAL, + mavlinkChannel(), + &msg, + 0, 0, // target system, component + 0, // time_boot_ms + 0, // flags + (float*)&q, + 0.0f, 0.0f, 0.0f, // angular_velocity_x, y, z + 0, // failure flags + NAN, NAN, + 0); // gimbal_device_id + respondWithMavlinkMessage(msg); + (void) mavlink_msg_gimbal_device_attitude_status_pack_chan( + _vehicleSystemId, + MAV_COMP_ID_GIMBAL2, + mavlinkChannel(), + &msg, + 0, 0, // target system, component + 0, // time_boot_ms + 0, // flags + (float*)&q, + 0.0f, 0.0f, 0.0f, // angular_velocity_x, y, z + 0, // failure flags + NAN, NAN, + 0); // gimbal_device_id + respondWithMavlinkMessage(msg); +} + void MockLink::sendStatusTextMessages() { _sendStatusTextMessages(); @@ -859,10 +938,21 @@ void MockLink::_handleParamSet(const mavlink_message_t &msg) Q_ASSERT(_mapParamName2Value[componentId].contains(paramId)); Q_ASSERT(request.param_type == _mapParamName2MavParamType[componentId][paramId]); - // Save the new value + // Apply failure behaviors before committing change. + if (_paramSetFailureMode == FailParamSetFirstAttemptNoAck && _paramSetFailureFirstAttemptPending) { + qCDebug(MockLinkLog) << "Param set failure: first attempt no ack" << paramId; + _paramSetFailureFirstAttemptPending = false; + return; + } + + if (_paramSetFailureMode == FailParamSetNoAck) { + qCDebug(MockLinkLog) << "Param set failure: no ack" << paramId; + return; + } + + // Normal success path _setParamFloatUnionIntoMap(componentId, paramId, request.param_value); - // Respond with a param_value to ack mavlink_message_t responseMsg; mavlink_msg_param_value_pack_chan( _vehicleSystemId, @@ -936,6 +1026,17 @@ void MockLink::_handleParamRequestRead(const mavlink_message_t &msg) return; } + if (_paramRequestReadFailureMode == FailParamRequestReadFirstAttemptNoResponse && _paramRequestReadFailureFirstAttemptPending) { + qCDebug(MockLinkLog) << "Param request read failure: first attempt no response" << paramId; + _paramRequestReadFailureFirstAttemptPending = false; + return; + } + + if (_paramRequestReadFailureMode == FailParamRequestReadNoResponse) { + qCDebug(MockLinkLog) << "Param request read failure: no response" << paramId; + return; + } + (void) mavlink_msg_param_value_pack_chan( _vehicleSystemId, componentId, // component id @@ -1045,6 +1146,19 @@ void MockLink::_handleInProgressCommandLong(const mavlink_command_long_t &reques } } +void MockLink::_handleCommandLongSetMessageInterval(const mavlink_command_long_t &request, bool &accepted) +{ + accepted = false; + + if (request.param1 == MAVLINK_MSG_ID_GIMBAL_MANAGER_STATUS) { + _sendGimbalManagerStatusNow = true; + accepted = true; + } else if (request.param1 == MAVLINK_MSG_ID_GIMBAL_DEVICE_ATTITUDE_STATUS) { + _sendGimbalDeviceAttitudeStatusNow = true; + accepted = true; + } +} + void MockLink::_handleCommandLong(const mavlink_message_t &msg) { static bool firstCmdUser3 = true; @@ -1091,8 +1205,10 @@ void MockLink::_handleCommandLong(const mavlink_message_t &msg) break; case MAV_CMD_REQUEST_MESSAGE: { + bool accepted = false; bool noAck = false; - if (_handleRequestMessage(request, noAck)) { + _handleRequestMessage(request, accepted, noAck); + if (accepted) { if (noAck) { return; } @@ -1141,6 +1257,16 @@ void MockLink::_handleCommandLong(const mavlink_message_t &msg) case MockLink::MAV_CMD_MOCKLINK_RESULT_IN_PROGRESS_NO_ACK: _handleInProgressCommandLong(request); return; + case MAV_CMD_SET_MESSAGE_INTERVAL: + { + bool accepted = false; + + _handleCommandLongSetMessageInterval(request, accepted); + if (accepted) { + commandResult = MAV_RESULT_ACCEPTED; + } + break; + } } mavlink_message_t commandAck{}; @@ -1672,112 +1798,137 @@ void MockLink::_moveADSBVehicle(int vehicleIndex) coord.setAltitude(100); // Keeping altitude constant for simplicity } -bool MockLink::_handleRequestMessage(const mavlink_command_long_t &request, bool &noAck) +void MockLink::_handleRequestMessageAutopilotVersion(const mavlink_command_long_t &request, bool &accepted) { - noAck = false; + accepted = true; - switch (static_cast(request.param1)) { - case MAVLINK_MSG_ID_AUTOPILOT_VERSION: - { - switch (_failureMode) { - case MockConfiguration::FailNone: - break; - case MockConfiguration::FailInitialConnectRequestMessageAutopilotVersionFailure: - return false; - case MockConfiguration::FailInitialConnectRequestMessageAutopilotVersionLost: - return true; - default: - break; - } + switch (_failureMode) { + case MockConfiguration::FailNone: + break; + case MockConfiguration::FailInitialConnectRequestMessageAutopilotVersionFailure: + accepted = false; + return; + case MockConfiguration::FailInitialConnectRequestMessageAutopilotVersionLost: + accepted = true; + return; + default: + break; + } - _respondWithAutopilotVersion(); - return true; + _respondWithAutopilotVersion(); +} + +void MockLink::_handleRequestMessageDebug(const mavlink_command_long_t &request, bool &accepted, bool &noAck) +{ + accepted = true; + noAck = false; + + switch (_requestMessageFailureMode) { + case FailRequestMessageNone: + break; + case FailRequestMessageCommandAcceptedMsgNotSent: + return; + case FailRequestMessageCommandUnsupported: + accepted = false; + return; + case FailRequestMessageCommandNoResponse: + accepted = false; + noAck = true; + return; } - case MAVLINK_MSG_ID_PROTOCOL_VERSION: - { - switch (_failureMode) { - case MockConfiguration::FailNone: - break; - case MockConfiguration::FailInitialConnectRequestMessageProtocolVersionFailure: - return false; - case MockConfiguration::FailInitialConnectRequestMessageProtocolVersionLost: - return true; - default: - break; - } - const uint8_t nullHash[8]{}; - mavlink_message_t responseMsg{}; - (void) mavlink_msg_protocol_version_pack_chan( - _vehicleSystemId, - _vehicleComponentId, - mavlinkChannel(), - &responseMsg, - 200, - 100, - 200, - nullHash, - nullHash - ); - respondWithMavlinkMessage(responseMsg); - return true; + mavlink_message_t responseMsg{}; + (void) mavlink_msg_debug_pack_chan( + _vehicleSystemId, + _vehicleComponentId, + mavlinkChannel(), + &responseMsg, + 0, 0, 0 + ); + respondWithMavlinkMessage(responseMsg); +} + +void MockLink::_handleRequestMessageAvailableModes(const mavlink_command_long_t &request, bool &accepted) +{ + accepted = true; + + if (request.param2 == 0) { + // Request for available modes to be streamed out + if (_availableModesWorkerNextModeIndex != 0) { + qCWarning(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: _availableModesWorker already running - _availableModesWorkerNextModeIndex:" << _availableModesWorkerNextModeIndex; + accepted = false; + return; + } + qCDebug(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: starting available modes sequence worker"; + _availableModesWorkerNextModeIndex = 1; // Start with the first mode in sequence (1-based index) + } else { + // Request for specific mode + if (request.param2 > _availableFlightModes.count()) { + qCWarning(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: requested mode index out of range" << request.param2 << _availableFlightModes.count(); + accepted = false; + return; + } + qCDebug(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: received specific mode request for index" << request.param2; + _availableModesWorkerNextModeIndex = -request.param2; // Negative index indicates a specific single mode request } +} + +void MockLink::_handleRequestMessageGimbalManagerInformation(const mavlink_command_long_t &request, bool &accepted) +{ + accepted = true; + + mavlink_message_t responseMsg{}; + (void) mavlink_msg_gimbal_manager_information_pack_chan( + _vehicleSystemId, + _vehicleComponentId, + mavlinkChannel(), + &responseMsg, + 0, // time_boot_ms + GIMBAL_MANAGER_CAP_FLAGS_HAS_ROLL_AXIS | GIMBAL_MANAGER_CAP_FLAGS_HAS_PITCH_AXIS | GIMBAL_MANAGER_CAP_FLAGS_HAS_YAW_AXIS, + MAV_COMP_ID_GIMBAL, + -45, 45, + -45, 45, + -180, 180); + respondWithMavlinkMessage(responseMsg); + (void) mavlink_msg_gimbal_manager_information_pack_chan( + _vehicleSystemId, + _vehicleComponentId, + mavlinkChannel(), + &responseMsg, + 0, // time_boot_ms + GIMBAL_MANAGER_CAP_FLAGS_HAS_ROLL_AXIS | GIMBAL_MANAGER_CAP_FLAGS_HAS_PITCH_AXIS | GIMBAL_MANAGER_CAP_FLAGS_HAS_YAW_AXIS, + MAV_COMP_ID_GIMBAL2, + -45, 45, + -45, 45, + -180, 180); + respondWithMavlinkMessage(responseMsg); +} + +void MockLink::_handleRequestMessage(const mavlink_command_long_t &request, bool &accepted, bool &noAck) +{ + accepted = false; + noAck = false; + + switch (static_cast(request.param1)) { + case MAVLINK_MSG_ID_AUTOPILOT_VERSION: + _handleRequestMessageAutopilotVersion(request, accepted); + break; case MAVLINK_MSG_ID_COMPONENT_METADATA: if (_firmwareType == MAV_AUTOPILOT_PX4) { _sendGeneralMetaData(); - return true; + accepted = true; } break; case MAVLINK_MSG_ID_DEBUG: - { - switch (_requestMessageFailureMode) { - case FailRequestMessageNone: - break; - case FailRequestMessageCommandAcceptedMsgNotSent: - return true; - case FailRequestMessageCommandUnsupported: - return false; - case FailRequestMessageCommandNoResponse: - noAck = true; - return true; - } - - mavlink_message_t responseMsg{}; - (void) mavlink_msg_debug_pack_chan( - _vehicleSystemId, - _vehicleComponentId, - mavlinkChannel(), - &responseMsg, - 0, 0, 0 - ); - respondWithMavlinkMessage(responseMsg); - - return true; - } + _handleRequestMessageDebug(request, accepted, noAck); + break; case MAVLINK_MSG_ID_AVAILABLE_MODES: - { - if (request.param2 == 0) { - // Request for available modes to be streamed out - if (_availableModesWorkerNextModeIndex != 0) { - qCWarning(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: _availableModesWorker already running - _availableModesWorkerNextModeIndex:" << _availableModesWorkerNextModeIndex; - return false; - } - qCDebug(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: starting available modes sequence worker"; - _availableModesWorkerNextModeIndex = 1; // Start with the first mode in sequence (1-based index) - } else { - // Request for specific mode - if (request.param2 > _availableFlightModes.count()) { - qCWarning(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: requested mode index out of range" << request.param2 << _availableFlightModes.count(); - return false; - } - qCDebug(MockLinkLog) << "MAVLINK_MSG_ID_AVAILABLE_MODES: received specific mode request for index" << request.param2; - _availableModesWorkerNextModeIndex = -request.param2; // Negative index indicates a specific single mode request - } - return true; - } + _handleRequestMessageAvailableModes(request, accepted); + break; + case MAVLINK_MSG_ID_GIMBAL_MANAGER_INFORMATION: + _handleRequestMessageGimbalManagerInformation(request, accepted); + break; } - - return false; } void MockLink::_sendGeneralMetaData() @@ -1909,7 +2060,7 @@ void MockLink::_sendVideoInfo() } } -void MockLink::_sendAvailableMode(uint8_t modeIndexOneBased) +void MockLink::_sendAvailableMode(uint8_t modeIndexOneBased) { if (modeIndexOneBased > _availableModesCount()) { qCWarning(MockLinkLog) << "modeIndexOneBased out of range" << modeIndexOneBased << _availableModesCount(); diff --git a/src/Comms/MockLink/MockLink.h b/src/Comms/MockLink/MockLink.h index 56a76984ee30..2388daf91a01 100644 --- a/src/Comms/MockLink/MockLink.h +++ b/src/Comms/MockLink/MockLink.h @@ -21,6 +21,8 @@ #include #include +#include + class MockLinkFTP; class MockLinkWorker; class QThread; @@ -92,6 +94,26 @@ class MockLink : public LinkInterface }; void setRequestMessageFailureMode(RequestMessageFailureMode_t failureMode) { _requestMessageFailureMode = failureMode; } + enum ParamSetFailureMode_t { + FailParamSetNone, ///< Normal behavior + FailParamSetNoAck, ///< Do not send PARAM_VALUE ack + FailParamSetFirstAttemptNoAck, ///< Skip ack on first attempt, respond to retry + }; + void setParamSetFailureMode(ParamSetFailureMode_t mode) { + _paramSetFailureMode = mode; + _paramSetFailureFirstAttemptPending = (mode == FailParamSetFirstAttemptNoAck); + } + + enum ParamRequestReadFailureMode_t { + FailParamRequestReadNone, ///< Normal behavior + FailParamRequestReadNoResponse, ///< Do not respond to PARAM_REQUEST_READ + FailParamRequestReadFirstAttemptNoResponse, ///< Skip response on first attempt, respond to retry + }; + void setParamRequestReadFailureMode(ParamRequestReadFailureMode_t mode) { + _paramRequestReadFailureMode = mode; + _paramRequestReadFailureFirstAttemptPending = (mode == FailParamRequestReadFirstAttemptNoResponse); + } + static MockLink *startPX4MockLink(bool sendStatusText, MockConfiguration::FailureMode_t failureMode = MockConfiguration::FailNone); static MockLink *startGenericMockLink(bool sendStatusText, MockConfiguration::FailureMode_t failureMode = MockConfiguration::FailNone); static MockLink *startNoInitialConnectMockLink(bool sendStatusText, MockConfiguration::FailureMode_t failureMode = MockConfiguration::FailNone); @@ -155,13 +177,18 @@ private slots: void _handleFTP(const mavlink_message_t &msg); void _handleCommandLong(const mavlink_message_t &msg); void _handleInProgressCommandLong(const mavlink_command_long_t &request); + void _handleCommandLongSetMessageInterval(const mavlink_command_long_t &request, bool &acccepted); void _handleManualControl(const mavlink_message_t &msg); void _handlePreFlightCalibration(const mavlink_command_long_t &request); void _handleTakeoff(const mavlink_command_long_t &request); void _handleLogRequestList(const mavlink_message_t &msg); void _handleLogRequestData(const mavlink_message_t &msg); void _handleParamMapRC(const mavlink_message_t &msg); - bool _handleRequestMessage(const mavlink_command_long_t &request, bool &noAck); + void _handleRequestMessage(const mavlink_command_long_t &request, bool &accepted, bool &noAck); + void _handleRequestMessageAutopilotVersion(const mavlink_command_long_t &request, bool &accepted); + void _handleRequestMessageDebug(const mavlink_command_long_t &request, bool &accepted, bool &noAck); + void _handleRequestMessageAvailableModes(const mavlink_command_long_t &request, bool &accepted); + void _handleRequestMessageGimbalManagerInformation(const mavlink_command_long_t &request, bool &accepted); void _sendHeartBeat(); void _sendHighLatency2(); @@ -172,6 +199,8 @@ private slots: void _sendVibration(); void _sendSysStatus(); void _sendBatteryStatus(); + void _sendGimbalManagerStatus(); + void _sendGimbalDeviceAttitudeStatus(); void _sendChunkedStatusText(uint16_t chunkId, bool missingChunks); void _sendStatusTextMessages(); void _respondWithAutopilotVersion(); @@ -252,7 +281,14 @@ private slots: uint32_t _logDownloadCurrentOffset = 0; ///< Current offset we are sending from uint32_t _logDownloadBytesRemaining = 0; ///< Number of bytes still to send, 0 = send inactive + bool _sendGimbalManagerStatusNow = false; + bool _sendGimbalDeviceAttitudeStatusNow = false; + RequestMessageFailureMode_t _requestMessageFailureMode = FailRequestMessageNone; + ParamSetFailureMode_t _paramSetFailureMode = FailParamSetNone; + bool _paramSetFailureFirstAttemptPending = false; + ParamRequestReadFailureMode_t _paramRequestReadFailureMode = FailParamRequestReadNone; + bool _paramRequestReadFailureFirstAttemptPending = false; QMap _receivedMavCommandCountMap; QMap> _mapParamName2Value; @@ -286,4 +322,6 @@ private slots: static constexpr bool _mavlinkStarted = true; static QList _availableFlightModes; + + std::atomic _disconnectedEmitted{false}; }; diff --git a/src/Comms/MockLink/MockLinkFTP.cc b/src/Comms/MockLink/MockLinkFTP.cc index 68af3ee7c44e..575b16236077 100644 --- a/src/Comms/MockLink/MockLinkFTP.cc +++ b/src/Comms/MockLink/MockLinkFTP.cc @@ -12,7 +12,7 @@ #include "QGCLoggingCategory.h" #include "QGCTemporaryFile.h" -QGC_LOGGING_CATEGORY(MockLinkFTPLog, "MockLinkMissionItemHandlerLog") +QGC_LOGGING_CATEGORY(MockLinkFTPLog, "Comms.MockLink.MockLinkFTP") MockLinkFTP::MockLinkFTP(uint8_t systemIdServer, uint8_t componentIdServer, MockLink *mockLink) : QObject(mockLink) diff --git a/src/Comms/MockLink/MockLinkFTP.h b/src/Comms/MockLink/MockLinkFTP.h index 0bdb89378283..b3f79507481d 100644 --- a/src/Comms/MockLink/MockLinkFTP.h +++ b/src/Comms/MockLink/MockLinkFTP.h @@ -114,4 +114,3 @@ class MockLinkFTP : public QObject static constexpr uint8_t _sessionId = 1; ///< We only support a single fixed session }; - diff --git a/src/Comms/MockLink/MockLinkMissionItemHandler.cc b/src/Comms/MockLink/MockLinkMissionItemHandler.cc index 53caa78760dc..b6fc6c603c77 100644 --- a/src/Comms/MockLink/MockLinkMissionItemHandler.cc +++ b/src/Comms/MockLink/MockLinkMissionItemHandler.cc @@ -13,7 +13,7 @@ #include "MockLink.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(MockLinkMissionItemHandlerLog, "qgc.comms.mocklink.mocklinkmissionitemhandler") +QGC_LOGGING_CATEGORY(MockLinkMissionItemHandlerLog, "Comms.MockLink.MockLinkMissionItemHandler") MockLinkMissionItemHandler::MockLinkMissionItemHandler(MockLink *mockLink) : QObject(mockLink) diff --git a/src/Comms/MockLink/MockLinkMissionItemHandler.h b/src/Comms/MockLink/MockLinkMissionItemHandler.h index 88bcc8be1175..eefd9d43df38 100644 --- a/src/Comms/MockLink/MockLinkMissionItemHandler.h +++ b/src/Comms/MockLink/MockLinkMissionItemHandler.h @@ -111,4 +111,3 @@ private slots: bool _failReadRequest1FirstResponse = true; bool _failWriteMissionCountFirstResponse = true; }; - diff --git a/src/Comms/QGCSerialPortInfo.cc b/src/Comms/QGCSerialPortInfo.cc index 89ae730b1d7d..e811532dfef9 100644 --- a/src/Comms/QGCSerialPortInfo.cc +++ b/src/Comms/QGCSerialPortInfo.cc @@ -17,7 +17,7 @@ #include #include -QGC_LOGGING_CATEGORY(QGCSerialPortInfoLog, "qgc.comms.qgcserialportinfo") +QGC_LOGGING_CATEGORY(QGCSerialPortInfoLog, "Comms.QGCSerialPortInfo") bool QGCSerialPortInfo::_jsonLoaded = false; bool QGCSerialPortInfo::_jsonDataValid = false; @@ -28,18 +28,18 @@ QList QGCSerialPortInfo::_boardManufac QGCSerialPortInfo::QGCSerialPortInfo() : QSerialPortInfo() { - // qCDebug(QGCSerialPortInfoLog) << Q_FUNC_INFO << this; + qCDebug(QGCSerialPortInfoLog) << this; } QGCSerialPortInfo::QGCSerialPortInfo(const QSerialPort &port) : QSerialPortInfo(port) { - // qCDebug(QGCSerialPortInfoLog) << Q_FUNC_INFO << this; + qCDebug(QGCSerialPortInfoLog) << this; } QGCSerialPortInfo::~QGCSerialPortInfo() { - // qCDebug(QGCSerialPortInfoLog) << Q_FUNC_INFO << this; + qCDebug(QGCSerialPortInfoLog) << this; } bool QGCSerialPortInfo::_loadJsonData() diff --git a/src/Comms/SerialLink.cc b/src/Comms/SerialLink.cc index 30b71c19cbe5..176888de9e73 100644 --- a/src/Comms/SerialLink.cc +++ b/src/Comms/SerialLink.cc @@ -14,7 +14,7 @@ #include #include -QGC_LOGGING_CATEGORY(SerialLinkLog, "qgc.comms.seriallink") +QGC_LOGGING_CATEGORY(SerialLinkLog, "Comms.SerialLink") namespace { constexpr int CONNECT_TIMEOUT_MS = 1000; @@ -61,11 +61,9 @@ void SerialConfiguration::setPortName(const QString &name) void SerialConfiguration::copyFrom(const LinkConfiguration *source) { - Q_ASSERT(source); LinkConfiguration::copyFrom(source); - const SerialConfiguration* const serialSource = qobject_cast(source); - Q_ASSERT(serialSource); + const SerialConfiguration* serialSource = qobject_cast(source); setBaud(serialSource->baud()); setDataBits(serialSource->dataBits()); @@ -75,6 +73,7 @@ void SerialConfiguration::copyFrom(const LinkConfiguration *source) setPortName(serialSource->portName()); setPortDisplayName(serialSource->portDisplayName()); setUsbDirect(serialSource->usbDirect()); + setdtrForceLow(serialSource->dtrForceLow()); } void SerialConfiguration::loadSettings(QSettings &settings, const QString &root) @@ -88,6 +87,7 @@ void SerialConfiguration::loadSettings(QSettings &settings, const QString &root) setParity(static_cast(settings.value("parity", _parity).toInt())); setPortName(settings.value("portName", _portName).toString()); setPortDisplayName(settings.value("portDisplayName", _portDisplayName).toString()); + setdtrForceLow(settings.value("dtrForceLow", _dtrForceLow).toBool()); settings.endGroup(); } @@ -103,6 +103,7 @@ void SerialConfiguration::saveSettings(QSettings &settings, const QString &root) settings.setValue("parity", _parity); settings.setValue("portName", _portName); settings.setValue("portDisplayName", _portDisplayName); + settings.setValue("dtrForceLow", _dtrForceLow); settings.endGroup(); } @@ -189,7 +190,7 @@ SerialWorker::SerialWorker(const SerialConfiguration *config, QObject *parent) : QObject(parent) , _serialConfig(config) { - // qCDebug(SerialLinkLog) << this; + qCDebug(SerialLinkLog) << this; (void) qRegisterMetaType("QSerialPort::SerialPortError"); } @@ -198,7 +199,7 @@ SerialWorker::~SerialWorker() { disconnectFromPort(); - // qCDebug(SerialLinkLog) << this; + qCDebug(SerialLinkLog) << this; } bool SerialWorker::isConnected() const @@ -208,11 +209,13 @@ bool SerialWorker::isConnected() const void SerialWorker::setupPort() { - Q_ASSERT(!_port); - _port = new QSerialPort(this); + if (!_port) { + _port = new QSerialPort(this); + } - Q_ASSERT(!_timer); - _timer = new QTimer(this); + if (!_timer) { + _timer = new QTimer(this); + } (void) connect(_port, &QSerialPort::aboutToClose, this, &SerialWorker::_onPortDisconnected); (void) connect(_port, &QSerialPort::readyRead, this, &SerialWorker::_onPortReadyRead); @@ -223,7 +226,6 @@ void SerialWorker::setupPort() } */ (void) connect(_timer, &QTimer::timeout, this, &SerialWorker::_checkPortAvailability); - _timer->start(CONNECT_TIMEOUT_MS); } void SerialWorker::connectToPort() @@ -271,6 +273,7 @@ void SerialWorker::disconnectFromPort() } qCDebug(SerialLinkLog) << "Attempting to close port:" << _port->portName(); + _port->close(); } @@ -312,13 +315,17 @@ void SerialWorker::_onPortConnected() { qCDebug(SerialLinkLog) << "Port connected:" << _port->portName(); - _port->setDataTerminalReady(true); + _port->setDataTerminalReady(_serialConfig->dtrForceLow() ? false : true); _port->setBaudRate(_serialConfig->baud()); _port->setDataBits(static_cast(_serialConfig->dataBits())); _port->setFlowControl(static_cast(_serialConfig->flowControl())); _port->setStopBits(static_cast(_serialConfig->stopBits())); _port->setParity(static_cast(_serialConfig->parity())); + if (_timer) { + _timer->start(CONNECT_TIMEOUT_MS); + } + _errorEmitted = false; emit connected(); } @@ -326,6 +333,11 @@ void SerialWorker::_onPortConnected() void SerialWorker::_onPortDisconnected() { qCDebug(SerialLinkLog) << "Port disconnected:" << _port->portName(); + + if (_timer) { + _timer->stop(); + } + _errorEmitted = false; emit disconnected(); } @@ -399,7 +411,7 @@ SerialLink::SerialLink(SharedLinkConfigurationPtr &config, QObject *parent) , _worker(new SerialWorker(_serialConfig)) , _workerThread(new QThread(this)) { - // qCDebug(SerialLinkLog) << this; + qCDebug(SerialLinkLog) << this; _workerThread->setObjectName(QStringLiteral("Serial_%1").arg(_serialConfig->name())); @@ -419,19 +431,22 @@ SerialLink::SerialLink(SharedLinkConfigurationPtr &config, QObject *parent) SerialLink::~SerialLink() { - (void) QMetaObject::invokeMethod(_worker, "disconnectFromPort", Qt::BlockingQueuedConnection); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectFromPort", Qt::BlockingQueuedConnection); + _onDisconnected(); + } _workerThread->quit(); if (!_workerThread->wait(DISCONNECT_TIMEOUT_MS)) { qCWarning(SerialLinkLog) << "Failed to wait for Serial Thread to close"; } - // qCDebug(SerialLinkLog) << this; + qCDebug(SerialLinkLog) << this; } bool SerialLink::isConnected() const { - return _worker->isConnected(); + return _worker && _worker->isConnected(); } bool SerialLink::_connect() @@ -441,17 +456,22 @@ bool SerialLink::_connect() void SerialLink::disconnect() { - (void) QMetaObject::invokeMethod(_worker, "disconnectFromPort", Qt::QueuedConnection); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectFromPort", Qt::QueuedConnection); + } } void SerialLink::_onConnected() { + _disconnectedEmitted = false; emit connected(); } void SerialLink::_onDisconnected() { - emit disconnected(); + if (!_disconnectedEmitted.exchange(true)) { + emit disconnected(); + } } void SerialLink::_onErrorOccurred(const QString &errorString) diff --git a/src/Comms/SerialLink.h b/src/Comms/SerialLink.h index 80ced02cbfd5..f82e642ee5c3 100644 --- a/src/Comms/SerialLink.h +++ b/src/Comms/SerialLink.h @@ -9,17 +9,18 @@ #pragma once +#include "LinkConfiguration.h" +#include "LinkInterface.h" + #include #include - #ifdef Q_OS_ANDROID #include "qserialport.h" #else #include #endif -#include "LinkConfiguration.h" -#include "LinkInterface.h" +#include class QThread; class QTimer; @@ -39,6 +40,7 @@ class SerialConfiguration : public LinkConfiguration Q_PROPERTY(QString portName READ portName WRITE setPortName NOTIFY portNameChanged) Q_PROPERTY(QString portDisplayName READ portDisplayName NOTIFY portDisplayNameChanged) Q_PROPERTY(bool usbDirect READ usbDirect WRITE setUsbDirect NOTIFY usbDirectChanged) + Q_PROPERTY(bool dtrForceLow READ dtrForceLow WRITE setdtrForceLow NOTIFY dtrForceLowChanged) public: explicit SerialConfiguration(const QString &name, QObject *parent = nullptr); @@ -76,6 +78,9 @@ class SerialConfiguration : public LinkConfiguration bool usbDirect() const { return _usbDirect; } void setUsbDirect(bool usbDirect) { if (usbDirect != _usbDirect) { _usbDirect = usbDirect; emit usbDirectChanged(); } } + bool dtrForceLow() const { return _dtrForceLow; } + void setdtrForceLow(bool dtrForceLow) { if (dtrForceLow != _dtrForceLow) { _dtrForceLow = dtrForceLow; emit dtrForceLowChanged(); } } + static QStringList supportedBaudRates(); static QString cleanPortDisplayName(const QString &name); @@ -88,6 +93,7 @@ class SerialConfiguration : public LinkConfiguration void portNameChanged(); void portDisplayNameChanged(); void usbDirectChanged(); + void dtrForceLowChanged(); private: qint32 _baud = QSerialPort::Baud57600; @@ -98,6 +104,7 @@ class SerialConfiguration : public LinkConfiguration QString _portName; QString _portDisplayName; bool _usbDirect = false; + bool _dtrForceLow = false; }; /*===========================================================================*/ @@ -173,4 +180,5 @@ private slots: const SerialConfiguration *_serialConfig = nullptr; SerialWorker *_worker = nullptr; QThread *_workerThread = nullptr; + std::atomic _disconnectedEmitted{false}; }; diff --git a/src/Comms/TCPLink.cc b/src/Comms/TCPLink.cc index 95ace349cf3c..c6dddf1a4ca8 100644 --- a/src/Comms/TCPLink.cc +++ b/src/Comms/TCPLink.cc @@ -13,13 +13,15 @@ #include #include +#include #include -QGC_LOGGING_CATEGORY(TCPLinkLog, "qgc.comms.tcplink") +QGC_LOGGING_CATEGORY(TCPLinkLog, "Comms.TCPLink") namespace { - constexpr int CONNECT_TIMEOUT_MS = 1000; - constexpr int TYPE_OF_SERVICE = 32; // Set ToS for low delay + constexpr int CONNECT_TIMEOUT_MS = 3000; + constexpr int DISCONNECT_TIMEOUT_MS = 3000; + constexpr int TYPE_OF_SERVICE = 32; // Set ToS to priority for low delay } /*===========================================================================*/ @@ -27,7 +29,7 @@ namespace { TCPConfiguration::TCPConfiguration(const QString &name, QObject *parent) : LinkConfiguration(name, parent) { - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; } TCPConfiguration::TCPConfiguration(const TCPConfiguration *copy, QObject *parent) @@ -35,21 +37,36 @@ TCPConfiguration::TCPConfiguration(const TCPConfiguration *copy, QObject *parent , _host(copy->host()) , _port(copy->port()) { - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; } TCPConfiguration::~TCPConfiguration() { - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; +} + +void TCPConfiguration::setHost(const QString &host) +{ + const QString cleanHost = host.trimmed(); + if (cleanHost != _host) { + _host = cleanHost; + emit hostChanged(); + } +} + +void TCPConfiguration::setPort(quint16 port) +{ + if (port != _port) { + _port = port; + emit portChanged(); + } } void TCPConfiguration::copyFrom(const LinkConfiguration *source) { - Q_ASSERT(source); LinkConfiguration::copyFrom(source); - const TCPConfiguration* const tcpSource = qobject_cast(source); - Q_ASSERT(tcpSource); + const TCPConfiguration* tcpSource = qobject_cast(source); setHost(tcpSource->host()); setPort(tcpSource->port()); @@ -81,14 +98,14 @@ TCPWorker::TCPWorker(const TCPConfiguration *config, QObject *parent) : QObject(parent) , _config(config) { - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; } TCPWorker::~TCPWorker() { disconnectFromHost(); - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; } bool TCPWorker::isConnected() const @@ -98,8 +115,9 @@ bool TCPWorker::isConnected() const void TCPWorker::setupSocket() { - Q_ASSERT(!_socket); - _socket = new QTcpSocket(this); + if (!_socket) { + _socket = new QTcpSocket(this); + } _socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); _socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); @@ -117,8 +135,8 @@ void TCPWorker::setupSocket() qCDebug(TCPLinkLog) << "TCP State Changed:" << state; }); - (void) connect(_socket, &QTcpSocket::hostFound, this, []() { - qCDebug(TCPLinkLog) << "TCP Host Found"; + (void) connect(_socket, &QTcpSocket::hostFound, this, [this]() { + qCDebug(TCPLinkLog) << "TCP Host Found" << _socket->peerName() << _socket->peerAddress() << _socket->peerPort(); }); } } @@ -130,6 +148,11 @@ void TCPWorker::connectToHost() return; } + if (_config->host().isEmpty()) { + emit errorOccurred(tr("Connection Failed: Host address is empty")); + return; + } + _errorEmitted = false; qCDebug(TCPLinkLog) << "Attempting to connect to host:" << _config->host() << "port:" << _config->port(); @@ -138,9 +161,8 @@ void TCPWorker::connectToHost() if (!_socket->waitForConnected(CONNECT_TIMEOUT_MS)) { qCWarning(TCPLinkLog) << "Connection to" << _config->host() << ":" << _config->port() << "failed:" << _socket->errorString(); - if (!_errorEmitted) { + if (!_errorEmitted.exchange(true)) { emit errorOccurred(tr("Connection Failed: %1").arg(_socket->errorString())); - _errorEmitted = true; } _onSocketDisconnected(); @@ -159,6 +181,10 @@ void TCPWorker::disconnectFromHost() qCDebug(TCPLinkLog) << "Attempting to disconnect from host:" << _config->host() << "port:" << _config->port(); _socket->disconnectFromHost(); + + if (_socket->state() != QAbstractSocket::UnconnectedState) { + _socket->waitForDisconnected(1000); + } } void TCPWorker::writeData(const QByteArray &data) @@ -206,7 +232,9 @@ void TCPWorker::_onSocketDisconnected() void TCPWorker::_onSocketReadyRead() { const QByteArray data = _socket->readAll(); - emit dataReceived(data); + if (!data.isEmpty()) { + emit dataReceived(data); + } } void TCPWorker::_onSocketBytesWritten(qint64 bytes) @@ -216,14 +244,11 @@ void TCPWorker::_onSocketBytesWritten(qint64 bytes) void TCPWorker::_onSocketErrorOccurred(QAbstractSocket::SocketError socketError) { - Q_UNUSED(socketError); const QString errorString = _socket->errorString(); + qCWarning(TCPLinkLog) << "Socket error:" << socketError << errorString; - qCWarning(TCPLinkLog) << "Socket error:" << errorString; - - if (!_errorEmitted) { + if (!_errorEmitted.exchange(true)) { emit errorOccurred(errorString); - _errorEmitted = true; } } @@ -235,7 +260,7 @@ TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) , _worker(new TCPWorker(_tcpConfig)) , _workerThread(new QThread(this)) { - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; _workerThread->setObjectName(QStringLiteral("TCP_%1").arg(_tcpConfig->name())); @@ -255,19 +280,22 @@ TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) TCPLink::~TCPLink() { - TCPLink::disconnect(); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectFromHost", Qt::BlockingQueuedConnection); + _onDisconnected(); + } _workerThread->quit(); - if (!_workerThread->wait()) { + if (!_workerThread->wait(DISCONNECT_TIMEOUT_MS)) { qCWarning(TCPLinkLog) << "Failed to wait for TCP Thread to close"; } - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + qCDebug(TCPLinkLog) << this; } bool TCPLink::isConnected() const { - return _worker->isConnected(); + return _worker && _worker->isConnected(); } bool TCPLink::_connect() @@ -277,17 +305,22 @@ bool TCPLink::_connect() void TCPLink::disconnect() { - (void) QMetaObject::invokeMethod(_worker, "disconnectFromHost", Qt::QueuedConnection); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectFromHost", Qt::QueuedConnection); + } } void TCPLink::_onConnected() { + _disconnectedEmitted = false; emit connected(); } void TCPLink::_onDisconnected() { - emit disconnected(); + if (!_disconnectedEmitted.exchange(true)) { + emit disconnected(); + } } void TCPLink::_onErrorOccurred(const QString &errorString) diff --git a/src/Comms/TCPLink.h b/src/Comms/TCPLink.h index b77a493551f2..e14e0be30139 100644 --- a/src/Comms/TCPLink.h +++ b/src/Comms/TCPLink.h @@ -9,14 +9,15 @@ #pragma once +#include "LinkConfiguration.h" +#include "LinkInterface.h" + #include #include #include #include -#include -#include "LinkConfiguration.h" -#include "LinkInterface.h" +#include class QTcpSocket; class QThread; @@ -35,7 +36,7 @@ class TCPConfiguration : public LinkConfiguration public: explicit TCPConfiguration(const QString &name, QObject *parent = nullptr); explicit TCPConfiguration(const TCPConfiguration *copy, QObject *parent = nullptr); - virtual ~TCPConfiguration(); + ~TCPConfiguration() override; LinkType type() const override { return LinkConfiguration::TypeTcp; } void copyFrom(const LinkConfiguration *source) override; @@ -44,17 +45,17 @@ class TCPConfiguration : public LinkConfiguration QString settingsURL() const override { return QStringLiteral("TcpSettings.qml"); } QString settingsTitle() const override { return tr("TCP Link Settings"); } - QString host() const { return _host.toString(); } - void setHost(const QString &host) { if (host != _host.toString()) { _host.setAddress(host); emit hostChanged(); } } + QString host() const { return _host; } + void setHost(const QString &host); quint16 port() const { return _port; } - void setPort(quint16 port) { if (port != _port) { _port = port; emit portChanged(); } } + void setPort(quint16 port); signals: void hostChanged(); void portChanged(); private: - QHostAddress _host; + QString _host; quint16 _port = 5760; }; @@ -66,7 +67,7 @@ class TCPWorker : public QObject public: explicit TCPWorker(const TCPConfiguration *config, QObject *parent = nullptr); - ~TCPWorker(); + ~TCPWorker() override; bool isConnected() const; @@ -93,7 +94,7 @@ private slots: private: const TCPConfiguration *_config = nullptr; QTcpSocket *_socket = nullptr; - bool _errorEmitted = false; + std::atomic _errorEmitted{false}; }; /*===========================================================================*/ @@ -104,7 +105,7 @@ class TCPLink : public LinkInterface public: explicit TCPLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); - virtual ~TCPLink(); + ~TCPLink() override; bool isConnected() const override; void disconnect() override; @@ -124,4 +125,5 @@ private slots: const TCPConfiguration *_tcpConfig = nullptr; TCPWorker *_worker = nullptr; QThread *_workerThread = nullptr; + std::atomic _disconnectedEmitted{false}; }; diff --git a/src/Comms/UDPLink.cc b/src/Comms/UDPLink.cc index 34212d889dba..852e7b05c0dc 100644 --- a/src/Comms/UDPLink.cc +++ b/src/Comms/UDPLink.cc @@ -21,7 +21,7 @@ #include #include -QGC_LOGGING_CATEGORY(UDPLinkLog, "qgc.comms.udplink") +QGC_LOGGING_CATEGORY(UDPLinkLog, "Comms.UDPLink") namespace { constexpr int BUFFER_TRIGGER_SIZE = 10 * 1024; @@ -49,7 +49,7 @@ UDPConfiguration::UDPConfiguration(const QString &name, QObject *parent) UDPConfiguration::UDPConfiguration(const UDPConfiguration *source, QObject *parent) : LinkConfiguration(source, parent) { - // qCDebug(UDPLinkLog) << Q_FUNC_INFO << this; + qCDebug(UDPLinkLog) << this; UDPConfiguration::copyFrom(source); } @@ -58,7 +58,7 @@ UDPConfiguration::~UDPConfiguration() { _targetHosts.clear(); - // qCDebug(UDPLinkLog) << Q_FUNC_INFO << this; + qCDebug(UDPLinkLog) << this; } void UDPConfiguration::setAutoConnect(bool autoc) @@ -68,12 +68,11 @@ void UDPConfiguration::setAutoConnect(bool autoc) const QString targetHostIP = settings->udpTargetHostIP()->rawValue().toString(); const quint16 targetHostPort = settings->udpTargetHostPort()->rawValue().toUInt(); if (autoc) { - setLocalPort(settings->udpListenPort()->rawValue().toInt()); + setLocalPort(settings->udpListenPort()->rawValue().toInt()); if (!targetHostIP.isEmpty()) { addHost(targetHostIP, targetHostPort); } - } - else { + } else { setLocalPort(0); if (!targetHostIP.isEmpty()) { removeHost(targetHostIP, targetHostPort); @@ -85,11 +84,9 @@ void UDPConfiguration::setAutoConnect(bool autoc) void UDPConfiguration::copyFrom(const LinkConfiguration *source) { - Q_ASSERT(source); LinkConfiguration::copyFrom(source); - const UDPConfiguration *const udpSource = qobject_cast(source); - Q_ASSERT(udpSource); + const UDPConfiguration *udpSource = qobject_cast(source); setLocalPort(udpSource->localPort()); _targetHosts.clear(); @@ -269,14 +266,14 @@ UDPWorker::UDPWorker(const UDPConfiguration *config, QObject *parent) : QObject(parent) , _udpConfig(config) { - // qCDebug(UDPLinkLog) << Q_FUNC_INFO << this; + qCDebug(UDPLinkLog) << this; } UDPWorker::~UDPWorker() { disconnectLink(); - // qCDebug(UDPLinkLog) << Q_FUNC_INFO << this; + qCDebug(UDPLinkLog) << this; } bool UDPWorker::isConnected() const @@ -286,8 +283,9 @@ bool UDPWorker::isConnected() const void UDPWorker::setupSocket() { - Q_ASSERT(!_socket); - _socket = new QUdpSocket(this); + if (!_socket) { + _socket = new QUdpSocket(this); + } const QList localAddresses = QNetworkInterface::allAddresses(); _localAddresses = QSet(localAddresses.constBegin(), localAddresses.constEnd()); @@ -366,11 +364,16 @@ void UDPWorker::disconnectLink() _deregisterZeroconf(); #endif - if (isConnected()) { - (void) _socket->leaveMulticastGroup(_multicastGroup); - _socket->close(); + if (!isConnected()) { + qCDebug(UDPLinkLog) << "Already disconnected"; + return; } + qCDebug(UDPLinkLog) << "Disconnecting UDP link"; + + (void) _socket->leaveMulticastGroup(_multicastGroup); + _socket->close(); + _sessionTargets.clear(); } @@ -493,8 +496,6 @@ void UDPWorker::_zeroconfRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags f { Q_UNUSED(sdRef); Q_UNUSED(flags); Q_UNUSED(name); Q_UNUSED(regtype); Q_UNUSED(domain); - // qCDebug(UDPLinkLog) << Q_FUNC_INFO; - UDPWorker *const worker = static_cast(context); if (errorCode != kDNSServiceErr_NoError) { emit worker->errorOccurred(tr("Zeroconf Register Error: %1").arg(errorCode)); @@ -564,7 +565,7 @@ UDPLink::UDPLink(SharedLinkConfigurationPtr &config, QObject *parent) , _worker(new UDPWorker(_udpConfig)) , _workerThread(new QThread(this)) { - // qCDebug(UDPLinkLog) << Q_FUNC_INFO << this; + qCDebug(UDPLinkLog) << this; _workerThread->setObjectName(QStringLiteral("UDP_%1").arg(_udpConfig->name())); @@ -584,19 +585,22 @@ UDPLink::UDPLink(SharedLinkConfigurationPtr &config, QObject *parent) UDPLink::~UDPLink() { - UDPLink::disconnect(); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::BlockingQueuedConnection); + _onDisconnected(); + } _workerThread->quit(); if (!_workerThread->wait()) { qCWarning(UDPLinkLog) << "Failed to wait for UDP Thread to close"; } - // qCDebug(UDPLinkLog) << Q_FUNC_INFO << this; + qCDebug(UDPLinkLog) << this; } bool UDPLink::isConnected() const { - return _worker->isConnected(); + return _worker && _worker->isConnected(); } bool UDPLink::_connect() @@ -606,17 +610,22 @@ bool UDPLink::_connect() void UDPLink::disconnect() { - (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::QueuedConnection); + if (isConnected()) { + (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::QueuedConnection); + } } void UDPLink::_onConnected() { + _disconnectedEmitted = false; emit connected(); } void UDPLink::_onDisconnected() { - emit disconnected(); + if (!_disconnectedEmitted.exchange(true)) { + emit disconnected(); + } } void UDPLink::_onErrorOccurred(const QString &errorString) diff --git a/src/Comms/UDPLink.h b/src/Comms/UDPLink.h index 6643af1e5cdb..472c7e40053e 100644 --- a/src/Comms/UDPLink.h +++ b/src/Comms/UDPLink.h @@ -9,6 +9,9 @@ #pragma once +#include "LinkConfiguration.h" +#include "LinkInterface.h" + #include #include #include @@ -16,6 +19,8 @@ #include #include +#include + #ifdef QGC_ZEROCONF_ENABLED #ifdef Q_OS_WIN #define WIN32_LEAN_AND_MEAN @@ -23,9 +28,6 @@ #include #endif -#include "LinkConfiguration.h" -#include "LinkInterface.h" - class QUdpSocket; class QThread; @@ -189,4 +191,5 @@ private slots: const UDPConfiguration *_udpConfig = nullptr; UDPWorker *_worker = nullptr; QThread *_workerThread = nullptr; + std::atomic _disconnectedEmitted{false}; }; diff --git a/src/Comms/USBBoardInfo.json b/src/Comms/USBBoardInfo.json index 52b137b9dd29..e3945b9b0a08 100644 --- a/src/Comms/USBBoardInfo.json +++ b/src/Comms/USBBoardInfo.json @@ -24,6 +24,7 @@ { "vendorID": 9900, "productID": 1, "boardClass": "Pixhawk", "name": "Omnibus F4 SD" }, { "vendorID": 8137, "productID": 28, "boardClass": "Pixhawk", "name": "PX4 FMUK66 v3.x" }, { "vendorID": 8137, "productID": 36, "boardClass": "Pixhawk", "name": "Tropic-Community VMU" }, + { "vendorID": 8137, "productID": 37, "boardClass": "Pixhawk", "name": "MR-TROPIC" }, { "vendorID": 1155, "productID": 41775, "boardClass": "Pixhawk", "name": "PX4 FMU ModalAI FCv1" }, { "vendorID": 1155, "productID": 41776, "boardClass": "Pixhawk", "name": "PX4 FMU ModalAI FCv2" }, { "vendorID":12642, "productID": 75, "boardClass": "Pixhawk", "name": "PX4 DurandalV1" }, @@ -54,6 +55,8 @@ { "vendorID": 9900, "productID": 4132, "boardClass": "Pixhawk", "name": "mRo Control Zero H7 OEM" }, { "vendorID": 9900, "productID": 4388, "boardClass": "Pixhawk", "name": "3DR Control Zero H7 OEM Rev G" }, + { "vendorID": 2106, "productID": 7120, "boardClass": "Pixhawk", "name": "PX4 Accton Godwit GA1" }, + { "vendorID": 1027, "productID": 24597, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "3DR Radio" }, { "vendorID": 4292, "productID": 60000, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "SILabs Radio" }, { "vendorID": 12346, "productID": 4097, "boardClass": "SiK Radio", "name": "DroneBridge Radio", "comment": "ESP32-based telemetry radio" }, @@ -66,12 +69,15 @@ { "vendorID": 8352, "productID": 16733, "boardClass": "OpenPilot", "name": "OpenPilot CC3D" }, { "vendorID": 8352, "productID": 16734, "boardClass": "OpenPilot", "name": "OpenPilot Revolution" }, { "vendorID": 8352, "productID": 16848, "boardClass": "OpenPilot", "name": "Taulabs Sparky2" }, - { "vendorID": 13891, "productID": 5600, "boardClass": "Pixhawk", "name": "ZeroOne X6" } + { "vendorID": 13891, "productID": 5600, "boardClass": "Pixhawk", "name": "ZeroOne X6" }, + { "vendorID": 8355, "productID": 16888, "boardClass": "Pixhawk", "name": "Svehicle e2" } ], "boardDescriptionFallback": [ { "regExp": "^ZeroOne x6.x$", "boardClass": "Pixhawk" }, { "regExp": "^ZeroOne BL x6.x$", "boardClass": "Pixhawk" }, + { "regExp": "^Svehicle e2.x$", "boardClass": "Pixhawk" }, + { "regExp": "^Svehicle BL e2.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 FMU v6U.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 BL FMU v6U.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 FMU v6X.x$", "boardClass": "Pixhawk" }, @@ -111,6 +117,8 @@ { "regExp": "^ARK Pi6X.x$", "boardClass": "Pixhawk" }, { "regExp": "^ARK BL Pi6X.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 TROPIC Community","boardClass": "Pixhawk" }, + { "regExp": "^PX4 MR-TROPIC", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 BL MR-TROPIC", "boardClass": "Pixhawk" }, { "regExp": "^FT231X USB UART$", "boardClass": "SiK Radio" }, { "regExp": "USB UART$", "boardClass": "SiK Radio", "androidOnly": true, "comment": "Very broad fallback, too dangerous for non-android" } ], diff --git a/src/Comms/UdpIODevice.cc b/src/Comms/UdpIODevice.cc index a96114744c69..8c256fe8f9a4 100644 --- a/src/Comms/UdpIODevice.cc +++ b/src/Comms/UdpIODevice.cc @@ -10,7 +10,7 @@ #include "UdpIODevice.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(UdpIODeviceLog, "qgc.comms.udpiodevice") +QGC_LOGGING_CATEGORY(UdpIODeviceLog, "Comms.UdpIODevice") UdpIODevice::UdpIODevice(QObject *parent) : QUdpSocket(parent) diff --git a/src/FactSystem/CMakeLists.txt b/src/FactSystem/CMakeLists.txt index dd4efa68c7ff..7f3ec2802efd 100644 --- a/src/FactSystem/CMakeLists.txt +++ b/src/FactSystem/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Fact System Module +# Parameter management and data binding infrastructure +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE Fact.cc @@ -20,4 +25,7 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# Fact Controls UI +# ---------------------------------------------------------------------------- add_subdirectory(FactControls) diff --git a/src/FactSystem/Fact.cc b/src/FactSystem/Fact.cc index 8f81464bb50e..564ed097787d 100644 --- a/src/FactSystem/Fact.cc +++ b/src/FactSystem/Fact.cc @@ -12,8 +12,9 @@ #include "QGCApplication.h" #include "QGCCorePlugin.h" #include "QGCLoggingCategory.h" +#include "SettingsManager.h" -QGC_LOGGING_CATEGORY(FactLog, "qgc.factsystem.fact") +QGC_LOGGING_CATEGORY(FactLog, "FactSystem.Fact") Fact::Fact(QObject *parent) : QObject(parent) @@ -48,9 +49,17 @@ Fact::Fact(const QString& settingsGroup, FactMetaData *metaData, QObject *parent { // qCDebug(FactLog) << Q_FUNC_INFO << this; - QGCCorePlugin::instance()->adjustSettingMetaData(settingsGroup, *metaData); + bool visible = true; + SettingsManager::adjustSettingMetaData(settingsGroup, *metaData, visible); setMetaData(metaData, true /* setDefaultFromMetaData */); + if (!qgcApp()->runningUnitTests()) { + if (metaData->defaultValueAvailable() && !visible) { + // If setting is not visible, we force to default value + _rawValue = metaData->rawDefaultValue(); + } + } + _init(); } @@ -312,16 +321,29 @@ QStringList Fact::selectedBitmaskStrings() const QString Fact::_variantToString(const QVariant &variant, int decimalPlaces) const { + if (!variant.isValid()) { + return invalidValueString(decimalPlaces); + } + QString valueString; + const auto stripNegativeZero = [](QString &candidate) { + static const QRegularExpression reNegativeZero(QStringLiteral("^-0\\.0+$")); + const auto match = reNegativeZero.match(candidate); + if (match.hasMatch() || candidate == QStringLiteral("-0")) { + candidate = candidate.mid(1); + } + }; + switch (type()) { case FactMetaData::valueTypeFloat: { const float fValue = variant.toFloat(); if (qIsNaN(fValue)) { - valueString = QStringLiteral("--.--"); + valueString = invalidValueString(decimalPlaces); } else { valueString = QStringLiteral("%1").arg(fValue, 0, 'f', decimalPlaces); + stripNegativeZero(valueString); } } break; @@ -329,9 +351,10 @@ QString Fact::_variantToString(const QVariant &variant, int decimalPlaces) const { const double dValue = variant.toDouble(); if (qIsNaN(dValue)) { - valueString = QStringLiteral("--.--"); + valueString = invalidValueString(decimalPlaces); } else { valueString = QStringLiteral("%1").arg(dValue, 0, 'f', decimalPlaces); + stripNegativeZero(valueString); } break; } @@ -342,7 +365,7 @@ QString Fact::_variantToString(const QVariant &variant, int decimalPlaces) const { const double dValue = variant.toDouble(); if (qIsNaN(dValue)) { - valueString = QStringLiteral("--:--:--"); + valueString = invalidValueString(decimalPlaces); } else { QTime time(0, 0, 0, 0); time = time.addSecs(dValue); @@ -358,6 +381,22 @@ QString Fact::_variantToString(const QVariant &variant, int decimalPlaces) const return valueString; } +QString Fact::invalidValueString(int decimalPlaces) const { + switch (type()) { + case FactMetaData::valueTypeFloat: + case FactMetaData::valueTypeDouble: + if (decimalPlaces <= 0) { + return QStringLiteral("--"); + } + return QStringLiteral("--.") + + QString(decimalPlaces, QLatin1Char('-')); + case FactMetaData::valueTypeElapsedTimeInSeconds: + return QStringLiteral("--:--:--"); + default: + return QStringLiteral("--"); + } +} + QString Fact::rawValueStringFullPrecision() const { return _variantToString(rawValue(), 18); diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index 922e0681dd03..ab6409d0931f 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -56,6 +56,7 @@ class Fact : public QObject Q_PROPERTY(QVariant value READ cookedValue WRITE setCookedValue NOTIFY valueChanged) Q_PROPERTY(QVariant rawValue READ rawValue WRITE setRawValue NOTIFY rawValueChanged) Q_PROPERTY(bool valueEqualsDefault READ valueEqualsDefault NOTIFY valueChanged) + Q_PROPERTY(QString invalidValueString READ invalidValueString CONSTANT) Q_PROPERTY(QString valueString READ cookedValueString NOTIFY valueChanged) Q_PROPERTY(QString enumOrValueString READ enumOrValueString NOTIFY valueChanged) Q_PROPERTY(double increment READ cookedIncrement CONSTANT) @@ -71,7 +72,7 @@ class Fact : public QObject explicit Fact(int componentId, const QString &name, FactMetaData::ValueType_t type, QObject *parent = nullptr); explicit Fact(const Fact &other, QObject *parent = nullptr); - /// Creates a Fact using the name and type from metaData. Also calls QGCCorePlugin::adjustSettingsMetaData allowing + /// Creates a Fact using the name and type from metaData. Also calls SettingsManager::adjustSettingMetaData allowing /// custom builds to override the metadata. explicit Fact(const QString &settingsGroup, FactMetaData *metaData, QObject *parent = nullptr); virtual ~Fact(); @@ -116,6 +117,8 @@ class Fact : public QObject FactMetaData::ValueType_t type() const { return _type; } QString cookedUnits() const; QString rawUnits() const; + QString invalidValueString(int decimalPlaces) const; + QString invalidValueString() const { return invalidValueString(decimalPlaces()); } QString rawValueString() const; QString cookedValueString() const; bool valueEqualsDefault() const; @@ -191,7 +194,7 @@ class Fact : public QObject QString _name; int _componentId = -1; - QVariant _rawValue = 0; + QVariant _rawValue; // QVariant::Invalid FactMetaData::ValueType_t _type = FactMetaData::valueTypeInt32; FactMetaData *_metaData = nullptr; bool _sendValueChangedSignals = true; diff --git a/src/FactSystem/FactControls/FactPanelController.cc b/src/FactSystem/FactControls/FactPanelController.cc index 89c69efb789b..864ecc6b71a3 100644 --- a/src/FactSystem/FactControls/FactPanelController.cc +++ b/src/FactSystem/FactControls/FactPanelController.cc @@ -17,7 +17,7 @@ #include -QGC_LOGGING_CATEGORY(FactPanelControllerLog, "qgc.factsystem.factcontrols.factpanelcontroller") +QGC_LOGGING_CATEGORY(FactPanelControllerLog, "FactSystem.FactPanelController") FactPanelController::FactPanelController(QObject *parent) : QObject(parent) diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index 32c85c7f6cdc..6884f22c8a9d 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -21,7 +21,7 @@ QGCTextField { property Fact fact: null onEditingFinished: _onEditingFinished() - + function _onEditingFinished() { var errorString = fact.validate(text, false /* convertOnly */) if (errorString === "") { diff --git a/src/FactSystem/FactControls/FactTextFieldGrid.qml b/src/FactSystem/FactControls/FactTextFieldGrid.qml index 38ef9c992d58..edaa9ced24c2 100644 --- a/src/FactSystem/FactControls/FactTextFieldGrid.qml +++ b/src/FactSystem/FactControls/FactTextFieldGrid.qml @@ -16,7 +16,7 @@ GridLayout { Repeater { model: parent.factList - QGCLabel { + QGCLabel { text: { if (factLabels) { return factLabels[index] diff --git a/src/FactSystem/FactControls/LabelledFactComboBox.qml b/src/FactSystem/FactControls/LabelledFactComboBox.qml index 93e08a5eb9ab..f865ed96880a 100644 --- a/src/FactSystem/FactControls/LabelledFactComboBox.qml +++ b/src/FactSystem/FactControls/LabelledFactComboBox.qml @@ -28,7 +28,7 @@ RowLayout { signal activated(int index) QGCLabel { - id: label + id: label Layout.fillWidth: true } @@ -36,8 +36,7 @@ RowLayout { id: _comboBox Layout.preferredWidth: comboBoxPreferredWidth sizeToContents: true - + onActivated: (index) => { parent.activated(index) } } } - diff --git a/src/FactSystem/FactControls/LabelledFactLabel.qml b/src/FactSystem/FactControls/LabelledFactLabel.qml index 13ab54a19a21..ed020983ded5 100644 --- a/src/FactSystem/FactControls/LabelledFactLabel.qml +++ b/src/FactSystem/FactControls/LabelledFactLabel.qml @@ -12,7 +12,6 @@ import QtQuick.Layouts import QGroundControl import QGroundControl.Controls -import QGroundControl.ScreenTools import QGroundControl.FactControls @@ -35,4 +34,3 @@ RowLayout { Layout.preferredWidth: labelPreferredWidth } } - diff --git a/src/FactSystem/FactControls/LabelledFactTextField.qml b/src/FactSystem/FactControls/LabelledFactTextField.qml index 9491f55596a2..320526195dd9 100644 --- a/src/FactSystem/FactControls/LabelledFactTextField.qml +++ b/src/FactSystem/FactControls/LabelledFactTextField.qml @@ -37,4 +37,3 @@ RowLayout { Layout.preferredWidth: textFieldPreferredWidth } } - diff --git a/src/FactSystem/FactGroup.cc b/src/FactSystem/FactGroup.cc index a3931d760a11..d330444a1554 100644 --- a/src/FactSystem/FactGroup.cc +++ b/src/FactSystem/FactGroup.cc @@ -10,7 +10,7 @@ #include "FactGroup.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(FactGroupLog, "qgc.factsystem.factgroup") +QGC_LOGGING_CATEGORY(FactGroupLog, "FactSystem.FactGroup") FactGroup::FactGroup(int updateRateMsecs, const QString &metaDataFile, QObject *parent, bool ignoreCamelCase) : QObject(parent) diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index c1048b53478f..3b816681f010 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -16,7 +16,7 @@ #include -QGC_LOGGING_CATEGORY(FactMetaDataLog, "qgc.factsystem.factmetadata") +QGC_LOGGING_CATEGORY(FactMetaDataLog, "FactSystem.FactMetaData") // Built in translations for all Facts const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] = { diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index 0db8fc74d7a0..11b89f90e349 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -19,6 +19,8 @@ Q_DECLARE_LOGGING_CATEGORY(FactMetaDataLog) +class SettingsManager; + /// Holds the meta data associated with a Fact. This is kept in a separate object from the Fact itself /// since you may have multiple instances of the same Fact. But there is only ever one FactMetaData /// instance or each Fact. @@ -26,6 +28,9 @@ class FactMetaData : public QObject { Q_OBJECT QML_ELEMENT + + friend class SettingsManager; + public: enum ValueType_t { valueTypeUint8, diff --git a/src/FactSystem/FactValueSliderListModel.cc b/src/FactSystem/FactValueSliderListModel.cc index ee0e1f168e1f..eb844a44367b 100644 --- a/src/FactSystem/FactValueSliderListModel.cc +++ b/src/FactSystem/FactValueSliderListModel.cc @@ -13,7 +13,7 @@ #include -QGC_LOGGING_CATEGORY(FactValueSliderListModelLog, "qgc.factsystem.factvaluesliderlistmodel") +QGC_LOGGING_CATEGORY(FactValueSliderListModelLog, "FactSystem.FactValueSliderListModel") FactValueSliderListModel::FactValueSliderListModel(const Fact &fact, QObject *parent) : QAbstractListModel(parent) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 9f44e8bf0666..0adb1c2fd38c 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -19,22 +19,25 @@ #include "QGCApplication.h" #include "QGCLoggingCategory.h" #include "Vehicle.h" +#include "QGCStateMachine.h" +#include "MultiVehicleManager.h" #include #include #include #include -QGC_LOGGING_CATEGORY(ParameterManagerLog, "qgc.factsystem.parametermanager") -QGC_LOGGING_CATEGORY(ParameterManagerVerbose1Log, "qgc.factsystem.parametermanager1:verbose") -QGC_LOGGING_CATEGORY(ParameterManagerVerbose2Log, "qgc.factsystem.parametermanager2:verbose") -QGC_LOGGING_CATEGORY(ParameterManagerDebugCacheFailureLog, "qgc.factsystem.parametermanager.debugcachefailure") // Turn on to debug parameter cache crc misses +QGC_LOGGING_CATEGORY(ParameterManagerLog, "FactSystem.ParameterManager") +QGC_LOGGING_CATEGORY(ParameterManagerVerbose1Log, "FactSystem.ParameterManager:verbose1") +QGC_LOGGING_CATEGORY(ParameterManagerVerbose2Log, "FactSystem.ParameterManager:verbose2") +QGC_LOGGING_CATEGORY(ParameterManagerDebugCacheFailureLog, "FactSystem.ParameterManager:debugCacheFailure") // Turn on to debug parameter cache crc misses ParameterManager::ParameterManager(Vehicle *vehicle) : QObject(vehicle) , _vehicle(vehicle) , _logReplay(!vehicle->vehicleLinkManager()->primaryLink().expired() && vehicle->vehicleLinkManager()->primaryLink().lock()->isLogReplay()) , _tryftp(vehicle->apmFirmware()) + , _disableAllRetries(_logReplay) { qCDebug(ParameterManagerLog) << this; @@ -43,13 +46,19 @@ ParameterManager::ParameterManager(Vehicle *vehicle) return; } + if (_logReplay) { + qCDebug(ParameterManagerLog) << this << "In log replay mode"; + } + _initialRequestTimeoutTimer.setSingleShot(true); _initialRequestTimeoutTimer.setInterval(5000); (void) connect(&_initialRequestTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_initialRequestTimeout); _waitingParamTimeoutTimer.setSingleShot(true); _waitingParamTimeoutTimer.setInterval(3000); - (void) connect(&_waitingParamTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_waitingParamTimeout); + if (!_logReplay) { + (void) connect(&_waitingParamTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_waitingParamTimeout); + } // Ensure the cache directory exists (void) QFileInfo(QSettings().fileName()).dir().mkdir("ParamCache"); @@ -63,18 +72,11 @@ ParameterManager::~ParameterManager() void ParameterManager::_updateProgressBar() { int waitingReadParamIndexCount = 0; - int waitingReadParamNameCount = 0; int waitingWriteParamCount = 0; for (const int compId: _waitingReadParamIndexMap.keys()) { waitingReadParamIndexCount += _waitingReadParamIndexMap[compId].count(); } - for (const int compId: _waitingReadParamNameMap.keys()) { - waitingReadParamNameCount += _waitingReadParamNameMap[compId].count(); - } - for (const int compId: _waitingWriteParamNameMap.keys()) { - waitingWriteParamCount += _waitingWriteParamNameMap[compId].count(); - } if (waitingReadParamIndexCount == 0) { if (_readParamIndexProgressActive) { @@ -87,37 +89,8 @@ void ParameterManager::_updateProgressBar() _setLoadProgress(static_cast(_totalParamCount - waitingReadParamIndexCount) / static_cast(_totalParamCount)); return; } - - if (waitingWriteParamCount == 0) { - if (_writeParamProgressActive) { - _writeParamProgressActive = false; - _waitingWriteParamBatchCount = 0; - _setLoadProgress(0.0); - emit pendingWritesChanged(false); - return; - } - } else { - _writeParamProgressActive = true; - _setLoadProgress(static_cast(qMax(_waitingWriteParamBatchCount - waitingWriteParamCount, 1)) / static_cast(_waitingWriteParamBatchCount + 1)); - emit pendingWritesChanged(true); - return; - } - - if (waitingReadParamNameCount == 0) { - if (_readParamNameProgressActive) { - _readParamNameProgressActive = false; - _waitingReadParamNameBatchCount = 0; - _setLoadProgress(0.0); - return; - } - } else { - _readParamNameProgressActive = true; - _setLoadProgress(static_cast(qMax(_waitingReadParamNameBatchCount - waitingReadParamNameCount, 1)) / static_cast(_waitingReadParamNameBatchCount + 1)); - return; - } } - void ParameterManager::mavlinkMessageReceived(const mavlink_message_t &message) { if (_tryftp && (message.compid == MAV_COMP_ID_AUTOPILOT1) && !_initialLoadComplete) @@ -137,32 +110,8 @@ void ParameterManager::mavlinkMessageReceived(const mavlink_message_t &message) paramUnion.type = param_value.param_type; QVariant parameterValue; - - switch (paramUnion.type) { - case MAV_PARAM_TYPE_REAL32: - parameterValue = QVariant(paramUnion.param_float); - break; - case MAV_PARAM_TYPE_UINT8: - parameterValue = QVariant(paramUnion.param_uint8); - break; - case MAV_PARAM_TYPE_INT8: - parameterValue = QVariant(paramUnion.param_int8); - break; - case MAV_PARAM_TYPE_UINT16: - parameterValue = QVariant(paramUnion.param_uint16); - break; - case MAV_PARAM_TYPE_INT16: - parameterValue = QVariant(paramUnion.param_int16); - break; - case MAV_PARAM_TYPE_UINT32: - parameterValue = QVariant(paramUnion.param_uint32); - break; - case MAV_PARAM_TYPE_INT32: - parameterValue = QVariant(paramUnion.param_int32); - break; - default: - qCCritical(ParameterManagerLog) << "ParameterManager::_handleParamValue - unsupported MAV_PARAM_TYPE" << paramUnion.type; - break; + if (!_mavlinkParamUnionToVariant(paramUnion, parameterValue)) { + return; } _handleParamValue(message.compid, parameterName, param_value.param_count, param_value.param_index, static_cast(param_value.param_type), parameterValue); @@ -184,7 +133,7 @@ void ParameterManager::_handleParamValue(int componentId, const QString ¶met // ArduPilot has this strange behavior of streaming parameters that we didn't ask for. This even happens before it responds to the // PARAM_REQUEST_LIST. We disregard any of this until the initial request is responded to. if ((parameterIndex == 65535) && (parameterName != QStringLiteral("_HASH_CHECK")) && _initialRequestTimeoutTimer.isActive()) { - qCDebug(ParameterManagerVerbose1Log) << "Disregarding unrequested param prior to initial list response" << parameterName; + qCDebug(ParameterManagerLog) << "Disregarding unrequested param prior to initial list response" << parameterName; return; } @@ -232,16 +181,10 @@ void ParameterManager::_handleParamValue(int componentId, const QString ¶met _waitingReadParamIndexMap[componentId][waitingIndex] = 0; } - // The read and write waiting lists for this component are initialized the empty - _waitingReadParamNameMap[componentId] = QMap(); - _waitingWriteParamNameMap[componentId] = QMap(); - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Seeing component for first time - paramcount:" << parameterCount; } - if (!_waitingReadParamIndexMap[componentId].contains(parameterIndex) && - !_waitingReadParamNameMap[componentId].contains(parameterName) && - !_waitingWriteParamNameMap[componentId].contains(parameterName)) { + if (!_waitingReadParamIndexMap[componentId].contains(parameterIndex)) { qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "Unrequested param update" << parameterName; } @@ -252,22 +195,8 @@ void ParameterManager::_handleParamValue(int componentId, const QString ¶met _fillIndexBatchQueue(false /* waitingParamTimeout */); } - (void) _waitingReadParamNameMap[componentId].remove(parameterName); - (void) _waitingWriteParamNameMap[componentId].remove(parameterName); - if (!_waitingReadParamIndexMap[componentId].isEmpty()) { - qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingReadParamIndexMap:" << _waitingReadParamIndexMap[componentId]; - } - if (!_waitingReadParamNameMap[componentId].isEmpty()) { - qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingReadParamNameMap" << _waitingReadParamNameMap[componentId]; - } - if (!_waitingWriteParamNameMap[componentId].isEmpty()) { - qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingWriteParamNameMap" << _waitingWriteParamNameMap[componentId]; - } - // Track how many parameters we are still waiting for int waitingReadParamIndexCount = 0; - int waitingReadParamNameCount = 0; - int waitingWriteParamNameCount = 0; for (const int waitingComponentId: _waitingReadParamIndexMap.keys()) { waitingReadParamIndexCount += _waitingReadParamIndexMap[waitingComponentId].count(); @@ -276,22 +205,8 @@ void ParameterManager::_handleParamValue(int componentId, const QString ¶met qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingReadParamIndexCount:" << waitingReadParamIndexCount; } - for (const int waitingComponentId: _waitingReadParamNameMap.keys()) { - waitingReadParamNameCount += _waitingReadParamNameMap[waitingComponentId].count(); - } - if (waitingReadParamNameCount) { - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingReadParamNameCount:" << waitingReadParamNameCount; - } - - for (const int waitingComponentId: _waitingWriteParamNameMap.keys()) { - waitingWriteParamNameCount += _waitingWriteParamNameMap[waitingComponentId].count(); - } - if (waitingWriteParamNameCount) { - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingWriteParamNameCount:" << waitingWriteParamNameCount; - } - - const int readWaitingParamCount = waitingReadParamIndexCount + waitingReadParamNameCount; - const int totalWaitingParamCount = readWaitingParamCount + waitingWriteParamNameCount; + const int readWaitingParamCount = waitingReadParamIndexCount; + const int totalWaitingParamCount = readWaitingParamCount; if (totalWaitingParamCount) { // More params to wait for, restart timer _waitingParamTimeoutTimer.start(); @@ -330,39 +245,239 @@ void ParameterManager::_handleParamValue(int componentId, const QString ¶met // which invalidate the cache. The Solo also streams param updates in flight for things like gimbal values // which in turn causes a perf problem with all the param cache updates. if (!_logReplay && _vehicle->px4Firmware()) { - if (((_prevWaitingReadParamIndexCount + _prevWaitingReadParamNameCount) != 0) && (readWaitingParamCount == 0)) { + if (_prevWaitingReadParamIndexCount != 0 && readWaitingParamCount == 0) { // All reads just finished, update the cache _writeLocalParamCache(_vehicle->id(), componentId); } } _prevWaitingReadParamIndexCount = waitingReadParamIndexCount; - _prevWaitingReadParamNameCount = waitingReadParamNameCount; - _prevWaitingWriteParamNameCount = waitingWriteParamNameCount; _checkInitialLoadComplete(); qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "_parameterUpdate complete"; } -void ParameterManager::_factRawValueUpdateWorker(int componentId, const QString &name, FactMetaData::ValueType_t valueType, const QVariant &rawValue) +QString ParameterManager::_vehicleAndComponentString(int componentId) const { - if (_waitingWriteParamNameMap.contains(componentId)) { - if (_waitingWriteParamNameMap[componentId].contains(name)) { - (void) _waitingWriteParamNameMap[componentId].remove(name); + // If there are multiple vehicles include the vehicle id for disambiguation + QString vehicleIdStr; + if (MultiVehicleManager::instance()->vehicles()->count() > 1) { + vehicleIdStr = QStringLiteral("veh: %1").arg(_vehicle->id()); + } + + // IF we have parameters for multiple components include the component id for disambiguation + QString componentIdStr; + if (_mapCompId2FactMap.keys().count() > 1) { + componentIdStr = QStringLiteral("comp: %1").arg(componentId); + } + + if (!vehicleIdStr.isEmpty() && !componentIdStr.isEmpty()) { + return vehicleIdStr + QStringLiteral(" ") + componentIdStr; + } else if (!vehicleIdStr.isEmpty()) { + return vehicleIdStr; + } else if (!componentIdStr.isEmpty()) { + return componentIdStr; + } else { + return QString(); + } +} + +void ParameterManager::_mavlinkParamSet(int componentId, const QString ¶mName, FactMetaData::ValueType_t valueType, const QVariant &rawValue) +{ + auto paramSetEncoder = [this, componentId, paramName, valueType, rawValue](uint8_t systemId, uint8_t channel, mavlink_message_t *message) -> void { + const MAV_PARAM_TYPE paramType = factTypeToMavType(valueType); + + mavlink_param_union_t union_value{}; + if (!_fillMavlinkParamUnion(valueType, rawValue, union_value)) { + return; + } + + char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1] = {}; + (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN); + + (void) mavlink_msg_param_set_pack_chan( + MAVLinkProtocol::instance()->getSystemId(), + MAVLinkProtocol::getComponentId(), + channel, + message, + static_cast(_vehicle->id()), + static_cast(componentId), + paramId, + union_value.param_float, + static_cast(paramType)); + }; + + auto checkForCorrectParamValue = [this, componentId, paramName, rawValue](const mavlink_message_t &message) -> bool { + if (message.compid != componentId) { + return false; + } + + mavlink_param_value_t param_value{}; + mavlink_msg_param_value_decode(&message, ¶m_value); + + // This will null terminate the name string + char parameterNameWithNull[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1] = {}; + (void) strncpy(parameterNameWithNull, param_value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN); + const QString parameterName(parameterNameWithNull); + + if (parameterName != paramName) { + return false; + } + + // Check that the value matches what we expect within tolerance, if it doesn't match then this message is not for us + QVariant receivedValue; + mavlink_param_union_t param_union; + param_union.param_float = param_value.param_value; + param_union.type = param_value.param_type; + if (!_mavlinkParamUnionToVariant(param_union, receivedValue)) { + return false; + } + if (rawValue.typeId() != receivedValue.typeId()) { + qCWarning(ParameterManagerLog) << "QVariant type mismatch on PARAM_VALUE ack for" << paramName << ": expected type" << rawValue.typeId() << "got type" << receivedValue.typeId(); + return false; + } + if (param_value.param_type == MAV_PARAM_TYPE_REAL32) { + // Float comparison must be fuzzy + return QGC::fuzzyCompare(rawValue.toFloat(), receivedValue.toFloat()); } else { - _waitingWriteParamBatchCount++; + return receivedValue == rawValue; } - _waitingWriteParamNameMap[componentId][name] = 0; // Add new entry and set retry count - _updateProgressBar(); - _waitingParamTimeoutTimer.start(); - _saveRequired = true; - } else { - qCWarning(ParameterManagerLog) << "Internal error ParameterManager::_factValueUpdateWorker: component id not found" << componentId; + }; + + // State Machine: + // Send PARAM_SET - 2 retries after initial attempt + // Increment pending write count + // Wait for PARAM_VALUE ack + // Decrement pending write count + // + // timeout: + // Decrement pending write count + // Back up to PARAM_SET for retries + // + // error: + // Refresh parameter from vehicle + // Notify user of failure + + // Create states + auto stateMachine = new QGCStateMachine(QStringLiteral("ParameterManager PARAM_SET"), vehicle(), this); + auto sendParamSetState = new SendMavlinkMessageState(stateMachine, paramSetEncoder, kParamSetRetryCount); + auto incPendingWriteCountState = new FunctionState(QStringLiteral("ParameterManager increment pending write count"), stateMachine, [this]() { + _incrementPendingWriteCount(); + }); + auto decPendingWriteCountState = new FunctionState(QStringLiteral("ParameterManager decrement pending write count"), stateMachine, [this]() { + _decrementPendingWriteCount(); + }); + auto retryDecPendingWriteCountState = new FunctionState(QStringLiteral("ParameterManager retry decrement pending write count"), stateMachine, [this]() { + _decrementPendingWriteCount(); + }); + auto waitAckState = new WaitForMavlinkMessageState(stateMachine, MAVLINK_MSG_ID_PARAM_VALUE, kWaitForParamValueAckMs, checkForCorrectParamValue); + auto paramRefreshState = new FunctionState(QStringLiteral("ParameterManager param refresh"), stateMachine, [this, componentId, paramName]() { + refreshParameter(componentId, paramName); + }); + auto userNotifyState = new ShowAppMessageState(stateMachine, QStringLiteral("Parameter write failed: param: %1 %2").arg(paramName).arg(_vehicleAndComponentString(componentId))); + auto logSuccessState = new FunctionState(QStringLiteral("ParameterManager log success"), stateMachine, [this, componentId, paramName]() { + qCDebug(ParameterManagerLog) << "Parameter write succeeded: param:" << paramName << _vehicleAndComponentString(componentId); + emit _paramSetSuccess(componentId, paramName); + }); + auto logFailureState = new FunctionState(QStringLiteral("ParameterManager log failure"), stateMachine, [this, componentId, paramName]() { + qCDebug(ParameterManagerLog) << "Parameter write failed: param:" << paramName << _vehicleAndComponentString(componentId); + emit _paramSetFailure(componentId, paramName); + }); + auto finalState = new QGCFinalState(stateMachine); + + // Successful state machine transitions + stateMachine->setInitialState(sendParamSetState); + sendParamSetState->addThisTransition (&QGCState::advance, incPendingWriteCountState); + incPendingWriteCountState->addThisTransition(&QGCState::advance, waitAckState); + waitAckState->addThisTransition (&QGCState::advance, decPendingWriteCountState); + decPendingWriteCountState->addThisTransition(&QGCState::advance, logSuccessState); + logSuccessState->addThisTransition (&QGCState::advance, finalState); + + // Retry transitions + waitAckState->addTransition(waitAckState, &WaitForMavlinkMessageState::timeout, retryDecPendingWriteCountState); // Retry on timeout + retryDecPendingWriteCountState->addThisTransition(&QGCState::advance, sendParamSetState); + + // Error transitions + sendParamSetState->addThisTransition(&QGCState::error, logFailureState); // Error is signaled after retries exhausted or internal error + + // Error state branching transitions + logFailureState->addThisTransition (&QGCState::advance, userNotifyState); + userNotifyState->addThisTransition (&QGCState::advance, paramRefreshState); + paramRefreshState->addThisTransition(&QGCState::advance, finalState); + + qCDebug(ParameterManagerLog) << "Starting state machine for PARAM_SET on: " << paramName << _vehicleAndComponentString(componentId); + stateMachine->start(); +} + +bool ParameterManager::_fillMavlinkParamUnion(FactMetaData::ValueType_t valueType, const QVariant &rawValue, mavlink_param_union_t ¶mUnion) const +{ + bool ok = false; + + switch (valueType) { + case FactMetaData::valueTypeUint8: + paramUnion.param_uint8 = static_cast(rawValue.toUInt(&ok)); + break; + case FactMetaData::valueTypeInt8: + paramUnion.param_int8 = static_cast(rawValue.toInt(&ok)); + break; + case FactMetaData::valueTypeUint16: + paramUnion.param_uint16 = static_cast(rawValue.toUInt(&ok)); + break; + case FactMetaData::valueTypeInt16: + paramUnion.param_int16 = static_cast(rawValue.toInt(&ok)); + break; + case FactMetaData::valueTypeUint32: + paramUnion.param_uint32 = static_cast(rawValue.toUInt(&ok)); + break; + case FactMetaData::valueTypeFloat: + paramUnion.param_float = rawValue.toFloat(&ok); + break; + case FactMetaData::valueTypeInt32: + paramUnion.param_int32 = static_cast(rawValue.toInt(&ok)); + break; + default: + qCCritical(ParameterManagerLog) << "Internal Error: Unsupported fact value type" << valueType; + paramUnion.param_int32 = static_cast(rawValue.toInt(&ok)); + break; } - _sendParamSetToVehicle(componentId, name, valueType, rawValue); - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Update parameter (_waitingParamTimeoutTimer started) - compId:name:rawValue" << componentId << name << rawValue; + if (!ok) { + qCCritical(ParameterManagerLog) << "Fact Failed to Convert to Param Type:" << valueType; + return false; + } + + return true; +} + +bool ParameterManager::_mavlinkParamUnionToVariant(const mavlink_param_union_t ¶mUnion, QVariant &outValue) const +{ + switch (paramUnion.type) { + case MAV_PARAM_TYPE_REAL32: + outValue = QVariant(paramUnion.param_float); + return true; + case MAV_PARAM_TYPE_UINT8: + outValue = QVariant(paramUnion.param_uint8); + return true; + case MAV_PARAM_TYPE_INT8: + outValue = QVariant(paramUnion.param_int8); + return true; + case MAV_PARAM_TYPE_UINT16: + outValue = QVariant(paramUnion.param_uint16); + return true; + case MAV_PARAM_TYPE_INT16: + outValue = QVariant(paramUnion.param_int16); + return true; + case MAV_PARAM_TYPE_UINT32: + outValue = QVariant(paramUnion.param_uint32); + return true; + case MAV_PARAM_TYPE_INT32: + outValue = QVariant(paramUnion.param_int32); + return true; + default: + qCCritical(ParameterManagerLog) << "ParameterManager::_mavlinkParamUnionToVariant - unsupported MAV_PARAM_TYPE" << paramUnion.type; + return false; + } } void ParameterManager::_factRawValueUpdated(const QVariant &rawValue) @@ -373,7 +488,7 @@ void ParameterManager::_factRawValueUpdated(const QVariant &rawValue) return; } - _factRawValueUpdateWorker(fact->componentId(), fact->name(), fact->type(), rawValue); + _mavlinkParamSet(fact->componentId(), fact->name(), fact->type(), rawValue); } void ParameterManager::_ftpDownloadComplete(const QString &fileName, const QString &errorMsg) @@ -510,25 +625,10 @@ int ParameterManager::_actualComponentId(int componentId) const void ParameterManager::refreshParameter(int componentId, const QString ¶mName) { componentId = _actualComponentId(componentId); - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParameter - name:" << paramName << ")"; - if (_waitingReadParamNameMap.contains(componentId)) { - const QString mappedParamName = _remapParamNameToVersion(paramName); - - if (_waitingReadParamNameMap[componentId].contains(mappedParamName)) { - (void) _waitingReadParamNameMap[componentId].remove(mappedParamName); - } else { - _waitingReadParamNameBatchCount++; - } - _waitingReadParamNameMap[componentId][mappedParamName] = 0; // Add new wait entry and update retry count - _updateProgressBar(); - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "restarting _waitingParamTimeout"; - _waitingParamTimeoutTimer.start(); - } else { - qCWarning(ParameterManagerLog) << "Internal error"; - } + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParameter - name:" << paramName << ")"; - _readParameterRaw(componentId, paramName, -1); + _mavlinkParamRequestRead(componentId, paramName, -1, true /* notifyFailure */); } void ParameterManager::refreshParametersPrefix(int componentId, const QString &namePrefix) @@ -622,7 +722,7 @@ bool ParameterManager::_fillIndexBatchQueue(bool waitingParamTimeout) } else { // Retry again _indexBatchQueue.append(paramIndex); - _readParameterRaw(componentId, "", paramIndex); + _mavlinkParamRequestRead(componentId, QString(), paramIndex, false /* notifyFailure */); qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Read re-request for (paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")"; } } @@ -656,53 +756,6 @@ void ParameterManager::_waitingParamTimeout() _checkInitialLoadComplete(); - constexpr int maxBatchSize = 10; - int batchCount = 0; - if (!paramsRequested) { - for (const int componentId: _waitingWriteParamNameMap.keys()) { - for (const QString ¶mName: _waitingWriteParamNameMap[componentId].keys()) { - paramsRequested = true; - _waitingWriteParamNameMap[componentId][paramName]++; // Bump retry count - if (_waitingWriteParamNameMap[componentId][paramName] <= _maxReadWriteRetry) { - const Fact *const fact = getParameter(componentId, paramName); - _sendParamSetToVehicle(componentId, paramName, fact->type(), fact->rawValue()); - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Write resend for (paramName:" << paramName << "retryCount:" << _waitingWriteParamNameMap[componentId][paramName] << ")"; - if (++batchCount > maxBatchSize) { - goto Out; - } - } else { - // Exceeded max retry count, notify user - _waitingWriteParamNameMap[componentId].remove(paramName); - const QString errorMsg = tr("Parameter write failed: veh:%1 comp:%2 param:%3").arg(_vehicle->id()).arg(componentId).arg(paramName); - qCDebug(ParameterManagerLog) << errorMsg; - qgcApp()->showAppMessage(errorMsg); - } - } - } - } - - if (!paramsRequested) { - for (const int componentId: _waitingReadParamNameMap.keys()) { - for (const QString ¶mName: _waitingReadParamNameMap[componentId].keys()) { - paramsRequested = true; - _waitingReadParamNameMap[componentId][paramName]++; // Bump retry count - if (_waitingReadParamNameMap[componentId][paramName] <= _maxReadWriteRetry) { - _readParameterRaw(componentId, paramName, -1); - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Read re-request for (paramName:" << paramName << "retryCount:" << _waitingReadParamNameMap[componentId][paramName] << ")"; - if (++batchCount > maxBatchSize) { - goto Out; - } - } else { - // Exceeded max retry count, notify user - (void) _waitingReadParamNameMap[componentId].remove(paramName); - const QString errorMsg = tr("Parameter read failed: veh:%1 comp:%2 param:%3").arg(_vehicle->id()).arg(componentId).arg(paramName); - qCDebug(ParameterManagerLog) << errorMsg; - qgcApp()->showAppMessage(errorMsg); - } - } - } - } - Out: if (paramsRequested) { qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "Restarting _waitingParamTimeoutTimer - re-request"; @@ -710,85 +763,98 @@ void ParameterManager::_waitingParamTimeout() } } -void ParameterManager::_readParameterRaw(int componentId, const QString ¶mName, int paramIndex) const +void ParameterManager::_mavlinkParamRequestRead(int componentId, const QString ¶mName, int paramIndex, bool notifyFailure) { - const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); - if (!sharedLink) { - return; - } - - mavlink_message_t msg{}; - char fixedParamName[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN]; + auto paramRequestReadEncoder = [this, componentId, paramName, paramIndex](uint8_t systemId, uint8_t channel, mavlink_message_t *message) -> void { + char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1] = {}; + (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN); + + (void) mavlink_msg_param_request_read_pack_chan(MAVLinkProtocol::instance()->getSystemId(), // QGC system id + MAVLinkProtocol::getComponentId(), // QGC component id + channel, + message, + static_cast(_vehicle->id()), + static_cast(componentId), + paramId, + static_cast(paramIndex)); + }; - (void) strncpy(fixedParamName, paramName.toStdString().c_str(), sizeof(fixedParamName)); - (void) mavlink_msg_param_request_read_pack_chan(MAVLinkProtocol::instance()->getSystemId(), // QGC system id - MAVLinkProtocol::getComponentId(), // QGC component id - sharedLink->mavlinkChannel(), - &msg, // Pack into this mavlink_message_t - _vehicle->id(), // Target system id - componentId, // Target component id - fixedParamName, // Named parameter being requested - paramIndex); // Parameter index being requested, -1 for named - (void) _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); -} + auto checkForCorrectParamValue = [this, componentId, paramName, paramIndex](const mavlink_message_t &message) -> bool { + if (message.compid != componentId) { + return false; + } -void ParameterManager::_sendParamSetToVehicle(int componentId, const QString ¶mName, FactMetaData::ValueType_t valueType, const QVariant &value) const -{ - const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); - if (!sharedLink) { - return; - } + mavlink_param_value_t param_value{}; + mavlink_msg_param_value_decode(&message, ¶m_value); - mavlink_param_set_t p{}; - p.param_type = factTypeToMavType(valueType); + // This will null terminate the name string + char parameterNameWithNull[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1] = {}; + (void) strncpy(parameterNameWithNull, param_value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN); + const QString parameterName(parameterNameWithNull); - mavlink_param_union_t union_value{}; + // Check that this is for the parameter we requested + if (paramIndex != -1) { + // Index based request + if (param_value.param_index != paramIndex) { + return false; + } + } else { + // Name based request + if (parameterName != paramName) { + return false; + } + } - bool ok = false; - switch (valueType) { - case FactMetaData::valueTypeUint8: - union_value.param_uint8 = static_cast(value.toUInt(&ok)); - break; - case FactMetaData::valueTypeInt8: - union_value.param_int8 = static_cast(value.toInt(&ok)); - break; - case FactMetaData::valueTypeUint16: - union_value.param_uint16 = static_cast(value.toUInt(&ok)); - break; - case FactMetaData::valueTypeInt16: - union_value.param_int16 = static_cast(value.toInt(&ok)); - break; - case FactMetaData::valueTypeUint32: - union_value.param_uint32 = static_cast(value.toUInt(&ok)); - break; - case FactMetaData::valueTypeFloat: - union_value.param_float = value.toFloat(&ok); - break; - default: - qCCritical(ParameterManagerLog) << "Unsupported fact value type" << valueType; - case FactMetaData::valueTypeInt32: - union_value.param_int32 = static_cast(value.toInt(&ok)); - break; - } + return true; + }; - if (!ok) { - qCCritical(ParameterManagerLog) << "Fact Failed to Convert to Param Type:" << value; - return; + // State Machine: + // Send PARAM_REQUEST_READ - 2 retries after initial attempt + // Wait for PARAM_VALUE ack + // + // timeout: + // Back up to PARAM_REQUEST_READ for retries + // + // error: + // Notify user of failure + + // Create states + auto stateMachine = new QGCStateMachine(QStringLiteral("PARAM_REQUEST_READ"), vehicle(), this); + auto sendParamRequestReadState = new SendMavlinkMessageState(stateMachine, paramRequestReadEncoder, kParamRequestReadRetryCount); + auto waitAckState = new WaitForMavlinkMessageState(stateMachine, MAVLINK_MSG_ID_PARAM_VALUE, kWaitForParamValueAckMs, checkForCorrectParamValue); + auto userNotifyState = new ShowAppMessageState(stateMachine, QStringLiteral("Parameter read failed: param: %1 %2").arg(paramName).arg(_vehicleAndComponentString(componentId))); + auto logSuccessState = new FunctionState(QStringLiteral("Log success"), stateMachine, [this, componentId, paramName, paramIndex]() { + qCDebug(ParameterManagerLog) << "PARAM_REQUEST_READ succeeded: name:" << paramName << "index" << paramIndex << _vehicleAndComponentString(componentId); + emit _paramRequestReadSuccess(componentId, paramName, paramIndex); + }); + auto logFailureState = new FunctionState(QStringLiteral("Log failure"), stateMachine, [this, componentId, paramName, paramIndex]() { + qCDebug(ParameterManagerLog) << "PARAM_REQUEST_READ failed: param:" << paramName << "index" << paramIndex << _vehicleAndComponentString(componentId); + emit _paramRequestReadFailure(componentId, paramName, paramIndex); + }); + auto finalState = new QGCFinalState(stateMachine); + + // Successful state machine transitions + stateMachine->setInitialState(sendParamRequestReadState); + sendParamRequestReadState->addThisTransition(&QGCState::advance, waitAckState); + waitAckState->addThisTransition (&QGCState::advance, logSuccessState); + logSuccessState->addThisTransition (&QGCState::advance, finalState); + + // Retry transitions + waitAckState->addTransition(waitAckState, &WaitForMavlinkMessageState::timeout, sendParamRequestReadState); // Retry on timeout + + // Error transitions + sendParamRequestReadState->addThisTransition(&QGCState::error, logFailureState); // Error is signaled after retries exhausted or internal error + + // Error state branching transitions + if (notifyFailure) { + logFailureState->addThisTransition (&QGCState::advance, userNotifyState); + } else { + logFailureState->addThisTransition (&QGCState::advance, finalState); } + userNotifyState->addThisTransition (&QGCState::advance, finalState); - p.param_value = union_value.param_float; - p.target_system = static_cast(_vehicle->id()); - p.target_component = static_cast(componentId); - - (void) strncpy(p.param_id, paramName.toStdString().c_str(), sizeof(p.param_id)); - - mavlink_message_t msg{}; - (void) mavlink_msg_param_set_encode_chan(MAVLinkProtocol::instance()->getSystemId(), - MAVLinkProtocol::getComponentId(), - sharedLink->mavlinkChannel(), - &msg, - &p); - (void) _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); + qCDebug(ParameterManagerLog) << "Starting state machine for PARAM_REQUEST_READ on: " << paramName << _vehicleAndComponentString(componentId); + stateMachine->start(); } void ParameterManager::_writeLocalParamCache(int vehicleId, int componentId) @@ -1136,6 +1202,18 @@ void ParameterManager::_checkInitialLoadComplete() void ParameterManager::_initialRequestTimeout() { + if (_logReplay) { + // Signal load complete + qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "_initialRequestTimeout (log replay): Signalling load complete"; + _initialLoadComplete = true; + _missingParameters = false; + _parametersReady = true; + _vehicle->autopilotPlugin()->parametersReadyPreChecks(); + emit parametersReadyChanged(true); + emit missingParametersChanged(_missingParameters); + return; + } + if (!_disableAllRetries && (++_initialRequestRetryCount <= _maxInitialRequestListRetry)) { qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "Retrying initial parameter request list"; refreshAllParameters(); @@ -1299,13 +1377,7 @@ QList ParameterManager::componentIds() const bool ParameterManager::pendingWrites() const { - for (const int compId: _waitingWriteParamNameMap.keys()) { - if (!_waitingWriteParamNameMap[compId].isEmpty()) { - return true; - } - } - - return false; + return _pendingWritesCount > 0; } Vehicle *ParameterManager::vehicle() @@ -1495,14 +1567,13 @@ bool ParameterManager::_parseParamFile(const QString& filename) } fact->containerSetRawValue(parameterValue); } + Success: file.close(); /* Create empty waiting lists as we have all parameters */ _paramCountMap[componentId] = num_params; _totalParamCount += num_params; _waitingReadParamIndexMap[componentId] = QMap(); - _waitingReadParamNameMap[componentId] = QMap(); - _waitingWriteParamNameMap[componentId] = QMap(); _checkInitialLoadComplete(); _setLoadProgress(0.0); return true; @@ -1511,3 +1582,24 @@ bool ParameterManager::_parseParamFile(const QString& filename) file.close(); return false; } + +void ParameterManager::_incrementPendingWriteCount() +{ + _pendingWritesCount++; + if (_pendingWritesCount == 1) { + emit pendingWritesChanged(true); + } +} + +void ParameterManager::_decrementPendingWriteCount() +{ + if (_pendingWritesCount == 0) { + qCWarning(ParameterManagerLog) << "Internal Error: _pendingWriteCount == 0"; + return; + } + + _pendingWritesCount--; + if (_pendingWritesCount == 0) { + emit pendingWritesChanged(false); + } +} diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index afe0f2e72c53..005883899a1b 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -98,6 +98,11 @@ class ParameterManager : public QObject static constexpr int defaultComponentId = -1; + // These are public for creating unit tests + static constexpr int kParamSetRetryCount = 2; ///< Number of retries for PARAM_SET + static constexpr int kParamRequestReadRetryCount = 2; ///< Number of retries for PARAM_REQUEST_READ + static constexpr int kWaitForParamValueAckMs = 1000; ///< Time to wait for param value ack after set param + signals: void parametersReadyChanged(bool parametersReady); void missingParametersChanged(bool missingParameters); @@ -105,6 +110,12 @@ class ParameterManager : public QObject void pendingWritesChanged(bool pendingWrites); void factAdded(int componentId, Fact *fact); + // These signals are used to verify unit tests + void _paramSetSuccess(int componentId, const QString ¶mName); + void _paramSetFailure(int componentId, const QString ¶mName); + void _paramRequestReadSuccess(int componentId, const QString ¶mName, int paramIndex); + void _paramRequestReadFailure(int componentId, const QString ¶mName, int paramIndex); + private slots: void _factRawValueUpdated(const QVariant &rawValue); @@ -112,20 +123,21 @@ private slots: /// Called whenever a parameter is updated or first seen. void _handleParamValue(int componentId, const QString ¶meterName, int parameterCount, int parameterIndex, MAV_PARAM_TYPE mavParamType, const QVariant ¶meterValue); /// Writes the parameter update to mavlink, sets up for write wait - void _factRawValueUpdateWorker(int componentId, const QString &name, FactMetaData::ValueType_t valueType, const QVariant &rawValue); + void _mavlinkParamSet(int componentId, const QString &name, FactMetaData::ValueType_t valueType, const QVariant &rawValue); void _waitingParamTimeout(); void _tryCacheLookup(); void _initialRequestTimeout(); /// Translates ParameterManager::defaultComponentId to real component id if needed int _actualComponentId(int componentId) const; - void _readParameterRaw(int componentId, const QString ¶mName, int paramIndex) const; - void _sendParamSetToVehicle(int componentId, const QString ¶mName, FactMetaData::ValueType_t valueType, const QVariant &value) const; + void _mavlinkParamRequestRead(int componentId, const QString ¶mName, int paramIndex, bool notifyFailure); void _writeLocalParamCache(int vehicleId, int componentId); void _tryCacheHashLoad(int vehicleId, int componentId, const QVariant &hashValue); void _loadMetaData(); void _clearMetaData(); /// Remap a parameter from one firmware version to another QString _remapParamNameToVersion(const QString ¶mName) const; + bool _fillMavlinkParamUnion(FactMetaData::ValueType_t valueType, const QVariant &rawValue, mavlink_param_union_t ¶mUnion) const; + bool _mavlinkParamUnionToVariant(const mavlink_param_union_t ¶mUnion, QVariant &outValue) const; /// The offline editing vehicle can have custom loaded params bolted into it. void _loadOfflineEditingParams(); QString _logVehiclePrefix(int componentId) const; @@ -141,6 +153,9 @@ private slots: /// Parse the binary parameter file and inject the parameters in the qgc fact system. /// See: https://github.com/ArduPilot/ardupilot/tree/master/libraries/AP_Filesystem bool _parseParamFile(const QString &filename); + void _incrementPendingWriteCount(); + void _decrementPendingWriteCount(); + QString _vehicleAndComponentString(int componentId) const; static QVariant _stringToTypedVariant(const QString &string, FactMetaData::ValueType_t type, bool failOk = false); @@ -153,7 +168,6 @@ private slots: bool _missingParameters = false; ///< true: parameter missing from initial load bool _initialLoadComplete = false; ///< true: Initial load of all parameters complete, whether successful or not bool _waitingForDefaultComponent = false; ///< true: last chance wait for default component params - bool _saveRequired = false; ///< true: _saveToEEPROM should be called bool _metaDataAddedToFacts = false; ///< true: FactMetaData has been adde to the default component facts bool _logReplay = false; ///< true: running with log replay link @@ -166,31 +180,23 @@ private slots: // Wait counts from previous parameter update cycle int _prevWaitingReadParamIndexCount = 0; - int _prevWaitingReadParamNameCount = 0; - int _prevWaitingWriteParamNameCount = 0; bool _readParamIndexProgressActive = false; - bool _readParamNameProgressActive = false; - bool _writeParamProgressActive = false; static constexpr int _maxInitialRequestListRetry = 4; ///< Maximum retries for request list int _initialRequestRetryCount = 0; ///< Current retry count for request list static constexpr int _maxInitialLoadRetrySingleParam = 5; ///< Maximum retries for initial index based load of a single param - static constexpr int _maxReadWriteRetry = 5; ///< Maximum retries read/write - bool _disableAllRetries = false; ///< true: Don't retry any requests (used for testing) + bool _disableAllRetries = false; ///< true: Don't retry any requests (used for testing and logReplay) bool _indexBatchQueueActive = false; ///< true: we are actively batching re-requests for missing index base params, false: index based re-request has not yet started QList _indexBatchQueue; ///< The current queue of index re-requests QMap _paramCountMap; ///< Key: Component id, Value: count of parameters in this component QMap> _waitingReadParamIndexMap; ///< Key: Component id, Value: Map { Key: parameter index still waiting for, Value: retry count } - QMap> _waitingReadParamNameMap; ///< Key: Component id, Value: Map { Key: parameter name still waiting for, Value: retry count } - QMap> _waitingWriteParamNameMap; ///< Key: Component id, Value: Map { Key: parameter name still waiting for, Value: retry count } QMap> _failedReadParamIndexMap; ///< Key: Component id, Value: failed parameter index int _totalParamCount = 0; ///< Number of parameters across all components - int _waitingWriteParamBatchCount = 0; ///< Number of parameters which are batched up waiting on write responses - int _waitingReadParamNameBatchCount = 0; ///< Number of parameters which are batched up waiting on read responses + int _pendingWritesCount = 0; ///< Number of parameters with pending writes QTimer _initialRequestTimeoutTimer; QTimer _waitingParamTimeoutTimer; diff --git a/src/FactSystem/SettingsFact.cc b/src/FactSystem/SettingsFact.cc index 43cb6a452143..e67a76b3ee95 100644 --- a/src/FactSystem/SettingsFact.cc +++ b/src/FactSystem/SettingsFact.cc @@ -11,10 +11,11 @@ #include "QGCApplication.h" #include "QGCCorePlugin.h" #include "QGCLoggingCategory.h" +#include "SettingsManager.h" #include -QGC_LOGGING_CATEGORY(SettingsFactLog, "qgc.factsystem.settingsfact") +QGC_LOGGING_CATEGORY(SettingsFactLog, "FactSystem.SettingsFact") SettingsFact::SettingsFact(QObject *parent) : Fact(parent) @@ -34,7 +35,7 @@ SettingsFact::SettingsFact(const QString &settingsGroup, FactMetaData *metaData, } // Allow core plugin a chance to override the default value - _visible = QGCCorePlugin::instance()->adjustSettingMetaData(settingsGroup, *metaData); + SettingsManager::adjustSettingMetaData(settingsGroup, *metaData, _visible); setMetaData(metaData); if (metaData->defaultValueAvailable()) { @@ -49,7 +50,7 @@ SettingsFact::SettingsFact(const QString &settingsGroup, FactMetaData *metaData, _rawValue = typedValue; } else { // Setting is not visible, force to default value always - settings.setValue(_name, rawDefaultValue); + // Note that we specifically do not save this back to QSettings such that a Settings Override file change is not a permanent change _rawValue = rawDefaultValue; } } diff --git a/src/FirmwarePlugin/APM/APMBatteryIndicator.qml b/src/FirmwarePlugin/APM/APMBatteryIndicator.qml new file mode 100644 index 000000000000..86882a938e03 --- /dev/null +++ b/src/FirmwarePlugin/APM/APMBatteryIndicator.qml @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls +import QGroundControl.FactControls + +ColumnLayout { + FactPanelController { id: controller } + + property Fact batt1Monitor: controller.getParameterFact(-1, "BATT_MONITOR") + property string disabledString: qsTr("- disabled") + + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Low Voltage Failsafe") + visible: batt1Monitor.rawValue !== 0 + + LabelledFactComboBox { + label: qsTr("Vehicle Action") + fact: controller.getParameterFact(-1, "BATT_FS_LOW_ACT") + indexModel: false + } + + FactSlider { + Layout.fillWidth: true + label: qsTr("Voltage Trigger") + (value == 0 ? disabledString : "") + fact: controller.getParameterFact(-1, "BATT_LOW_VOLT") + from: 0 + to: 100 + majorTickStepSize: 5 + } + + FactSlider { + Layout.fillWidth: true + label: qsTr("mAh Trigger") + (value == 0 ? disabledString : "") + fact: controller.getParameterFact(-1, "BATT_LOW_MAH") + from: 0 + to: 30000 + majorTickStepSize: 1000 + } + } + + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Critical Voltage Failsafe") + visible: batt1Monitor.rawValue !== 0 + + LabelledFactComboBox { + label: qsTr("Vehicle Action") + fact: controller.getParameterFact(-1, "BATT_FS_CRT_ACT") + indexModel: false + } + + FactSlider { + Layout.fillWidth: true + label: qsTr("Voltage Trigger") + (value == 0 ? disabledString : "") + fact: controller.getParameterFact(-1, "BATT_CRT_VOLT") + from: 0 + to: 100 + majorTickStepSize: 5 + } + + FactSlider { + Layout.fillWidth: true + label: qsTr("mAh Trigger") + (value == 0 ? disabledString : "") + fact: controller.getParameterFact(-1, "BATT_CRT_MAH") + from: 0 + to: 30000 + majorTickStepSize: 1000 + } + } +} diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 5860c9ca4fbf..405d89a3d0a9 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -34,7 +34,7 @@ #include #include -QGC_LOGGING_CATEGORY(APMFirmwarePluginLog, "APMFirmwarePluginLog") +QGC_LOGGING_CATEGORY(APMFirmwarePluginLog, "FirmwarePlugin.APMFirmwarePlugin") APMFirmwarePlugin::APMFirmwarePlugin(QObject *parent) : FirmwarePlugin(parent) @@ -441,39 +441,77 @@ void APMFirmwarePlugin::initializeStreamRates(Vehicle *vehicle) instanceData->lastBatteryStatusTime = instanceData->lastHomePositionTime = QTime::currentTime(); } +APMFirmwarePlugin::FirmwareParameterHeader APMFirmwarePlugin::_parseParamsHeader(const QString &filePath) +{ + APMFirmwarePlugin::FirmwareParameterHeader data{}; + + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return data; + } + + QTextStream stream(&file); + while (!stream.atEnd()) { + const QString line = stream.readLine(); + + // Stop once non-comment parameter rows begin and we already saw some header + if (!line.startsWith('#')) { + break; + } + + using namespace Qt::StringLiterals; + + static const QRegularExpression reStack(uR"(^#\s*Stack:\s*(.+)\s*$)"_s); + auto match = reStack.match(line); + if (match.hasMatch()) { + const QString firmwareTypeStr = match.captured(1).trimmed(); + const MAV_AUTOPILOT firmwareType = QGCMAVLink::firmwareTypeFromString(firmwareTypeStr); + data.firmwareType = firmwareType; + continue; + } + + static const QRegularExpression reVehicle(uR"(^#\s*Vehicle:\s*(.+)\s*$)"_s); + match = reVehicle.match(line); + if (match.hasMatch()) { + const QString vehicleTypeStr = match.captured(1).trimmed(); + const MAV_TYPE vehicleType = QGCMAVLink::vehicleTypeFromString(vehicleTypeStr); + data.vehicleType = vehicleType; + continue; + } + + static const QRegularExpression reVersion(uR"(^#\s*Version:\s*([0-9]+(?:\.[0-9]+){0,2})(?:\s+([A-Za-z0-9]+))?\s*$)"_s); + match = reVersion.match(line); + if (match.hasMatch()) { + const QString versionNumber = match.captured(1).trimmed(); + data.versionNumber = QVersionNumber::fromString(versionNumber); + + const QString versionType = match.captured(2).trimmed(); + if (versionType.isEmpty()) { + data.versionType = FIRMWARE_VERSION_TYPE_OFFICIAL; + } else { + data.versionType = QGCMAVLink::firmwareVersionTypeFromString(versionType); + } + continue; + } + + static const QRegularExpression reGit(uR"(^#\s*Git Revision:\s*([0-9a-fA-F]+)\s*$)"_s); + match = reGit.match(line); + if (match.hasMatch()) { + data.gitRevision = match.captured(1).trimmed(); + continue; + } + } + + return data; +} void APMFirmwarePlugin::initializeVehicle(Vehicle *vehicle) { if (vehicle->isOfflineEditingVehicle()) { - switch (vehicle->vehicleType()) { - case MAV_TYPE_QUADROTOR: - case MAV_TYPE_HEXAROTOR: - case MAV_TYPE_OCTOROTOR: - case MAV_TYPE_TRICOPTER: - case MAV_TYPE_COAXIAL: - case MAV_TYPE_HELICOPTER: - vehicle->setFirmwareVersion(4, 6, 2); - break; - case MAV_TYPE_VTOL_TAILSITTER_DUOROTOR: - case MAV_TYPE_VTOL_TAILSITTER_QUADROTOR: - case MAV_TYPE_VTOL_TILTROTOR: - case MAV_TYPE_VTOL_FIXEDROTOR: - case MAV_TYPE_VTOL_TAILSITTER: - case MAV_TYPE_VTOL_TILTWING: - case MAV_TYPE_VTOL_RESERVED5: - case MAV_TYPE_FIXED_WING: - vehicle->setFirmwareVersion(4, 6, 2); - break; - case MAV_TYPE_GROUND_ROVER: - case MAV_TYPE_SURFACE_BOAT: - vehicle->setFirmwareVersion(4, 6, 2); - break; - case MAV_TYPE_SUBMARINE: - vehicle->setFirmwareVersion(4, 6, 0); - break; - default: - // No version set - break; + const QString offlineParameterFile = offlineEditingParamFile(vehicle); + const APMFirmwarePlugin::FirmwareParameterHeader offlineParameterHeader = _parseParamsHeader(offlineParameterFile); + if (offlineParameterHeader.vehicleType != MAV_TYPE_GENERIC) { + vehicle->setFirmwareVersion(offlineParameterHeader.versionNumber.majorVersion(), offlineParameterHeader.versionNumber.minorVersion(), offlineParameterHeader.versionNumber.microVersion()); } } else { initializeStreamRates(vehicle); @@ -606,7 +644,7 @@ QString APMFirmwarePlugin::getHobbsMeter(Vehicle* vehicle) const const QString timeStr = QString::asprintf("%04d:%02d:%02d", hours, minutes, seconds); qCDebug(VehicleLog) << "Hobbs Meter string:" << timeStr; return timeStr; -} +} bool APMFirmwarePlugin::hasGripper(const Vehicle *vehicle) const { @@ -623,24 +661,8 @@ const QVariantList &APMFirmwarePlugin::toolIndicators(const Vehicle *vehicle) // First call the base class to get the standard QGC list _toolIndicatorList = FirmwarePlugin::toolIndicators(vehicle); - // Find the generic flight mode indicator and replace with the custom one - for (int i = 0; i < _toolIndicatorList.size(); i++) { - if (_toolIndicatorList.at(i).toUrl().toString().contains("FlightModeIndicator.qml")) { - _toolIndicatorList[i] = QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/APMFlightModeIndicator.qml")); - break; - } - } - - // Find the generic battery indicator and replace with the custom one - for (int i = 0; i < _toolIndicatorList.size(); i++) { - if (_toolIndicatorList.at(i).toUrl().toString().contains("BatteryIndicator.qml")) { - _toolIndicatorList[i] = QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/APMBatteryIndicator.qml")); - break; - } - } - - // Then add the forwarding support indicator - _toolIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/APMSupportForwardingIndicator.qml"))); + // Add the forwarding support indicator + _toolIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/APM/APMSupportForwardingIndicator.qml"))); } return _toolIndicatorList; @@ -1255,11 +1277,6 @@ void APMFirmwarePlugin::guidedModeChangeEquivalentAirspeedMetersSecond(Vehicle * ); // param 5-7 unused } -QVariant APMFirmwarePlugin::mainStatusIndicatorContentItem(const Vehicle*) const -{ - return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/APMMainStatusIndicatorContentItem.qml")); -} - void APMFirmwarePlugin::_setBaroGndTemp(Vehicle* vehicle, qreal temp) { if (!vehicle) { @@ -1374,3 +1391,16 @@ bool APMFirmwarePlugin::stopCompensatingBaro(const Vehicle *vehicle, QPairmultiRotor()) { + return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/APM/APMFlightModeIndicator.qml")); + } else if (indicatorName == "MainStatus") { + return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/APM/APMMainStatusIndicator.qml")); + } + + return QVariant(); +} diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index 1652bce96d11..7a58a2f64e34 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -77,8 +77,8 @@ class APMFirmwarePlugin : public FirmwarePlugin double minimumEquivalentAirspeed(Vehicle *vehicle) const override; bool fixedWingAirSpeedLimitsAvailable(Vehicle *vehicle) const override; void guidedModeChangeEquivalentAirspeedMetersSecond(Vehicle* vehicle, double airspeed_equiv) const override; - QVariant mainStatusIndicatorContentItem(const Vehicle* vehicle) const override; void sendGCSMotionReport(Vehicle *vehicle, const FollowMe::GCSMotionReport& motionReport, uint8_t estimatationCapabilities) const override; + QVariant expandedToolbarIndicatorSource (const Vehicle* vehicle, const QString& indicatorName) const override; // support for changing speed in Copter guide mode: bool mulirotorSpeedLimitsAvailable(Vehicle *vehicle) const override; @@ -139,6 +139,15 @@ private slots: static uint8_t _reencodeMavlinkChannel(); static QMutex &_reencodeMavlinkChannelMutex(); + + struct FirmwareParameterHeader { + MAV_AUTOPILOT firmwareType = MAV_AUTOPILOT_GENERIC; + MAV_TYPE vehicleType = MAV_TYPE_GENERIC; + QVersionNumber versionNumber; + FIRMWARE_VERSION_TYPE versionType; + QString gitRevision; + }; + static FirmwareParameterHeader _parseParamsHeader(const QString &filePath); }; /*===========================================================================*/ diff --git a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc index 7c5b7e4f04ea..9471fd0ba167 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc @@ -14,7 +14,7 @@ #include "ArduSubFirmwarePlugin.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(APMFirmwarePluginFactoryLog, "qgc.firmwareplugin.apmfirmwarepluginfactory"); +QGC_LOGGING_CATEGORY(APMFirmwarePluginFactoryLog, "FirmwarePlugin.APMFirmwarePluginFactory"); APMFirmwarePluginFactory APMFirmwarePluginFactory(nullptr); diff --git a/src/FirmwarePlugin/APM/APMFlightModeIndicator.qml b/src/FirmwarePlugin/APM/APMFlightModeIndicator.qml new file mode 100644 index 000000000000..bb968c8d4ecd --- /dev/null +++ b/src/FirmwarePlugin/APM/APMFlightModeIndicator.qml @@ -0,0 +1,72 @@ +/**************************************************************************** + * + * (c) 2009-2022 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls +import QGroundControl.FactControls + +SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Return to Launch") + visible: activeVehicle.multiRotor + + property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property Fact rtlAltFact: controller.getParameterFact(-1, "RTL_ALT") + + FactPanelController { id: controller } + + RowLayout { + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelWidth * 2 + + QGCLabel { + id: label + Layout.fillWidth: true + text: qsTr("Return At") + } + + QGCComboBox { + id: returnAtCombo + sizeToContents: true + model: [ qsTr("Current altitude"), qsTr("Specified altitude") ] + + function setCurrentIndex() { + if (rtlAltFact.value === 0) { + returnAtCombo.currentIndex = 0 + } else { + returnAtCombo.currentIndex = 1 + } + } + + Component.onCompleted: setCurrentIndex() + + onActivated: (index) => { + if (index === 0) { + rtlAltFact.rawValue = 0 + } else { + rtlAltFact.rawValue = 1500 + } + } + + Connections { + target: rtlAltFact + onRawValueChanged: returnAtCombo.setCurrentIndex() + } + } + + FactTextField { + fact: rtlAltFact + enabled: rtlAltFact.rawValue !== 0 + } + } +} diff --git a/src/UI/toolbar/APMMainStatusIndicatorContentItem.qml b/src/FirmwarePlugin/APM/APMMainStatusIndicator.qml similarity index 99% rename from src/UI/toolbar/APMMainStatusIndicatorContentItem.qml rename to src/FirmwarePlugin/APM/APMMainStatusIndicator.qml index 4529c3ccb0b5..635f81df698b 100644 --- a/src/UI/toolbar/APMMainStatusIndicatorContentItem.qml +++ b/src/FirmwarePlugin/APM/APMMainStatusIndicator.qml @@ -13,10 +13,6 @@ import QtQuick.Layouts import QGroundControl import QGroundControl.Controls - - - - import QGroundControl.FactControls ColumnLayout { diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.cc b/src/FirmwarePlugin/APM/APMParameterMetaData.cc index 58798500ad66..dbb09770cc17 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.cc +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.cc @@ -15,8 +15,8 @@ #include #include -QGC_LOGGING_CATEGORY(APMParameterMetaDataLog, "qgc.firmwareplugin.apm.apmparametermetadata") -QGC_LOGGING_CATEGORY(APMParameterMetaDataVerboseLog, "qgc.firmwareplugin.apm.apmparametermetadata:verbose") +QGC_LOGGING_CATEGORY(APMParameterMetaDataLog, "FirmwarePlugin.APMParameterMetaData") +QGC_LOGGING_CATEGORY(APMParameterMetaDataVerboseLog, "FirmwarePlugin.APMParameterMetaData:verbose") APMParameterMetaData::APMParameterMetaData(QObject *parent) : QObject(parent) diff --git a/src/UI/toolbar/APMSupportForwardingIndicator.qml b/src/FirmwarePlugin/APM/APMSupportForwardingIndicator.qml similarity index 97% rename from src/UI/toolbar/APMSupportForwardingIndicator.qml rename to src/FirmwarePlugin/APM/APMSupportForwardingIndicator.qml index 97a22f9e8851..d16e91c390aa 100644 --- a/src/UI/toolbar/APMSupportForwardingIndicator.qml +++ b/src/FirmwarePlugin/APM/APMSupportForwardingIndicator.qml @@ -27,12 +27,12 @@ Item { Component { id: forwardingSupportInfoPage - + ToolIndicatorPage { contentComponent: SettingsGroupLayout { QGCLabel { text: qsTr("Mavlink traffic is being forwarded to a support server") } - LabelledLabel { + LabelledLabel { label: qsTr("Server name:") labelText: QGroundControl.settingsManager.mavlinkSettings.forwardMavlinkAPMSupportHostName.value } @@ -49,7 +49,7 @@ Item { source: "/qmlimages/ForwardingSupportIconGreen.svg" fillMode: Image.PreserveAspectFit } - + MouseArea { anchors.fill: parent onClicked: mainWindow.showIndicatorDrawer(forwardingSupportInfoPage, control) diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc index c86410018897..d8e04f52218d 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc @@ -43,7 +43,7 @@ ArduPlaneFirmwarePlugin::ArduPlaneFirmwarePlugin(QObject *parent) { APMPlaneMode::THERMAL , _thermalFlightMode }, { APMPlaneMode::LOITER2QLAND , _loiter2qlandFlightMode }, { APMPlaneMode::AUTOLAND , _autolandFlightMode }, - + }); static FlightModeList availableFlightModes = { @@ -108,6 +108,11 @@ QString ArduPlaneFirmwarePlugin::stabilizedFlightMode() const return _modeEnumToString.value(APMPlaneMode::STABILIZE, _stabilizeFlightMode); } +QString ArduPlaneFirmwarePlugin::pauseFlightMode() const +{ + return _modeEnumToString.value(APMPlaneMode::LOITER, _loiterFlightMode); +} + void ArduPlaneFirmwarePlugin::updateAvailableFlightModes(FlightModeList &modeList) { for (FirmwareFlightMode &mode: modeList) { diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h index a61643aca675..b33871ad9b47 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h @@ -52,11 +52,11 @@ class ArduPlaneFirmwarePlugin : public APMFirmwarePlugin explicit ArduPlaneFirmwarePlugin(QObject *parent = nullptr); ~ArduPlaneFirmwarePlugin(); - QString pauseFlightMode() const override { return QString("Loiter"); } QString offlineEditingParamFile(Vehicle *vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/APM/Plane.OfflineEditing.params"); } QString autoDisarmParameter(Vehicle *vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral("LAND_DISARMDELAY"); } int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const override; const FirmwarePlugin::remapParamNameMajorVersionMap_t ¶mNameRemapMajorVersionMap() const override { return _remapParamName; } + QString pauseFlightMode() const override; QString takeOffFlightMode() const override; QString stabilizedFlightMode() const override; void updateAvailableFlightModes(FlightModeList &modeList) override; diff --git a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc index b8a5d351c116..d37a1d3815fc 100644 --- a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc @@ -81,6 +81,16 @@ QString ArduRoverFirmwarePlugin::stabilizedFlightMode() const return _modeEnumToString.value(APMRoverMode::MANUAL, _manualFlightMode); } +QString ArduRoverFirmwarePlugin::pauseFlightMode() const +{ + return _modeEnumToString.value(APMRoverMode::HOLD, _holdFlightMode); +} + +QString ArduRoverFirmwarePlugin::followFlightMode() const +{ + return _modeEnumToString.value(APMRoverMode::FOLLOW, _followFlightMode); +} + void ArduRoverFirmwarePlugin::updateAvailableFlightModes(FlightModeList &modeList) { for (FirmwareFlightMode &mode: modeList) { diff --git a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h index 9f65de12bf7d..3a20415bbf0c 100644 --- a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h @@ -40,8 +40,6 @@ class ArduRoverFirmwarePlugin : public APMFirmwarePlugin explicit ArduRoverFirmwarePlugin(QObject *parent = nullptr); ~ArduRoverFirmwarePlugin(); - QString pauseFlightMode() const override { return QStringLiteral("Hold"); } - QString followFlightMode() const override { return QStringLiteral("Follow"); } void guidedModeChangeAltitude(Vehicle* vehicle, double altitudeChange, bool pauseVehicle) override; int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const override; const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap() const override { return _remapParamName; } @@ -49,6 +47,8 @@ class ArduRoverFirmwarePlugin : public APMFirmwarePlugin bool supportsSmartRTL() const override { return true; } QString offlineEditingParamFile(Vehicle *vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/APM/Rover.OfflineEditing.params"); } + QString pauseFlightMode() const override; + QString followFlightMode() const override; QString stabilizedFlightMode() const override; void updateAvailableFlightModes(FlightModeList &modeList) override; diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc index eff3ca85f665..848e669cfede 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc @@ -11,7 +11,7 @@ #include "QGCLoggingCategory.h" #include "Vehicle.h" -QGC_LOGGING_CATEGORY(APMSubmarineFactGroupLog, "qgc.firmwareplugin.apm.ardusubfirmwareplugin") +QGC_LOGGING_CATEGORY(APMSubmarineFactGroupLog, "FirmwarePlugin.ArduSubFirmwarePlugin") APMSubmarineFactGroup::APMSubmarineFactGroup(QObject *parent) : FactGroup(300, QStringLiteral(":/json/Vehicle/SubmarineFact.json"), parent) @@ -175,33 +175,6 @@ bool ArduSubFirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabiliti return ((capabilities & available) == capabilities); } -const QVariantList &ArduSubFirmwarePlugin::toolIndicators(const Vehicle *vehicle) -{ - Q_UNUSED(vehicle); - //-- Sub specific list of indicators (Enter your modified list here) - if (_toolIndicators.isEmpty()) { - _toolIndicators = QVariantList({ - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Controls/BatteryIndicator.qml")), - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/JoystickIndicator.qml")), - }); - } - return _toolIndicators; -} - -const QVariantList& ArduSubFirmwarePlugin::modeIndicators(const Vehicle *vehicle) -{ - Q_UNUSED(vehicle); - //-- Sub specific list of indicators (Enter your modified list here) - if (_modeIndicators.isEmpty()) { - _modeIndicators = QVariantList({ - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/ModeIndicator.qml")), - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/ArmedIndicator.qml")), - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/MultiVehicleSelector.qml")), - }); - } - return _modeIndicators; -} - void ArduSubFirmwarePlugin::_handleNamedValueFloat(mavlink_message_t *message) { mavlink_named_value_float_t value{}; diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h index 2392e7b789a6..d7231ebc228f 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h @@ -109,8 +109,6 @@ class ArduSubFirmwarePlugin : public APMFirmwarePlugin QString brandImageOutdoor(const Vehicle *vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImageSub"); } const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap() const override { return _remapParamName; } int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const override; - const QVariantList &toolIndicators(const Vehicle *vehicle) override; - const QVariantList &modeIndicators(const Vehicle *vehicle) override; bool adjustIncomingMavlinkMessage(Vehicle *vehicle, mavlink_message_t *message) override; QMap *factGroups() override; void adjustMetaData(MAV_TYPE vehicleType, FactMetaData *metaData) override; @@ -138,7 +136,6 @@ class ArduSubFirmwarePlugin : public APMFirmwarePlugin private: QVariantList _toolIndicators; - QVariantList _modeIndicators; static bool _remapParamNameIntialized; QMap _factRenameMap; static FirmwarePlugin::remapParamNameMajorVersionMap_t _remapParamName; diff --git a/src/FirmwarePlugin/APM/CMakeLists.txt b/src/FirmwarePlugin/APM/CMakeLists.txt index d87a8ab7084f..c164262adca6 100644 --- a/src/FirmwarePlugin/APM/CMakeLists.txt +++ b/src/FirmwarePlugin/APM/CMakeLists.txt @@ -107,3 +107,20 @@ qt_add_resources(${CMAKE_PROJECT_NAME} "qgcresources_apm_params" "${ArduPilotParams_SOURCE_DIR}/Sub-4.1/apm.pdef.xml" "${ArduPilotParams_SOURCE_DIR}/Sub-4.5/apm.pdef.xml" ) + +# Add qml module +qt_add_library(APMFirmwareModule STATIC) + +qt_add_qml_module(APMFirmwareModule + URI QGroundControl.FirmwarePlugin.APM + VERSION 1.0 + RESOURCE_PREFIX /qml + QML_FILES + APMBatteryIndicator.qml + APMFlightModeIndicator.qml + APMMainStatusIndicator.qml + APMSupportForwardingIndicator.qml + NO_PLUGIN +) + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE APMFirmwareModule) diff --git a/src/FirmwarePlugin/APM/Copter.OfflineEditing.params b/src/FirmwarePlugin/APM/Copter.OfflineEditing.params index 0412f36b1ecf..e44cf725372c 100644 --- a/src/FirmwarePlugin/APM/Copter.OfflineEditing.params +++ b/src/FirmwarePlugin/APM/Copter.OfflineEditing.params @@ -1,29 +1,41 @@ # Onboard parameters for Vehicle 1 # # Stack: ArduPilot -# Vehicle: Quadrotor -# Version: 4.6.2 -# Git Revision: 1ebd4d99 +# Vehicle: Multi-Rotor +# Version: 4.0.5 rc +# Git Revision: b319b27 # # Vehicle-Id Component-Id Name Value Type 1 1 ACRO_BAL_PITCH 1.000000000000000000 9 1 1 ACRO_BAL_ROLL 1.000000000000000000 9 -1 1 ACRO_OPTIONS 0 2 1 1 ACRO_RP_EXPO 0.300000011920928955 9 -1 1 ACRO_RP_RATE 360.000000000000000000 9 -1 1 ACRO_RP_RATE_TC 0.000000000000000000 9 +1 1 ACRO_RP_P 4.500000000000000000 9 1 1 ACRO_THR_MID 0.000000000000000000 9 1 1 ACRO_TRAINER 2 2 +1 1 ACRO_YAW_P 4.500000000000000000 9 1 1 ACRO_Y_EXPO 0.000000000000000000 9 -1 1 ACRO_Y_RATE 202.500000000000000000 9 -1 1 ACRO_Y_RATE_TC 0.000000000000000000 9 -1 1 ADSB_TYPE 0 2 +1 1 ADSB_EMIT_TYPE 14 2 +1 1 ADSB_ENABLE 1 2 +1 1 ADSB_ICAO_ID 0 6 +1 1 ADSB_ICAO_SPECL 0 6 +1 1 ADSB_LEN_WIDTH 1 2 +1 1 ADSB_LIST_ALT 0 4 +1 1 ADSB_LIST_MAX 25 4 +1 1 ADSB_LIST_RADIUS 2000 6 +1 1 ADSB_LOG 1 2 +1 1 ADSB_OFFSET_LAT 4 2 +1 1 ADSB_OFFSET_LON 1 2 +1 1 ADSB_RF_CAPABLE 0 2 +1 1 ADSB_RF_SELECT 1 2 +1 1 ADSB_SQUAWK 1200 4 1 1 AHRS_COMP_BETA 0.100000001490116119 9 -1 1 AHRS_EKF_TYPE 3 2 +1 1 AHRS_CUSTOM_PIT 0.000000000000000000 9 +1 1 AHRS_CUSTOM_ROLL 0.000000000000000000 9 +1 1 AHRS_CUSTOM_YAW 0.000000000000000000 9 +1 1 AHRS_EKF_TYPE 2 2 1 1 AHRS_GPS_GAIN 1.000000000000000000 9 1 1 AHRS_GPS_MINSATS 6 2 1 1 AHRS_GPS_USE 1 2 -1 1 AHRS_OPTIONS 0 4 1 1 AHRS_ORIENTATION 0 2 1 1 AHRS_RP_P 0.200000002980232239 9 1 1 AHRS_TRIM_X 0.000000000000000000 9 @@ -31,15 +43,11 @@ 1 1 AHRS_TRIM_Z 0.000000000000000000 9 1 1 AHRS_WIND_MAX 0 2 1 1 AHRS_YAW_P 0.200000002980232239 9 -1 1 ANGLE_MAX 3000 4 +1 1 ANGLE_MAX 4500 4 1 1 ARMING_ACCTHRESH 0.750000000000000000 9 1 1 ARMING_CHECK 1 6 -1 1 ARMING_MAGTHRESH 100 4 1 1 ARMING_MIS_ITEMS 0 6 -1 1 ARMING_NEED_LOC 0 2 -1 1 ARMING_OPTIONS 0 6 1 1 ARMING_RUDDER 2 2 -1 1 ARSPD_ENABLE 0 2 1 1 ATC_ACCEL_P_MAX 110000.000000000000000000 9 1 1 ATC_ACCEL_R_MAX 110000.000000000000000000 9 1 1 ATC_ACCEL_Y_MAX 27000.000000000000000000 9 @@ -49,90 +57,47 @@ 1 1 ATC_ANG_RLL_P 4.500000000000000000 9 1 1 ATC_ANG_YAW_P 4.500000000000000000 9 1 1 ATC_INPUT_TC 0.150000005960464478 9 -1 1 ATC_LAND_P_MULT 1.000000000000000000 9 -1 1 ATC_LAND_R_MULT 1.000000000000000000 9 -1 1 ATC_LAND_Y_MULT 1.000000000000000000 9 1 1 ATC_RATE_FF_ENAB 1 2 1 1 ATC_RATE_P_MAX 0.000000000000000000 9 1 1 ATC_RATE_R_MAX 0.000000000000000000 9 1 1 ATC_RATE_Y_MAX 0.000000000000000000 9 1 1 ATC_RAT_PIT_D 0.003599999938160181 9 -1 1 ATC_RAT_PIT_D_FF 0.000000000000000000 9 1 1 ATC_RAT_PIT_FF 0.000000000000000000 9 1 1 ATC_RAT_PIT_FLTD 20.000000000000000000 9 1 1 ATC_RAT_PIT_FLTE 0.000000000000000000 9 1 1 ATC_RAT_PIT_FLTT 20.000000000000000000 9 1 1 ATC_RAT_PIT_I 0.135000005364418030 9 1 1 ATC_RAT_PIT_IMAX 0.500000000000000000 9 -1 1 ATC_RAT_PIT_NEF 0 2 -1 1 ATC_RAT_PIT_NTF 0 2 1 1 ATC_RAT_PIT_P 0.135000005364418030 9 -1 1 ATC_RAT_PIT_PDMX 0.000000000000000000 9 -1 1 ATC_RAT_PIT_SMAX 0.000000000000000000 9 1 1 ATC_RAT_RLL_D 0.003599999938160181 9 -1 1 ATC_RAT_RLL_D_FF 0.000000000000000000 9 1 1 ATC_RAT_RLL_FF 0.000000000000000000 9 1 1 ATC_RAT_RLL_FLTD 20.000000000000000000 9 1 1 ATC_RAT_RLL_FLTE 0.000000000000000000 9 1 1 ATC_RAT_RLL_FLTT 20.000000000000000000 9 1 1 ATC_RAT_RLL_I 0.135000005364418030 9 1 1 ATC_RAT_RLL_IMAX 0.500000000000000000 9 -1 1 ATC_RAT_RLL_NEF 0 2 -1 1 ATC_RAT_RLL_NTF 0 2 1 1 ATC_RAT_RLL_P 0.135000005364418030 9 -1 1 ATC_RAT_RLL_PDMX 0.000000000000000000 9 -1 1 ATC_RAT_RLL_SMAX 0.000000000000000000 9 1 1 ATC_RAT_YAW_D 0.000000000000000000 9 -1 1 ATC_RAT_YAW_D_FF 0.000000000000000000 9 1 1 ATC_RAT_YAW_FF 0.000000000000000000 9 -1 1 ATC_RAT_YAW_FLTD 20.000000000000000000 9 +1 1 ATC_RAT_YAW_FLTD 0.000000000000000000 9 1 1 ATC_RAT_YAW_FLTE 2.500000000000000000 9 1 1 ATC_RAT_YAW_FLTT 20.000000000000000000 9 -1 1 ATC_RAT_YAW_I 0.019999999552965164 9 +1 1 ATC_RAT_YAW_I 0.017999999225139618 9 1 1 ATC_RAT_YAW_IMAX 0.500000000000000000 9 -1 1 ATC_RAT_YAW_NEF 0 2 -1 1 ATC_RAT_YAW_NTF 0 2 -1 1 ATC_RAT_YAW_P 0.300000011920928955 9 -1 1 ATC_RAT_YAW_PDMX 0.000000000000000000 9 -1 1 ATC_RAT_YAW_SMAX 0.000000000000000000 9 +1 1 ATC_RAT_YAW_P 0.180000007152557373 9 1 1 ATC_SLEW_YAW 6000.000000000000000000 9 -1 1 ATC_THR_G_BOOST 0.000000000000000000 9 1 1 ATC_THR_MIX_MAN 0.100000001490116119 9 1 1 ATC_THR_MIX_MAX 0.500000000000000000 9 1 1 ATC_THR_MIX_MIN 0.100000001490116119 9 -1 1 AUTOTUNE_AGGR 0.075000002980232239 9 +1 1 AUTOTUNE_AGGR 0.100000001490116119 9 1 1 AUTOTUNE_AXES 7 2 -1 1 AUTOTUNE_MIN_D 0.000500000023748726 9 -1 1 AUTO_OPTIONS 0 6 +1 1 AUTOTUNE_MIN_D 0.001000000047497451 9 1 1 AVD_ENABLE 0 2 -1 1 AVOID_ACCEL_MAX 3.000000000000000000 9 -1 1 AVOID_ALT_MIN 0.000000000000000000 9 1 1 AVOID_ANGLE_MAX 1000 4 -1 1 AVOID_BACKUP_DZ 0.100000001490116119 9 -1 1 AVOID_BACKUP_SPD 0.750000000000000000 9 -1 1 AVOID_BACKZ_SPD 0.750000000000000000 9 1 1 AVOID_BEHAVE 0 2 1 1 AVOID_DIST_MAX 5.000000000000000000 9 1 1 AVOID_ENABLE 3 2 1 1 AVOID_MARGIN 2.000000000000000000 9 -1 1 BARO1_DEVID 65540 6 -1 1 BARO1_GND_PRESS 101325.062500000000000000 9 -1 1 BARO1_WCF_ENABLE 0 2 -1 1 BARO2_DEVID 65796 6 -1 1 BARO2_GND_PRESS 101324.507812500000000000 9 -1 1 BARO2_WCF_ENABLE 0 2 -1 1 BARO3_DEVID 0 6 -1 1 BARO3_GND_PRESS 0.000000000000000000 9 -1 1 BARO3_WCF_ENABLE 0 2 -1 1 BARO_ALTERR_MAX 2000.000000000000000000 9 -1 1 BARO_ALT_OFFSET 0.000000000000000000 9 -1 1 BARO_EXT_BUS -1 2 -1 1 BARO_FIELD_ELV 0.000000000000000000 9 -1 1 BARO_FLTR_RNG 0 2 -1 1 BARO_GND_TEMP 0.000000000000000000 9 -1 1 BARO_OPTIONS 0 4 -1 1 BARO_PRIMARY 0 2 -1 1 BARO_PROBE_EXT 0 6 1 1 BATT2_MONITOR 0 2 1 1 BATT3_MONITOR 0 2 1 1 BATT4_MONITOR 0 2 @@ -141,83 +106,87 @@ 1 1 BATT7_MONITOR 0 2 1 1 BATT8_MONITOR 0 2 1 1 BATT9_MONITOR 0 2 -1 1 BATT_AMP_OFFSET 0.000000000000000000 9 -1 1 BATT_AMP_PERVLT 17.000000000000000000 9 -1 1 BATT_ARM_MAH 0 6 -1 1 BATT_ARM_VOLT 0.000000000000000000 9 -1 1 BATT_CAPACITY 3300 6 -1 1 BATT_CRT_MAH 0.000000000000000000 9 -1 1 BATT_CRT_VOLT 0.000000000000000000 9 -1 1 BATT_CURR_PIN 12 2 -1 1 BATT_FS_CRT_ACT 0 2 -1 1 BATT_FS_LOW_ACT 0 2 -1 1 BATT_FS_VOLTSRC 0 2 -1 1 BATT_LOW_MAH 0.000000000000000000 9 -1 1 BATT_LOW_TIMER 10 2 -1 1 BATT_LOW_VOLT 10.500000000000000000 9 -1 1 BATT_MONITOR 4 2 -1 1 BATT_OPTIONS 0 6 -1 1 BATT_SERIAL_NUM -1 6 -1 1 BATT_VLT_OFFSET 0.000000000000000000 9 -1 1 BATT_VOLT_MULT 10.100000381469726563 9 -1 1 BATT_VOLT_PIN 13 2 +1 1 BATT_MONITOR 0 2 +1 1 BCN_ALT 0.000000000000000000 9 +1 1 BCN_LATITUDE 0.000000000000000000 9 +1 1 BCN_LONGITUDE 0.000000000000000000 9 +1 1 BCN_ORIENT_YAW 0 4 1 1 BCN_TYPE 0 2 +1 1 BRD_ALT_CONFIG 0 2 1 1 BRD_BOOT_DELAY 0 4 -1 1 BRD_OPTIONS 0 6 +1 1 BRD_IMUHEAT_I 0.070000000298023224 9 +1 1 BRD_IMUHEAT_IMAX 70.000000000000000000 9 +1 1 BRD_IMUHEAT_P 50.000000000000000000 9 +1 1 BRD_IMU_TARGTEMP 45 2 +1 1 BRD_IO_ENABLE 1 2 +1 1 BRD_OPTIONS 1 6 +1 1 BRD_PWM_COUNT 4 2 +1 1 BRD_PWM_VOLT_SEL 0 2 1 1 BRD_RTC_TYPES 1 2 1 1 BRD_RTC_TZ_MIN 0 4 +1 1 BRD_SAFETYENABLE 1 2 1 1 BRD_SAFETYOPTION 3 4 -1 1 BRD_SAFETY_DEFLT 0 2 -1 1 BRD_SAFETY_MASK 16368 6 -1 1 BRD_SD_FENCE 0 4 -1 1 BRD_SD_MISSION 0 4 -1 1 BRD_SERIAL_NUM 0 6 +1 1 BRD_SAFETY_MASK 16383 6 +1 1 BRD_SBUS_OUT 0 2 +1 1 BRD_SD_SLOWDOWN 0 2 +1 1 BRD_SER1_RTSCTS 2 2 +1 1 BRD_SER2_RTSCTS 2 2 +1 1 BRD_SERIAL_NUM 0 4 +1 1 BRD_TYPE 3 2 1 1 BRD_VBUS_MIN 4.300000190734863281 9 1 1 BRD_VSERVO_MIN 0.000000000000000000 9 1 1 BTN_ENABLE 0 2 -1 1 CAM1_TYPE 0 2 -1 1 CAM2_TYPE 0 2 1 1 CAM_AUTO_ONLY 0 2 +1 1 CAM_DURATION 10 2 +1 1 CAM_FEEDBACK_PIN -1 2 +1 1 CAM_FEEDBACK_POL 1 2 1 1 CAM_MAX_ROLL 0 4 -1 1 CAM_RC_TYPE 0 2 +1 1 CAM_MIN_INTERVAL 0 4 +1 1 CAM_RELAY_ON 1 2 +1 1 CAM_SERVO_OFF 1100 4 +1 1 CAM_SERVO_ON 1300 4 +1 1 CAM_TRIGG_DIST 0.000000000000000000 9 +1 1 CAM_TRIGG_TYPE 0 2 +1 1 CAM_TYPE 0 2 1 1 CAN_D1_PROTOCOL 1 2 -1 1 CAN_D1_PROTOCOL2 0 2 1 1 CAN_D2_PROTOCOL 1 2 -1 1 CAN_D2_PROTOCOL2 0 2 -1 1 CAN_LOGLEVEL 0 2 1 1 CAN_P1_DRIVER 0 2 1 1 CAN_P2_DRIVER 0 2 -1 1 CC_TYPE 0 2 +1 1 CAN_SLCAN_CPORT 0 2 +1 1 CAN_SLCAN_SERNUM -1 2 +1 1 CAN_SLCAN_TIMOUT 0 4 1 1 CHUTE_ENABLED 0 2 -1 1 CIRCLE_OPTIONS 1 4 +1 1 CIRCLE_CONTROL 1 4 1 1 CIRCLE_RADIUS 1000.000000000000000000 9 1 1 CIRCLE_RATE 20.000000000000000000 9 1 1 COMPASS_AUTODEC 1 2 1 1 COMPASS_AUTO_ROT 2 2 1 1 COMPASS_CAL_FIT 16.000000000000000000 9 -1 1 COMPASS_DEC 0.223357215523719788 9 -1 1 COMPASS_DEV_ID 97539 6 -1 1 COMPASS_DEV_ID2 131874 6 -1 1 COMPASS_DEV_ID3 263178 6 -1 1 COMPASS_DEV_ID4 97283 6 -1 1 COMPASS_DEV_ID5 97795 6 -1 1 COMPASS_DEV_ID6 98051 6 +1 1 COMPASS_CUS_PIT 0.000000000000000000 9 +1 1 COMPASS_CUS_ROLL 0.000000000000000000 9 +1 1 COMPASS_CUS_YAW 0.000000000000000000 9 +1 1 COMPASS_DEC 0.000000000000000000 9 +1 1 COMPASS_DEV_ID 590114 6 +1 1 COMPASS_DEV_ID2 0 6 +1 1 COMPASS_DEV_ID3 0 6 +1 1 COMPASS_DEV_ID4 0 6 +1 1 COMPASS_DEV_ID5 0 6 +1 1 COMPASS_DEV_ID6 0 6 1 1 COMPASS_DEV_ID7 0 6 1 1 COMPASS_DEV_ID8 0 6 -1 1 COMPASS_DIA2_X 1.000000000000000000 9 -1 1 COMPASS_DIA2_Y 1.000000000000000000 9 -1 1 COMPASS_DIA2_Z 1.000000000000000000 9 -1 1 COMPASS_DIA3_X 1.000000000000000000 9 -1 1 COMPASS_DIA3_Y 1.000000000000000000 9 -1 1 COMPASS_DIA3_Z 1.000000000000000000 9 +1 1 COMPASS_DIA2_X 0.000000000000000000 9 +1 1 COMPASS_DIA2_Y 0.000000000000000000 9 +1 1 COMPASS_DIA2_Z 0.000000000000000000 9 +1 1 COMPASS_DIA3_X 0.000000000000000000 9 +1 1 COMPASS_DIA3_Y 0.000000000000000000 9 +1 1 COMPASS_DIA3_Z 0.000000000000000000 9 1 1 COMPASS_DIA_X 1.000000000000000000 9 1 1 COMPASS_DIA_Y 1.000000000000000000 9 1 1 COMPASS_DIA_Z 1.000000000000000000 9 -1 1 COMPASS_DISBLMSK 0 6 1 1 COMPASS_ENABLE 1 2 1 1 COMPASS_EXTERN2 0 2 1 1 COMPASS_EXTERN3 0 2 -1 1 COMPASS_EXTERNAL 1 2 +1 1 COMPASS_EXTERNAL 0 2 1 1 COMPASS_FLTR_RNG 0 2 1 1 COMPASS_LEARN 0 2 1 1 COMPASS_MOT2_X 0.000000000000000000 9 @@ -240,132 +209,93 @@ 1 1 COMPASS_ODI_Y 0.000000000000000000 9 1 1 COMPASS_ODI_Z 0.000000000000000000 9 1 1 COMPASS_OFFS_MAX 1800 4 -1 1 COMPASS_OFS2_X 5.000000000000000000 9 -1 1 COMPASS_OFS2_Y 13.000000000000000000 9 -1 1 COMPASS_OFS2_Z -18.000000000000000000 9 -1 1 COMPASS_OFS3_X 5.000000000000000000 9 -1 1 COMPASS_OFS3_Y 13.000000000000000000 9 -1 1 COMPASS_OFS3_Z -18.000000000000000000 9 -1 1 COMPASS_OFS_X 5.000000000000000000 9 -1 1 COMPASS_OFS_Y 13.000000000000000000 9 -1 1 COMPASS_OFS_Z -18.000000000000000000 9 +1 1 COMPASS_OFS2_X 0.000000000000000000 9 +1 1 COMPASS_OFS2_Y 0.000000000000000000 9 +1 1 COMPASS_OFS2_Z 0.000000000000000000 9 +1 1 COMPASS_OFS3_X 0.000000000000000000 9 +1 1 COMPASS_OFS3_Y 0.000000000000000000 9 +1 1 COMPASS_OFS3_Z 0.000000000000000000 9 +1 1 COMPASS_OFS_X 0.000000000000000000 9 +1 1 COMPASS_OFS_Y 0.000000000000000000 9 +1 1 COMPASS_OFS_Z 0.000000000000000000 9 1 1 COMPASS_OPTIONS 0 4 1 1 COMPASS_ORIENT 0 2 1 1 COMPASS_ORIENT2 0 2 1 1 COMPASS_ORIENT3 0 2 1 1 COMPASS_PMOT_EN 0 2 -1 1 COMPASS_PRIO1_ID 97539 6 -1 1 COMPASS_PRIO2_ID 131874 6 -1 1 COMPASS_PRIO3_ID 263178 6 -1 1 COMPASS_SCALE 1.000000000000000000 9 -1 1 COMPASS_SCALE2 1.000000000000000000 9 -1 1 COMPASS_SCALE3 1.000000000000000000 9 +1 1 COMPASS_PRIO1_ID 590114 6 +1 1 COMPASS_PRIO2_ID 0 6 +1 1 COMPASS_PRIO3_ID 0 6 +1 1 COMPASS_SCALE 0.000000000000000000 9 +1 1 COMPASS_SCALE2 0.000000000000000000 9 +1 1 COMPASS_SCALE3 0.000000000000000000 9 +1 1 COMPASS_TYPEMASK 32 6 1 1 COMPASS_USE 1 2 1 1 COMPASS_USE2 1 2 1 1 COMPASS_USE3 1 2 -1 1 CUST_ROT_ENABLE 0 2 1 1 DEV_OPTIONS 0 6 -1 1 DID_ENABLE 0 2 1 1 DISARM_DELAY 10 2 -1 1 EAHRS_TYPE 0 2 -1 1 EFI_TYPE 0 2 -1 1 EK2_ENABLE 0 2 -1 1 EK3_ABIAS_P_NSE 0.019999999552965164 9 -1 1 EK3_ACC_BIAS_LIM 1.000000000000000000 9 -1 1 EK3_ACC_P_NSE 0.349999994039535522 9 -1 1 EK3_AFFINITY 0 6 -1 1 EK3_ALT_M_NSE 2.000000000000000000 9 -1 1 EK3_BCN_DELAY 50 2 -1 1 EK3_BCN_I_GTE 500 4 -1 1 EK3_BCN_M_NSE 1.000000000000000000 9 -1 1 EK3_BETA_MASK 0 2 -1 1 EK3_CHECK_SCALE 100 4 -1 1 EK3_DRAG_BCOEF_X 0.000000000000000000 9 -1 1 EK3_DRAG_BCOEF_Y 0.000000000000000000 9 -1 1 EK3_DRAG_MCOEF 0.000000000000000000 9 -1 1 EK3_DRAG_M_NSE 0.500000000000000000 9 -1 1 EK3_EAS_I_GATE 400 4 -1 1 EK3_EAS_M_NSE 1.399999976158142090 9 -1 1 EK3_ENABLE 1 2 -1 1 EK3_ERR_THRESH 0.200000002980232239 9 -1 1 EK3_FLOW_DELAY 10 2 -1 1 EK3_FLOW_I_GATE 300 4 -1 1 EK3_FLOW_M_NSE 0.250000000000000000 9 -1 1 EK3_FLOW_USE 1 2 -1 1 EK3_GBIAS_P_NSE 0.001000000047497451 9 -1 1 EK3_GLITCH_RAD 25 2 -1 1 EK3_GND_EFF_DZ 4.000000000000000000 9 -1 1 EK3_GPS_CHECK 31 2 -1 1 EK3_GPS_VACC_MAX 0.000000000000000000 9 -1 1 EK3_GSF_RST_MAX 2 2 -1 1 EK3_GSF_RUN_MASK 3 2 -1 1 EK3_GSF_USE_MASK 3 2 -1 1 EK3_GYRO_P_NSE 0.014999999664723873 9 -1 1 EK3_HGT_DELAY 60 4 -1 1 EK3_HGT_I_GATE 500 4 -1 1 EK3_HRT_FILT 2.000000000000000000 9 -1 1 EK3_IMU_MASK 3 2 -1 1 EK3_LOG_LEVEL 0 2 -1 1 EK3_MAGB_P_NSE 0.000099999997473788 9 -1 1 EK3_MAGE_P_NSE 0.001000000047497451 9 -1 1 EK3_MAG_CAL 3 2 -1 1 EK3_MAG_EF_LIM 50 4 -1 1 EK3_MAG_I_GATE 300 4 -1 1 EK3_MAG_MASK 0 2 -1 1 EK3_MAG_M_NSE 0.050000000745058060 9 -1 1 EK3_MAX_FLOW 2.500000000000000000 9 -1 1 EK3_NOAID_M_NSE 10.000000000000000000 9 -1 1 EK3_OGNM_TEST_SF 2.000000000000000000 9 -1 1 EK3_OGN_HGT_MASK 0 2 -1 1 EK3_OPTIONS 0 6 -1 1 EK3_POSNE_M_NSE 0.500000000000000000 9 -1 1 EK3_POS_I_GATE 500 4 -1 1 EK3_PRIMARY 0 2 -1 1 EK3_RNG_I_GATE 500 4 -1 1 EK3_RNG_M_NSE 0.500000000000000000 9 -1 1 EK3_RNG_USE_HGT -1 2 -1 1 EK3_RNG_USE_SPD 2.000000000000000000 9 -1 1 EK3_SRC1_POSXY 3 2 -1 1 EK3_SRC1_POSZ 1 2 -1 1 EK3_SRC1_VELXY 3 2 -1 1 EK3_SRC1_VELZ 3 2 -1 1 EK3_SRC1_YAW 1 2 -1 1 EK3_SRC2_POSXY 0 2 -1 1 EK3_SRC2_POSZ 1 2 -1 1 EK3_SRC2_VELXY 0 2 -1 1 EK3_SRC2_VELZ 0 2 -1 1 EK3_SRC2_YAW 0 2 -1 1 EK3_SRC3_POSXY 0 2 -1 1 EK3_SRC3_POSZ 1 2 -1 1 EK3_SRC3_VELXY 0 2 -1 1 EK3_SRC3_VELZ 0 2 -1 1 EK3_SRC3_YAW 0 2 -1 1 EK3_SRC_OPTIONS 1 4 -1 1 EK3_TAU_OUTPUT 25 2 -1 1 EK3_TERR_GRAD 0.100000001490116119 9 -1 1 EK3_VELD_M_NSE 0.500000000000000000 9 -1 1 EK3_VELNE_M_NSE 0.300000011920928955 9 -1 1 EK3_VEL_I_GATE 500 4 -1 1 EK3_VIS_VERR_MAX 0.899999976158142090 9 -1 1 EK3_VIS_VERR_MIN 0.100000001490116119 9 -1 1 EK3_WENC_VERR 0.100000001490116119 9 -1 1 EK3_WIND_PSCALE 1.000000000000000000 9 -1 1 EK3_WIND_P_NSE 0.200000002980232239 9 -1 1 EK3_YAW_I_GATE 300 4 -1 1 EK3_YAW_M_NSE 0.500000000000000000 9 +1 1 EK2_ABIAS_P_NSE 0.004999999888241291 9 +1 1 EK2_ACC_P_NSE 0.600000023841857910 9 +1 1 EK2_ALT_M_NSE 3.000000000000000000 9 +1 1 EK2_ALT_SOURCE 0 2 +1 1 EK2_BCN_DELAY 50 2 +1 1 EK2_BCN_I_GTE 500 4 +1 1 EK2_BCN_M_NSE 1.000000000000000000 9 +1 1 EK2_CHECK_SCALE 100 4 +1 1 EK2_EAS_I_GATE 400 4 +1 1 EK2_EAS_M_NSE 1.399999976158142090 9 +1 1 EK2_ENABLE 1 2 +1 1 EK2_EXTNAV_DELAY 10 2 +1 1 EK2_FLOW_DELAY 10 2 +1 1 EK2_FLOW_I_GATE 300 4 +1 1 EK2_FLOW_M_NSE 0.250000000000000000 9 +1 1 EK2_FLOW_USE 1 2 +1 1 EK2_GBIAS_P_NSE 0.000099999997473788 9 +1 1 EK2_GLITCH_RAD 25 2 +1 1 EK2_GPS_CHECK 31 2 +1 1 EK2_GPS_TYPE 0 2 +1 1 EK2_GSCL_P_NSE 0.000500000023748726 9 +1 1 EK2_GYRO_P_NSE 0.029999999329447746 9 +1 1 EK2_HGT_DELAY 60 4 +1 1 EK2_HGT_I_GATE 500 4 +1 1 EK2_HRT_FILT 2.000000000000000000 9 +1 1 EK2_IMU_MASK 3 2 +1 1 EK2_LOG_MASK 1 2 +1 1 EK2_MAGB_P_NSE 0.000099999997473788 9 +1 1 EK2_MAGE_P_NSE 0.001000000047497451 9 +1 1 EK2_MAG_CAL 3 2 +1 1 EK2_MAG_EF_LIM 50 4 +1 1 EK2_MAG_I_GATE 300 4 +1 1 EK2_MAG_MASK 0 2 +1 1 EK2_MAG_M_NSE 0.050000000745058060 9 +1 1 EK2_MAX_FLOW 2.500000000000000000 9 +1 1 EK2_NOAID_M_NSE 10.000000000000000000 9 +1 1 EK2_OGN_HGT_MASK 0 2 +1 1 EK2_POSNE_M_NSE 1.000000000000000000 9 +1 1 EK2_POS_I_GATE 500 4 +1 1 EK2_RNG_I_GATE 500 4 +1 1 EK2_RNG_M_NSE 0.500000000000000000 9 +1 1 EK2_RNG_USE_HGT -1 2 +1 1 EK2_RNG_USE_SPD 2.000000000000000000 9 +1 1 EK2_TAU_OUTPUT 25 2 +1 1 EK2_TERR_GRAD 0.100000001490116119 9 +1 1 EK2_VELD_M_NSE 0.699999988079071045 9 +1 1 EK2_VELNE_M_NSE 0.500000000000000000 9 +1 1 EK2_VEL_I_GATE 500 4 +1 1 EK2_WIND_PSCALE 0.500000000000000000 9 +1 1 EK2_WIND_P_NSE 0.100000001490116119 9 +1 1 EK2_YAW_I_GATE 300 4 +1 1 EK2_YAW_M_NSE 0.500000000000000000 9 +1 1 EK3_ENABLE 0 2 1 1 ESC_CALIBRATION 0 2 -1 1 ESC_TLM_MAV_OFS 0 2 1 1 FENCE_ACTION 1 2 1 1 FENCE_ALT_MAX 100.000000000000000000 9 -1 1 FENCE_ALT_MIN -10.000000000000000000 9 -1 1 FENCE_AUTOENABLE 0 2 1 1 FENCE_ENABLE 0 2 1 1 FENCE_MARGIN 2.000000000000000000 9 -1 1 FENCE_OPTIONS 0 4 -1 1 FENCE_RADIUS 150.000000000000000000 9 +1 1 FENCE_RADIUS 300.000000000000000000 9 1 1 FENCE_TOTAL 0 2 1 1 FENCE_TYPE 7 2 -1 1 FFT_ENABLE 0 2 1 1 FHLD_BRAKE_RATE 8 2 1 1 FHLD_FILT_HZ 5.000000000000000000 9 1 1 FHLD_FLOW_MAX 0.600000023841857910 9 @@ -374,130 +304,118 @@ 1 1 FHLD_XY_I 0.300000011920928955 9 1 1 FHLD_XY_IMAX 3000.000000000000000000 9 1 1 FHLD_XY_P 0.200000002980232239 9 -1 1 FILT1_TYPE 0 2 -1 1 FILT2_TYPE 0 2 -1 1 FILT3_TYPE 0 2 -1 1 FILT4_TYPE 0 2 -1 1 FILT5_TYPE 0 2 -1 1 FILT6_TYPE 0 2 -1 1 FILT7_TYPE 0 2 -1 1 FILT8_TYPE 0 2 -1 1 FLIGHT_OPTIONS 0 6 +1 1 FLOW_ADDR 0 2 +1 1 FLOW_FXSCALER 0 4 +1 1 FLOW_FYSCALER 0 4 +1 1 FLOW_ORIENT_YAW 0 4 +1 1 FLOW_POS_X 0.000000000000000000 9 +1 1 FLOW_POS_Y 0.000000000000000000 9 +1 1 FLOW_POS_Z 0.000000000000000000 9 1 1 FLOW_TYPE 0 2 -1 1 FLTMODE1 7 2 -1 1 FLTMODE2 9 2 -1 1 FLTMODE3 6 2 -1 1 FLTMODE4 3 2 -1 1 FLTMODE5 5 2 +1 1 FLTMODE1 0 2 +1 1 FLTMODE2 0 2 +1 1 FLTMODE3 0 2 +1 1 FLTMODE4 0 2 +1 1 FLTMODE5 0 2 1 1 FLTMODE6 0 2 1 1 FLTMODE_CH 5 2 -1 1 FLTMODE_GCSBLOCK 0 6 1 1 FOLL_ENABLE 0 2 -1 1 FORMAT_VERSION 120 4 -1 1 FRAME_CLASS 1 2 -1 1 FRAME_TYPE 0 2 -1 1 FRSKY_DNLINK1_ID 20 2 -1 1 FRSKY_DNLINK2_ID 7 2 -1 1 FRSKY_DNLINK_ID 27 2 -1 1 FRSKY_OPTIONS 0 2 -1 1 FRSKY_UPLINK_ID 13 2 +1 1 FRAME_CLASS 0 2 +1 1 FRAME_TYPE 1 2 1 1 FS_CRASH_CHECK 1 2 -1 1 FS_DR_ENABLE 2 2 -1 1 FS_DR_TIMEOUT 30 4 1 1 FS_EKF_ACTION 1 2 -1 1 FS_EKF_FILT 5.000000000000000000 9 1 1 FS_EKF_THRESH 0.800000011920928955 9 1 1 FS_GCS_ENABLE 0 2 -1 1 FS_GCS_TIMEOUT 5.000000000000000000 9 -1 1 FS_OPTIONS 16 6 +1 1 FS_OPTIONS 0 6 1 1 FS_THR_ENABLE 1 2 1 1 FS_THR_VALUE 975 4 1 1 FS_VIBE_ENABLE 1 2 1 1 GCS_PID_MASK 0 4 -1 1 GEN_TYPE 0 2 +1 1 GND_ABS_PRESS 85515.125000000000000000 9 +1 1 GND_ABS_PRESS2 85533.453125000000000000 9 +1 1 GND_ABS_PRESS3 0.000000000000000000 9 +1 1 GND_ALT_OFFSET 0.000000000000000000 9 1 1 GND_EFFECT_COMP 1 2 -1 1 GPS1_CAN_NODEID 0 6 -1 1 GPS1_CAN_OVRIDE 0 6 -1 1 GPS1_COM_PORT 1 2 -1 1 GPS1_DELAY_MS 0 4 -1 1 GPS1_GNSS_MODE 0 2 -1 1 GPS1_MB_TYPE 0 2 -1 1 GPS1_POS_X 0.000000000000000000 9 -1 1 GPS1_POS_Y 0.000000000000000000 9 -1 1 GPS1_POS_Z 0.000000000000000000 9 -1 1 GPS1_RATE_MS 200 4 -1 1 GPS1_TYPE 1 2 -1 1 GPS2_TYPE 0 2 +1 1 GND_EXT_BUS -1 2 +1 1 GND_FLTR_RNG 0 2 +1 1 GND_PRIMARY 0 2 +1 1 GND_PROBE_EXT 0 6 +1 1 GND_TEMP 0.000000000000000000 9 1 1 GPS_AUTO_CONFIG 1 2 1 1 GPS_AUTO_SWITCH 1 2 1 1 GPS_BLEND_MASK 5 2 +1 1 GPS_BLEND_TC 10.000000000000000000 9 +1 1 GPS_DELAY_MS 0 4 +1 1 GPS_DELAY_MS2 0 4 1 1 GPS_DRV_OPTIONS 0 4 +1 1 GPS_GNSS_MODE 0 2 +1 1 GPS_GNSS_MODE2 0 2 1 1 GPS_HDOP_GOOD 140 4 1 1 GPS_INJECT_TO 127 2 +1 1 GPS_MIN_DGPS 100 2 1 1 GPS_MIN_ELEV -100 2 1 1 GPS_NAVFILTER 8 2 -1 1 GPS_PRIMARY 0 2 +1 1 GPS_POS1_X 0.000000000000000000 9 +1 1 GPS_POS1_Y 0.000000000000000000 9 +1 1 GPS_POS1_Z 0.000000000000000000 9 +1 1 GPS_POS2_X 0.000000000000000000 9 +1 1 GPS_POS2_Y 0.000000000000000000 9 +1 1 GPS_POS2_Z 0.000000000000000000 9 +1 1 GPS_RATE_MS 200 4 +1 1 GPS_RATE_MS2 200 4 1 1 GPS_RAW_DATA 0 2 1 1 GPS_SAVE_CFG 2 2 1 1 GPS_SBAS_MODE 2 2 1 1 GPS_SBP_LOGMASK -256 4 +1 1 GPS_TYPE 1 2 +1 1 GPS_TYPE2 0 2 1 1 GRIP_ENABLE 0 2 -1 1 GUID_OPTIONS 0 6 -1 1 GUID_TIMEOUT 3.000000000000000000 9 -1 1 INITIAL_MODE 0 2 -1 1 INS_ACC1_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2OFFS_X 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Y 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Z 0.001000000047497451 9 -1 1 INS_ACC2SCAL_X 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Y 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Z 1.001000046730041504 9 -1 1 INS_ACC2_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2_ID 2753036 6 +1 1 INS_ACC2OFFS_X 0.000000000000000000 9 +1 1 INS_ACC2OFFS_Y 0.000000000000000000 9 +1 1 INS_ACC2OFFS_Z 0.000000000000000000 9 +1 1 INS_ACC2SCAL_X 1.000000000000000000 9 +1 1 INS_ACC2SCAL_Y 1.000000000000000000 9 +1 1 INS_ACC2SCAL_Z 1.000000000000000000 9 +1 1 INS_ACC2_ID 2883874 6 1 1 INS_ACC3OFFS_X 0.000000000000000000 9 1 1 INS_ACC3OFFS_Y 0.000000000000000000 9 1 1 INS_ACC3OFFS_Z 0.000000000000000000 9 1 1 INS_ACC3SCAL_X 1.000000000000000000 9 1 1 INS_ACC3SCAL_Y 1.000000000000000000 9 1 1 INS_ACC3SCAL_Z 1.000000000000000000 9 -1 1 INS_ACC3_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC3_ID 0 6 +1 1 INS_ACC3_ID 3015690 6 1 1 INS_ACCEL_FILTER 20 4 -1 1 INS_ACCOFFS_X 0.001000000047497451 9 -1 1 INS_ACCOFFS_Y 0.001000000047497451 9 -1 1 INS_ACCOFFS_Z 0.001000000047497451 9 -1 1 INS_ACCSCAL_X 1.001000046730041504 9 -1 1 INS_ACCSCAL_Y 1.001000046730041504 9 -1 1 INS_ACCSCAL_Z 1.001000046730041504 9 +1 1 INS_ACCOFFS_X 0.000000000000000000 9 +1 1 INS_ACCOFFS_Y 0.000000000000000000 9 +1 1 INS_ACCOFFS_Z 0.000000000000000000 9 +1 1 INS_ACCSCAL_X 1.000000000000000000 9 +1 1 INS_ACCSCAL_Y 1.000000000000000000 9 +1 1 INS_ACCSCAL_Z 1.000000000000000000 9 1 1 INS_ACC_BODYFIX 2 2 -1 1 INS_ACC_ID 2753028 6 +1 1 INS_ACC_ID 3081250 6 1 1 INS_ENABLE_MASK 127 2 -1 1 INS_FAST_SAMPLE 1 2 -1 1 INS_GYR1_CALTEMP 25.018352508544921875 9 -1 1 INS_GYR2OFFS_X -0.000007097929483280 9 -1 1 INS_GYR2OFFS_Y 0.000048979549319483 9 -1 1 INS_GYR2OFFS_Z 0.000025458299205638 9 -1 1 INS_GYR2_CALTEMP 25.018352508544921875 9 -1 1 INS_GYR2_ID 2752780 6 -1 1 INS_GYR3OFFS_X 0.000000000000000000 9 -1 1 INS_GYR3OFFS_Y 0.000000000000000000 9 -1 1 INS_GYR3OFFS_Z 0.000000000000000000 9 -1 1 INS_GYR3_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR3_ID 0 6 -1 1 INS_GYROFFS_X 0.000021559313609032 9 -1 1 INS_GYROFFS_Y 0.000020211216906318 9 -1 1 INS_GYROFFS_Z 0.000024633345674374 9 +1 1 INS_FAST_SAMPLE 7 2 +1 1 INS_GYR2OFFS_X -0.001730036456137896 9 +1 1 INS_GYR2OFFS_Y -0.013567177578806877 9 +1 1 INS_GYR2OFFS_Z 0.016979435458779335 9 +1 1 INS_GYR2_ID 2883874 6 +1 1 INS_GYR3OFFS_X -0.012815136462450027 9 +1 1 INS_GYR3OFFS_Y -0.001344033051282167 9 +1 1 INS_GYR3OFFS_Z 0.011734368279576302 9 +1 1 INS_GYR3_ID 3015690 6 +1 1 INS_GYROFFS_X 0.021145243197679520 9 +1 1 INS_GYROFFS_Y -0.022721750661730766 9 +1 1 INS_GYROFFS_Z 0.019216123968362808 9 1 1 INS_GYRO_FILTER 20 4 -1 1 INS_GYRO_RATE 0 2 1 1 INS_GYR_CAL 1 2 -1 1 INS_GYR_ID 2752772 6 -1 1 INS_HNTC2_ENABLE 0 2 +1 1 INS_GYR_ID 3081250 6 1 1 INS_HNTCH_ENABLE 0 2 1 1 INS_LOG_BAT_CNT 1024 4 1 1 INS_LOG_BAT_LGCT 32 4 1 1 INS_LOG_BAT_LGIN 20 2 1 1 INS_LOG_BAT_MASK 0 2 1 1 INS_LOG_BAT_OPT 0 2 +1 1 INS_NOTCH_ENABLE 0 2 1 1 INS_POS1_X 0.000000000000000000 9 1 1 INS_POS1_Y 0.000000000000000000 9 1 1 INS_POS1_Z 0.000000000000000000 9 @@ -507,35 +425,30 @@ 1 1 INS_POS3_X 0.000000000000000000 9 1 1 INS_POS3_Y 0.000000000000000000 9 1 1 INS_POS3_Z 0.000000000000000000 9 -1 1 INS_RAW_LOG_OPT 0 4 1 1 INS_STILL_THRESH 2.500000000000000000 9 -1 1 INS_TCAL1_ENABLE 0 2 -1 1 INS_TCAL2_ENABLE 0 2 -1 1 INS_TCAL3_ENABLE 0 2 -1 1 INS_TCAL_OPTIONS 0 6 1 1 INS_TRIM_OPTION 1 2 1 1 INS_USE 1 2 1 1 INS_USE2 1 2 1 1 INS_USE3 1 2 -1 1 KDE_NPOLE 14 2 1 1 LAND_ALT_LOW 1000 4 1 1 LAND_REPOSITION 1 2 1 1 LAND_SPEED 50 4 1 1 LAND_SPEED_HIGH 0 4 -1 1 LGR_ENABLE 0 2 +1 1 LGR_DEPLOY_ALT 0 4 +1 1 LGR_DEPLOY_PIN -1 2 +1 1 LGR_DEPLOY_POL 0 2 +1 1 LGR_OPTIONS 3 4 +1 1 LGR_RETRACT_ALT 0 4 +1 1 LGR_STARTUP 0 2 +1 1 LGR_WOW_PIN -1 2 +1 1 LGR_WOW_POL 0 2 1 1 LOG_BACKEND_TYPE 1 2 -1 1 LOG_BITMASK 180222 6 -1 1 LOG_BLK_RATEMAX 0.000000000000000000 9 -1 1 LOG_DARM_RATEMAX 0.000000000000000000 9 +1 1 LOG_BITMASK 176126 6 1 1 LOG_DISARMED 0 2 -1 1 LOG_FILE_BUFSIZE 200 4 +1 1 LOG_FILE_BUFSIZE 50 2 1 1 LOG_FILE_DSRMROT 0 2 -1 1 LOG_FILE_MB_FREE 500 4 -1 1 LOG_FILE_RATEMAX 0.000000000000000000 9 1 1 LOG_FILE_TIMEOUT 5 4 1 1 LOG_MAV_BUFSIZE 8 2 -1 1 LOG_MAV_RATEMAX 0.000000000000000000 9 -1 1 LOG_MAX_FILES 500 4 1 1 LOG_REPLAY 0 2 1 1 LOIT_ACC_MAX 500.000000000000000000 9 1 1 LOIT_ANG_MAX 0.000000000000000000 9 @@ -546,18 +459,38 @@ 1 1 MIS_OPTIONS 0 4 1 1 MIS_RESTART 0 2 1 1 MIS_TOTAL 0 4 -1 1 MNT1_TYPE 0 2 -1 1 MNT2_TYPE 0 2 +1 1 MNT_ANGMAX_PAN 4500 4 +1 1 MNT_ANGMAX_ROL 4500 4 +1 1 MNT_ANGMAX_TIL 4500 4 +1 1 MNT_ANGMIN_PAN -4500 4 +1 1 MNT_ANGMIN_ROL -4500 4 +1 1 MNT_ANGMIN_TIL -4500 4 +1 1 MNT_DEFLT_MODE 3 2 +1 1 MNT_JSTICK_SPD 0 2 +1 1 MNT_LEAD_PTCH 0.000000000000000000 9 +1 1 MNT_LEAD_RLL 0.000000000000000000 9 +1 1 MNT_NEUTRAL_X 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Y 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Z 0.000000000000000000 9 +1 1 MNT_RC_IN_PAN 0 2 +1 1 MNT_RC_IN_ROLL 0 2 +1 1 MNT_RC_IN_TILT 0 2 +1 1 MNT_RETRACT_X 0.000000000000000000 9 +1 1 MNT_RETRACT_Y 0.000000000000000000 9 +1 1 MNT_RETRACT_Z 0.000000000000000000 9 +1 1 MNT_STAB_PAN 0 2 +1 1 MNT_STAB_ROLL 0 2 +1 1 MNT_STAB_TILT 0 2 +1 1 MNT_TYPE 0 2 1 1 MOT_BAT_CURR_MAX 0.000000000000000000 9 1 1 MOT_BAT_CURR_TC 5.000000000000000000 9 1 1 MOT_BAT_IDX 0 2 -1 1 MOT_BAT_VOLT_MAX 12.800000190734863281 9 -1 1 MOT_BAT_VOLT_MIN 9.600000381469726563 9 +1 1 MOT_BAT_VOLT_MAX 0.000000000000000000 9 +1 1 MOT_BAT_VOLT_MIN 0.000000000000000000 9 1 1 MOT_BOOST_SCALE 0.000000000000000000 9 1 1 MOT_HOVER_LEARN 2 2 -1 1 MOT_OPTIONS 0 4 -1 1 MOT_PWM_MAX 2000 4 -1 1 MOT_PWM_MIN 1000 4 +1 1 MOT_PWM_MAX 0 4 +1 1 MOT_PWM_MIN 0 4 1 1 MOT_PWM_TYPE 0 2 1 1 MOT_SAFE_DISARM 0 2 1 1 MOT_SAFE_TIME 1.000000000000000000 9 @@ -567,30 +500,20 @@ 1 1 MOT_SPIN_MAX 0.949999988079071045 9 1 1 MOT_SPIN_MIN 0.150000005960464478 9 1 1 MOT_SPOOL_TIME 0.500000000000000000 9 -1 1 MOT_SPOOL_TIM_DN 0.000000000000000000 9 1 1 MOT_THST_EXPO 0.649999976158142090 9 -1 1 MOT_THST_HOVER 0.389999985694885254 9 +1 1 MOT_THST_HOVER 0.349999994039535522 9 1 1 MOT_YAW_HEADROOM 200 4 -1 1 MSP_OPTIONS 0 2 -1 1 MSP_OSD_NCELLS 0 2 -1 1 NET_ENABLE 0 2 -1 1 NET_P1_TYPE 0 2 -1 1 NET_P2_TYPE 0 2 -1 1 NET_P3_TYPE 0 2 -1 1 NET_P4_TYPE 0 2 -1 1 NMEA_MSG_EN 3 4 -1 1 NMEA_RATE_MS 100 4 +1 1 NTF_BUZZ_ENABLE 1 2 1 1 NTF_BUZZ_ON_LVL 1 2 -1 1 NTF_BUZZ_PIN -1 2 -1 1 NTF_BUZZ_TYPES 5 2 +1 1 NTF_BUZZ_PIN 0 2 1 1 NTF_BUZZ_VOLUME 100 2 1 1 NTF_DISPLAY_TYPE 0 2 1 1 NTF_LED_BRIGHT 3 2 1 1 NTF_LED_LEN 1 2 1 1 NTF_LED_OVERRIDE 0 2 -1 1 NTF_LED_TYPES 123079 6 +1 1 NTF_LED_TYPES 199 6 +1 1 NTF_OREO_THEME 0 2 1 1 OA_TYPE 0 2 -1 1 OSD_TYPE 0 2 1 1 PHLD_BRAKE_ANGLE 3000 4 1 1 PHLD_BRAKE_RATE 8 4 1 1 PILOT_ACCEL_Z 250 4 @@ -599,54 +522,40 @@ 1 1 PILOT_THR_BHV 0 4 1 1 PILOT_THR_FILT 0.000000000000000000 9 1 1 PILOT_TKOFF_ALT 0.000000000000000000 9 -1 1 PILOT_Y_EXPO 0.000000000000000000 9 -1 1 PILOT_Y_RATE 202.500000000000000000 9 -1 1 PILOT_Y_RATE_TC 0.000000000000000000 9 -1 1 PLDP_DELAY 0.000000000000000000 9 -1 1 PLDP_RNG_MAX 0.000000000000000000 9 -1 1 PLDP_SPEED_DN 0.000000000000000000 9 -1 1 PLDP_THRESH 0.899999976158142090 9 1 1 PLND_ENABLED 0 2 -1 1 PRX1_TYPE 0 2 -1 1 PRX2_TYPE 0 2 -1 1 PRX3_TYPE 0 2 -1 1 PRX4_TYPE 0 2 -1 1 PRX5_TYPE 0 2 -1 1 PRX_ALT_MIN 1.000000000000000000 9 -1 1 PRX_FILT 0.250000000000000000 9 -1 1 PRX_IGN_GND 0 2 -1 1 PRX_LOG_RAW 0 2 +1 1 PRX_IGN_ANG1 0 4 +1 1 PRX_IGN_ANG2 0 4 +1 1 PRX_IGN_ANG3 0 4 +1 1 PRX_IGN_ANG4 0 4 +1 1 PRX_IGN_ANG5 0 4 +1 1 PRX_IGN_ANG6 0 4 +1 1 PRX_IGN_WID1 0 2 +1 1 PRX_IGN_WID2 0 2 +1 1 PRX_IGN_WID3 0 2 +1 1 PRX_IGN_WID4 0 2 +1 1 PRX_IGN_WID5 0 2 +1 1 PRX_IGN_WID6 0 2 +1 1 PRX_ORIENT 0 2 +1 1 PRX_TYPE 0 2 +1 1 PRX_YAW_CORR 0 4 1 1 PSC_ACCZ_D 0.000000000000000000 9 -1 1 PSC_ACCZ_D_FF 0.000000000000000000 9 1 1 PSC_ACCZ_FF 0.000000000000000000 9 1 1 PSC_ACCZ_FLTD 0.000000000000000000 9 1 1 PSC_ACCZ_FLTE 20.000000000000000000 9 1 1 PSC_ACCZ_FLTT 0.000000000000000000 9 1 1 PSC_ACCZ_I 1.000000000000000000 9 1 1 PSC_ACCZ_IMAX 800.000000000000000000 9 -1 1 PSC_ACCZ_NEF 0 2 -1 1 PSC_ACCZ_NTF 0 2 1 1 PSC_ACCZ_P 0.500000000000000000 9 -1 1 PSC_ACCZ_PDMX 0.000000000000000000 9 -1 1 PSC_ACCZ_SMAX 0.000000000000000000 9 +1 1 PSC_ACC_XY_FILT 2.000000000000000000 9 1 1 PSC_ANGLE_MAX 0.000000000000000000 9 -1 1 PSC_JERK_XY 5.000000000000000000 9 -1 1 PSC_JERK_Z 5.000000000000000000 9 1 1 PSC_POSXY_P 1.000000000000000000 9 1 1 PSC_POSZ_P 1.000000000000000000 9 -1 1 PSC_VELXY_D 0.250000000000000000 9 -1 1 PSC_VELXY_FF 0.000000000000000000 9 -1 1 PSC_VELXY_FLTD 5.000000000000000000 9 -1 1 PSC_VELXY_FLTE 5.000000000000000000 9 +1 1 PSC_VELXY_D 0.500000000000000000 9 +1 1 PSC_VELXY_D_FILT 5.000000000000000000 9 +1 1 PSC_VELXY_FILT 5.000000000000000000 9 1 1 PSC_VELXY_I 1.000000000000000000 9 1 1 PSC_VELXY_IMAX 1000.000000000000000000 9 1 1 PSC_VELXY_P 2.000000000000000000 9 -1 1 PSC_VELZ_D 0.000000000000000000 9 -1 1 PSC_VELZ_FF 0.000000000000000000 9 -1 1 PSC_VELZ_FLTD 5.000000000000000000 9 -1 1 PSC_VELZ_FLTE 5.000000000000000000 9 -1 1 PSC_VELZ_I 0.000000000000000000 9 -1 1 PSC_VELZ_IMAX 1000.000000000000000000 9 1 1 PSC_VELZ_P 5.000000000000000000 9 1 1 RALLY_INCL_HOME 1 2 1 1 RALLY_LIMIT_KM 0.300000011920928955 9 @@ -694,50 +603,50 @@ 1 1 RC16_REVERSED 0 2 1 1 RC16_TRIM 1500 4 1 1 RC1_DZ 20 4 -1 1 RC1_MAX 2000 4 -1 1 RC1_MIN 1000 4 +1 1 RC1_MAX 1900 4 +1 1 RC1_MIN 1100 4 1 1 RC1_OPTION 0 4 1 1 RC1_REVERSED 0 2 1 1 RC1_TRIM 1500 4 1 1 RC2_DZ 20 4 -1 1 RC2_MAX 2000 4 -1 1 RC2_MIN 1000 4 +1 1 RC2_MAX 1900 4 +1 1 RC2_MIN 1100 4 1 1 RC2_OPTION 0 4 1 1 RC2_REVERSED 0 2 1 1 RC2_TRIM 1500 4 1 1 RC3_DZ 30 4 -1 1 RC3_MAX 2000 4 -1 1 RC3_MIN 1000 4 +1 1 RC3_MAX 1900 4 +1 1 RC3_MIN 1100 4 1 1 RC3_OPTION 0 4 1 1 RC3_REVERSED 0 2 1 1 RC3_TRIM 1500 4 1 1 RC4_DZ 20 4 -1 1 RC4_MAX 2000 4 -1 1 RC4_MIN 1000 4 +1 1 RC4_MAX 1900 4 +1 1 RC4_MIN 1100 4 1 1 RC4_OPTION 0 4 1 1 RC4_REVERSED 0 2 1 1 RC4_TRIM 1500 4 1 1 RC5_DZ 0 4 -1 1 RC5_MAX 2000 4 -1 1 RC5_MIN 1000 4 +1 1 RC5_MAX 1900 4 +1 1 RC5_MIN 1100 4 1 1 RC5_OPTION 0 4 1 1 RC5_REVERSED 0 2 1 1 RC5_TRIM 1500 4 1 1 RC6_DZ 0 4 -1 1 RC6_MAX 2000 4 -1 1 RC6_MIN 1000 4 +1 1 RC6_MAX 1900 4 +1 1 RC6_MIN 1100 4 1 1 RC6_OPTION 0 4 1 1 RC6_REVERSED 0 2 1 1 RC6_TRIM 1500 4 1 1 RC7_DZ 0 4 -1 1 RC7_MAX 2000 4 -1 1 RC7_MIN 1000 4 -1 1 RC7_OPTION 7 4 +1 1 RC7_MAX 1900 4 +1 1 RC7_MIN 1100 4 +1 1 RC7_OPTION 0 4 1 1 RC7_REVERSED 0 2 1 1 RC7_TRIM 1500 4 1 1 RC8_DZ 0 4 -1 1 RC8_MAX 2000 4 -1 1 RC8_MIN 1000 4 +1 1 RC8_MAX 1900 4 +1 1 RC8_MIN 1100 4 1 1 RC8_OPTION 0 4 1 1 RC8_REVERSED 0 2 1 1 RC8_TRIM 1500 4 @@ -751,69 +660,216 @@ 1 1 RCMAP_ROLL 1 2 1 1 RCMAP_THROTTLE 3 2 1 1 RCMAP_YAW 4 2 -1 1 RC_FS_TIMEOUT 1.000000000000000000 9 -1 1 RC_OPTIONS 32 6 +1 1 RC_OPTIONS 0 6 1 1 RC_OVERRIDE_TIME 3.000000000000000000 9 -1 1 RC_PROTOCOLS 1 6 1 1 RC_SPEED 490 4 -1 1 RELAY1_DEFAULT 0 2 -1 1 RELAY1_FUNCTION 1 2 -1 1 RELAY1_INVERTED 0 2 -1 1 RELAY1_PIN 13 4 -1 1 RELAY2_FUNCTION 0 2 -1 1 RELAY3_FUNCTION 0 2 -1 1 RELAY4_FUNCTION 0 2 -1 1 RELAY5_FUNCTION 0 2 -1 1 RELAY6_FUNCTION 0 2 +1 1 RELAY_DEFAULT 0 2 +1 1 RELAY_PIN 54 2 +1 1 RELAY_PIN2 55 2 +1 1 RELAY_PIN3 -1 2 +1 1 RELAY_PIN4 -1 2 +1 1 RELAY_PIN5 -1 2 +1 1 RELAY_PIN6 -1 2 +1 1 RNGFND1_ADDR 0 2 +1 1 RNGFND1_FUNCTION 0 2 +1 1 RNGFND1_GNDCLEAR 10 2 +1 1 RNGFND1_MAX_CM 700 4 +1 1 RNGFND1_MIN_CM 20 4 +1 1 RNGFND1_OFFSET 0.000000000000000000 9 +1 1 RNGFND1_ORIENT 25 2 +1 1 RNGFND1_PIN -1 2 +1 1 RNGFND1_POS_X 0.000000000000000000 9 +1 1 RNGFND1_POS_Y 0.000000000000000000 9 +1 1 RNGFND1_POS_Z 0.000000000000000000 9 +1 1 RNGFND1_PWRRNG 0 4 +1 1 RNGFND1_RMETRIC 1 2 +1 1 RNGFND1_SCALING 3.000000000000000000 9 +1 1 RNGFND1_STOP_PIN -1 2 1 1 RNGFND1_TYPE 0 2 +1 1 RNGFND2_ADDR 0 2 +1 1 RNGFND2_FUNCTION 0 2 +1 1 RNGFND2_GNDCLEAR 10 2 +1 1 RNGFND2_MAX_CM 700 4 +1 1 RNGFND2_MIN_CM 20 4 +1 1 RNGFND2_OFFSET 0.000000000000000000 9 +1 1 RNGFND2_ORIENT 25 2 +1 1 RNGFND2_PIN -1 2 +1 1 RNGFND2_POS_X 0.000000000000000000 9 +1 1 RNGFND2_POS_Y 0.000000000000000000 9 +1 1 RNGFND2_POS_Z 0.000000000000000000 9 +1 1 RNGFND2_PWRRNG 0 4 +1 1 RNGFND2_RMETRIC 1 2 +1 1 RNGFND2_SCALING 3.000000000000000000 9 +1 1 RNGFND2_STOP_PIN -1 2 1 1 RNGFND2_TYPE 0 2 +1 1 RNGFND3_ADDR 0 2 +1 1 RNGFND3_FUNCTION 0 2 +1 1 RNGFND3_GNDCLEAR 10 2 +1 1 RNGFND3_MAX_CM 700 4 +1 1 RNGFND3_MIN_CM 20 4 +1 1 RNGFND3_OFFSET 0.000000000000000000 9 +1 1 RNGFND3_ORIENT 25 2 +1 1 RNGFND3_PIN -1 2 +1 1 RNGFND3_POS_X 0.000000000000000000 9 +1 1 RNGFND3_POS_Y 0.000000000000000000 9 +1 1 RNGFND3_POS_Z 0.000000000000000000 9 +1 1 RNGFND3_PWRRNG 0 4 +1 1 RNGFND3_RMETRIC 1 2 +1 1 RNGFND3_SCALING 3.000000000000000000 9 +1 1 RNGFND3_STOP_PIN -1 2 1 1 RNGFND3_TYPE 0 2 +1 1 RNGFND4_ADDR 0 2 +1 1 RNGFND4_FUNCTION 0 2 +1 1 RNGFND4_GNDCLEAR 10 2 +1 1 RNGFND4_MAX_CM 700 4 +1 1 RNGFND4_MIN_CM 20 4 +1 1 RNGFND4_OFFSET 0.000000000000000000 9 +1 1 RNGFND4_ORIENT 25 2 +1 1 RNGFND4_PIN -1 2 +1 1 RNGFND4_POS_X 0.000000000000000000 9 +1 1 RNGFND4_POS_Y 0.000000000000000000 9 +1 1 RNGFND4_POS_Z 0.000000000000000000 9 +1 1 RNGFND4_PWRRNG 0 4 +1 1 RNGFND4_RMETRIC 1 2 +1 1 RNGFND4_SCALING 3.000000000000000000 9 +1 1 RNGFND4_STOP_PIN -1 2 1 1 RNGFND4_TYPE 0 2 +1 1 RNGFND5_ADDR 0 2 +1 1 RNGFND5_FUNCTION 0 2 +1 1 RNGFND5_GNDCLEAR 10 2 +1 1 RNGFND5_MAX_CM 700 4 +1 1 RNGFND5_MIN_CM 20 4 +1 1 RNGFND5_OFFSET 0.000000000000000000 9 +1 1 RNGFND5_ORIENT 25 2 +1 1 RNGFND5_PIN -1 2 +1 1 RNGFND5_POS_X 0.000000000000000000 9 +1 1 RNGFND5_POS_Y 0.000000000000000000 9 +1 1 RNGFND5_POS_Z 0.000000000000000000 9 +1 1 RNGFND5_PWRRNG 0 4 +1 1 RNGFND5_RMETRIC 1 2 +1 1 RNGFND5_SCALING 3.000000000000000000 9 +1 1 RNGFND5_STOP_PIN -1 2 1 1 RNGFND5_TYPE 0 2 +1 1 RNGFND6_ADDR 0 2 +1 1 RNGFND6_FUNCTION 0 2 +1 1 RNGFND6_GNDCLEAR 10 2 +1 1 RNGFND6_MAX_CM 700 4 +1 1 RNGFND6_MIN_CM 20 4 +1 1 RNGFND6_OFFSET 0.000000000000000000 9 +1 1 RNGFND6_ORIENT 25 2 +1 1 RNGFND6_PIN -1 2 +1 1 RNGFND6_POS_X 0.000000000000000000 9 +1 1 RNGFND6_POS_Y 0.000000000000000000 9 +1 1 RNGFND6_POS_Z 0.000000000000000000 9 +1 1 RNGFND6_PWRRNG 0 4 +1 1 RNGFND6_RMETRIC 1 2 +1 1 RNGFND6_SCALING 3.000000000000000000 9 +1 1 RNGFND6_STOP_PIN -1 2 1 1 RNGFND6_TYPE 0 2 +1 1 RNGFND7_ADDR 0 2 +1 1 RNGFND7_FUNCTION 0 2 +1 1 RNGFND7_GNDCLEAR 10 2 +1 1 RNGFND7_MAX_CM 700 4 +1 1 RNGFND7_MIN_CM 20 4 +1 1 RNGFND7_OFFSET 0.000000000000000000 9 +1 1 RNGFND7_ORIENT 25 2 +1 1 RNGFND7_PIN -1 2 +1 1 RNGFND7_POS_X 0.000000000000000000 9 +1 1 RNGFND7_POS_Y 0.000000000000000000 9 +1 1 RNGFND7_POS_Z 0.000000000000000000 9 +1 1 RNGFND7_PWRRNG 0 4 +1 1 RNGFND7_RMETRIC 1 2 +1 1 RNGFND7_SCALING 3.000000000000000000 9 +1 1 RNGFND7_STOP_PIN -1 2 1 1 RNGFND7_TYPE 0 2 +1 1 RNGFND8_ADDR 0 2 +1 1 RNGFND8_FUNCTION 0 2 +1 1 RNGFND8_GNDCLEAR 10 2 +1 1 RNGFND8_MAX_CM 700 4 +1 1 RNGFND8_MIN_CM 20 4 +1 1 RNGFND8_OFFSET 0.000000000000000000 9 +1 1 RNGFND8_ORIENT 25 2 +1 1 RNGFND8_PIN -1 2 +1 1 RNGFND8_POS_X 0.000000000000000000 9 +1 1 RNGFND8_POS_Y 0.000000000000000000 9 +1 1 RNGFND8_POS_Z 0.000000000000000000 9 +1 1 RNGFND8_PWRRNG 0 4 +1 1 RNGFND8_RMETRIC 1 2 +1 1 RNGFND8_SCALING 3.000000000000000000 9 +1 1 RNGFND8_STOP_PIN -1 2 1 1 RNGFND8_TYPE 0 2 +1 1 RNGFND9_ADDR 0 2 +1 1 RNGFND9_FUNCTION 0 2 +1 1 RNGFND9_GNDCLEAR 10 2 +1 1 RNGFND9_MAX_CM 700 4 +1 1 RNGFND9_MIN_CM 20 4 +1 1 RNGFND9_OFFSET 0.000000000000000000 9 +1 1 RNGFND9_ORIENT 25 2 +1 1 RNGFND9_PIN -1 2 +1 1 RNGFND9_POS_X 0.000000000000000000 9 +1 1 RNGFND9_POS_Y 0.000000000000000000 9 +1 1 RNGFND9_POS_Z 0.000000000000000000 9 +1 1 RNGFND9_PWRRNG 0 4 +1 1 RNGFND9_RMETRIC 1 2 +1 1 RNGFND9_SCALING 3.000000000000000000 9 +1 1 RNGFND9_STOP_PIN -1 2 1 1 RNGFND9_TYPE 0 2 +1 1 RNGFNDA_ADDR 0 2 +1 1 RNGFNDA_FUNCTION 0 2 +1 1 RNGFNDA_GNDCLEAR 10 2 +1 1 RNGFNDA_MAX_CM 700 4 +1 1 RNGFNDA_MIN_CM 20 4 +1 1 RNGFNDA_OFFSET 0.000000000000000000 9 +1 1 RNGFNDA_ORIENT 25 2 +1 1 RNGFNDA_PIN -1 2 +1 1 RNGFNDA_POS_X 0.000000000000000000 9 +1 1 RNGFNDA_POS_Y 0.000000000000000000 9 +1 1 RNGFNDA_POS_Z 0.000000000000000000 9 +1 1 RNGFNDA_PWRRNG 0 4 +1 1 RNGFNDA_RMETRIC 1 2 +1 1 RNGFNDA_SCALING 3.000000000000000000 9 +1 1 RNGFNDA_STOP_PIN -1 2 1 1 RNGFNDA_TYPE 0 2 -1 1 RNGFND_FILT 0.500000000000000000 9 -1 1 RPM1_TYPE 0 2 +1 1 RNGFND_GAIN 0.800000011920928955 9 +1 1 RPM2_PIN -1 2 +1 1 RPM2_SCALING 1.000000000000000000 9 1 1 RPM2_TYPE 0 2 +1 1 RPM_MAX 100000.000000000000000000 9 +1 1 RPM_MIN 10.000000000000000000 9 +1 1 RPM_MIN_QUAL 0.500000000000000000 9 +1 1 RPM_PIN 54 2 +1 1 RPM_SCALING 1.000000000000000000 9 +1 1 RPM_TYPE 0 2 1 1 RSSI_TYPE 0 2 -1 1 RTL_ALT 1500 6 +1 1 RTL_ALT 1500 4 1 1 RTL_ALT_FINAL 0 4 -1 1 RTL_ALT_TYPE 0 2 1 1 RTL_CLIMB_MIN 0 4 1 1 RTL_CONE_SLOPE 3.000000000000000000 9 1 1 RTL_LOIT_TIME 5000 6 -1 1 RTL_OPTIONS 0 6 1 1 RTL_SPEED 0 4 1 1 SCHED_DEBUG 0 2 1 1 SCHED_LOOP_RATE 400 4 -1 1 SCHED_OPTIONS 0 2 1 1 SCR_ENABLE 0 2 1 1 SERIAL0_BAUD 115 6 1 1 SERIAL0_PROTOCOL 2 2 1 1 SERIAL1_BAUD 57 6 1 1 SERIAL1_OPTIONS 0 4 -1 1 SERIAL1_PROTOCOL 2 2 +1 1 SERIAL1_PROTOCOL 1 2 1 1 SERIAL2_BAUD 57 6 1 1 SERIAL2_OPTIONS 0 4 -1 1 SERIAL2_PROTOCOL 2 2 -1 1 SERIAL3_BAUD 230 6 +1 1 SERIAL2_PROTOCOL 1 2 +1 1 SERIAL3_BAUD 38 6 1 1 SERIAL3_OPTIONS 0 4 1 1 SERIAL3_PROTOCOL 5 2 -1 1 SERIAL4_BAUD 230 6 +1 1 SERIAL4_BAUD 38 6 1 1 SERIAL4_OPTIONS 0 4 1 1 SERIAL4_PROTOCOL 5 2 1 1 SERIAL5_BAUD 57 6 1 1 SERIAL5_OPTIONS 0 4 -1 1 SERIAL5_PROTOCOL -1 2 -1 1 SERIAL6_BAUD 57 6 +1 1 SERIAL5_PROTOCOL 1 2 +1 1 SERIAL6_BAUD 115200 6 1 1 SERIAL6_OPTIONS 0 4 -1 1 SERIAL6_PROTOCOL -1 2 -1 1 SERIAL7_BAUD 57 6 -1 1 SERIAL7_OPTIONS 0 4 -1 1 SERIAL7_PROTOCOL -1 2 +1 1 SERIAL6_PROTOCOL 22 2 1 1 SERIAL_PASS1 0 2 1 1 SERIAL_PASS2 -1 2 1 1 SERIAL_PASSTIMO 15 2 @@ -852,22 +908,22 @@ 1 1 SERVO16_MIN 1100 4 1 1 SERVO16_REVERSED 0 2 1 1 SERVO16_TRIM 1500 4 -1 1 SERVO1_FUNCTION 33 4 +1 1 SERVO1_FUNCTION 0 4 1 1 SERVO1_MAX 1900 4 1 1 SERVO1_MIN 1100 4 1 1 SERVO1_REVERSED 0 2 1 1 SERVO1_TRIM 1500 4 -1 1 SERVO2_FUNCTION 34 4 +1 1 SERVO2_FUNCTION 0 4 1 1 SERVO2_MAX 1900 4 1 1 SERVO2_MIN 1100 4 1 1 SERVO2_REVERSED 0 2 1 1 SERVO2_TRIM 1500 4 -1 1 SERVO3_FUNCTION 35 4 +1 1 SERVO3_FUNCTION 0 4 1 1 SERVO3_MAX 1900 4 1 1 SERVO3_MIN 1100 4 1 1 SERVO3_REVERSED 0 2 1 1 SERVO3_TRIM 1500 4 -1 1 SERVO4_FUNCTION 36 4 +1 1 SERVO4_FUNCTION 0 4 1 1 SERVO4_MAX 1900 4 1 1 SERVO4_MIN 1100 4 1 1 SERVO4_REVERSED 0 2 @@ -897,397 +953,34 @@ 1 1 SERVO9_MIN 1100 4 1 1 SERVO9_REVERSED 0 2 1 1 SERVO9_TRIM 1500 4 -1 1 SERVO_32_ENABLE 0 2 -1 1 SERVO_DSHOT_ESC 0 2 -1 1 SERVO_DSHOT_RATE 0 2 -1 1 SERVO_FTW_MASK 0 6 -1 1 SERVO_FTW_POLES 14 2 -1 1 SERVO_FTW_RVMASK 0 6 -1 1 SERVO_GPIO_MASK 0 6 +1 1 SERVO_BLH_AUTO 0 2 +1 1 SERVO_BLH_DEBUG 0 2 +1 1 SERVO_BLH_MASK 0 6 +1 1 SERVO_BLH_OTYPE 0 2 +1 1 SERVO_BLH_POLES 14 2 +1 1 SERVO_BLH_PORT 0 2 +1 1 SERVO_BLH_REMASK 0 6 +1 1 SERVO_BLH_TEST 0 2 +1 1 SERVO_BLH_TMOUT 0 4 +1 1 SERVO_BLH_TRATE 10 4 1 1 SERVO_RATE 50 4 -1 1 SERVO_RC_FS_MSK 0 6 1 1 SERVO_ROB_POSMAX 4095 6 1 1 SERVO_ROB_POSMIN 0 6 1 1 SERVO_SBUS_RATE 50 4 1 1 SERVO_VOLZ_MASK 0 6 -1 1 SERVO_VOLZ_RANGE 200 4 1 1 SID_AXIS 0 2 1 1 SIMPLE 0 2 -1 1 SIM_ACC1_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC1_RND 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC2_RND 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC3_RND 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACCEL1_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL2_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL3_FAIL 0.000000000000000000 9 -1 1 SIM_ACC_FAIL_MSK 0 2 -1 1 SIM_ACC_FILE_RW 0 2 -1 1 SIM_ACC_TRIM_X 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Y 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Z 0.000000000000000000 9 -1 1 SIM_ADSB_ALT 1000.000000000000000000 9 -1 1 SIM_ADSB_COUNT -1 4 -1 1 SIM_ADSB_RADIUS 10000.000000000000000000 9 -1 1 SIM_ADSB_TX 0 2 -1 1 SIM_ADSB_TYPES 1 2 -1 1 SIM_ARSPD2_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD2_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD2_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD2_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD2_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD2_RND 2.000000000000000000 9 -1 1 SIM_ARSPD2_SIGN 0 2 -1 1 SIM_ARSPD_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD_RND 2.000000000000000000 9 -1 1 SIM_ARSPD_SIGN 0 2 -1 1 SIM_BAR2_DELAY 0 4 -1 1 SIM_BAR2_DISABLE 0 2 -1 1 SIM_BAR2_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR2_FREEZE 0 2 -1 1 SIM_BAR2_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR2_RND 0.200000002980232239 9 -1 1 SIM_BAR2_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_UP 0.000000000000000000 9 -1 1 SIM_BAR3_DELAY 0 4 -1 1 SIM_BAR3_DISABLE 0 2 -1 1 SIM_BAR3_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR3_FREEZE 0 2 -1 1 SIM_BAR3_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR3_RND 0.200000002980232239 9 -1 1 SIM_BAR3_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_UP 0.000000000000000000 9 -1 1 SIM_BARO_COUNT 2 2 -1 1 SIM_BARO_DELAY 0 4 -1 1 SIM_BARO_DISABLE 0 2 -1 1 SIM_BARO_DRIFT 0.000000000000000000 9 -1 1 SIM_BARO_FREEZE 0 2 -1 1 SIM_BARO_GLITCH 0.000000000000000000 9 -1 1 SIM_BARO_RND 0.000000000000000000 9 -1 1 SIM_BARO_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BARO_WCF_DN 0.000000000000000000 9 -1 1 SIM_BARO_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BARO_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_UP 0.000000000000000000 9 -1 1 SIM_BATT_CAP_AH 0.000000000000000000 9 -1 1 SIM_BATT_VOLTAGE 12.600000381469726563 9 -1 1 SIM_BAUDLIMIT_EN 0 2 -1 1 SIM_CAN_SRV_MSK 0 6 -1 1 SIM_CAN_TYPE1 1 2 -1 1 SIM_CAN_TYPE2 1 2 -1 1 SIM_CLAMP_CH 0 2 -1 1 SIM_DRIFT_SPEED 0.050000000745058060 9 -1 1 SIM_DRIFT_TIME 5.000000000000000000 9 -1 1 SIM_EFI_TYPE 0 2 -1 1 SIM_ENGINE_FAIL 0 2 -1 1 SIM_ENGINE_MUL 1.000000000000000000 9 -1 1 SIM_ESC_ARM_RPM 0.000000000000000000 9 -1 1 SIM_ESC_TELEM 1 2 -1 1 SIM_FLOAT_EXCEPT 1 2 -1 1 SIM_FLOW_DELAY 0 2 -1 1 SIM_FLOW_ENABLE 0 2 -1 1 SIM_FLOW_POS_X 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Y 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Z 0.000000000000000000 9 -1 1 SIM_FLOW_RATE 10 4 -1 1 SIM_FLOW_RND 0.050000000745058060 9 -1 1 SIM_FTOWESC_ENA 0 2 -1 1 SIM_FTOWESC_POW 4095 6 -1 1 SIM_GND_BEHAV -1 2 -1 1 SIM_GPS2_ACC 0.300000011920928955 9 -1 1 SIM_GPS2_ALT_OFS 0 4 -1 1 SIM_GPS2_BYTELOS 0.000000000000000000 9 -1 1 SIM_GPS2_DISABLE 1 2 -1 1 SIM_GPS2_DRFTALT 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_X 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Y 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Z 0.000000000000000000 9 -1 1 SIM_GPS2_HDG 0 2 -1 1 SIM_GPS2_HZ 5 2 -1 1 SIM_GPS2_JAM 0 2 -1 1 SIM_GPS2_LAG_MS 100 4 -1 1 SIM_GPS2_LCKTIME 0 4 -1 1 SIM_GPS2_NOISE 0.000000000000000000 9 -1 1 SIM_GPS2_NUMSATS 10 2 -1 1 SIM_GPS2_POS_X 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS2_TYPE 1 2 -1 1 SIM_GPS2_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Z 0.000000000000000000 9 -1 1 SIM_GPS_ACC 0.300000011920928955 9 -1 1 SIM_GPS_ALT_OFS 0 4 -1 1 SIM_GPS_BYTELOSS 0.000000000000000000 9 -1 1 SIM_GPS_DISABLE 0 2 -1 1 SIM_GPS_DRIFTALT 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_X 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Y 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Z 0.000000000000000000 9 -1 1 SIM_GPS_HDG 0 2 -1 1 SIM_GPS_HZ 5 2 -1 1 SIM_GPS_JAM 0 2 -1 1 SIM_GPS_LAG_MS 100 4 -1 1 SIM_GPS_LOCKTIME 0 4 -1 1 SIM_GPS_LOG_NUM 0 4 -1 1 SIM_GPS_NOISE 0.000000000000000000 9 -1 1 SIM_GPS_NUMSATS 10 2 -1 1 SIM_GPS_POS_X 0.000000000000000000 9 -1 1 SIM_GPS_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS_TYPE 1 2 -1 1 SIM_GPS_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Z 0.000000000000000000 9 -1 1 SIM_GRPE_ENABLE 0 2 -1 1 SIM_GRPE_PIN -1 2 -1 1 SIM_GRPS_ENABLE 0 2 -1 1 SIM_GRPS_GRAB 2000 4 -1 1 SIM_GRPS_PIN -1 2 -1 1 SIM_GRPS_RELEASE 1000 4 -1 1 SIM_GRPS_REVERSE 0 2 -1 1 SIM_GYR1_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR1_RND 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR2_RND 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR3_RND 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR_FAIL_MSK 0 2 -1 1 SIM_GYR_FILE_RW 0 2 -1 1 SIM_IE24_ENABLE 0 2 -1 1 SIM_IE24_ERROR 0 6 -1 1 SIM_IE24_STATE -1 2 -1 1 SIM_IMUT1_ENABLE 0 2 -1 1 SIM_IMUT2_ENABLE 0 2 -1 1 SIM_IMUT3_ENABLE 0 2 -1 1 SIM_IMUT_END 45.000000000000000000 9 -1 1 SIM_IMUT_FIXED 0.000000000000000000 9 -1 1 SIM_IMUT_START 25.000000000000000000 9 -1 1 SIM_IMUT_TCONST 300.000000000000000000 9 -1 1 SIM_IMU_COUNT 2 2 -1 1 SIM_IMU_ORIENT 0 2 -1 1 SIM_IMU_POS_X 0.000000000000000000 9 -1 1 SIM_IMU_POS_Y 0.000000000000000000 9 -1 1 SIM_IMU_POS_Z 0.000000000000000000 9 -1 1 SIM_INIT_ALT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LAT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LON_OFS 0.000000000000000000 9 -1 1 SIM_INS_THR_MIN 0.100000001490116119 9 -1 1 SIM_JSON_MASTER 0 2 -1 1 SIM_LED_LAYOUT 0 2 -1 1 SIM_LOOP_DELAY 0 6 -1 1 SIM_MAG1_DEVID 97539 6 -1 1 SIM_MAG1_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG1_FAIL 0 2 -1 1 SIM_MAG1_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG1_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG1_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG1_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG1_ORIENT 0 2 -1 1 SIM_MAG1_SCALING 1.000000000000000000 9 -1 1 SIM_MAG2_DEVID 131874 6 -1 1 SIM_MAG2_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG2_FAIL 0 2 -1 1 SIM_MAG2_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG2_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG2_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG2_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG2_ORIENT 0 2 -1 1 SIM_MAG2_SCALING 1.000000000000000000 9 -1 1 SIM_MAG3_DEVID 263178 6 -1 1 SIM_MAG3_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG3_FAIL 0 2 -1 1 SIM_MAG3_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG3_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG3_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG3_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG3_ORIENT 0 2 -1 1 SIM_MAG3_SCALING 1.000000000000000000 9 -1 1 SIM_MAG4_DEVID 97283 6 -1 1 SIM_MAG5_DEVID 97795 6 -1 1 SIM_MAG6_DEVID 98051 6 -1 1 SIM_MAG7_DEVID 0 6 -1 1 SIM_MAG8_DEVID 0 6 -1 1 SIM_MAG_ALY_HGT 1.000000000000000000 9 -1 1 SIM_MAG_ALY_X 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Y 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Z 0.000000000000000000 9 -1 1 SIM_MAG_DELAY 0 4 -1 1 SIM_MAG_MOT_X 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Y 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Z 0.000000000000000000 9 -1 1 SIM_MAG_RND 0.000000000000000000 9 -1 1 SIM_MAG_SAVE_IDS 1 2 -1 1 SIM_ODOM_ENABLE 0 2 -1 1 SIM_OH_MASK 0 6 -1 1 SIM_OH_RELAY_MSK -1 4 -1 1 SIM_OPOS_ALT 584.000000000000000000 9 -1 1 SIM_OPOS_HDG 353.000000000000000000 9 -1 1 SIM_OPOS_LAT -35.363262176513671875 9 -1 1 SIM_OPOS_LNG 149.165237426757812500 9 -1 1 SIM_PARA_ENABLE 0 2 -1 1 SIM_PARA_PIN -1 2 -1 1 SIM_PIN_MASK 0 4 -1 1 SIM_PLD_ALT_LMT 15.000000000000000000 9 -1 1 SIM_PLD_DIST_LMT 10.000000000000000000 9 -1 1 SIM_PLD_ENABLE 0 2 -1 1 SIM_PLD_HEIGHT 0.000000000000000000 9 -1 1 SIM_PLD_LAT 0.000000000000000000 9 -1 1 SIM_PLD_LON 0.000000000000000000 9 -1 1 SIM_PLD_OPTIONS 0 2 -1 1 SIM_PLD_ORIENT 24 2 -1 1 SIM_PLD_RATE 100 6 -1 1 SIM_PLD_SHIP 0 2 -1 1 SIM_PLD_TYPE 0 2 -1 1 SIM_PLD_YAW 0 4 -1 1 SIM_RATE_HZ 1200 4 -1 1 SIM_RC_CHANCOUNT 16 2 -1 1 SIM_RC_FAIL 0 2 -1 1 SIM_RICH_CTRL -1 2 -1 1 SIM_RICH_ENABLE 0 2 -1 1 SIM_SERVO_DELAY 0.000000000000000000 9 -1 1 SIM_SERVO_FILTER 0.000000000000000000 9 -1 1 SIM_SERVO_SPEED 0.140000000596046448 9 -1 1 SIM_SHIP_DSIZE 10.000000000000000000 9 -1 1 SIM_SHIP_ENABLE 0 2 -1 1 SIM_SHIP_OFS_X 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Y 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Z 0.000000000000000000 9 -1 1 SIM_SHIP_PSIZE 1000.000000000000000000 9 -1 1 SIM_SHIP_SPEED 3.000000000000000000 9 -1 1 SIM_SHIP_SYSID 17 2 -1 1 SIM_SHOVE_TIME 0 6 -1 1 SIM_SHOVE_X 0.000000000000000000 9 -1 1 SIM_SHOVE_Y 0.000000000000000000 9 -1 1 SIM_SHOVE_Z 0.000000000000000000 9 -1 1 SIM_SLUP_ENABLE 0 2 -1 1 SIM_SONAR_GLITCH 0.000000000000000000 9 -1 1 SIM_SONAR_POS_X 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Y 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Z 0.000000000000000000 9 -1 1 SIM_SONAR_RND 0.000000000000000000 9 -1 1 SIM_SONAR_ROT 25 2 -1 1 SIM_SONAR_SCALE 12.121199607849121094 9 -1 1 SIM_SPEEDUP 1.000000000000000000 9 -1 1 SIM_SPR_ENABLE 0 2 -1 1 SIM_SPR_PUMP -1 2 -1 1 SIM_SPR_SPIN -1 2 -1 1 SIM_TA_ENABLE 1 2 -1 1 SIM_TEMP_BFACTOR 0.000000000000000000 9 -1 1 SIM_TEMP_BRD_OFF 20.000000000000000000 9 -1 1 SIM_TEMP_START 25.000000000000000000 9 -1 1 SIM_TEMP_TCONST 30.000000000000000000 9 -1 1 SIM_TERRAIN 1 2 -1 1 SIM_THML_SCENARI 0 2 -1 1 SIM_TIDE_DIR 0.000000000000000000 9 -1 1 SIM_TIDE_SPEED 0.000000000000000000 9 -1 1 SIM_TIME_JITTER 0 4 -1 1 SIM_TWIST_TIME 0 6 -1 1 SIM_TWIST_X 0.000000000000000000 9 -1 1 SIM_TWIST_Y 0.000000000000000000 9 -1 1 SIM_TWIST_Z 0.000000000000000000 9 -1 1 SIM_UART_LOSS 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_X 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Y 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Z 0.000000000000000000 9 -1 1 SIM_VIB_MOT_HMNC 1 4 -1 1 SIM_VIB_MOT_MASK 0 6 -1 1 SIM_VIB_MOT_MAX 0.000000000000000000 9 -1 1 SIM_VIB_MOT_MULT 1.000000000000000000 9 -1 1 SIM_VICON_FAIL 0 2 -1 1 SIM_VICON_GLIT_X 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Y 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Z 0.000000000000000000 9 -1 1 SIM_VICON_POS_X 0.000000000000000000 9 -1 1 SIM_VICON_POS_Y 0.000000000000000000 9 -1 1 SIM_VICON_POS_Z 0.000000000000000000 9 -1 1 SIM_VICON_TMASK 3 2 -1 1 SIM_VICON_VGLI_X 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Y 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Z 0.000000000000000000 9 -1 1 SIM_VICON_YAW 0 4 -1 1 SIM_VICON_YAWERR 0 4 -1 1 SIM_WAVE_AMP 0.500000000000000000 9 -1 1 SIM_WAVE_DIR 0.000000000000000000 9 -1 1 SIM_WAVE_ENABLE 0 2 -1 1 SIM_WAVE_LENGTH 10.000000000000000000 9 -1 1 SIM_WAVE_SPEED 0.500000000000000000 9 -1 1 SIM_WIND_DIR 180.000000000000000000 9 -1 1 SIM_WIND_DIR_Z 0.000000000000000000 9 -1 1 SIM_WIND_SPD 0.000000000000000000 9 -1 1 SIM_WIND_T 0 2 -1 1 SIM_WIND_TC 5.000000000000000000 9 -1 1 SIM_WIND_TURB 0.000000000000000000 9 -1 1 SIM_WIND_T_ALT 60.000000000000000000 9 -1 1 SIM_WIND_T_COEF 0.009999999776482582 9 -1 1 SIM_WOW_PIN -1 2 1 1 SPRAY_ENABLE 0 2 -1 1 SR0_ADSB 4 4 -1 1 SR0_EXTRA1 4 4 -1 1 SR0_EXTRA2 4 4 -1 1 SR0_EXTRA3 4 4 -1 1 SR0_EXT_STAT 4 4 +1 1 SR0_ADSB 0 4 +1 1 SR0_EXTRA1 0 4 +1 1 SR0_EXTRA2 0 4 +1 1 SR0_EXTRA3 0 4 +1 1 SR0_EXT_STAT 0 4 1 1 SR0_PARAMS 0 4 -1 1 SR0_POSITION 4 4 -1 1 SR0_RAW_CTRL 4 4 -1 1 SR0_RAW_SENS 4 4 -1 1 SR0_RC_CHAN 4 4 +1 1 SR0_POSITION 0 4 +1 1 SR0_RAW_CTRL 0 4 +1 1 SR0_RAW_SENS 0 4 +1 1 SR0_RC_CHAN 0 4 1 1 SR1_ADSB 0 4 1 1 SR1_EXTRA1 0 4 1 1 SR1_EXTRA2 0 4 @@ -1318,88 +1011,41 @@ 1 1 SR3_RAW_CTRL 0 4 1 1 SR3_RAW_SENS 0 4 1 1 SR3_RC_CHAN 0 4 -1 1 SR4_ADSB 0 4 -1 1 SR4_EXTRA1 0 4 -1 1 SR4_EXTRA2 0 4 -1 1 SR4_EXTRA3 0 4 -1 1 SR4_EXT_STAT 0 4 -1 1 SR4_PARAMS 0 4 -1 1 SR4_POSITION 0 4 -1 1 SR4_RAW_CTRL 0 4 -1 1 SR4_RAW_SENS 0 4 -1 1 SR4_RC_CHAN 0 4 -1 1 SR5_ADSB 0 4 -1 1 SR5_EXTRA1 0 4 -1 1 SR5_EXTRA2 0 4 -1 1 SR5_EXTRA3 0 4 -1 1 SR5_EXT_STAT 0 4 -1 1 SR5_PARAMS 0 4 -1 1 SR5_POSITION 0 4 -1 1 SR5_RAW_CTRL 0 4 -1 1 SR5_RAW_SENS 0 4 -1 1 SR5_RC_CHAN 0 4 -1 1 SR6_ADSB 0 4 -1 1 SR6_EXTRA1 0 4 -1 1 SR6_EXTRA2 0 4 -1 1 SR6_EXTRA3 0 4 -1 1 SR6_EXT_STAT 0 4 -1 1 SR6_PARAMS 0 4 -1 1 SR6_POSITION 0 4 -1 1 SR6_RAW_CTRL 0 4 -1 1 SR6_RAW_SENS 0 4 -1 1 SR6_RC_CHAN 0 4 1 1 SRTL_ACCURACY 2.000000000000000000 9 -1 1 SRTL_OPTIONS 0 6 1 1 SRTL_POINTS 300 4 1 1 STAT_BOOTCNT 1 4 1 1 STAT_FLTTIME 0 6 -1 1 STAT_RESET 1 6 -1 1 STAT_RUNTIME 30 6 +1 1 STAT_RESET 0 6 +1 1 STAT_RUNTIME 0 6 1 1 SUPER_SIMPLE 0 2 -1 1 SURFTRAK_MODE 1 2 -1 1 SURFTRAK_TC 1.000000000000000000 9 1 1 SYSID_ENFORCE 0 2 1 1 SYSID_MYGCS 255 4 +1 1 SYSID_SW_MREV 120 4 1 1 SYSID_THISMAV 1 4 1 1 TCAL_ENABLED 0 2 1 1 TELEM_DELAY 0 2 -1 1 TEMP1_TYPE 0 2 -1 1 TEMP2_TYPE 0 2 -1 1 TEMP3_TYPE 0 2 -1 1 TEMP_LOG 0 2 -1 1 TERRAIN_CACHE_SZ 12 4 1 1 TERRAIN_ENABLE 1 2 -1 1 TERRAIN_MARGIN 0.050000000745058060 9 -1 1 TERRAIN_OFS_MAX 30.000000000000000000 9 +1 1 TERRAIN_FOLLOW 0 2 1 1 TERRAIN_OPTIONS 0 4 1 1 TERRAIN_SPACING 100 4 -1 1 THROW_ALT_MAX 0 4 -1 1 THROW_ALT_MIN 0 4 1 1 THROW_MOT_START 0 2 1 1 THROW_NEXTMODE 18 2 1 1 THROW_TYPE 0 2 1 1 THR_DZ 100 4 -1 1 TKOFF_RPM_MAX 0 4 -1 1 TKOFF_RPM_MIN 0 4 -1 1 TKOFF_SLEW_TIME 2.000000000000000000 9 -1 1 TKOFF_THR_MAX 0.899999976158142090 9 1 1 TUNE 0 2 1 1 TUNE_MAX 0.000000000000000000 9 1 1 TUNE_MIN 0.000000000000000000 9 +1 1 VISO_ORIENT 0 2 +1 1 VISO_POS_X 0.000000000000000000 9 +1 1 VISO_POS_Y 0.000000000000000000 9 +1 1 VISO_POS_Z 0.000000000000000000 9 1 1 VISO_TYPE 0 2 -1 1 VTX_ENABLE 0 2 -1 1 WINCH_TYPE 0 2 -1 1 WPNAV_ACCEL 250.000000000000000000 9 -1 1 WPNAV_ACCEL_C 0.000000000000000000 9 +1 1 WPNAV_ACCEL 100.000000000000000000 9 1 1 WPNAV_ACCEL_Z 100.000000000000000000 9 -1 1 WPNAV_JERK 1.000000000000000000 9 1 1 WPNAV_RADIUS 200.000000000000000000 9 1 1 WPNAV_RFND_USE 1 2 -1 1 WPNAV_SPEED 1000.000000000000000000 9 +1 1 WPNAV_SPEED 500.000000000000000000 9 1 1 WPNAV_SPEED_DN 150.000000000000000000 9 1 1 WPNAV_SPEED_UP 250.000000000000000000 9 -1 1 WPNAV_TER_MARGIN 10.000000000000000000 9 1 1 WP_NAVALT_MIN 0.000000000000000000 9 1 1 WP_YAW_BEHAVIOR 2 2 -1 1 WVANE_ENABLE 0 2 -1 1 ZIGZ_AUTO_ENABLE 0 2 diff --git a/src/FirmwarePlugin/APM/Plane.OfflineEditing.params b/src/FirmwarePlugin/APM/Plane.OfflineEditing.params index 35172d40ce1e..c80f3634b44f 100644 --- a/src/FirmwarePlugin/APM/Plane.OfflineEditing.params +++ b/src/FirmwarePlugin/APM/Plane.OfflineEditing.params @@ -1,82 +1,58 @@ # Onboard parameters for Vehicle 1 # # Stack: ArduPilot -# Vehicle: Fixed wing aircraft -# Version: 4.6.2 -# Git Revision: 1ebd4d99 +# Vehicle: Fixed Wing +# Version: 4.0.9 +# Git Revision: b319b27 # # Vehicle-Id Component-Id Name Value Type 1 1 ACRO_LOCKING 1 2 -1 1 ACRO_PITCH_RATE 180 4 +1 1 ACRO_PITCH_RATE 90 4 1 1 ACRO_ROLL_RATE 180 4 -1 1 ACRO_YAW_RATE 0 4 -1 1 ADSB_TYPE 0 2 +1 1 ADSB_ENABLE 0 2 1 1 AFS_ENABLE 0 2 1 1 AHRS_COMP_BETA 0.100000001490116119 9 -1 1 AHRS_EKF_TYPE 3 2 +1 1 AHRS_CUSTOM_PIT 0.000000000000000000 9 +1 1 AHRS_CUSTOM_ROLL 0.000000000000000000 9 +1 1 AHRS_CUSTOM_YAW 0.000000000000000000 9 +1 1 AHRS_EKF_TYPE 2 2 1 1 AHRS_GPS_GAIN 1.000000000000000000 9 1 1 AHRS_GPS_MINSATS 6 2 1 1 AHRS_GPS_USE 1 2 -1 1 AHRS_OPTIONS 0 4 -1 1 AHRS_ORIENTATION 0 2 +1 1 AHRS_ORIENTATION 4 2 1 1 AHRS_RP_P 0.200000002980232239 9 -1 1 AHRS_TRIM_X 0.000000000000000000 9 -1 1 AHRS_TRIM_Y 0.000000000000000000 9 +1 1 AHRS_TRIM_X -0.005102053750306368 9 +1 1 AHRS_TRIM_Y -0.040495343506336212 9 1 1 AHRS_TRIM_Z 0.000000000000000000 9 -1 1 AHRS_WIND_MAX 0 2 +1 1 AHRS_WIND_MAX 12 2 1 1 AHRS_YAW_P 0.200000002980232239 9 -1 1 AIRSPEED_CRUISE 22.000000000000000000 9 -1 1 AIRSPEED_MAX 30 4 -1 1 AIRSPEED_MIN 10 4 -1 1 AIRSPEED_STALL 0.000000000000000000 9 +1 1 ALT_CTRL_ALG 0 2 +1 1 ALT_HOLD_FBWCM 1500 4 +1 1 ALT_HOLD_RTL 8000 6 1 1 ALT_OFFSET 0 4 1 1 ARMING_ACCTHRESH 0.750000000000000000 9 -1 1 ARMING_CHECK 1 6 -1 1 ARMING_MAGTHRESH 100 4 +1 1 ARMING_CHECK 0 6 1 1 ARMING_MIS_ITEMS 0 6 -1 1 ARMING_OPTIONS 0 6 1 1 ARMING_REQUIRE 1 2 -1 1 ARMING_RUDDER 1 2 +1 1 ARMING_RUDDER 2 2 1 1 ARSPD2_TYPE 0 2 1 1 ARSPD_AUTOCAL 0 2 1 1 ARSPD_BUS 1 2 -1 1 ARSPD_DEVID 0 6 -1 1 ARSPD_OFFSET 2014.252685546875000000 9 -1 1 ARSPD_OFF_PCNT 0 2 -1 1 ARSPD_OPTIONS 11 6 -1 1 ARSPD_PIN 1 2 +1 1 ARSPD_FBW_MAX 22 4 +1 1 ARSPD_FBW_MIN 14 4 +1 1 ARSPD_OFFSET 0.000000000000000000 9 +1 1 ARSPD_OPTIONS 0 6 +1 1 ARSPD_PIN 15 2 1 1 ARSPD_PRIMARY 0 2 1 1 ARSPD_PSI_RANGE 1.000000000000000000 9 -1 1 ARSPD_RATIO 2.000000000000000000 9 +1 1 ARSPD_RATIO 1.993600010871887207 9 1 1 ARSPD_SKIP_CAL 0 2 -1 1 ARSPD_TUBE_ORDR 2 2 -1 1 ARSPD_TYPE 2 2 -1 1 ARSPD_USE 1 2 -1 1 ARSPD_WIND_GATE 5.000000000000000000 9 -1 1 ARSPD_WIND_MAX 0.000000000000000000 9 -1 1 ARSPD_WIND_WARN 0.000000000000000000 9 -1 1 AUTOTUNE_AXES 7 2 +1 1 ARSPD_TUBE_ORDER 2 2 +1 1 ARSPD_TYPE 1 2 +1 1 ARSPD_USE 0 2 1 1 AUTOTUNE_LEVEL 6 2 -1 1 AUTOTUNE_OPTIONS 0 6 +1 1 AUTO_FBW_STEER 0 2 1 1 AVD_ENABLE 0 2 -1 1 BARO1_DEVID 65540 6 -1 1 BARO1_GND_PRESS 101325.851562500000000000 9 -1 1 BARO1_WCF_ENABLE 0 2 -1 1 BARO2_DEVID 65796 6 -1 1 BARO2_GND_PRESS 101327.273437500000000000 9 -1 1 BARO2_WCF_ENABLE 0 2 -1 1 BARO3_DEVID 0 6 -1 1 BARO3_GND_PRESS 0.000000000000000000 9 -1 1 BARO3_WCF_ENABLE 0 2 -1 1 BARO_ALTERR_MAX 2000.000000000000000000 9 -1 1 BARO_ALT_OFFSET 0.000000000000000000 9 -1 1 BARO_EXT_BUS -1 2 -1 1 BARO_FIELD_ELV 0.000000000000000000 9 -1 1 BARO_FLTR_RNG 0 2 -1 1 BARO_GND_TEMP 0.000000000000000000 9 -1 1 BARO_OPTIONS 0 4 -1 1 BARO_PRIMARY 0 2 -1 1 BARO_PROBE_EXT 0 6 1 1 BATT2_MONITOR 0 2 1 1 BATT3_MONITOR 0 2 1 1 BATT4_MONITOR 0 2 @@ -86,13 +62,14 @@ 1 1 BATT8_MONITOR 0 2 1 1 BATT9_MONITOR 0 2 1 1 BATT_AMP_OFFSET 0.000000000000000000 9 -1 1 BATT_AMP_PERVLT 17.000000000000000000 9 +1 1 BATT_AMP_PERVLT 39.876998901367187500 9 1 1 BATT_ARM_MAH 0 6 -1 1 BATT_ARM_VOLT 0.000000000000000000 9 -1 1 BATT_CAPACITY 3300 6 +1 1 BATT_ARM_VOLT 14.500000000000000000 9 +1 1 BATT_BUS 0 2 +1 1 BATT_CAPACITY 16800 6 1 1 BATT_CRT_MAH 0.000000000000000000 9 1 1 BATT_CRT_VOLT 0.000000000000000000 9 -1 1 BATT_CURR_PIN 12 2 +1 1 BATT_CURR_PIN 15 2 1 1 BATT_FS_CRT_ACT 0 2 1 1 BATT_FS_LOW_ACT 0 2 1 1 BATT_FS_VOLTSRC 0 2 @@ -100,121 +77,142 @@ 1 1 BATT_LOW_TIMER 10 2 1 1 BATT_LOW_VOLT 0.000000000000000000 9 1 1 BATT_MONITOR 4 2 -1 1 BATT_OPTIONS 0 6 1 1 BATT_SERIAL_NUM -1 6 -1 1 BATT_VLT_OFFSET 0.000000000000000000 9 -1 1 BATT_VOLT_MULT 10.100000381469726563 9 -1 1 BATT_VOLT_PIN 13 2 +1 1 BATT_VOLT_MULT 12.020000457763671875 9 +1 1 BATT_VOLT_PIN 14 2 1 1 BATT_WATT_MAX 0 4 +1 1 BRD_ALT_CONFIG 0 2 1 1 BRD_BOOT_DELAY 0 4 -1 1 BRD_OPTIONS 0 6 +1 1 BRD_IMUHEAT_I 0.070000000298023224 9 +1 1 BRD_IMUHEAT_IMAX 70.000000000000000000 9 +1 1 BRD_IMUHEAT_P 50.000000000000000000 9 +1 1 BRD_IMU_TARGTEMP 45 2 +1 1 BRD_IO_ENABLE 1 2 +1 1 BRD_OPTIONS 1 6 +1 1 BRD_PWM_COUNT 4 2 +1 1 BRD_PWM_VOLT_SEL 0 2 1 1 BRD_RTC_TYPES 1 2 1 1 BRD_RTC_TZ_MIN 0 4 -1 1 BRD_SAFETYOPTION 3 4 -1 1 BRD_SAFETY_DEFLT 0 2 +1 1 BRD_SAFETYENABLE 0 2 +1 1 BRD_SAFETYOPTION 0 4 1 1 BRD_SAFETY_MASK 0 6 -1 1 BRD_SD_FENCE 0 4 -1 1 BRD_SD_MISSION 0 4 -1 1 BRD_SERIAL_NUM 0 6 +1 1 BRD_SBUS_OUT 0 2 +1 1 BRD_SD_SLOWDOWN 0 2 +1 1 BRD_SER1_RTSCTS 0 2 +1 1 BRD_SER2_RTSCTS 0 2 +1 1 BRD_SERIAL_NUM 0 4 +1 1 BRD_TYPE 3 2 1 1 BRD_VBUS_MIN 4.300000190734863281 9 1 1 BRD_VSERVO_MIN 0.000000000000000000 9 1 1 BTN_ENABLE 0 2 -1 1 CAM1_TYPE 0 2 -1 1 CAM2_TYPE 0 2 1 1 CAM_AUTO_ONLY 0 2 +1 1 CAM_DURATION 10 2 +1 1 CAM_FEEDBACK_PIN -1 2 +1 1 CAM_FEEDBACK_POL 1 2 1 1 CAM_MAX_ROLL 0 4 -1 1 CAM_RC_TYPE 0 2 +1 1 CAM_MIN_INTERVAL 0 4 +1 1 CAM_RELAY_ON 1 2 +1 1 CAM_SERVO_OFF 1100 4 +1 1 CAM_SERVO_ON 1300 4 +1 1 CAM_TRIGG_DIST 0.000000000000000000 9 +1 1 CAM_TRIGG_TYPE 0 2 +1 1 CAM_TYPE 0 2 1 1 CAN_D1_PROTOCOL 1 2 -1 1 CAN_D1_PROTOCOL2 0 2 +1 1 CAN_D1_UC_ESC_BM 0 6 +1 1 CAN_D1_UC_NODE 10 2 +1 1 CAN_D1_UC_SRV_BM 0 6 +1 1 CAN_D1_UC_SRV_RT 50 4 1 1 CAN_D2_PROTOCOL 1 2 -1 1 CAN_D2_PROTOCOL2 0 2 -1 1 CAN_LOGLEVEL 0 2 -1 1 CAN_P1_DRIVER 0 2 -1 1 CAN_P2_DRIVER 0 2 +1 1 CAN_P1_BITRATE 1000000 6 +1 1 CAN_P1_DRIVER 1 2 +1 1 CAN_P2_BITRATE 1000000 6 +1 1 CAN_P2_DRIVER 1 2 +1 1 CAN_SLCAN_CPORT 0 2 +1 1 CAN_SLCAN_SERNUM -1 2 +1 1 CAN_SLCAN_TIMOUT 0 4 +1 1 CHUTE_CHAN 0 2 1 1 CHUTE_ENABLED 0 2 1 1 COMPASS_AUTODEC 1 2 -1 1 COMPASS_AUTO_ROT 2 2 -1 1 COMPASS_CAL_FIT 16.000000000000000000 9 -1 1 COMPASS_DEC 0.223357215523719788 9 -1 1 COMPASS_DEV_ID 97539 6 -1 1 COMPASS_DEV_ID2 131874 6 -1 1 COMPASS_DEV_ID3 263178 6 -1 1 COMPASS_DEV_ID4 97283 6 -1 1 COMPASS_DEV_ID5 97795 6 -1 1 COMPASS_DEV_ID6 98051 6 +1 1 COMPASS_AUTO_ROT 0 2 +1 1 COMPASS_CAL_FIT 8.000000000000000000 9 +1 1 COMPASS_CUS_PIT 0.000000000000000000 9 +1 1 COMPASS_CUS_ROLL 0.000000000000000000 9 +1 1 COMPASS_CUS_YAW 0.000000000000000000 9 +1 1 COMPASS_DEC -0.329219996929168701 9 +1 1 COMPASS_DEV_ID 0 6 +1 1 COMPASS_DEV_ID2 0 6 +1 1 COMPASS_DEV_ID3 590114 6 +1 1 COMPASS_DEV_ID4 0 6 +1 1 COMPASS_DEV_ID5 0 6 +1 1 COMPASS_DEV_ID6 0 6 1 1 COMPASS_DEV_ID7 0 6 1 1 COMPASS_DEV_ID8 0 6 -1 1 COMPASS_DIA2_X 1.000000000000000000 9 -1 1 COMPASS_DIA2_Y 1.000000000000000000 9 -1 1 COMPASS_DIA2_Z 1.000000000000000000 9 +1 1 COMPASS_DIA2_X 0.948000013828277588 9 +1 1 COMPASS_DIA2_Y 1.065999984741210938 9 +1 1 COMPASS_DIA2_Z 0.986000001430511475 9 1 1 COMPASS_DIA3_X 1.000000000000000000 9 1 1 COMPASS_DIA3_Y 1.000000000000000000 9 1 1 COMPASS_DIA3_Z 1.000000000000000000 9 -1 1 COMPASS_DIA_X 1.000000000000000000 9 -1 1 COMPASS_DIA_Y 1.000000000000000000 9 -1 1 COMPASS_DIA_Z 1.000000000000000000 9 -1 1 COMPASS_DISBLMSK 0 6 +1 1 COMPASS_DIA_X 1.011999964714050293 9 +1 1 COMPASS_DIA_Y 1.019000053405761719 9 +1 1 COMPASS_DIA_Z 0.970000028610229492 9 1 1 COMPASS_ENABLE 1 2 -1 1 COMPASS_EXTERN2 0 2 +1 1 COMPASS_EXTERN2 1 2 1 1 COMPASS_EXTERN3 0 2 1 1 COMPASS_EXTERNAL 1 2 1 1 COMPASS_FLTR_RNG 0 2 1 1 COMPASS_LEARN 0 2 1 1 COMPASS_MOT2_X 0.000000000000000000 9 -1 1 COMPASS_MOT2_Y 0.000000000000000000 9 +1 1 COMPASS_MOT2_Y 3.200000047683715820 9 1 1 COMPASS_MOT2_Z 0.000000000000000000 9 1 1 COMPASS_MOT3_X 0.000000000000000000 9 1 1 COMPASS_MOT3_Y 0.000000000000000000 9 1 1 COMPASS_MOT3_Z 0.000000000000000000 9 -1 1 COMPASS_MOTCT 0 2 +1 1 COMPASS_MOTCT 2 2 1 1 COMPASS_MOT_X 0.000000000000000000 9 1 1 COMPASS_MOT_Y 0.000000000000000000 9 1 1 COMPASS_MOT_Z 0.000000000000000000 9 -1 1 COMPASS_ODI2_X 0.000000000000000000 9 -1 1 COMPASS_ODI2_Y 0.000000000000000000 9 -1 1 COMPASS_ODI2_Z 0.000000000000000000 9 +1 1 COMPASS_ODI2_X -0.013000000268220901 9 +1 1 COMPASS_ODI2_Y 0.014000000432133675 9 +1 1 COMPASS_ODI2_Z 0.010999999940395355 9 1 1 COMPASS_ODI3_X 0.000000000000000000 9 1 1 COMPASS_ODI3_Y 0.000000000000000000 9 1 1 COMPASS_ODI3_Z 0.000000000000000000 9 -1 1 COMPASS_ODI_X 0.000000000000000000 9 -1 1 COMPASS_ODI_Y 0.000000000000000000 9 +1 1 COMPASS_ODI_X -0.004000000189989805 9 +1 1 COMPASS_ODI_Y -0.016000000759959221 9 1 1 COMPASS_ODI_Z 0.000000000000000000 9 1 1 COMPASS_OFFS_MAX 1800 4 -1 1 COMPASS_OFS2_X 5.000000000000000000 9 -1 1 COMPASS_OFS2_Y 13.000000000000000000 9 -1 1 COMPASS_OFS2_Z -18.000000000000000000 9 -1 1 COMPASS_OFS3_X 5.000000000000000000 9 -1 1 COMPASS_OFS3_Y 13.000000000000000000 9 -1 1 COMPASS_OFS3_Z -18.000000000000000000 9 -1 1 COMPASS_OFS_X 5.000000000000000000 9 -1 1 COMPASS_OFS_Y 13.000000000000000000 9 -1 1 COMPASS_OFS_Z -18.000000000000000000 9 +1 1 COMPASS_OFS2_X 54.000000000000000000 9 +1 1 COMPASS_OFS2_Y 100.000000000000000000 9 +1 1 COMPASS_OFS2_Z 172.000000000000000000 9 +1 1 COMPASS_OFS3_X 0.000000000000000000 9 +1 1 COMPASS_OFS3_Y 0.000000000000000000 9 +1 1 COMPASS_OFS3_Z 0.000000000000000000 9 +1 1 COMPASS_OFS_X 69.000000000000000000 9 +1 1 COMPASS_OFS_Y -98.000000000000000000 9 +1 1 COMPASS_OFS_Z -22.000000000000000000 9 1 1 COMPASS_OPTIONS 0 4 -1 1 COMPASS_ORIENT 0 2 +1 1 COMPASS_ORIENT 8 2 1 1 COMPASS_ORIENT2 0 2 1 1 COMPASS_ORIENT3 0 2 1 1 COMPASS_PMOT_EN 0 2 -1 1 COMPASS_PRIO1_ID 97539 6 -1 1 COMPASS_PRIO2_ID 131874 6 -1 1 COMPASS_PRIO3_ID 263178 6 -1 1 COMPASS_SCALE 1.000000000000000000 9 -1 1 COMPASS_SCALE2 1.000000000000000000 9 -1 1 COMPASS_SCALE3 1.000000000000000000 9 +1 1 COMPASS_PRIO1_ID 1257473 6 +1 1 COMPASS_PRIO2_ID 97539 6 +1 1 COMPASS_PRIO3_ID 590114 6 +1 1 COMPASS_SCALE 1.029999971389770508 9 +1 1 COMPASS_SCALE2 1.049999952316284180 9 +1 1 COMPASS_SCALE3 1.027450680732727051 9 +1 1 COMPASS_TYPEMASK 0 6 1 1 COMPASS_USE 1 2 1 1 COMPASS_USE2 1 2 -1 1 COMPASS_USE3 1 2 -1 1 CRASH_ACC_THRESH 0 2 +1 1 COMPASS_USE3 0 2 +1 1 CRASH_ACC_THRESH 40 2 1 1 CRASH_DETECT 0 2 -1 1 CRUISE_ALT_FLOOR 0.000000000000000000 9 -1 1 CUST_ROT_ENABLE 0 2 -1 1 DID_ENABLE 0 2 -1 1 DSPOILER_AILMTCH 100 2 -1 1 DSPOILER_CROW_W1 0 2 -1 1 DSPOILER_CROW_W2 0 2 -1 1 DSPOILER_OPTS 3 2 -1 1 DSPOILR_RUD_RATE 100 4 -1 1 EAHRS_TYPE 0 2 -1 1 EFI_TYPE 0 2 +1 1 DSPOILER_AILMTCH 0 2 +1 1 DSPOILER_CROW_W1 30 2 +1 1 DSPOILER_CROW_W2 100 2 +1 1 DSPOILER_OPTS 0 2 +1 1 DSPOILR_RUD_RATE 0 4 1 1 EK2_ABIAS_P_NSE 0.004999999888241291 9 1 1 EK2_ACC_P_NSE 0.600000023841857910 9 1 1 EK2_ALT_M_NSE 3.000000000000000000 9 @@ -226,6 +224,7 @@ 1 1 EK2_EAS_I_GATE 400 4 1 1 EK2_EAS_M_NSE 1.399999976158142090 9 1 1 EK2_ENABLE 1 2 +1 1 EK2_EXTNAV_DELAY 10 2 1 1 EK2_FLOW_DELAY 10 2 1 1 EK2_FLOW_I_GATE 500 4 1 1 EK2_FLOW_M_NSE 0.150000005960464478 9 @@ -235,14 +234,12 @@ 1 1 EK2_GPS_CHECK 31 2 1 1 EK2_GPS_TYPE 0 2 1 1 EK2_GSCL_P_NSE 0.000500000023748726 9 -1 1 EK2_GSF_RST_MAX 2 2 -1 1 EK2_GSF_RUN_MASK 3 2 -1 1 EK2_GSF_USE_MASK 3 2 1 1 EK2_GYRO_P_NSE 0.029999999329447746 9 1 1 EK2_HGT_DELAY 60 4 1 1 EK2_HGT_I_GATE 500 4 1 1 EK2_HRT_FILT 2.000000000000000000 9 1 1 EK2_IMU_MASK 3 2 +1 1 EK2_LOG_MASK 1 2 1 1 EK2_MAGB_P_NSE 0.000099999997473788 9 1 1 EK2_MAGE_P_NSE 0.001000000047497451 9 1 1 EK2_MAG_CAL 0 2 @@ -253,7 +250,6 @@ 1 1 EK2_MAX_FLOW 2.500000000000000000 9 1 1 EK2_NOAID_M_NSE 10.000000000000000000 9 1 1 EK2_OGN_HGT_MASK 0 2 -1 1 EK2_OPTIONS 0 6 1 1 EK2_POSNE_M_NSE 1.000000000000000000 9 1 1 EK2_POS_I_GATE 500 4 1 1 EK2_RNG_I_GATE 500 4 @@ -269,446 +265,512 @@ 1 1 EK2_WIND_P_NSE 0.100000001490116119 9 1 1 EK2_YAW_I_GATE 300 4 1 1 EK2_YAW_M_NSE 0.500000000000000000 9 -1 1 EK3_ABIAS_P_NSE 0.019999999552965164 9 -1 1 EK3_ACC_BIAS_LIM 1.000000000000000000 9 -1 1 EK3_ACC_P_NSE 0.349999994039535522 9 -1 1 EK3_AFFINITY 0 6 -1 1 EK3_ALT_M_NSE 3.000000000000000000 9 -1 1 EK3_BCN_DELAY 50 2 -1 1 EK3_BCN_I_GTE 500 4 -1 1 EK3_BCN_M_NSE 1.000000000000000000 9 -1 1 EK3_BETA_MASK 0 2 -1 1 EK3_CHECK_SCALE 150 4 -1 1 EK3_DRAG_BCOEF_X 0.000000000000000000 9 -1 1 EK3_DRAG_BCOEF_Y 0.000000000000000000 9 -1 1 EK3_DRAG_MCOEF 0.000000000000000000 9 -1 1 EK3_DRAG_M_NSE 0.500000000000000000 9 -1 1 EK3_EAS_I_GATE 400 4 -1 1 EK3_EAS_M_NSE 1.399999976158142090 9 -1 1 EK3_ENABLE 1 2 -1 1 EK3_ERR_THRESH 0.200000002980232239 9 -1 1 EK3_FLOW_DELAY 10 2 -1 1 EK3_FLOW_I_GATE 500 4 -1 1 EK3_FLOW_M_NSE 0.150000005960464478 9 -1 1 EK3_FLOW_USE 2 2 -1 1 EK3_GBIAS_P_NSE 0.001000000047497451 9 -1 1 EK3_GLITCH_RAD 25 2 -1 1 EK3_GND_EFF_DZ 4.000000000000000000 9 -1 1 EK3_GPS_CHECK 31 2 -1 1 EK3_GPS_VACC_MAX 0.000000000000000000 9 -1 1 EK3_GSF_RST_MAX 2 2 -1 1 EK3_GSF_RUN_MASK 3 2 -1 1 EK3_GSF_USE_MASK 3 2 -1 1 EK3_GYRO_P_NSE 0.014999999664723873 9 -1 1 EK3_HGT_DELAY 60 4 -1 1 EK3_HGT_I_GATE 500 4 -1 1 EK3_HRT_FILT 2.000000000000000000 9 -1 1 EK3_IMU_MASK 3 2 -1 1 EK3_LOG_LEVEL 0 2 -1 1 EK3_MAGB_P_NSE 0.000099999997473788 9 -1 1 EK3_MAGE_P_NSE 0.001000000047497451 9 -1 1 EK3_MAG_CAL 0 2 -1 1 EK3_MAG_EF_LIM 50 4 -1 1 EK3_MAG_I_GATE 300 4 -1 1 EK3_MAG_MASK 0 2 -1 1 EK3_MAG_M_NSE 0.050000000745058060 9 -1 1 EK3_MAX_FLOW 2.500000000000000000 9 -1 1 EK3_NOAID_M_NSE 10.000000000000000000 9 -1 1 EK3_OGNM_TEST_SF 2.000000000000000000 9 -1 1 EK3_OGN_HGT_MASK 0 2 -1 1 EK3_OPTIONS 0 6 -1 1 EK3_POSNE_M_NSE 0.500000000000000000 9 -1 1 EK3_POS_I_GATE 500 4 -1 1 EK3_PRIMARY 0 2 -1 1 EK3_RNG_I_GATE 500 4 -1 1 EK3_RNG_M_NSE 0.500000000000000000 9 -1 1 EK3_RNG_USE_HGT -1 2 -1 1 EK3_RNG_USE_SPD 2.000000000000000000 9 -1 1 EK3_SRC1_POSXY 3 2 -1 1 EK3_SRC1_POSZ 1 2 -1 1 EK3_SRC1_VELXY 3 2 -1 1 EK3_SRC1_VELZ 3 2 -1 1 EK3_SRC1_YAW 1 2 -1 1 EK3_SRC2_POSXY 0 2 -1 1 EK3_SRC2_POSZ 1 2 -1 1 EK3_SRC2_VELXY 0 2 -1 1 EK3_SRC2_VELZ 0 2 -1 1 EK3_SRC2_YAW 0 2 -1 1 EK3_SRC3_POSXY 0 2 -1 1 EK3_SRC3_POSZ 1 2 -1 1 EK3_SRC3_VELXY 0 2 -1 1 EK3_SRC3_VELZ 0 2 -1 1 EK3_SRC3_YAW 0 2 -1 1 EK3_SRC_OPTIONS 1 4 -1 1 EK3_TAU_OUTPUT 25 2 -1 1 EK3_TERR_GRAD 0.100000001490116119 9 -1 1 EK3_VELD_M_NSE 0.699999988079071045 9 -1 1 EK3_VELNE_M_NSE 0.500000000000000000 9 -1 1 EK3_VEL_I_GATE 500 4 -1 1 EK3_VIS_VERR_MAX 0.899999976158142090 9 -1 1 EK3_VIS_VERR_MIN 0.100000001490116119 9 -1 1 EK3_WENC_VERR 0.100000001490116119 9 -1 1 EK3_WIND_PSCALE 1.000000000000000000 9 -1 1 EK3_WIND_P_NSE 0.100000001490116119 9 -1 1 EK3_YAW_I_GATE 300 4 -1 1 EK3_YAW_M_NSE 0.500000000000000000 9 -1 1 ESC_TLM_MAV_OFS 0 2 -1 1 FBWB_CLIMB_RATE 2 2 +1 1 EK3_ENABLE 0 2 +1 1 FBWA_TDRAG_CHAN 0 2 +1 1 FBWB_CLIMB_RATE 5 2 1 1 FBWB_ELEV_REV 0 2 -1 1 FENCE_ACTION 1 2 -1 1 FENCE_ALT_MAX 100.000000000000000000 9 -1 1 FENCE_ALT_MIN -10.000000000000000000 9 +1 1 FENCE_ACTION 0 2 1 1 FENCE_AUTOENABLE 0 2 -1 1 FENCE_ENABLE 0 2 -1 1 FENCE_MARGIN 2.000000000000000000 9 -1 1 FENCE_OPTIONS 1 4 -1 1 FENCE_RADIUS 300.000000000000000000 9 -1 1 FENCE_RET_ALT 0 4 +1 1 FENCE_CHANNEL 0 2 +1 1 FENCE_MAXALT 0 4 +1 1 FENCE_MINALT 0 4 +1 1 FENCE_RETALT 0 4 1 1 FENCE_RET_RALLY 0 2 1 1 FENCE_TOTAL 0 2 -1 1 FENCE_TYPE 4 2 -1 1 FFT_ENABLE 0 2 -1 1 FILT1_TYPE 0 2 -1 1 FILT2_TYPE 0 2 -1 1 FILT3_TYPE 0 2 -1 1 FILT4_TYPE 0 2 -1 1 FILT5_TYPE 0 2 -1 1 FILT6_TYPE 0 2 -1 1 FILT7_TYPE 0 2 -1 1 FILT8_TYPE 0 2 -1 1 FLAP_1_PERCNT 0 2 -1 1 FLAP_1_SPEED 0 2 +1 1 FLAP_1_PERCNT 50 2 +1 1 FLAP_1_SPEED 14 2 1 1 FLAP_2_PERCNT 0 2 1 1 FLAP_2_SPEED 0 2 -1 1 FLAP_SLEWRATE 75 2 -1 1 FLIGHT_OPTIONS 0 6 +1 1 FLAP_IN_CHANNEL 0 2 +1 1 FLAP_SLEWRATE 25 2 +1 1 FLIGHT_OPTIONS 4352 6 +1 1 FLOW_ADDR 0 2 +1 1 FLOW_FXSCALER 0 4 +1 1 FLOW_FYSCALER 0 4 +1 1 FLOW_ORIENT_YAW 0 4 +1 1 FLOW_POS_X 0.000000000000000000 9 +1 1 FLOW_POS_Y 0.000000000000000000 9 +1 1 FLOW_POS_Z 0.000000000000000000 9 1 1 FLOW_TYPE 0 2 -1 1 FLTMODE1 10 2 -1 1 FLTMODE2 11 2 -1 1 FLTMODE3 12 2 -1 1 FLTMODE4 5 2 -1 1 FLTMODE5 2 2 -1 1 FLTMODE6 0 2 -1 1 FLTMODE_CH 8 2 -1 1 FLTMODE_GCSBLOCK 0 6 -1 1 FOLL_ENABLE 0 2 +1 1 FLTMODE1 19 2 +1 1 FLTMODE2 19 2 +1 1 FLTMODE3 19 2 +1 1 FLTMODE4 6 2 +1 1 FLTMODE5 6 2 +1 1 FLTMODE6 11 2 +1 1 FLTMODE_CH 6 2 1 1 FORMAT_VERSION 13 4 -1 1 FRSKY_DNLINK1_ID 20 2 -1 1 FRSKY_DNLINK2_ID 7 2 -1 1 FRSKY_DNLINK_ID 27 2 -1 1 FRSKY_OPTIONS 0 2 -1 1 FRSKY_UPLINK_ID 13 2 -1 1 FS_EKF_THRESH 0.800000011920928955 9 -1 1 FS_GCS_ENABL 0 2 -1 1 FS_LONG_ACTN 0 2 -1 1 FS_LONG_TIMEOUT 5.000000000000000000 9 -1 1 FS_SHORT_ACTN 0 2 -1 1 FS_SHORT_TIMEOUT 1.500000000000000000 9 +1 1 FS_GCS_ENABL 3 2 +1 1 FS_LONG_ACTN 1 2 +1 1 FS_LONG_TIMEOUT 10.000000000000000000 9 +1 1 FS_SHORT_ACTN 1 2 +1 1 FS_SHORT_TIMEOUT 4.000000000000000000 9 1 1 FWD_BAT_IDX 0 2 -1 1 FWD_BAT_VOLT_MAX 0.000000000000000000 9 -1 1 FWD_BAT_VOLT_MIN 0.000000000000000000 9 +1 1 FWD_BAT_VOLT_MAX 16.799999237060546875 9 +1 1 FWD_BAT_VOLT_MIN 14.000000000000000000 9 1 1 GCS_PID_MASK 0 4 -1 1 GEN_TYPE 0 2 1 1 GLIDE_SLOPE_MIN 15 4 1 1 GLIDE_SLOPE_THR 5.000000000000000000 9 -1 1 GPS1_CAN_NODEID 0 6 -1 1 GPS1_CAN_OVRIDE 0 6 -1 1 GPS1_COM_PORT 1 2 -1 1 GPS1_DELAY_MS 0 4 -1 1 GPS1_GNSS_MODE 0 2 -1 1 GPS1_MB_TYPE 0 2 -1 1 GPS1_POS_X 0.000000000000000000 9 -1 1 GPS1_POS_Y 0.000000000000000000 9 -1 1 GPS1_POS_Z 0.000000000000000000 9 -1 1 GPS1_RATE_MS 200 4 -1 1 GPS1_TYPE 1 2 -1 1 GPS2_TYPE 0 2 -1 1 GPS_AUTO_CONFIG 1 2 -1 1 GPS_AUTO_SWITCH 1 2 +1 1 GND_ABS_PRESS 85533.164062500000000000 9 +1 1 GND_ABS_PRESS2 85546.882812500000000000 9 +1 1 GND_ABS_PRESS3 85625.093750000000000000 9 +1 1 GND_ALT_OFFSET 0.000000000000000000 9 +1 1 GND_EXT_BUS -1 2 +1 1 GND_FLTR_RNG 0 2 +1 1 GND_PRIMARY 0 2 +1 1 GND_PROBE_EXT 0 6 +1 1 GND_TEMP 0.000000000000000000 9 +1 1 GPS_AUTO_CONFIG 0 2 +1 1 GPS_AUTO_SWITCH 0 2 1 1 GPS_BLEND_MASK 5 2 +1 1 GPS_BLEND_TC 10.000000000000000000 9 +1 1 GPS_DELAY_MS 0 4 +1 1 GPS_DELAY_MS2 0 4 1 1 GPS_DRV_OPTIONS 0 4 +1 1 GPS_GNSS_MODE 0 2 +1 1 GPS_GNSS_MODE2 0 2 1 1 GPS_INJECT_TO 127 2 +1 1 GPS_MIN_DGPS 100 2 1 1 GPS_MIN_ELEV -100 2 1 1 GPS_NAVFILTER 8 2 -1 1 GPS_PRIMARY 0 2 +1 1 GPS_POS1_X 0.000000000000000000 9 +1 1 GPS_POS1_Y 0.000000000000000000 9 +1 1 GPS_POS1_Z 0.000000000000000000 9 +1 1 GPS_POS2_X 0.000000000000000000 9 +1 1 GPS_POS2_Y 0.000000000000000000 9 +1 1 GPS_POS2_Z 0.000000000000000000 9 +1 1 GPS_RATE_MS 200 4 +1 1 GPS_RATE_MS2 200 4 1 1 GPS_RAW_DATA 0 2 1 1 GPS_SAVE_CFG 2 2 1 1 GPS_SBAS_MODE 2 2 1 1 GPS_SBP_LOGMASK -256 4 +1 1 GPS_TYPE 9 2 +1 1 GPS_TYPE2 0 2 1 1 GRIP_ENABLE 0 2 1 1 GROUND_STEER_ALT 0.000000000000000000 9 1 1 GROUND_STEER_DPS 90 4 -1 1 GUIDED_D 0.000000000000000000 9 -1 1 GUIDED_D_FF 0.000000000000000000 9 -1 1 GUIDED_FF 0.000000000000000000 9 -1 1 GUIDED_FLTD 5.000000000000000000 9 -1 1 GUIDED_FLTE 5.000000000000000000 9 -1 1 GUIDED_FLTT 5.000000000000000000 9 -1 1 GUIDED_I 0.000000000000000000 9 -1 1 GUIDED_IMAX 10.000000000000000000 9 -1 1 GUIDED_NEF 0 2 -1 1 GUIDED_NTF 0 2 -1 1 GUIDED_P 5000.000000000000000000 9 -1 1 GUIDED_PDMX 0.000000000000000000 9 -1 1 GUIDED_SMAX 0.000000000000000000 9 +1 1 HIL_ERR_LIMIT 5.000000000000000000 9 +1 1 HIL_MODE 0 2 +1 1 HIL_SERVOS 0 2 1 1 HOME_RESET_ALT 0 2 1 1 ICE_ENABLE 0 2 -1 1 INITIAL_MODE 0 2 -1 1 INS_ACC1_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2OFFS_X 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Y 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Z 0.001000000047497451 9 -1 1 INS_ACC2SCAL_X 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Y 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Z 1.001000046730041504 9 -1 1 INS_ACC2_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2_ID 2753036 6 -1 1 INS_ACC3OFFS_X 0.000000000000000000 9 -1 1 INS_ACC3OFFS_Y 0.000000000000000000 9 -1 1 INS_ACC3OFFS_Z 0.000000000000000000 9 -1 1 INS_ACC3SCAL_X 1.000000000000000000 9 -1 1 INS_ACC3SCAL_Y 1.000000000000000000 9 -1 1 INS_ACC3SCAL_Z 1.000000000000000000 9 -1 1 INS_ACC3_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC3_ID 0 6 +1 1 INITIAL_MODE 19 2 +1 1 INS_ACC2OFFS_X 0.054712962359189987 9 +1 1 INS_ACC2OFFS_Y -0.216669529676437378 9 +1 1 INS_ACC2OFFS_Z 0.314546972513198853 9 +1 1 INS_ACC2SCAL_X 0.997382998466491699 9 +1 1 INS_ACC2SCAL_Y 0.988359093666076660 9 +1 1 INS_ACC2SCAL_Z 0.998526811599731445 9 +1 1 INS_ACC2_ID 2883874 6 +1 1 INS_ACC3OFFS_X -0.124825842678546906 9 +1 1 INS_ACC3OFFS_Y -0.015632435679435730 9 +1 1 INS_ACC3OFFS_Z 0.176186725497245789 9 +1 1 INS_ACC3SCAL_X 0.997042357921600342 9 +1 1 INS_ACC3SCAL_Y 0.989098966121673584 9 +1 1 INS_ACC3SCAL_Z 0.994051754474639893 9 +1 1 INS_ACC3_ID 3015690 6 1 1 INS_ACCEL_FILTER 20 4 -1 1 INS_ACCOFFS_X 0.001000000047497451 9 -1 1 INS_ACCOFFS_Y 0.001000000047497451 9 -1 1 INS_ACCOFFS_Z 0.001000000047497451 9 -1 1 INS_ACCSCAL_X 1.001000046730041504 9 -1 1 INS_ACCSCAL_Y 1.001000046730041504 9 -1 1 INS_ACCSCAL_Z 1.001000046730041504 9 +1 1 INS_ACCOFFS_X 0.171545445919036865 9 +1 1 INS_ACCOFFS_Y -0.032949406653642654 9 +1 1 INS_ACCOFFS_Z 0.237508997321128845 9 +1 1 INS_ACCSCAL_X 0.998330771923065186 9 +1 1 INS_ACCSCAL_Y 0.896929979324340820 9 +1 1 INS_ACCSCAL_Z 1.001518845558166504 9 1 1 INS_ACC_BODYFIX 2 2 -1 1 INS_ACC_ID 2753028 6 +1 1 INS_ACC_ID 3081250 6 1 1 INS_ENABLE_MASK 127 2 -1 1 INS_FAST_SAMPLE 1 2 -1 1 INS_GYR1_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR2OFFS_X 0.000000000000000000 9 -1 1 INS_GYR2OFFS_Y 0.000000000000000000 9 -1 1 INS_GYR2OFFS_Z 0.000000000000000000 9 -1 1 INS_GYR2_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR2_ID 2752780 6 -1 1 INS_GYR3OFFS_X 0.000000000000000000 9 -1 1 INS_GYR3OFFS_Y 0.000000000000000000 9 -1 1 INS_GYR3OFFS_Z 0.000000000000000000 9 -1 1 INS_GYR3_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR3_ID 0 6 -1 1 INS_GYROFFS_X 0.000000000000000000 9 -1 1 INS_GYROFFS_Y 0.000000000000000000 9 -1 1 INS_GYROFFS_Z 0.000000000000000000 9 -1 1 INS_GYRO_FILTER 20 4 -1 1 INS_GYRO_RATE 0 2 -1 1 INS_GYR_CAL 0 2 -1 1 INS_GYR_ID 2752772 6 -1 1 INS_HNTC2_ENABLE 0 2 -1 1 INS_HNTCH_ENABLE 0 2 +1 1 INS_FAST_SAMPLE 2 2 +1 1 INS_GYR2OFFS_X -0.004077028948813677 9 +1 1 INS_GYR2OFFS_Y -0.010312342084944248 9 +1 1 INS_GYR2OFFS_Z 0.017256719991564751 9 +1 1 INS_GYR2_ID 2883874 6 +1 1 INS_GYR3OFFS_X -0.009859869256615639 9 +1 1 INS_GYR3OFFS_Y -0.002577690407633781 9 +1 1 INS_GYR3OFFS_Z 0.008385097607970238 9 +1 1 INS_GYR3_ID 3015690 6 +1 1 INS_GYROFFS_X 0.018343556672334671 9 +1 1 INS_GYROFFS_Y -0.018574506044387817 9 +1 1 INS_GYROFFS_Z 0.018880937248468399 9 +1 1 INS_GYRO_FILTER 30 4 +1 1 INS_GYR_CAL 1 2 +1 1 INS_GYR_ID 3081250 6 +1 1 INS_HNTCH_ATT 50.000000000000000000 9 +1 1 INS_HNTCH_BW 50.000000000000000000 9 +1 1 INS_HNTCH_ENABLE 1 2 +1 1 INS_HNTCH_FREQ 100.000000000000000000 9 +1 1 INS_HNTCH_HMNCS 3 2 +1 1 INS_HNTCH_MODE 3 2 +1 1 INS_HNTCH_REF 1.000000000000000000 9 1 1 INS_LOG_BAT_CNT 1024 4 1 1 INS_LOG_BAT_LGCT 32 4 1 1 INS_LOG_BAT_LGIN 20 2 -1 1 INS_LOG_BAT_MASK 0 2 -1 1 INS_LOG_BAT_OPT 0 2 -1 1 INS_POS1_X 0.000000000000000000 9 +1 1 INS_LOG_BAT_MASK 2 2 +1 1 INS_LOG_BAT_OPT 2 2 +1 1 INS_NOTCH_ENABLE 0 2 +1 1 INS_POS1_X 0.035999998450279236 9 1 1 INS_POS1_Y 0.000000000000000000 9 1 1 INS_POS1_Z 0.000000000000000000 9 -1 1 INS_POS2_X 0.000000000000000000 9 +1 1 INS_POS2_X 0.035999998450279236 9 1 1 INS_POS2_Y 0.000000000000000000 9 1 1 INS_POS2_Z 0.000000000000000000 9 -1 1 INS_POS3_X 0.000000000000000000 9 +1 1 INS_POS3_X 0.035999998450279236 9 1 1 INS_POS3_Y 0.000000000000000000 9 1 1 INS_POS3_Z 0.000000000000000000 9 -1 1 INS_RAW_LOG_OPT 0 4 -1 1 INS_STILL_THRESH 0.050000000745058060 9 -1 1 INS_TCAL1_ENABLE 0 2 -1 1 INS_TCAL2_ENABLE 0 2 -1 1 INS_TCAL3_ENABLE 0 2 -1 1 INS_TCAL_OPTIONS 0 6 -1 1 INS_TRIM_OPTION 1 2 +1 1 INS_STILL_THRESH 3.000000000000000000 9 +1 1 INS_TRIM_OPTION 2 2 1 1 INS_USE 1 2 1 1 INS_USE2 1 2 1 1 INS_USE3 1 2 -1 1 KDE_NPOLE 14 2 -1 1 KFF_RDDRMIX 0.500000000000000000 9 +1 1 KFF_RDDRMIX 0.300000011920928955 9 1 1 KFF_THR2PTCH 0.000000000000000000 9 1 1 LAND_ABORT_DEG 0.000000000000000000 9 1 1 LAND_ABORT_THR 0 2 -1 1 LAND_DISARMDELAY 20 2 -1 1 LAND_FLAP_PERCNT 0 2 -1 1 LAND_FLARE_AIM 50 2 -1 1 LAND_FLARE_ALT 3.000000000000000000 9 -1 1 LAND_FLARE_SEC 3.000000000000000000 9 -1 1 LAND_OPTIONS 0 4 -1 1 LAND_PF_ALT 10.000000000000000000 9 -1 1 LAND_PF_ARSPD 0.000000000000000000 9 -1 1 LAND_PF_SEC 6.000000000000000000 9 -1 1 LAND_PITCH_DEG 1.000000000000000000 9 +1 1 LAND_DISARMDELAY 2 2 +1 1 LAND_DS_ABORTALT 0.000000000000000000 9 +1 1 LAND_DS_AIL_SCL 1.000000000000000000 9 +1 1 LAND_DS_APP_EXT 50.000000000000000000 9 +1 1 LAND_DS_ARSP_MAX 15.000000000000000000 9 +1 1 LAND_DS_ARSP_MIN 10.000000000000000000 9 +1 1 LAND_DS_D 0.000000000000000000 9 +1 1 LAND_DS_ELEV_PWM 1500 4 +1 1 LAND_DS_I 0.000000000000000000 9 +1 1 LAND_DS_IMAX 0 4 +1 1 LAND_DS_L1 30.000000000000000000 9 +1 1 LAND_DS_L1_I 0.000000000000000000 9 +1 1 LAND_DS_L1_TCON 0.400000005960464478 9 +1 1 LAND_DS_P 0.000000000000000000 9 +1 1 LAND_DS_SLEW_SPD 0.500000000000000000 9 +1 1 LAND_DS_SLOPE_A 1.000000000000000000 9 +1 1 LAND_DS_SLOPE_B 1.000000000000000000 9 +1 1 LAND_DS_V_DWN 2.000000000000000000 9 +1 1 LAND_DS_V_FWD 1.000000000000000000 9 +1 1 LAND_DS_YAW_LIM 10.000000000000000000 9 +1 1 LAND_FLAP_PERCNT 50 2 +1 1 LAND_FLARE_ALT 2.000000000000000000 9 +1 1 LAND_FLARE_SEC 1.500000000000000000 9 +1 1 LAND_PF_ALT 15.000000000000000000 9 +1 1 LAND_PF_ARSPD 12.000000000000000000 9 +1 1 LAND_PF_SEC 5.000000000000000000 9 +1 1 LAND_PITCH_CD 500 4 1 1 LAND_SLOPE_RCALC 2.000000000000000000 9 1 1 LAND_THEN_NEUTRL 0 2 1 1 LAND_THR_SLEW 0 2 1 1 LAND_TYPE 0 2 -1 1 LAND_WIND_COMP 50.000000000000000000 9 1 1 LEVEL_ROLL_LIMIT 5 2 -1 1 LGR_ENABLE 0 2 +1 1 LGR_DEPLOY_ALT 0 4 +1 1 LGR_DEPLOY_PIN -1 2 +1 1 LGR_DEPLOY_POL 0 2 +1 1 LGR_OPTIONS 3 4 +1 1 LGR_RETRACT_ALT 0 4 +1 1 LGR_STARTUP 0 2 +1 1 LGR_WOW_PIN -1 2 +1 1 LGR_WOW_POL 0 2 +1 1 LIM_PITCH_MAX 1600 4 +1 1 LIM_PITCH_MIN -800 4 +1 1 LIM_ROLL_CD 4000 4 1 1 LOG_BACKEND_TYPE 1 2 1 1 LOG_BITMASK 65535 6 -1 1 LOG_BLK_RATEMAX 0.000000000000000000 9 -1 1 LOG_DARM_RATEMAX 0.000000000000000000 9 -1 1 LOG_DISARMED 0 2 -1 1 LOG_FILE_BUFSIZE 200 4 +1 1 LOG_DISARMED 1 2 +1 1 LOG_FILE_BUFSIZE 50 2 1 1 LOG_FILE_DSRMROT 0 2 -1 1 LOG_FILE_MB_FREE 500 4 -1 1 LOG_FILE_RATEMAX 0.000000000000000000 9 1 1 LOG_FILE_TIMEOUT 5 4 1 1 LOG_MAV_BUFSIZE 8 2 -1 1 LOG_MAV_RATEMAX 0.000000000000000000 9 -1 1 LOG_MAX_FILES 500 4 1 1 LOG_REPLAY 0 2 1 1 MANUAL_RCMASK 0 6 -1 1 MAN_EXPO_PITCH 0 2 -1 1 MAN_EXPO_ROLL 0 2 -1 1 MAN_EXPO_RUDDER 0 2 -1 1 MIN_GROUNDSPEED 0.000000000000000000 9 +1 1 MIN_GNDSPD_CM 0 6 1 1 MIS_OPTIONS 0 4 1 1 MIS_RESTART 0 2 -1 1 MIS_TOTAL 0 4 -1 1 MIXING_GAIN 0.500000000000000000 9 -1 1 MIXING_OFFSET 0 4 -1 1 MNT1_TYPE 0 2 -1 1 MNT2_TYPE 0 2 -1 1 MSP_OPTIONS 0 2 -1 1 MSP_OSD_NCELLS 0 2 -1 1 NAVL1_DAMPING 0.750000000000000000 9 +1 1 MIS_TOTAL 40 4 +1 1 MIXING_GAIN 0.550000011920928955 9 +1 1 MIXING_OFFSET -80 4 +1 1 MNT_ANGMAX_PAN 4500 4 +1 1 MNT_ANGMAX_ROL 4500 4 +1 1 MNT_ANGMAX_TIL 4500 4 +1 1 MNT_ANGMIN_PAN -4500 4 +1 1 MNT_ANGMIN_ROL -4500 4 +1 1 MNT_ANGMIN_TIL -4500 4 +1 1 MNT_DEFLT_MODE 3 2 +1 1 MNT_JSTICK_SPD 0 2 +1 1 MNT_LEAD_PTCH 0.000000000000000000 9 +1 1 MNT_LEAD_RLL 0.000000000000000000 9 +1 1 MNT_NEUTRAL_X 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Y 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Z 0.000000000000000000 9 +1 1 MNT_RC_IN_PAN 0 2 +1 1 MNT_RC_IN_ROLL 0 2 +1 1 MNT_RC_IN_TILT 0 2 +1 1 MNT_RETRACT_X 0.000000000000000000 9 +1 1 MNT_RETRACT_Y 0.000000000000000000 9 +1 1 MNT_RETRACT_Z 0.000000000000000000 9 +1 1 MNT_STAB_PAN 0 2 +1 1 MNT_STAB_ROLL 0 2 +1 1 MNT_STAB_TILT 0 2 +1 1 MNT_TYPE 0 2 +1 1 NAVL1_DAMPING 0.829999983310699463 9 1 1 NAVL1_LIM_BANK 0.000000000000000000 9 1 1 NAVL1_PERIOD 15.000000000000000000 9 -1 1 NAVL1_XTRACK_I 0.019999999552965164 9 -1 1 NET_ENABLE 0 2 -1 1 NET_P1_TYPE 0 2 -1 1 NET_P2_TYPE 0 2 -1 1 NET_P3_TYPE 0 2 -1 1 NET_P4_TYPE 0 2 -1 1 NMEA_MSG_EN 3 4 -1 1 NMEA_RATE_MS 100 4 +1 1 NAVL1_XTRACK_I 0.100000001490116119 9 +1 1 NAV_CONTROLLER 1 2 +1 1 NTF_BUZZ_ENABLE 1 2 1 1 NTF_BUZZ_ON_LVL 1 2 -1 1 NTF_BUZZ_PIN -1 2 -1 1 NTF_BUZZ_TYPES 5 2 +1 1 NTF_BUZZ_PIN 0 2 1 1 NTF_BUZZ_VOLUME 100 2 1 1 NTF_DISPLAY_TYPE 0 2 1 1 NTF_LED_BRIGHT 3 2 1 1 NTF_LED_LEN 1 2 1 1 NTF_LED_OVERRIDE 0 2 -1 1 NTF_LED_TYPES 123079 6 -1 1 ONESHOT_MASK 0 6 -1 1 OSD_TYPE 0 2 -1 1 PLND_ENABLED 0 2 -1 1 PTCH2SRV_RLL 1.000000000000000000 9 +1 1 NTF_LED_TYPES 231 6 +1 1 NTF_OREO_THEME 0 2 +1 1 OVERRIDE_CHAN 0 2 +1 1 OVERRIDE_SAFETY 1 2 +1 1 PTCH2SRV_D 0.039999999105930328 9 +1 1 PTCH2SRV_FF 0.000000000000000000 9 +1 1 PTCH2SRV_I 0.300000011920928955 9 +1 1 PTCH2SRV_IMAX 3000 4 +1 1 PTCH2SRV_P 1.000000000000000000 9 +1 1 PTCH2SRV_RLL 1.200000047683715820 9 1 1 PTCH2SRV_RMAX_DN 90 4 1 1 PTCH2SRV_RMAX_UP 90 4 -1 1 PTCH2SRV_TCONST 0.250000000000000000 9 -1 1 PTCH_LIM_MAX_DEG 25.000000000000000000 9 -1 1 PTCH_LIM_MIN_DEG -20.000000000000000000 9 -1 1 PTCH_RATE_D 0.007265000138431787 9 -1 1 PTCH_RATE_D_FF 0.000000000000000000 9 -1 1 PTCH_RATE_FF 0.595722973346710205 9 -1 1 PTCH_RATE_FLTD 12.000000000000000000 9 -1 1 PTCH_RATE_FLTE 0.000000000000000000 9 -1 1 PTCH_RATE_FLTT 3.000000000000000000 9 -1 1 PTCH_RATE_I 0.109999999403953552 9 -1 1 PTCH_RATE_IMAX 0.666000008583068848 9 -1 1 PTCH_RATE_NEF 0 2 -1 1 PTCH_RATE_NTF 0 2 -1 1 PTCH_RATE_P 0.150000005960464478 9 -1 1 PTCH_RATE_PDMX 0.000000000000000000 9 -1 1 PTCH_RATE_SMAX 150.000000000000000000 9 -1 1 PTCH_TRIM_DEG 0.000000000000000000 9 -1 1 PUP_ENABLE 0 2 -1 1 QWIK_ENABLE 0 2 -1 1 Q_ENABLE 0 2 -1 1 RALLY_INCL_HOME 0 2 -1 1 RALLY_LIMIT_KM 5.000000000000000000 9 +1 1 PTCH2SRV_TCONST 0.379999995231628418 9 +1 1 Q_ACCEL_Z 250 4 +1 1 Q_ACRO_PIT_RATE 180.000000000000000000 9 +1 1 Q_ACRO_RLL_RATE 360.000000000000000000 9 +1 1 Q_ACRO_YAW_RATE 90.000000000000000000 9 +1 1 Q_ANGLE_MAX 1000 4 +1 1 Q_ASSIST_ALT 10 4 +1 1 Q_ASSIST_ANGLE 15 2 +1 1 Q_ASSIST_SPEED 11.000000000000000000 9 +1 1 Q_AUTOTUNE_AGGR 0.050000000745058060 9 +1 1 Q_AUTOTUNE_AXES 4 2 +1 1 Q_AUTOTUNE_MIN_D 0.001000000047497451 9 +1 1 Q_A_ACCEL_P_MAX 100000.000000000000000000 9 +1 1 Q_A_ACCEL_R_MAX 100000.000000000000000000 9 +1 1 Q_A_ACCEL_Y_MAX 4000.000000000000000000 9 +1 1 Q_A_ANGLE_BOOST 1 2 +1 1 Q_A_ANG_LIM_TC 0.500000000000000000 9 +1 1 Q_A_ANG_PIT_P 6.000000000000000000 9 +1 1 Q_A_ANG_RLL_P 6.000000000000000000 9 +1 1 Q_A_ANG_YAW_P 6.000000000000000000 9 +1 1 Q_A_INPUT_TC 0.400000005960464478 9 +1 1 Q_A_RATE_FF_ENAB 1 2 +1 1 Q_A_RATE_P_MAX 360.000000000000000000 9 +1 1 Q_A_RATE_R_MAX 360.000000000000000000 9 +1 1 Q_A_RATE_Y_MAX 20.000000000000000000 9 +1 1 Q_A_RAT_PIT_D 0.001000000047497451 9 +1 1 Q_A_RAT_PIT_FF 0.019999999552965164 9 +1 1 Q_A_RAT_PIT_FLTD 10.000000000000000000 9 +1 1 Q_A_RAT_PIT_FLTE 0.000000000000000000 9 +1 1 Q_A_RAT_PIT_FLTT 10.000000000000000000 9 +1 1 Q_A_RAT_PIT_I 0.300000011920928955 9 +1 1 Q_A_RAT_PIT_IMAX 0.250000000000000000 9 +1 1 Q_A_RAT_PIT_P 0.360000014305114746 9 +1 1 Q_A_RAT_RLL_D 0.004000000189989805 9 +1 1 Q_A_RAT_RLL_FF 0.059999998658895493 9 +1 1 Q_A_RAT_RLL_FLTD 10.000000000000000000 9 +1 1 Q_A_RAT_RLL_FLTE 0.000000000000000000 9 +1 1 Q_A_RAT_RLL_FLTT 10.000000000000000000 9 +1 1 Q_A_RAT_RLL_I 0.300000011920928955 9 +1 1 Q_A_RAT_RLL_IMAX 0.250000000000000000 9 +1 1 Q_A_RAT_RLL_P 0.550000011920928955 9 +1 1 Q_A_RAT_YAW_D 0.500000000000000000 9 +1 1 Q_A_RAT_YAW_FF 0.000000000000000000 9 +1 1 Q_A_RAT_YAW_FLTD 10.000000000000000000 9 +1 1 Q_A_RAT_YAW_FLTE 2.500000000000000000 9 +1 1 Q_A_RAT_YAW_FLTT 10.000000000000000000 9 +1 1 Q_A_RAT_YAW_I 0.500000000000000000 9 +1 1 Q_A_RAT_YAW_IMAX 0.100000016391277313 9 +1 1 Q_A_RAT_YAW_P 1.500000000000000000 9 +1 1 Q_A_SLEW_YAW 1000.000000000000000000 9 +1 1 Q_A_THR_MIX_MAN 0.500000000000000000 9 +1 1 Q_A_THR_MIX_MAX 0.660000026226043701 9 +1 1 Q_A_THR_MIX_MIN 0.200000002980232239 9 +1 1 Q_ENABLE 1 2 +1 1 Q_ESC_CAL 0 2 +1 1 Q_FRAME_CLASS 1 2 +1 1 Q_FRAME_TYPE 0 2 +1 1 Q_FW_LND_APR_RAD 90.000000000000000000 9 +1 1 Q_GUIDED_MODE 0 2 +1 1 Q_LAND_FINAL_ALT 6.000000000000000000 9 +1 1 Q_LAND_ICE_CUT 1 2 +1 1 Q_LAND_SPEED 30 4 +1 1 Q_LOIT_ACC_MAX 80.000000000000000000 9 +1 1 Q_LOIT_ANG_MAX 5.000000000000000000 9 +1 1 Q_LOIT_BRK_ACCEL 100.000000000000000000 9 +1 1 Q_LOIT_BRK_DELAY 0.100000001490116119 9 +1 1 Q_LOIT_BRK_JERK 1500.000000000000000000 9 +1 1 Q_LOIT_SPEED 200.000000000000000000 9 +1 1 Q_MAV_TYPE 0 2 +1 1 Q_M_BAT_CURR_MAX 120.000000000000000000 9 +1 1 Q_M_BAT_CURR_TC 5.000000000000000000 9 +1 1 Q_M_BAT_IDX 0 2 +1 1 Q_M_BAT_VOLT_MAX 16.799999237060546875 9 +1 1 Q_M_BAT_VOLT_MIN 13.199999809265136719 9 +1 1 Q_M_BOOST_SCALE 0.000000000000000000 9 +1 1 Q_M_HOVER_LEARN 1 2 +1 1 Q_M_PWM_MAX 2000 4 +1 1 Q_M_PWM_MIN 1000 4 +1 1 Q_M_PWM_TYPE 6 2 +1 1 Q_M_SAFE_DISARM 1 2 +1 1 Q_M_SAFE_TIME 1.000000000000000000 9 +1 1 Q_M_SLEW_DN_TIME 0.000000000000000000 9 +1 1 Q_M_SLEW_UP_TIME 0.000000000000000000 9 +1 1 Q_M_SPIN_ARM 0.079999998211860657 9 +1 1 Q_M_SPIN_MAX 1.000000000000000000 9 +1 1 Q_M_SPIN_MIN 0.119999997317790985 9 +1 1 Q_M_SPOOL_TIME 0.200000002980232239 9 +1 1 Q_M_THST_EXPO 0.660000026226043701 9 +1 1 Q_M_THST_HOVER 0.500000000000000000 9 +1 1 Q_M_YAW_HEADROOM 200 4 +1 1 Q_OPTIONS 448537 6 +1 1 Q_P_ACCZ_D 0.025000000372529030 9 +1 1 Q_P_ACCZ_FF 0.400000005960464478 9 +1 1 Q_P_ACCZ_FLTD 10.000000000000000000 9 +1 1 Q_P_ACCZ_FLTE 20.000000000000000000 9 +1 1 Q_P_ACCZ_FLTT 5.000000000000000000 9 +1 1 Q_P_ACCZ_I 0.250000000000000000 9 +1 1 Q_P_ACCZ_IMAX 500.000000000000000000 9 +1 1 Q_P_ACCZ_P 0.449999988079071045 9 +1 1 Q_P_ACC_XY_FILT 2.000000000000000000 9 +1 1 Q_P_ANGLE_MAX 10.000000000000000000 9 +1 1 Q_P_POSXY_P 0.600000023841857910 9 +1 1 Q_P_POSZ_P 1.750000000000000000 9 +1 1 Q_P_VELXY_D 0.250000000000000000 9 +1 1 Q_P_VELXY_D_FILT 10.000000000000000000 9 +1 1 Q_P_VELXY_FILT 20.000000000000000000 9 +1 1 Q_P_VELXY_I 0.300000011920928955 9 +1 1 Q_P_VELXY_IMAX 1000.000000000000000000 9 +1 1 Q_P_VELXY_P 2.799999952316284180 9 +1 1 Q_P_VELZ_P 6.000000000000000000 9 +1 1 Q_RC_SPEED 250 4 +1 1 Q_RTL_ALT 45 4 +1 1 Q_RTL_MODE 2 2 +1 1 Q_TAILSIT_ANGLE 45 2 +1 1 Q_TAILSIT_INPUT 0 2 +1 1 Q_TAILSIT_MASK 0 2 +1 1 Q_TAILSIT_MASKCH 0 2 +1 1 Q_TAILSIT_MOTMX 0 4 +1 1 Q_TAILSIT_RLL_MX 0.000000000000000000 9 +1 1 Q_TAILSIT_THSCMX 5.000000000000000000 9 +1 1 Q_TAILSIT_VFGAIN 0.000000000000000000 9 +1 1 Q_TAILSIT_VHGAIN 0.500000000000000000 9 +1 1 Q_TAILSIT_VHPOW 2.500000000000000000 9 +1 1 Q_THROTTLE_EXPO 0.200000002980232239 9 +1 1 Q_THR_MAX_PWM 2000 4 +1 1 Q_THR_MIN_PWM 1000 4 +1 1 Q_TILT_MASK 0 4 +1 1 Q_TILT_MAX 45 2 +1 1 Q_TILT_RATE_DN 0 4 +1 1 Q_TILT_RATE_UP 40 4 +1 1 Q_TILT_TYPE 0 2 +1 1 Q_TILT_YAW_ANGLE 0.000000000000000000 9 +1 1 Q_TKOFF_ARSP_LIM 0.000000000000000000 9 +1 1 Q_TKOFF_FAIL_SCL 0.000000000000000000 9 +1 1 Q_TRANSITION_MS 20000 4 +1 1 Q_TRANS_DECEL 0.300000011920928955 9 +1 1 Q_TRANS_FAIL 0 4 +1 1 Q_TRAN_PIT_MAX 7 2 +1 1 Q_TRIM_PITCH 0.000000000000000000 9 +1 1 Q_VELZ_MAX 250 4 +1 1 Q_VFWD_ALT 0.500000000000000000 9 +1 1 Q_VFWD_GAIN 0.050000000745058060 9 +1 1 Q_WP_ACCEL 85.000000000000000000 9 +1 1 Q_WP_ACCEL_Z 60.000000000000000000 9 +1 1 Q_WP_RADIUS 250.000000000000000000 9 +1 1 Q_WP_RFND_USE 0 2 +1 1 Q_WP_SPEED 200.000000000000000000 9 +1 1 Q_WP_SPEED_DN 120.000000000000000000 9 +1 1 Q_WP_SPEED_UP 150.000000000000000000 9 +1 1 Q_WVANE_GAIN 0.000000000000000000 9 +1 1 Q_WVANE_MINROLL 1.000000000000000000 9 +1 1 Q_YAW_RATE_MAX 7.500000000000000000 9 +1 1 RALLY_INCL_HOME 1 2 +1 1 RALLY_LIMIT_KM 2.500000000000000000 9 1 1 RALLY_TOTAL 0 2 1 1 RC10_DZ 0 4 -1 1 RC10_MAX 1900 4 -1 1 RC10_MIN 1100 4 +1 1 RC10_MAX 2000 4 +1 1 RC10_MIN 1000 4 1 1 RC10_OPTION 0 4 1 1 RC10_REVERSED 0 2 1 1 RC10_TRIM 1500 4 1 1 RC11_DZ 0 4 -1 1 RC11_MAX 1900 4 -1 1 RC11_MIN 1100 4 +1 1 RC11_MAX 2000 4 +1 1 RC11_MIN 1000 4 1 1 RC11_OPTION 0 4 1 1 RC11_REVERSED 0 2 1 1 RC11_TRIM 1500 4 1 1 RC12_DZ 0 4 -1 1 RC12_MAX 1900 4 -1 1 RC12_MIN 1100 4 +1 1 RC12_MAX 2000 4 +1 1 RC12_MIN 1000 4 1 1 RC12_OPTION 0 4 1 1 RC12_REVERSED 0 2 1 1 RC12_TRIM 1500 4 1 1 RC13_DZ 0 4 -1 1 RC13_MAX 1900 4 -1 1 RC13_MIN 1100 4 +1 1 RC13_MAX 2000 4 +1 1 RC13_MIN 1000 4 1 1 RC13_OPTION 0 4 1 1 RC13_REVERSED 0 2 1 1 RC13_TRIM 1500 4 1 1 RC14_DZ 0 4 -1 1 RC14_MAX 1900 4 -1 1 RC14_MIN 1100 4 +1 1 RC14_MAX 2000 4 +1 1 RC14_MIN 1000 4 1 1 RC14_OPTION 0 4 1 1 RC14_REVERSED 0 2 1 1 RC14_TRIM 1500 4 1 1 RC15_DZ 0 4 -1 1 RC15_MAX 1900 4 -1 1 RC15_MIN 1100 4 +1 1 RC15_MAX 2000 4 +1 1 RC15_MIN 1000 4 1 1 RC15_OPTION 0 4 1 1 RC15_REVERSED 0 2 1 1 RC15_TRIM 1500 4 1 1 RC16_DZ 0 4 -1 1 RC16_MAX 1900 4 -1 1 RC16_MIN 1100 4 +1 1 RC16_MAX 2000 4 +1 1 RC16_MIN 1000 4 1 1 RC16_OPTION 0 4 1 1 RC16_REVERSED 0 2 1 1 RC16_TRIM 1500 4 -1 1 RC1_DZ 30 4 -1 1 RC1_MAX 2000 4 -1 1 RC1_MIN 1000 4 +1 1 RC1_DZ 50 4 +1 1 RC1_MAX 2006 4 +1 1 RC1_MIN 982 4 1 1 RC1_OPTION 0 4 1 1 RC1_REVERSED 0 2 1 1 RC1_TRIM 1500 4 -1 1 RC2_DZ 30 4 -1 1 RC2_MAX 2000 4 -1 1 RC2_MIN 1000 4 +1 1 RC2_DZ 50 4 +1 1 RC2_MAX 2006 4 +1 1 RC2_MIN 982 4 1 1 RC2_OPTION 0 4 -1 1 RC2_REVERSED 0 2 -1 1 RC2_TRIM 1500 4 -1 1 RC3_DZ 30 4 -1 1 RC3_MAX 2000 4 -1 1 RC3_MIN 1000 4 +1 1 RC2_REVERSED 1 2 +1 1 RC2_TRIM 1492 4 +1 1 RC3_DZ 50 4 +1 1 RC3_MAX 2006 4 +1 1 RC3_MIN 982 4 1 1 RC3_OPTION 0 4 1 1 RC3_REVERSED 0 2 -1 1 RC3_TRIM 1000 4 -1 1 RC4_DZ 30 4 -1 1 RC4_MAX 2000 4 -1 1 RC4_MIN 1000 4 +1 1 RC3_TRIM 982 4 +1 1 RC4_DZ 50 4 +1 1 RC4_MAX 2006 4 +1 1 RC4_MIN 982 4 1 1 RC4_OPTION 0 4 1 1 RC4_REVERSED 0 2 -1 1 RC4_TRIM 1500 4 +1 1 RC4_TRIM 1495 4 1 1 RC5_DZ 0 4 -1 1 RC5_MAX 2000 4 -1 1 RC5_MIN 1000 4 -1 1 RC5_OPTION 0 4 +1 1 RC5_MAX 2006 4 +1 1 RC5_MIN 982 4 +1 1 RC5_OPTION 208 4 1 1 RC5_REVERSED 0 2 -1 1 RC5_TRIM 1500 4 +1 1 RC5_TRIM 1494 4 1 1 RC6_DZ 0 4 -1 1 RC6_MAX 2000 4 -1 1 RC6_MIN 1000 4 +1 1 RC6_MAX 1956 4 +1 1 RC6_MIN 1033 4 1 1 RC6_OPTION 0 4 1 1 RC6_REVERSED 0 2 -1 1 RC6_TRIM 1500 4 +1 1 RC6_TRIM 1494 4 1 1 RC7_DZ 0 4 -1 1 RC7_MAX 2000 4 -1 1 RC7_MIN 1000 4 -1 1 RC7_OPTION 0 4 +1 1 RC7_MAX 2006 4 +1 1 RC7_MIN 982 4 +1 1 RC7_OPTION 31 4 1 1 RC7_REVERSED 0 2 -1 1 RC7_TRIM 1500 4 +1 1 RC7_TRIM 1494 4 1 1 RC8_DZ 0 4 1 1 RC8_MAX 2000 4 1 1 RC8_MIN 1000 4 @@ -716,8 +778,8 @@ 1 1 RC8_REVERSED 0 2 1 1 RC8_TRIM 1500 4 1 1 RC9_DZ 0 4 -1 1 RC9_MAX 1900 4 -1 1 RC9_MIN 1100 4 +1 1 RC9_MAX 2000 4 +1 1 RC9_MIN 1000 4 1 1 RC9_OPTION 0 4 1 1 RC9_REVERSED 0 2 1 1 RC9_TRIM 1500 4 @@ -725,159 +787,309 @@ 1 1 RCMAP_ROLL 1 2 1 1 RCMAP_THROTTLE 3 2 1 1 RCMAP_YAW 4 2 -1 1 RC_OPTIONS 32 6 -1 1 RC_OVERRIDE_TIME 3.000000000000000000 9 -1 1 RC_PROTOCOLS 1 6 -1 1 RELAY1_DEFAULT 0 2 -1 1 RELAY1_FUNCTION 1 2 -1 1 RELAY1_INVERTED 0 2 -1 1 RELAY1_PIN 13 4 -1 1 RELAY2_FUNCTION 0 2 -1 1 RELAY3_FUNCTION 0 2 -1 1 RELAY4_FUNCTION 0 2 -1 1 RELAY5_FUNCTION 0 2 -1 1 RELAY6_FUNCTION 0 2 -1 1 RLL2SRV_RMAX 90 4 -1 1 RLL2SRV_TCONST 0.250000000000000000 9 -1 1 RLL_RATE_D 0.017430000007152557 9 -1 1 RLL_RATE_D_FF 0.000000000000000000 9 -1 1 RLL_RATE_FF 0.237212002277374268 9 -1 1 RLL_RATE_FLTD 12.000000000000000000 9 -1 1 RLL_RATE_FLTE 0.000000000000000000 9 -1 1 RLL_RATE_FLTT 3.000000000000000000 9 -1 1 RLL_RATE_I 0.250000000000000000 9 -1 1 RLL_RATE_IMAX 0.666000008583068848 9 -1 1 RLL_RATE_NEF 0 2 -1 1 RLL_RATE_NTF 0 2 -1 1 RLL_RATE_P 0.300000011920928955 9 -1 1 RLL_RATE_PDMX 0.000000000000000000 9 -1 1 RLL_RATE_SMAX 150.000000000000000000 9 -1 1 RNGFND1_TYPE 0 2 +1 1 RC_OPTIONS 40 6 +1 1 RC_OVERRIDE_TIME 0.000000000000000000 9 +1 1 RELAY_DEFAULT 0 2 +1 1 RELAY_PIN -1 2 +1 1 RELAY_PIN2 55 2 +1 1 RELAY_PIN3 -1 2 +1 1 RELAY_PIN4 -1 2 +1 1 RELAY_PIN5 -1 2 +1 1 RELAY_PIN6 -1 2 +1 1 RLL2SRV_D 0.079999998211860657 9 +1 1 RLL2SRV_FF 0.000000000000000000 9 +1 1 RLL2SRV_I 0.300000011920928955 9 +1 1 RLL2SRV_IMAX 3000 4 +1 1 RLL2SRV_P 1.000000000000000000 9 +1 1 RLL2SRV_RMAX 120 4 +1 1 RLL2SRV_TCONST 0.379999995231628418 9 +1 1 RNGFND1_ADDR 102 2 +1 1 RNGFND1_FUNCTION 0 2 +1 1 RNGFND1_GNDCLEAR 9 2 +1 1 RNGFND1_MAX_CM 4000 4 +1 1 RNGFND1_MIN_CM 5 4 +1 1 RNGFND1_OFFSET 0.000000000000000000 9 +1 1 RNGFND1_ORIENT 25 2 +1 1 RNGFND1_PIN -1 2 +1 1 RNGFND1_POS_X 0.000000000000000000 9 +1 1 RNGFND1_POS_Y 0.000000000000000000 9 +1 1 RNGFND1_POS_Z 0.100000001490116119 9 +1 1 RNGFND1_PWRRNG 0 4 +1 1 RNGFND1_RMETRIC 0 2 +1 1 RNGFND1_SCALING 1.000000000000000000 9 +1 1 RNGFND1_STOP_PIN -1 2 +1 1 RNGFND1_TYPE 7 2 +1 1 RNGFND2_ADDR 0 2 +1 1 RNGFND2_FUNCTION 0 2 +1 1 RNGFND2_GNDCLEAR 9 2 +1 1 RNGFND2_MAX_CM 3000 4 +1 1 RNGFND2_MIN_CM 5 4 +1 1 RNGFND2_OFFSET 0.000000000000000000 9 +1 1 RNGFND2_ORIENT 25 2 +1 1 RNGFND2_PIN -1 2 +1 1 RNGFND2_POS_X 0.000000000000000000 9 +1 1 RNGFND2_POS_Y 0.000000000000000000 9 +1 1 RNGFND2_POS_Z 0.100000001490116119 9 +1 1 RNGFND2_PWRRNG 0 4 +1 1 RNGFND2_RMETRIC 0 2 +1 1 RNGFND2_SCALING 3.000000000000000000 9 +1 1 RNGFND2_STOP_PIN -1 2 1 1 RNGFND2_TYPE 0 2 +1 1 RNGFND3_ADDR 0 2 +1 1 RNGFND3_FUNCTION 0 2 +1 1 RNGFND3_GNDCLEAR 10 2 +1 1 RNGFND3_MAX_CM 700 4 +1 1 RNGFND3_MIN_CM 20 4 +1 1 RNGFND3_OFFSET 0.000000000000000000 9 +1 1 RNGFND3_ORIENT 25 2 +1 1 RNGFND3_PIN -1 2 +1 1 RNGFND3_POS_X 0.000000000000000000 9 +1 1 RNGFND3_POS_Y 0.000000000000000000 9 +1 1 RNGFND3_POS_Z 0.000000000000000000 9 +1 1 RNGFND3_PWRRNG 0 4 +1 1 RNGFND3_RMETRIC 1 2 +1 1 RNGFND3_SCALING 3.000000000000000000 9 +1 1 RNGFND3_STOP_PIN -1 2 1 1 RNGFND3_TYPE 0 2 +1 1 RNGFND4_ADDR 0 2 +1 1 RNGFND4_FUNCTION 0 2 +1 1 RNGFND4_GNDCLEAR 10 2 +1 1 RNGFND4_MAX_CM 700 4 +1 1 RNGFND4_MIN_CM 20 4 +1 1 RNGFND4_OFFSET 0.000000000000000000 9 +1 1 RNGFND4_ORIENT 25 2 +1 1 RNGFND4_PIN -1 2 +1 1 RNGFND4_POS_X 0.000000000000000000 9 +1 1 RNGFND4_POS_Y 0.000000000000000000 9 +1 1 RNGFND4_POS_Z 0.000000000000000000 9 +1 1 RNGFND4_PWRRNG 0 4 +1 1 RNGFND4_RMETRIC 1 2 +1 1 RNGFND4_SCALING 3.000000000000000000 9 +1 1 RNGFND4_STOP_PIN -1 2 1 1 RNGFND4_TYPE 0 2 +1 1 RNGFND5_ADDR 0 2 +1 1 RNGFND5_FUNCTION 0 2 +1 1 RNGFND5_GNDCLEAR 10 2 +1 1 RNGFND5_MAX_CM 700 4 +1 1 RNGFND5_MIN_CM 20 4 +1 1 RNGFND5_OFFSET 0.000000000000000000 9 +1 1 RNGFND5_ORIENT 25 2 +1 1 RNGFND5_PIN -1 2 +1 1 RNGFND5_POS_X 0.000000000000000000 9 +1 1 RNGFND5_POS_Y 0.000000000000000000 9 +1 1 RNGFND5_POS_Z 0.000000000000000000 9 +1 1 RNGFND5_PWRRNG 0 4 +1 1 RNGFND5_RMETRIC 1 2 +1 1 RNGFND5_SCALING 3.000000000000000000 9 +1 1 RNGFND5_STOP_PIN -1 2 1 1 RNGFND5_TYPE 0 2 +1 1 RNGFND6_ADDR 0 2 +1 1 RNGFND6_FUNCTION 0 2 +1 1 RNGFND6_GNDCLEAR 10 2 +1 1 RNGFND6_MAX_CM 700 4 +1 1 RNGFND6_MIN_CM 20 4 +1 1 RNGFND6_OFFSET 0.000000000000000000 9 +1 1 RNGFND6_ORIENT 25 2 +1 1 RNGFND6_PIN -1 2 +1 1 RNGFND6_POS_X 0.000000000000000000 9 +1 1 RNGFND6_POS_Y 0.000000000000000000 9 +1 1 RNGFND6_POS_Z 0.000000000000000000 9 +1 1 RNGFND6_PWRRNG 0 4 +1 1 RNGFND6_RMETRIC 1 2 +1 1 RNGFND6_SCALING 3.000000000000000000 9 +1 1 RNGFND6_STOP_PIN -1 2 1 1 RNGFND6_TYPE 0 2 +1 1 RNGFND7_ADDR 0 2 +1 1 RNGFND7_FUNCTION 0 2 +1 1 RNGFND7_GNDCLEAR 10 2 +1 1 RNGFND7_MAX_CM 700 4 +1 1 RNGFND7_MIN_CM 20 4 +1 1 RNGFND7_OFFSET 0.000000000000000000 9 +1 1 RNGFND7_ORIENT 25 2 +1 1 RNGFND7_PIN -1 2 +1 1 RNGFND7_POS_X 0.000000000000000000 9 +1 1 RNGFND7_POS_Y 0.000000000000000000 9 +1 1 RNGFND7_POS_Z 0.000000000000000000 9 +1 1 RNGFND7_PWRRNG 0 4 +1 1 RNGFND7_RMETRIC 1 2 +1 1 RNGFND7_SCALING 3.000000000000000000 9 +1 1 RNGFND7_STOP_PIN -1 2 1 1 RNGFND7_TYPE 0 2 +1 1 RNGFND8_ADDR 0 2 +1 1 RNGFND8_FUNCTION 0 2 +1 1 RNGFND8_GNDCLEAR 10 2 +1 1 RNGFND8_MAX_CM 700 4 +1 1 RNGFND8_MIN_CM 20 4 +1 1 RNGFND8_OFFSET 0.000000000000000000 9 +1 1 RNGFND8_ORIENT 25 2 +1 1 RNGFND8_PIN -1 2 +1 1 RNGFND8_POS_X 0.000000000000000000 9 +1 1 RNGFND8_POS_Y 0.000000000000000000 9 +1 1 RNGFND8_POS_Z 0.000000000000000000 9 +1 1 RNGFND8_PWRRNG 0 4 +1 1 RNGFND8_RMETRIC 1 2 +1 1 RNGFND8_SCALING 3.000000000000000000 9 +1 1 RNGFND8_STOP_PIN -1 2 1 1 RNGFND8_TYPE 0 2 +1 1 RNGFND9_ADDR 0 2 +1 1 RNGFND9_FUNCTION 0 2 +1 1 RNGFND9_GNDCLEAR 10 2 +1 1 RNGFND9_MAX_CM 700 4 +1 1 RNGFND9_MIN_CM 20 4 +1 1 RNGFND9_OFFSET 0.000000000000000000 9 +1 1 RNGFND9_ORIENT 25 2 +1 1 RNGFND9_PIN -1 2 +1 1 RNGFND9_POS_X 0.000000000000000000 9 +1 1 RNGFND9_POS_Y 0.000000000000000000 9 +1 1 RNGFND9_POS_Z 0.000000000000000000 9 +1 1 RNGFND9_PWRRNG 0 4 +1 1 RNGFND9_RMETRIC 1 2 +1 1 RNGFND9_SCALING 3.000000000000000000 9 +1 1 RNGFND9_STOP_PIN -1 2 1 1 RNGFND9_TYPE 0 2 +1 1 RNGFNDA_ADDR 0 2 +1 1 RNGFNDA_FUNCTION 0 2 +1 1 RNGFNDA_GNDCLEAR 10 2 +1 1 RNGFNDA_MAX_CM 700 4 +1 1 RNGFNDA_MIN_CM 20 4 +1 1 RNGFNDA_OFFSET 0.000000000000000000 9 +1 1 RNGFNDA_ORIENT 25 2 +1 1 RNGFNDA_PIN -1 2 +1 1 RNGFNDA_POS_X 0.000000000000000000 9 +1 1 RNGFNDA_POS_Y 0.000000000000000000 9 +1 1 RNGFNDA_POS_Z 0.000000000000000000 9 +1 1 RNGFNDA_PWRRNG 0 4 +1 1 RNGFNDA_RMETRIC 1 2 +1 1 RNGFNDA_SCALING 3.000000000000000000 9 +1 1 RNGFNDA_STOP_PIN -1 2 1 1 RNGFNDA_TYPE 0 2 -1 1 RNGFND_LANDING 0 2 -1 1 RNGFND_LND_ORNT 25 2 -1 1 ROLL_LIMIT_DEG 65.000000000000000000 9 -1 1 RPM1_TYPE 0 2 +1 1 RNGFND_LANDING 1 2 +1 1 RPM2_PIN -1 2 +1 1 RPM2_SCALING 1.000000000000000000 9 1 1 RPM2_TYPE 0 2 -1 1 RSSI_TYPE 0 2 -1 1 RTL_ALTITUDE 100.000000000000000000 9 +1 1 RPM_MAX 100000.000000000000000000 9 +1 1 RPM_MIN 10.000000000000000000 9 +1 1 RPM_MIN_QUAL 0.500000000000000000 9 +1 1 RPM_PIN 54 2 +1 1 RPM_SCALING 1.000000000000000000 9 +1 1 RPM_TYPE 0 2 +1 1 RSSI_ANA_PIN 0 2 +1 1 RSSI_CHANNEL 8 2 +1 1 RSSI_CHAN_HIGH 2000 4 +1 1 RSSI_CHAN_LOW 1000 4 +1 1 RSSI_PIN_HIGH 5.000000000000000000 9 +1 1 RSSI_PIN_LOW 0.000000000000000000 9 +1 1 RSSI_TYPE 3 2 +1 1 RST_MISSION_CH 0 2 +1 1 RST_SWITCH_CH 0 2 1 1 RTL_AUTOLAND 0 2 1 1 RTL_CLIMB_MIN 0 4 -1 1 RTL_RADIUS 0 4 +1 1 RTL_RADIUS 65 4 1 1 RUDDER_ONLY 0 2 1 1 RUDD_DT_GAIN 10 2 -1 1 SCALING_SPEED 15.000000000000000000 9 +1 1 SCALING_SPEED 14.000000000000000000 9 1 1 SCHED_DEBUG 0 2 -1 1 SCHED_LOOP_RATE 50 4 -1 1 SCHED_OPTIONS 0 2 -1 1 SCR_ENABLE 0 2 +1 1 SCHED_LOOP_RATE 300 4 +1 1 SCR_DEBUG_LVL 0 2 +1 1 SCR_ENABLE 1 2 +1 1 SCR_HEAP_SIZE 402400 6 +1 1 SCR_VM_I_COUNT 50000 6 1 1 SERIAL0_BAUD 115 6 1 1 SERIAL0_PROTOCOL 2 2 -1 1 SERIAL1_BAUD 57 6 +1 1 SERIAL1_BAUD 115 6 1 1 SERIAL1_OPTIONS 0 4 1 1 SERIAL1_PROTOCOL 2 2 1 1 SERIAL2_BAUD 57 6 1 1 SERIAL2_OPTIONS 0 4 -1 1 SERIAL2_PROTOCOL 2 2 -1 1 SERIAL3_BAUD 230 6 +1 1 SERIAL2_PROTOCOL 1 2 +1 1 SERIAL3_BAUD 115 6 1 1 SERIAL3_OPTIONS 0 4 -1 1 SERIAL3_PROTOCOL 5 2 -1 1 SERIAL4_BAUD 230 6 +1 1 SERIAL3_PROTOCOL 20 2 +1 1 SERIAL4_BAUD 38 6 1 1 SERIAL4_OPTIONS 0 4 -1 1 SERIAL4_PROTOCOL 5 2 +1 1 SERIAL4_PROTOCOL -1 2 1 1 SERIAL5_BAUD 57 6 1 1 SERIAL5_OPTIONS 0 4 1 1 SERIAL5_PROTOCOL -1 2 1 1 SERIAL6_BAUD 57 6 1 1 SERIAL6_OPTIONS 0 4 1 1 SERIAL6_PROTOCOL -1 2 -1 1 SERIAL7_BAUD 57 6 -1 1 SERIAL7_OPTIONS 0 4 -1 1 SERIAL7_PROTOCOL -1 2 -1 1 SERIAL_PASS1 0 2 +1 1 SERIAL_PASS1 -1 2 1 1 SERIAL_PASS2 -1 2 1 1 SERIAL_PASSTIMO 15 2 -1 1 SERVO10_FUNCTION 0 4 -1 1 SERVO10_MAX 1900 4 -1 1 SERVO10_MIN 1100 4 +1 1 SERVO10_FUNCTION 33 4 +1 1 SERVO10_MAX 2000 4 +1 1 SERVO10_MIN 1000 4 1 1 SERVO10_REVERSED 0 2 -1 1 SERVO10_TRIM 1500 4 -1 1 SERVO11_FUNCTION 0 4 -1 1 SERVO11_MAX 1900 4 -1 1 SERVO11_MIN 1100 4 +1 1 SERVO10_TRIM 1000 4 +1 1 SERVO11_FUNCTION 34 4 +1 1 SERVO11_MAX 2000 4 +1 1 SERVO11_MIN 1000 4 1 1 SERVO11_REVERSED 0 2 -1 1 SERVO11_TRIM 1500 4 +1 1 SERVO11_TRIM 1000 4 1 1 SERVO12_FUNCTION 0 4 -1 1 SERVO12_MAX 1900 4 -1 1 SERVO12_MIN 1100 4 +1 1 SERVO12_MAX 2000 4 +1 1 SERVO12_MIN 1000 4 1 1 SERVO12_REVERSED 0 2 -1 1 SERVO12_TRIM 1500 4 -1 1 SERVO13_FUNCTION 0 4 -1 1 SERVO13_MAX 1900 4 -1 1 SERVO13_MIN 1100 4 +1 1 SERVO12_TRIM 1000 4 +1 1 SERVO13_FUNCTION 35 4 +1 1 SERVO13_MAX 2000 4 +1 1 SERVO13_MIN 1000 4 1 1 SERVO13_REVERSED 0 2 -1 1 SERVO13_TRIM 1500 4 -1 1 SERVO14_FUNCTION 0 4 -1 1 SERVO14_MAX 1900 4 -1 1 SERVO14_MIN 1100 4 +1 1 SERVO13_TRIM 1000 4 +1 1 SERVO14_FUNCTION 36 4 +1 1 SERVO14_MAX 2000 4 +1 1 SERVO14_MIN 1000 4 1 1 SERVO14_REVERSED 0 2 -1 1 SERVO14_TRIM 1500 4 +1 1 SERVO14_TRIM 1000 4 1 1 SERVO15_FUNCTION 0 4 -1 1 SERVO15_MAX 1900 4 -1 1 SERVO15_MIN 1100 4 +1 1 SERVO15_MAX 2000 4 +1 1 SERVO15_MIN 1000 4 1 1 SERVO15_REVERSED 0 2 1 1 SERVO15_TRIM 1500 4 1 1 SERVO16_FUNCTION 0 4 -1 1 SERVO16_MAX 1900 4 -1 1 SERVO16_MIN 1100 4 +1 1 SERVO16_MAX 2000 4 +1 1 SERVO16_MIN 1000 4 1 1 SERVO16_REVERSED 0 2 1 1 SERVO16_TRIM 1500 4 -1 1 SERVO1_FUNCTION 4 4 -1 1 SERVO1_MAX 1900 4 -1 1 SERVO1_MIN 1100 4 +1 1 SERVO1_FUNCTION 10 4 +1 1 SERVO1_MAX 2100 4 +1 1 SERVO1_MIN 900 4 1 1 SERVO1_REVERSED 0 2 1 1 SERVO1_TRIM 1500 4 -1 1 SERVO2_FUNCTION 19 4 -1 1 SERVO2_MAX 1900 4 -1 1 SERVO2_MIN 1100 4 -1 1 SERVO2_REVERSED 0 2 -1 1 SERVO2_TRIM 1500 4 -1 1 SERVO3_FUNCTION 70 4 +1 1 SERVO2_FUNCTION 80 4 +1 1 SERVO2_MAX 1460 4 +1 1 SERVO2_MIN 1200 4 +1 1 SERVO2_REVERSED 1 2 +1 1 SERVO2_TRIM 1350 4 +1 1 SERVO3_FUNCTION 0 4 1 1 SERVO3_MAX 2000 4 1 1 SERVO3_MIN 1000 4 1 1 SERVO3_REVERSED 0 2 -1 1 SERVO3_TRIM 1000 4 -1 1 SERVO4_FUNCTION 21 4 -1 1 SERVO4_MAX 1900 4 -1 1 SERVO4_MIN 1100 4 +1 1 SERVO3_TRIM 1500 4 +1 1 SERVO4_FUNCTION 79 4 +1 1 SERVO4_MAX 1575 4 +1 1 SERVO4_MIN 1330 4 1 1 SERVO4_REVERSED 0 2 -1 1 SERVO4_TRIM 1500 4 -1 1 SERVO5_FUNCTION 0 4 -1 1 SERVO5_MAX 1900 4 -1 1 SERVO5_MIN 1100 4 +1 1 SERVO4_TRIM 1440 4 +1 1 SERVO5_FUNCTION 24 4 +1 1 SERVO5_MAX 1690 4 +1 1 SERVO5_MIN 1300 4 1 1 SERVO5_REVERSED 0 2 -1 1 SERVO5_TRIM 1500 4 -1 1 SERVO6_FUNCTION 0 4 -1 1 SERVO6_MAX 1900 4 -1 1 SERVO6_MIN 1100 4 +1 1 SERVO5_TRIM 1550 4 +1 1 SERVO6_FUNCTION 41 4 +1 1 SERVO6_MAX 1990 4 +1 1 SERVO6_MIN 1135 4 1 1 SERVO6_REVERSED 0 2 -1 1 SERVO6_TRIM 1500 4 +1 1 SERVO6_TRIM 1450 4 1 1 SERVO7_FUNCTION 0 4 -1 1 SERVO7_MAX 1900 4 -1 1 SERVO7_MIN 1100 4 +1 1 SERVO7_MAX 2000 4 +1 1 SERVO7_MIN 1000 4 1 1 SERVO7_REVERSED 0 2 1 1 SERVO7_TRIM 1500 4 1 1 SERVO8_FUNCTION 0 4 -1 1 SERVO8_MAX 1900 4 -1 1 SERVO8_MIN 1100 4 +1 1 SERVO8_MAX 2000 4 +1 1 SERVO8_MIN 1000 4 1 1 SERVO8_REVERSED 0 2 1 1 SERVO8_TRIM 1500 4 1 1 SERVO9_FUNCTION 0 4 @@ -885,386 +1097,22 @@ 1 1 SERVO9_MIN 1100 4 1 1 SERVO9_REVERSED 0 2 1 1 SERVO9_TRIM 1500 4 -1 1 SERVO_32_ENABLE 0 2 1 1 SERVO_AUTO_TRIM 0 2 -1 1 SERVO_DSHOT_ESC 0 2 -1 1 SERVO_DSHOT_RATE 0 2 -1 1 SERVO_FTW_MASK 0 6 -1 1 SERVO_FTW_POLES 14 2 -1 1 SERVO_FTW_RVMASK 0 6 -1 1 SERVO_GPIO_MASK 0 6 +1 1 SERVO_BLH_AUTO 1 2 +1 1 SERVO_BLH_DEBUG 1 2 +1 1 SERVO_BLH_MASK 0 6 +1 1 SERVO_BLH_OTYPE 0 2 +1 1 SERVO_BLH_POLES 14 2 +1 1 SERVO_BLH_PORT 0 2 +1 1 SERVO_BLH_REMASK 0 6 +1 1 SERVO_BLH_TEST 0 2 +1 1 SERVO_BLH_TMOUT 0 4 +1 1 SERVO_BLH_TRATE 100 4 1 1 SERVO_RATE 50 4 -1 1 SERVO_RC_FS_MSK 0 6 1 1 SERVO_ROB_POSMAX 4095 6 1 1 SERVO_ROB_POSMIN 0 6 1 1 SERVO_SBUS_RATE 50 4 1 1 SERVO_VOLZ_MASK 0 6 -1 1 SERVO_VOLZ_RANGE 200 4 -1 1 SID_AXIS 0 2 -1 1 SIM_ACC1_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC1_RND 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC2_RND 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC3_RND 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACCEL1_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL2_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL3_FAIL 0.000000000000000000 9 -1 1 SIM_ACC_FAIL_MSK 0 2 -1 1 SIM_ACC_FILE_RW 0 2 -1 1 SIM_ACC_TRIM_X 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Y 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Z 0.000000000000000000 9 -1 1 SIM_ADSB_ALT 1000.000000000000000000 9 -1 1 SIM_ADSB_COUNT -1 4 -1 1 SIM_ADSB_RADIUS 10000.000000000000000000 9 -1 1 SIM_ADSB_TX 0 2 -1 1 SIM_ADSB_TYPES 1 2 -1 1 SIM_ARSPD2_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD2_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD2_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD2_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD2_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD2_RND 2.000000000000000000 9 -1 1 SIM_ARSPD2_SIGN 0 2 -1 1 SIM_ARSPD_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD_RND 2.000000000000000000 9 -1 1 SIM_ARSPD_SIGN 0 2 -1 1 SIM_BAR2_DELAY 0 4 -1 1 SIM_BAR2_DISABLE 0 2 -1 1 SIM_BAR2_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR2_FREEZE 0 2 -1 1 SIM_BAR2_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR2_RND 0.200000002980232239 9 -1 1 SIM_BAR2_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_UP 0.000000000000000000 9 -1 1 SIM_BAR3_DELAY 0 4 -1 1 SIM_BAR3_DISABLE 0 2 -1 1 SIM_BAR3_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR3_FREEZE 0 2 -1 1 SIM_BAR3_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR3_RND 0.200000002980232239 9 -1 1 SIM_BAR3_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_UP 0.000000000000000000 9 -1 1 SIM_BARO_COUNT 2 2 -1 1 SIM_BARO_DELAY 0 4 -1 1 SIM_BARO_DISABLE 0 2 -1 1 SIM_BARO_DRIFT 0.000000000000000000 9 -1 1 SIM_BARO_FREEZE 0 2 -1 1 SIM_BARO_GLITCH 0.000000000000000000 9 -1 1 SIM_BARO_RND 0.200000002980232239 9 -1 1 SIM_BARO_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BARO_WCF_DN 0.000000000000000000 9 -1 1 SIM_BARO_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BARO_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_UP 0.000000000000000000 9 -1 1 SIM_BATT_CAP_AH 0.000000000000000000 9 -1 1 SIM_BATT_VOLTAGE 12.600000381469726563 9 -1 1 SIM_BAUDLIMIT_EN 0 2 -1 1 SIM_CAN_SRV_MSK 0 6 -1 1 SIM_CAN_TYPE1 1 2 -1 1 SIM_CAN_TYPE2 1 2 -1 1 SIM_CLAMP_CH 0 2 -1 1 SIM_DRIFT_SPEED 0.050000000745058060 9 -1 1 SIM_DRIFT_TIME 5.000000000000000000 9 -1 1 SIM_EFI_TYPE 0 2 -1 1 SIM_ENGINE_FAIL 0 2 -1 1 SIM_ENGINE_MUL 1.000000000000000000 9 -1 1 SIM_ESC_ARM_RPM 0.000000000000000000 9 -1 1 SIM_ESC_TELEM 1 2 -1 1 SIM_FLOAT_EXCEPT 1 2 -1 1 SIM_FLOW_DELAY 0 2 -1 1 SIM_FLOW_ENABLE 0 2 -1 1 SIM_FLOW_POS_X 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Y 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Z 0.000000000000000000 9 -1 1 SIM_FLOW_RATE 10 4 -1 1 SIM_FLOW_RND 0.050000000745058060 9 -1 1 SIM_FTOWESC_ENA 0 2 -1 1 SIM_FTOWESC_POW 4095 6 -1 1 SIM_GND_BEHAV -1 2 -1 1 SIM_GPS2_ACC 0.300000011920928955 9 -1 1 SIM_GPS2_ALT_OFS 0 4 -1 1 SIM_GPS2_BYTELOS 0.000000000000000000 9 -1 1 SIM_GPS2_DISABLE 1 2 -1 1 SIM_GPS2_DRFTALT 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_X 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Y 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Z 0.000000000000000000 9 -1 1 SIM_GPS2_HDG 0 2 -1 1 SIM_GPS2_HZ 5 2 -1 1 SIM_GPS2_JAM 0 2 -1 1 SIM_GPS2_LAG_MS 100 4 -1 1 SIM_GPS2_LCKTIME 0 4 -1 1 SIM_GPS2_NOISE 0.000000000000000000 9 -1 1 SIM_GPS2_NUMSATS 10 2 -1 1 SIM_GPS2_POS_X 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS2_TYPE 1 2 -1 1 SIM_GPS2_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Z 0.000000000000000000 9 -1 1 SIM_GPS_ACC 0.300000011920928955 9 -1 1 SIM_GPS_ALT_OFS 0 4 -1 1 SIM_GPS_BYTELOSS 0.000000000000000000 9 -1 1 SIM_GPS_DISABLE 0 2 -1 1 SIM_GPS_DRIFTALT 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_X 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Y 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Z 0.000000000000000000 9 -1 1 SIM_GPS_HDG 0 2 -1 1 SIM_GPS_HZ 5 2 -1 1 SIM_GPS_JAM 0 2 -1 1 SIM_GPS_LAG_MS 100 4 -1 1 SIM_GPS_LOCKTIME 0 4 -1 1 SIM_GPS_LOG_NUM 0 4 -1 1 SIM_GPS_NOISE 0.000000000000000000 9 -1 1 SIM_GPS_NUMSATS 10 2 -1 1 SIM_GPS_POS_X 0.000000000000000000 9 -1 1 SIM_GPS_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS_TYPE 1 2 -1 1 SIM_GPS_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Z 0.000000000000000000 9 -1 1 SIM_GRPE_ENABLE 0 2 -1 1 SIM_GRPE_PIN -1 2 -1 1 SIM_GRPS_ENABLE 0 2 -1 1 SIM_GRPS_GRAB 2000 4 -1 1 SIM_GRPS_PIN -1 2 -1 1 SIM_GRPS_RELEASE 1000 4 -1 1 SIM_GRPS_REVERSE 0 2 -1 1 SIM_GYR1_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR1_RND 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR2_RND 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR3_RND 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR_FAIL_MSK 0 2 -1 1 SIM_GYR_FILE_RW 0 2 -1 1 SIM_IE24_ENABLE 0 2 -1 1 SIM_IE24_ERROR 0 6 -1 1 SIM_IE24_STATE -1 2 -1 1 SIM_IMUT1_ENABLE 0 2 -1 1 SIM_IMUT2_ENABLE 0 2 -1 1 SIM_IMUT3_ENABLE 0 2 -1 1 SIM_IMUT_END 45.000000000000000000 9 -1 1 SIM_IMUT_FIXED 0.000000000000000000 9 -1 1 SIM_IMUT_START 25.000000000000000000 9 -1 1 SIM_IMUT_TCONST 300.000000000000000000 9 -1 1 SIM_IMU_COUNT 2 2 -1 1 SIM_IMU_ORIENT 0 2 -1 1 SIM_IMU_POS_X 0.000000000000000000 9 -1 1 SIM_IMU_POS_Y 0.000000000000000000 9 -1 1 SIM_IMU_POS_Z 0.000000000000000000 9 -1 1 SIM_INIT_ALT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LAT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LON_OFS 0.000000000000000000 9 -1 1 SIM_INS_THR_MIN 0.100000001490116119 9 -1 1 SIM_JSON_MASTER 0 2 -1 1 SIM_LED_LAYOUT 0 2 -1 1 SIM_LOOP_DELAY 0 6 -1 1 SIM_MAG1_DEVID 97539 6 -1 1 SIM_MAG1_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG1_FAIL 0 2 -1 1 SIM_MAG1_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG1_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG1_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG1_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG1_ORIENT 0 2 -1 1 SIM_MAG1_SCALING 1.000000000000000000 9 -1 1 SIM_MAG2_DEVID 131874 6 -1 1 SIM_MAG2_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG2_FAIL 0 2 -1 1 SIM_MAG2_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG2_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG2_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG2_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG2_ORIENT 0 2 -1 1 SIM_MAG2_SCALING 1.000000000000000000 9 -1 1 SIM_MAG3_DEVID 263178 6 -1 1 SIM_MAG3_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG3_FAIL 0 2 -1 1 SIM_MAG3_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG3_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG3_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG3_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG3_ORIENT 0 2 -1 1 SIM_MAG3_SCALING 1.000000000000000000 9 -1 1 SIM_MAG4_DEVID 97283 6 -1 1 SIM_MAG5_DEVID 97795 6 -1 1 SIM_MAG6_DEVID 98051 6 -1 1 SIM_MAG7_DEVID 0 6 -1 1 SIM_MAG8_DEVID 0 6 -1 1 SIM_MAG_ALY_HGT 1.000000000000000000 9 -1 1 SIM_MAG_ALY_X 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Y 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Z 0.000000000000000000 9 -1 1 SIM_MAG_DELAY 0 4 -1 1 SIM_MAG_MOT_X 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Y 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Z 0.000000000000000000 9 -1 1 SIM_MAG_RND 0.000000000000000000 9 -1 1 SIM_MAG_SAVE_IDS 1 2 -1 1 SIM_ODOM_ENABLE 0 2 -1 1 SIM_OH_MASK 0 6 -1 1 SIM_OH_RELAY_MSK -1 4 -1 1 SIM_OPOS_ALT 584.000000000000000000 9 -1 1 SIM_OPOS_HDG 353.000000000000000000 9 -1 1 SIM_OPOS_LAT -35.363262176513671875 9 -1 1 SIM_OPOS_LNG 149.165237426757812500 9 -1 1 SIM_PARA_ENABLE 0 2 -1 1 SIM_PARA_PIN -1 2 -1 1 SIM_PIN_MASK 0 4 -1 1 SIM_PLD_ALT_LMT 15.000000000000000000 9 -1 1 SIM_PLD_DIST_LMT 10.000000000000000000 9 -1 1 SIM_PLD_ENABLE 0 2 -1 1 SIM_PLD_HEIGHT 0.000000000000000000 9 -1 1 SIM_PLD_LAT 0.000000000000000000 9 -1 1 SIM_PLD_LON 0.000000000000000000 9 -1 1 SIM_PLD_OPTIONS 0 2 -1 1 SIM_PLD_ORIENT 24 2 -1 1 SIM_PLD_RATE 100 6 -1 1 SIM_PLD_SHIP 0 2 -1 1 SIM_PLD_TYPE 0 2 -1 1 SIM_PLD_YAW 0 4 -1 1 SIM_RATE_HZ 1200 4 -1 1 SIM_RC_CHANCOUNT 16 2 -1 1 SIM_RC_FAIL 0 2 -1 1 SIM_RICH_CTRL -1 2 -1 1 SIM_RICH_ENABLE 0 2 -1 1 SIM_SERVO_DELAY 0.000000000000000000 9 -1 1 SIM_SERVO_FILTER 0.000000000000000000 9 -1 1 SIM_SERVO_SPEED 0.140000000596046448 9 -1 1 SIM_SHIP_DSIZE 10.000000000000000000 9 -1 1 SIM_SHIP_ENABLE 0 2 -1 1 SIM_SHIP_OFS_X 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Y 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Z 0.000000000000000000 9 -1 1 SIM_SHIP_PSIZE 1000.000000000000000000 9 -1 1 SIM_SHIP_SPEED 3.000000000000000000 9 -1 1 SIM_SHIP_SYSID 17 2 -1 1 SIM_SHOVE_TIME 0 6 -1 1 SIM_SHOVE_X 0.000000000000000000 9 -1 1 SIM_SHOVE_Y 0.000000000000000000 9 -1 1 SIM_SHOVE_Z 0.000000000000000000 9 -1 1 SIM_SLUP_ENABLE 0 2 -1 1 SIM_SONAR_GLITCH 0.000000000000000000 9 -1 1 SIM_SONAR_POS_X 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Y 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Z 0.000000000000000000 9 -1 1 SIM_SONAR_RND 0.000000000000000000 9 -1 1 SIM_SONAR_ROT 25 2 -1 1 SIM_SONAR_SCALE 12.121199607849121094 9 -1 1 SIM_SPEEDUP 1.000000000000000000 9 -1 1 SIM_SPR_ENABLE 0 2 -1 1 SIM_SPR_PUMP -1 2 -1 1 SIM_SPR_SPIN -1 2 -1 1 SIM_TA_ENABLE 1 2 -1 1 SIM_TEMP_BFACTOR 0.000000000000000000 9 -1 1 SIM_TEMP_BRD_OFF 20.000000000000000000 9 -1 1 SIM_TEMP_START 25.000000000000000000 9 -1 1 SIM_TEMP_TCONST 30.000000000000000000 9 -1 1 SIM_TERRAIN 1 2 -1 1 SIM_THML_SCENARI 0 2 -1 1 SIM_TIDE_DIR 0.000000000000000000 9 -1 1 SIM_TIDE_SPEED 0.000000000000000000 9 -1 1 SIM_TIME_JITTER 0 4 -1 1 SIM_TWIST_TIME 0 6 -1 1 SIM_TWIST_X 0.000000000000000000 9 -1 1 SIM_TWIST_Y 0.000000000000000000 9 -1 1 SIM_TWIST_Z 0.000000000000000000 9 -1 1 SIM_UART_LOSS 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_X 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Y 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Z 0.000000000000000000 9 -1 1 SIM_VIB_MOT_HMNC 1 4 -1 1 SIM_VIB_MOT_MASK 0 6 -1 1 SIM_VIB_MOT_MAX 0.000000000000000000 9 -1 1 SIM_VIB_MOT_MULT 1.000000000000000000 9 -1 1 SIM_VICON_FAIL 0 2 -1 1 SIM_VICON_GLIT_X 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Y 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Z 0.000000000000000000 9 -1 1 SIM_VICON_POS_X 0.000000000000000000 9 -1 1 SIM_VICON_POS_Y 0.000000000000000000 9 -1 1 SIM_VICON_POS_Z 0.000000000000000000 9 -1 1 SIM_VICON_TMASK 3 2 -1 1 SIM_VICON_VGLI_X 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Y 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Z 0.000000000000000000 9 -1 1 SIM_VICON_YAW 0 4 -1 1 SIM_VICON_YAWERR 0 4 -1 1 SIM_WAVE_AMP 0.500000000000000000 9 -1 1 SIM_WAVE_DIR 0.000000000000000000 9 -1 1 SIM_WAVE_ENABLE 0 2 -1 1 SIM_WAVE_LENGTH 10.000000000000000000 9 -1 1 SIM_WAVE_SPEED 0.500000000000000000 9 -1 1 SIM_WIND_DIR 180.000000000000000000 9 -1 1 SIM_WIND_DIR_Z 0.000000000000000000 9 -1 1 SIM_WIND_SPD 0.000000000000000000 9 -1 1 SIM_WIND_T 0 2 -1 1 SIM_WIND_TC 5.000000000000000000 9 -1 1 SIM_WIND_TURB 0.000000000000000000 9 -1 1 SIM_WIND_T_ALT 60.000000000000000000 9 -1 1 SIM_WIND_T_COEF 0.009999999776482582 9 -1 1 SIM_WOW_PIN -1 2 1 1 SOAR_ENABLE 0 2 1 1 SR0_ADSB 4 4 1 1 SR0_EXTRA1 4 4 @@ -1276,26 +1124,26 @@ 1 1 SR0_RAW_CTRL 4 4 1 1 SR0_RAW_SENS 4 4 1 1 SR0_RC_CHAN 4 4 -1 1 SR1_ADSB 5 4 -1 1 SR1_EXTRA1 1 4 -1 1 SR1_EXTRA2 1 4 -1 1 SR1_EXTRA3 1 4 -1 1 SR1_EXT_STAT 1 4 +1 1 SR1_ADSB 4 4 +1 1 SR1_EXTRA1 10 4 +1 1 SR1_EXTRA2 10 4 +1 1 SR1_EXTRA3 3 4 +1 1 SR1_EXT_STAT 2 4 1 1 SR1_PARAMS 10 4 -1 1 SR1_POSITION 1 4 -1 1 SR1_RAW_CTRL 1 4 -1 1 SR1_RAW_SENS 1 4 -1 1 SR1_RC_CHAN 1 4 +1 1 SR1_POSITION 3 4 +1 1 SR1_RAW_CTRL 4 4 +1 1 SR1_RAW_SENS 2 4 +1 1 SR1_RC_CHAN 2 4 1 1 SR2_ADSB 5 4 -1 1 SR2_EXTRA1 1 4 +1 1 SR2_EXTRA1 2 4 1 1 SR2_EXTRA2 1 4 -1 1 SR2_EXTRA3 1 4 +1 1 SR2_EXTRA3 2 4 1 1 SR2_EXT_STAT 1 4 1 1 SR2_PARAMS 10 4 -1 1 SR2_POSITION 1 4 -1 1 SR2_RAW_CTRL 1 4 -1 1 SR2_RAW_SENS 1 4 -1 1 SR2_RC_CHAN 1 4 +1 1 SR2_POSITION 2 4 +1 1 SR2_RAW_CTRL 2 4 +1 1 SR2_RAW_SENS 2 4 +1 1 SR2_RC_CHAN 2 4 1 1 SR3_ADSB 5 4 1 1 SR3_EXTRA1 1 4 1 1 SR3_EXTRA2 1 4 @@ -1306,42 +1154,12 @@ 1 1 SR3_RAW_CTRL 1 4 1 1 SR3_RAW_SENS 1 4 1 1 SR3_RC_CHAN 1 4 -1 1 SR4_ADSB 5 4 -1 1 SR4_EXTRA1 1 4 -1 1 SR4_EXTRA2 1 4 -1 1 SR4_EXTRA3 1 4 -1 1 SR4_EXT_STAT 1 4 -1 1 SR4_PARAMS 10 4 -1 1 SR4_POSITION 1 4 -1 1 SR4_RAW_CTRL 1 4 -1 1 SR4_RAW_SENS 1 4 -1 1 SR4_RC_CHAN 1 4 -1 1 SR5_ADSB 5 4 -1 1 SR5_EXTRA1 1 4 -1 1 SR5_EXTRA2 1 4 -1 1 SR5_EXTRA3 1 4 -1 1 SR5_EXT_STAT 1 4 -1 1 SR5_PARAMS 10 4 -1 1 SR5_POSITION 1 4 -1 1 SR5_RAW_CTRL 1 4 -1 1 SR5_RAW_SENS 1 4 -1 1 SR5_RC_CHAN 1 4 -1 1 SR6_ADSB 5 4 -1 1 SR6_EXTRA1 1 4 -1 1 SR6_EXTRA2 1 4 -1 1 SR6_EXTRA3 1 4 -1 1 SR6_EXT_STAT 1 4 -1 1 SR6_PARAMS 10 4 -1 1 SR6_POSITION 1 4 -1 1 SR6_RAW_CTRL 1 4 -1 1 SR6_RAW_SENS 1 4 -1 1 SR6_RC_CHAN 1 4 -1 1 STAB_PITCH_DOWN 2.000000000000000000 9 +1 1 STAB_PITCH_DOWN 0.000000000000000000 9 1 1 STALL_PREVENTION 1 2 -1 1 STAT_BOOTCNT 1 4 -1 1 STAT_FLTTIME 0 6 -1 1 STAT_RESET 1 6 -1 1 STAT_RUNTIME 61 6 +1 1 STAT_BOOTCNT 1436 4 +1 1 STAT_FLTTIME 54585 6 +1 1 STAT_RESET 214388672 6 +1 1 STAT_RUNTIME 538390 6 1 1 STEER2SRV_D 0.004999999888241291 9 1 1 STEER2SRV_DRTFCT 10.000000000000000000 9 1 1 STEER2SRV_DRTMIN 4500.000000000000000000 9 @@ -1349,105 +1167,91 @@ 1 1 STEER2SRV_FF 0.000000000000000000 9 1 1 STEER2SRV_I 0.200000002980232239 9 1 1 STEER2SRV_IMAX 1500 4 -1 1 STEER2SRV_MINSPD 1.000000000000000000 9 -1 1 STEER2SRV_P 1.799999952316284180 9 +1 1 STEER2SRV_MINSPD 4.000000000000000000 9 +1 1 STEER2SRV_P 0.400000005960464478 9 1 1 STEER2SRV_TCONST 0.750000000000000000 9 1 1 STICK_MIXING 1 2 -1 1 SYSID_ENFORCE 0 2 +1 1 SYSID_ENFORCE 1 2 1 1 SYSID_MYGCS 255 4 1 1 SYSID_THISMAV 1 4 +1 1 SYS_NUM_RESETS 1408 4 1 1 TECS_APPR_SMAX 0.000000000000000000 9 -1 1 TECS_CLMB_MAX 5.000000000000000000 9 -1 1 TECS_FLARE_HGT 1.000000000000000000 9 -1 1 TECS_HDEM_TCONST 3.000000000000000000 9 +1 1 TECS_CLMB_MAX 3.000000000000000000 9 1 1 TECS_HGT_OMEGA 3.000000000000000000 9 1 1 TECS_INTEG_GAIN 0.300000011920928955 9 -1 1 TECS_LAND_ARSPD -1.000000000000000000 9 +1 1 TECS_LAND_ARSPD 10.000000000000000000 9 1 1 TECS_LAND_DAMP 0.500000000000000000 9 1 1 TECS_LAND_IGAIN 0.000000000000000000 9 1 1 TECS_LAND_PDAMP 0.000000000000000000 9 1 1 TECS_LAND_PMAX 10 2 1 1 TECS_LAND_SINK 0.250000000000000000 9 1 1 TECS_LAND_SPDWGT -1.000000000000000000 9 -1 1 TECS_LAND_SRC 0.000000000000000000 9 +1 1 TECS_LAND_SRC 0.200000002980232239 9 1 1 TECS_LAND_TCONST 2.000000000000000000 9 -1 1 TECS_LAND_TDAMP 0.000000000000000000 9 +1 1 TECS_LAND_TDAMP 0.600000023841857910 9 1 1 TECS_LAND_THR -1.000000000000000000 9 1 1 TECS_OPTIONS 0 6 -1 1 TECS_PITCH_MAX 15 2 -1 1 TECS_PITCH_MIN 0 2 -1 1 TECS_PTCH_DAMP 0.300000011920928955 9 -1 1 TECS_PTCH_FF_K 0.000000000000000000 9 -1 1 TECS_PTCH_FF_V0 12.000000000000000000 9 +1 1 TECS_PITCH_MAX 12 2 +1 1 TECS_PITCH_MIN -6 2 +1 1 TECS_PTCH_DAMP 0.400000005960464478 9 1 1 TECS_RLL2THR 10.000000000000000000 9 -1 1 TECS_SINK_MAX 5.000000000000000000 9 -1 1 TECS_SINK_MIN 2.000000000000000000 9 -1 1 TECS_SPDWEIGHT 1.000000000000000000 9 +1 1 TECS_SINK_MAX 3.000000000000000000 9 +1 1 TECS_SINK_MIN 1.500000000000000000 9 +1 1 TECS_SPDWEIGHT 1.250000000000000000 9 1 1 TECS_SPD_OMEGA 2.000000000000000000 9 1 1 TECS_SYNAIRSPEED 0 2 -1 1 TECS_THR_DAMP 0.500000000000000000 9 -1 1 TECS_TIME_CONST 5.000000000000000000 9 +1 1 TECS_THR_DAMP 0.349999994039535522 9 +1 1 TECS_TIME_CONST 3.000000000000000000 9 1 1 TECS_TKOFF_IGAIN 0.000000000000000000 9 1 1 TECS_VERT_ACC 7.000000000000000000 9 1 1 TELEM_DELAY 0 2 -1 1 TEMP1_TYPE 0 2 -1 1 TEMP2_TYPE 0 2 -1 1 TEMP3_TYPE 0 2 -1 1 TEMP_LOG 0 2 -1 1 TERRAIN_CACHE_SZ 12 4 1 1 TERRAIN_ENABLE 1 2 -1 1 TERRAIN_FOLLOW 0 6 +1 1 TERRAIN_FOLLOW 0 2 1 1 TERRAIN_LOOKAHD 2000 4 -1 1 TERRAIN_MARGIN 0.050000000745058060 9 -1 1 TERRAIN_OFS_MAX 30.000000000000000000 9 1 1 TERRAIN_OPTIONS 0 4 1 1 TERRAIN_SPACING 100 4 1 1 THROTTLE_NUDGE 1 2 1 1 THR_FAILSAFE 1 2 1 1 THR_FS_VALUE 950 4 -1 1 THR_MAX 100 2 +1 1 THR_MAX 95 2 1 1 THR_MIN 0 2 1 1 THR_PASS_STAB 0 2 1 1 THR_SLEWRATE 100 2 1 1 THR_SUPP_MAN 0 2 1 1 TKOFF_ACCEL_CNT 1 2 -1 1 TKOFF_ALT 50 4 -1 1 TKOFF_DIST 200 4 -1 1 TKOFF_FLAP_PCNT 0 2 -1 1 TKOFF_GND_PITCH 5.000000000000000000 9 -1 1 TKOFF_LVL_ALT 10 4 -1 1 TKOFF_LVL_PITCH 15 2 -1 1 TKOFF_OPTIONS 0 6 +1 1 TKOFF_ALT 10 4 +1 1 TKOFF_DIST 500 4 +1 1 TKOFF_FLAP_PCNT 15 2 +1 1 TKOFF_LVL_ALT 15 4 +1 1 TKOFF_LVL_PITCH 10 2 1 1 TKOFF_PLIM_SEC 2.000000000000000000 9 1 1 TKOFF_ROTATE_SPD 0.000000000000000000 9 1 1 TKOFF_TDRAG_ELEV 0 2 1 1 TKOFF_TDRAG_SPD1 0.000000000000000000 9 1 1 TKOFF_THR_DELAY 2 2 1 1 TKOFF_THR_MAX 0 2 -1 1 TKOFF_THR_MAX_T 4.000000000000000000 9 -1 1 TKOFF_THR_MIN 0 2 1 1 TKOFF_THR_MINACC 0.000000000000000000 9 1 1 TKOFF_THR_MINSPD 0.000000000000000000 9 1 1 TKOFF_THR_SLEW 0 2 1 1 TKOFF_TIMEOUT 0 2 -1 1 TRIM_THROTTLE 50 2 +1 1 TRIM_ARSPD_CM 1500 6 +1 1 TRIM_AUTO 0 2 +1 1 TRIM_PITCH_CD 0 4 +1 1 TRIM_THROTTLE 65 2 1 1 TUNE_CHAN 0 2 1 1 TUNE_CHAN_MAX 2000 4 1 1 TUNE_CHAN_MIN 1000 4 1 1 TUNE_ERR_THRESH 0.150000005960464478 9 -1 1 TUNE_MODE_REVERT 1 2 +1 1 TUNE_MODE_REVERT 0 2 1 1 TUNE_PARAM 0 4 1 1 TUNE_RANGE 2.000000000000000000 9 1 1 TUNE_SELECTOR 0 2 -1 1 USE_REV_THRUST 2 6 -1 1 VISO_TYPE 0 2 -1 1 VTX_ENABLE 0 2 +1 1 USE_REV_THRUST 2 4 1 1 WP_LOITER_RAD 80 4 1 1 WP_MAX_RADIUS 0 4 -1 1 WP_RADIUS 50 4 -1 1 YAW2SRV_DAMP 0.000000000000000000 9 +1 1 WP_RADIUS 15 4 +1 1 YAW2SRV_DAMP 0.419999986886978149 9 1 1 YAW2SRV_IMAX 1500 4 -1 1 YAW2SRV_INT 0.000000000000000000 9 +1 1 YAW2SRV_INT 0.200000002980232239 9 1 1 YAW2SRV_RLL 1.000000000000000000 9 1 1 YAW2SRV_SLIP 0.000000000000000000 9 -1 1 YAW_RATE_ENABLE 0 2 diff --git a/src/FirmwarePlugin/APM/Rover.OfflineEditing.params b/src/FirmwarePlugin/APM/Rover.OfflineEditing.params index b7119e3d02d4..5e9dec30bc7c 100644 --- a/src/FirmwarePlugin/APM/Rover.OfflineEditing.params +++ b/src/FirmwarePlugin/APM/Rover.OfflineEditing.params @@ -1,18 +1,20 @@ # Onboard parameters for Vehicle 1 # # Stack: ArduPilot -# Vehicle: Ground rover -# Version: 4.6.2 -# Git Revision: 1ebd4d99 +# Vehicle: Rover +# Version: 4.0.0 dev +# Git Revision: b319b27 # # Vehicle-Id Component-Id Name Value Type 1 1 ACRO_TURN_RATE 180.000000000000000000 9 1 1 AHRS_COMP_BETA 0.100000001490116119 9 -1 1 AHRS_EKF_TYPE 3 2 +1 1 AHRS_CUSTOM_PIT 0.000000000000000000 9 +1 1 AHRS_CUSTOM_ROLL 0.000000000000000000 9 +1 1 AHRS_CUSTOM_YAW 0.000000000000000000 9 +1 1 AHRS_EKF_TYPE 2 2 1 1 AHRS_GPS_GAIN 1.000000000000000000 9 1 1 AHRS_GPS_MINSATS 6 2 1 1 AHRS_GPS_USE 1 2 -1 1 AHRS_OPTIONS 0 4 1 1 AHRS_ORIENTATION 0 2 1 1 AHRS_RP_P 0.200000002980232239 9 1 1 AHRS_TRIM_X 0.000000000000000000 9 @@ -20,105 +22,61 @@ 1 1 AHRS_TRIM_Z 0.000000000000000000 9 1 1 AHRS_WIND_MAX 0 2 1 1 AHRS_YAW_P 0.200000002980232239 9 -1 1 AIS_TYPE 0 2 1 1 ARMING_ACCTHRESH 0.750000000000000000 9 1 1 ARMING_CHECK 1 6 -1 1 ARMING_MAGTHRESH 100 4 1 1 ARMING_MIS_ITEMS 0 6 -1 1 ARMING_OPTIONS 0 6 1 1 ARMING_REQUIRE 1 2 1 1 ARMING_RUDDER 2 2 -1 1 ARSPD_ENABLE 0 2 -1 1 ATC_ACCEL_MAX 1.000000000000000000 9 +1 1 ARSPD_TYPE 0 2 +1 1 ATC_ACCEL_MAX 2.000000000000000000 9 1 1 ATC_BAL_D 0.029999999329447746 9 -1 1 ATC_BAL_D_FF 0.000000000000000000 9 1 1 ATC_BAL_FF 0.000000000000000000 9 1 1 ATC_BAL_FLTD 0.000000000000000000 9 1 1 ATC_BAL_FLTE 10.000000000000000000 9 1 1 ATC_BAL_FLTT 0.000000000000000000 9 1 1 ATC_BAL_I 1.500000000000000000 9 1 1 ATC_BAL_IMAX 1.000000000000000000 9 -1 1 ATC_BAL_LIM_TC 0.500000000000000000 9 -1 1 ATC_BAL_LIM_THR 0.600000023841857910 9 -1 1 ATC_BAL_NEF 0 2 -1 1 ATC_BAL_NTF 0 2 1 1 ATC_BAL_P 1.799999952316284180 9 -1 1 ATC_BAL_PDMX 0.000000000000000000 9 -1 1 ATC_BAL_PIT_FF 0.400000005960464478 9 -1 1 ATC_BAL_SMAX 0.000000000000000000 9 +1 1 ATC_BAL_SPD_FF 1.000000000000000000 9 1 1 ATC_BRAKE 1 2 1 1 ATC_DECEL_MAX 0.000000000000000000 9 1 1 ATC_SAIL_D 0.000000000000000000 9 -1 1 ATC_SAIL_D_FF 0.000000000000000000 9 1 1 ATC_SAIL_FF 0.000000000000000000 9 1 1 ATC_SAIL_FLTD 0.000000000000000000 9 1 1 ATC_SAIL_FLTE 10.000000000000000000 9 1 1 ATC_SAIL_FLTT 0.000000000000000000 9 1 1 ATC_SAIL_I 0.100000001490116119 9 1 1 ATC_SAIL_IMAX 1.000000000000000000 9 -1 1 ATC_SAIL_NEF 0 2 -1 1 ATC_SAIL_NTF 0 2 1 1 ATC_SAIL_P 1.000000000000000000 9 -1 1 ATC_SAIL_PDMX 0.000000000000000000 9 -1 1 ATC_SAIL_SMAX 0.000000000000000000 9 1 1 ATC_SPEED_D 0.000000000000000000 9 -1 1 ATC_SPEED_D_FF 0.000000000000000000 9 1 1 ATC_SPEED_FF 0.000000000000000000 9 1 1 ATC_SPEED_FLTD 0.000000000000000000 9 1 1 ATC_SPEED_FLTE 10.000000000000000000 9 1 1 ATC_SPEED_FLTT 0.000000000000000000 9 1 1 ATC_SPEED_I 0.200000002980232239 9 1 1 ATC_SPEED_IMAX 1.000000000000000000 9 -1 1 ATC_SPEED_NEF 0 2 -1 1 ATC_SPEED_NTF 0 2 -1 1 ATC_SPEED_P 0.100000001490116119 9 -1 1 ATC_SPEED_PDMX 0.000000000000000000 9 -1 1 ATC_SPEED_SMAX 0.000000000000000000 9 +1 1 ATC_SPEED_P 0.200000002980232239 9 1 1 ATC_STOP_SPEED 0.100000001490116119 9 -1 1 ATC_STR_ACC_MAX 120.000000000000000000 9 -1 1 ATC_STR_ANG_P 2.000000000000000000 9 +1 1 ATC_STR_ACC_MAX 180.000000000000000000 9 +1 1 ATC_STR_ANG_P 2.500000000000000000 9 1 1 ATC_STR_RAT_D 0.000000000000000000 9 -1 1 ATC_STR_RAT_D_FF 0.000000000000000000 9 -1 1 ATC_STR_RAT_FF 0.750000000000000000 9 +1 1 ATC_STR_RAT_FF 0.200000002980232239 9 1 1 ATC_STR_RAT_FLTD 0.000000000000000000 9 1 1 ATC_STR_RAT_FLTE 10.000000000000000000 9 1 1 ATC_STR_RAT_FLTT 0.000000000000000000 9 1 1 ATC_STR_RAT_I 0.200000002980232239 9 1 1 ATC_STR_RAT_IMAX 1.000000000000000000 9 -1 1 ATC_STR_RAT_MAX 120.000000000000000000 9 -1 1 ATC_STR_RAT_NEF 0 2 -1 1 ATC_STR_RAT_NTF 0 2 +1 1 ATC_STR_RAT_MAX 360.000000000000000000 9 1 1 ATC_STR_RAT_P 0.200000002980232239 9 -1 1 ATC_STR_RAT_PDMX 0.000000000000000000 9 -1 1 ATC_STR_RAT_SMAX 0.000000000000000000 9 -1 1 ATC_TURN_MAX_G 0.600000023841857910 9 1 1 AUTO_KICKSTART 0.000000000000000000 9 1 1 AUTO_TRIGGER_PIN -1 2 -1 1 AVOID_ACCEL_MAX 3.000000000000000000 9 -1 1 AVOID_BACKUP_DZ 0.100000001490116119 9 -1 1 AVOID_BACKUP_SPD 0.750000000000000000 9 -1 1 AVOID_BACKZ_SPD 0.750000000000000000 9 +1 1 AVOID_ANGLE_MAX 1000 4 1 1 AVOID_BEHAVE 1 2 +1 1 AVOID_DIST_MAX 5.000000000000000000 9 1 1 AVOID_ENABLE 3 2 1 1 AVOID_MARGIN 2.000000000000000000 9 -1 1 BAL_PITCH_MAX 10.000000000000000000 9 +1 1 BAL_PITCH_MAX 2.000000000000000000 9 1 1 BAL_PITCH_TRIM 0.000000000000000000 9 -1 1 BARO1_DEVID 65540 6 -1 1 BARO1_GND_PRESS 101323.500000000000000000 9 -1 1 BARO1_WCF_ENABLE 0 2 -1 1 BARO2_DEVID 65796 6 -1 1 BARO2_GND_PRESS 101323.945312500000000000 9 -1 1 BARO2_WCF_ENABLE 0 2 -1 1 BARO3_DEVID 0 6 -1 1 BARO3_GND_PRESS 0.000000000000000000 9 -1 1 BARO3_WCF_ENABLE 0 2 -1 1 BARO_ALT_OFFSET 0.000000000000000000 9 -1 1 BARO_EXT_BUS -1 2 -1 1 BARO_FIELD_ELV 0.000000000000000000 9 -1 1 BARO_FLTR_RNG 0 2 -1 1 BARO_GND_TEMP 0.000000000000000000 9 -1 1 BARO_PRIMARY 0 2 -1 1 BARO_PROBE_EXT 0 6 1 1 BATT2_MONITOR 0 2 1 1 BATT3_MONITOR 0 2 1 1 BATT4_MONITOR 0 2 @@ -127,81 +85,83 @@ 1 1 BATT7_MONITOR 0 2 1 1 BATT8_MONITOR 0 2 1 1 BATT9_MONITOR 0 2 -1 1 BATT_AMP_OFFSET 0.000000000000000000 9 -1 1 BATT_AMP_PERVLT 17.000000000000000000 9 -1 1 BATT_ARM_MAH 0 6 -1 1 BATT_ARM_VOLT 0.000000000000000000 9 -1 1 BATT_CAPACITY 3300 6 -1 1 BATT_CRT_MAH 0.000000000000000000 9 -1 1 BATT_CRT_VOLT 0.000000000000000000 9 -1 1 BATT_CURR_PIN 12 2 -1 1 BATT_FS_CRT_ACT 0 2 -1 1 BATT_FS_LOW_ACT 0 2 -1 1 BATT_FS_VOLTSRC 0 2 -1 1 BATT_LOW_MAH 0.000000000000000000 9 -1 1 BATT_LOW_TIMER 10 2 -1 1 BATT_LOW_VOLT 0.000000000000000000 9 -1 1 BATT_MONITOR 4 2 -1 1 BATT_OPTIONS 0 6 -1 1 BATT_SERIAL_NUM -1 6 -1 1 BATT_VLT_OFFSET 0.000000000000000000 9 -1 1 BATT_VOLT_MULT 10.100000381469726563 9 -1 1 BATT_VOLT_PIN 13 2 +1 1 BATT_MONITOR 0 2 +1 1 BCN_ALT 0.000000000000000000 9 +1 1 BCN_LATITUDE 0.000000000000000000 9 +1 1 BCN_LONGITUDE 0.000000000000000000 9 +1 1 BCN_ORIENT_YAW 0 4 1 1 BCN_TYPE 0 2 +1 1 BRD_ALT_CONFIG 0 2 1 1 BRD_BOOT_DELAY 0 4 -1 1 BRD_OPTIONS 0 6 +1 1 BRD_IMUHEAT_I 0.070000000298023224 9 +1 1 BRD_IMUHEAT_IMAX 70.000000000000000000 9 +1 1 BRD_IMUHEAT_P 50.000000000000000000 9 +1 1 BRD_IMU_TARGTEMP 45 2 +1 1 BRD_IO_ENABLE 1 2 +1 1 BRD_OPTIONS 1 6 +1 1 BRD_PWM_COUNT 4 2 +1 1 BRD_PWM_VOLT_SEL 0 2 1 1 BRD_RTC_TYPES 1 2 1 1 BRD_RTC_TZ_MIN 0 4 +1 1 BRD_SAFETYENABLE 1 2 1 1 BRD_SAFETYOPTION 7 4 -1 1 BRD_SAFETY_DEFLT 0 2 1 1 BRD_SAFETY_MASK 0 6 -1 1 BRD_SD_FENCE 0 4 -1 1 BRD_SD_MISSION 0 4 -1 1 BRD_SERIAL_NUM 0 6 +1 1 BRD_SBUS_OUT 0 2 +1 1 BRD_SD_SLOWDOWN 0 2 +1 1 BRD_SER1_RTSCTS 2 2 +1 1 BRD_SER2_RTSCTS 2 2 +1 1 BRD_SERIAL_NUM 0 4 +1 1 BRD_TYPE 3 2 1 1 BRD_VBUS_MIN 4.300000190734863281 9 1 1 BRD_VSERVO_MIN 0.000000000000000000 9 1 1 BTN_ENABLE 0 2 -1 1 CAM1_TYPE 0 2 -1 1 CAM2_TYPE 0 2 1 1 CAM_AUTO_ONLY 0 2 +1 1 CAM_DURATION 10 2 +1 1 CAM_FEEDBACK_PIN -1 2 +1 1 CAM_FEEDBACK_POL 1 2 1 1 CAM_MAX_ROLL 0 4 -1 1 CAM_RC_TYPE 0 2 +1 1 CAM_MIN_INTERVAL 0 4 +1 1 CAM_RELAY_ON 1 2 +1 1 CAM_SERVO_OFF 1100 4 +1 1 CAM_SERVO_ON 1300 4 +1 1 CAM_TRIGG_DIST 0.000000000000000000 9 +1 1 CAM_TRIGG_TYPE 0 2 +1 1 CAM_TYPE 0 2 1 1 CAN_D1_PROTOCOL 1 2 -1 1 CAN_D1_PROTOCOL2 0 2 1 1 CAN_D2_PROTOCOL 1 2 -1 1 CAN_D2_PROTOCOL2 0 2 -1 1 CAN_LOGLEVEL 0 2 1 1 CAN_P1_DRIVER 0 2 1 1 CAN_P2_DRIVER 0 2 -1 1 CIRC_DIR 0 2 -1 1 CIRC_RADIUS 20.000000000000000000 9 -1 1 CIRC_SPEED 0.000000000000000000 9 +1 1 CAN_SLCAN_CPORT 0 2 +1 1 CAN_SLCAN_SERNUM -1 2 +1 1 CAN_SLCAN_TIMOUT 0 4 1 1 COMPASS_AUTODEC 1 2 1 1 COMPASS_AUTO_ROT 2 2 1 1 COMPASS_CAL_FIT 16.000000000000000000 9 -1 1 COMPASS_DEC 0.223357245326042175 9 -1 1 COMPASS_DEV_ID 97539 6 -1 1 COMPASS_DEV_ID2 131874 6 -1 1 COMPASS_DEV_ID3 263178 6 -1 1 COMPASS_DEV_ID4 97283 6 -1 1 COMPASS_DEV_ID5 97795 6 -1 1 COMPASS_DEV_ID6 98051 6 +1 1 COMPASS_CUS_PIT 0.000000000000000000 9 +1 1 COMPASS_CUS_ROLL 0.000000000000000000 9 +1 1 COMPASS_CUS_YAW 0.000000000000000000 9 +1 1 COMPASS_DEC 0.000000000000000000 9 +1 1 COMPASS_DEV_ID 590114 6 +1 1 COMPASS_DEV_ID2 0 6 +1 1 COMPASS_DEV_ID3 0 6 +1 1 COMPASS_DEV_ID4 0 6 +1 1 COMPASS_DEV_ID5 0 6 +1 1 COMPASS_DEV_ID6 0 6 1 1 COMPASS_DEV_ID7 0 6 1 1 COMPASS_DEV_ID8 0 6 -1 1 COMPASS_DIA2_X 1.000000000000000000 9 -1 1 COMPASS_DIA2_Y 1.000000000000000000 9 -1 1 COMPASS_DIA2_Z 1.000000000000000000 9 -1 1 COMPASS_DIA3_X 1.000000000000000000 9 -1 1 COMPASS_DIA3_Y 1.000000000000000000 9 -1 1 COMPASS_DIA3_Z 1.000000000000000000 9 +1 1 COMPASS_DIA2_X 0.000000000000000000 9 +1 1 COMPASS_DIA2_Y 0.000000000000000000 9 +1 1 COMPASS_DIA2_Z 0.000000000000000000 9 +1 1 COMPASS_DIA3_X 0.000000000000000000 9 +1 1 COMPASS_DIA3_Y 0.000000000000000000 9 +1 1 COMPASS_DIA3_Z 0.000000000000000000 9 1 1 COMPASS_DIA_X 1.000000000000000000 9 1 1 COMPASS_DIA_Y 1.000000000000000000 9 1 1 COMPASS_DIA_Z 1.000000000000000000 9 -1 1 COMPASS_DISBLMSK 0 6 1 1 COMPASS_ENABLE 1 2 1 1 COMPASS_EXTERN2 0 2 1 1 COMPASS_EXTERN3 0 2 -1 1 COMPASS_EXTERNAL 1 2 +1 1 COMPASS_EXTERNAL 0 2 1 1 COMPASS_FLTR_RNG 0 2 1 1 COMPASS_LEARN 0 2 1 1 COMPASS_MOT2_X 0.000000000000000000 9 @@ -224,244 +184,190 @@ 1 1 COMPASS_ODI_Y 0.000000000000000000 9 1 1 COMPASS_ODI_Z 0.000000000000000000 9 1 1 COMPASS_OFFS_MAX 1800 4 -1 1 COMPASS_OFS2_X 5.000000000000000000 9 -1 1 COMPASS_OFS2_Y 13.000000000000000000 9 -1 1 COMPASS_OFS2_Z -18.000000000000000000 9 -1 1 COMPASS_OFS3_X 5.000000000000000000 9 -1 1 COMPASS_OFS3_Y 13.000000000000000000 9 -1 1 COMPASS_OFS3_Z -18.000000000000000000 9 -1 1 COMPASS_OFS_X 5.000000000000000000 9 -1 1 COMPASS_OFS_Y 13.000000000000000000 9 -1 1 COMPASS_OFS_Z -18.000000000000000000 9 +1 1 COMPASS_OFS2_X 0.000000000000000000 9 +1 1 COMPASS_OFS2_Y 0.000000000000000000 9 +1 1 COMPASS_OFS2_Z 0.000000000000000000 9 +1 1 COMPASS_OFS3_X 0.000000000000000000 9 +1 1 COMPASS_OFS3_Y 0.000000000000000000 9 +1 1 COMPASS_OFS3_Z 0.000000000000000000 9 +1 1 COMPASS_OFS_X 0.000000000000000000 9 +1 1 COMPASS_OFS_Y 0.000000000000000000 9 +1 1 COMPASS_OFS_Z 0.000000000000000000 9 1 1 COMPASS_OPTIONS 0 4 1 1 COMPASS_ORIENT 0 2 1 1 COMPASS_ORIENT2 0 2 1 1 COMPASS_ORIENT3 0 2 1 1 COMPASS_PMOT_EN 0 2 -1 1 COMPASS_PRIO1_ID 97539 6 -1 1 COMPASS_PRIO2_ID 131874 6 -1 1 COMPASS_PRIO3_ID 263178 6 -1 1 COMPASS_SCALE 1.000000000000000000 9 -1 1 COMPASS_SCALE2 1.000000000000000000 9 -1 1 COMPASS_SCALE3 1.000000000000000000 9 +1 1 COMPASS_PRIO1_ID 590114 6 +1 1 COMPASS_PRIO2_ID 0 6 +1 1 COMPASS_PRIO3_ID 0 6 +1 1 COMPASS_SCALE 0.000000000000000000 9 +1 1 COMPASS_SCALE2 0.000000000000000000 9 +1 1 COMPASS_SCALE3 0.000000000000000000 9 +1 1 COMPASS_TYPEMASK 32 6 1 1 COMPASS_USE 1 2 1 1 COMPASS_USE2 1 2 1 1 COMPASS_USE3 1 2 1 1 CRASH_ANGLE 0 2 -1 1 CRUISE_SPEED 5.000000000000000000 9 -1 1 CRUISE_THROTTLE 30 2 -1 1 CUST_ROT_ENABLE 0 2 -1 1 DID_ENABLE 0 2 -1 1 DOCK_DIR -1.000000000000000000 9 -1 1 DOCK_HDG_CORR_EN 0 2 -1 1 DOCK_HDG_CORR_WT 0.750000000000000000 9 -1 1 DOCK_SPEED 0.000000000000000000 9 -1 1 DOCK_STOP_DIST 0.300000011920928955 9 -1 1 EAHRS_TYPE 0 2 -1 1 EFI_TYPE 0 2 -1 1 EK2_ENABLE 0 2 -1 1 EK3_ABIAS_P_NSE 0.019999999552965164 9 -1 1 EK3_ACC_BIAS_LIM 1.000000000000000000 9 -1 1 EK3_ACC_P_NSE 0.349999994039535522 9 -1 1 EK3_AFFINITY 0 6 -1 1 EK3_ALT_M_NSE 2.000000000000000000 9 -1 1 EK3_BCN_DELAY 50 2 -1 1 EK3_BCN_I_GTE 500 4 -1 1 EK3_BCN_M_NSE 1.000000000000000000 9 -1 1 EK3_BETA_MASK 0 2 -1 1 EK3_CHECK_SCALE 100 4 -1 1 EK3_DRAG_BCOEF_X 0.000000000000000000 9 -1 1 EK3_DRAG_BCOEF_Y 0.000000000000000000 9 -1 1 EK3_DRAG_MCOEF 0.000000000000000000 9 -1 1 EK3_DRAG_M_NSE 0.500000000000000000 9 -1 1 EK3_EAS_I_GATE 400 4 -1 1 EK3_EAS_M_NSE 1.399999976158142090 9 -1 1 EK3_ENABLE 1 2 -1 1 EK3_ERR_THRESH 0.200000002980232239 9 -1 1 EK3_FLOW_DELAY 10 2 -1 1 EK3_FLOW_I_GATE 300 4 -1 1 EK3_FLOW_M_NSE 0.250000000000000000 9 -1 1 EK3_FLOW_USE 1 2 -1 1 EK3_GBIAS_P_NSE 0.001000000047497451 9 -1 1 EK3_GLITCH_RAD 25 2 -1 1 EK3_GND_EFF_DZ 4.000000000000000000 9 -1 1 EK3_GPS_CHECK 31 2 -1 1 EK3_GPS_VACC_MAX 0.000000000000000000 9 -1 1 EK3_GSF_RST_MAX 2 2 -1 1 EK3_GSF_RUN_MASK 3 2 -1 1 EK3_GSF_USE_MASK 3 2 -1 1 EK3_GYRO_P_NSE 0.014999999664723873 9 -1 1 EK3_HGT_DELAY 60 4 -1 1 EK3_HGT_I_GATE 500 4 -1 1 EK3_HRT_FILT 2.000000000000000000 9 -1 1 EK3_IMU_MASK 3 2 -1 1 EK3_LOG_LEVEL 0 2 -1 1 EK3_MAGB_P_NSE 0.000099999997473788 9 -1 1 EK3_MAGE_P_NSE 0.001000000047497451 9 -1 1 EK3_MAG_CAL 2 2 -1 1 EK3_MAG_EF_LIM 50 4 -1 1 EK3_MAG_I_GATE 300 4 -1 1 EK3_MAG_MASK 0 2 -1 1 EK3_MAG_M_NSE 0.050000000745058060 9 -1 1 EK3_MAX_FLOW 2.500000000000000000 9 -1 1 EK3_NOAID_M_NSE 10.000000000000000000 9 -1 1 EK3_OGNM_TEST_SF 2.000000000000000000 9 -1 1 EK3_OGN_HGT_MASK 0 2 -1 1 EK3_OPTIONS 0 6 -1 1 EK3_POSNE_M_NSE 0.500000000000000000 9 -1 1 EK3_POS_I_GATE 500 4 -1 1 EK3_PRIMARY 0 2 -1 1 EK3_RNG_I_GATE 500 4 -1 1 EK3_RNG_M_NSE 0.500000000000000000 9 -1 1 EK3_RNG_USE_HGT -1 2 -1 1 EK3_RNG_USE_SPD 2.000000000000000000 9 -1 1 EK3_SRC1_POSXY 3 2 -1 1 EK3_SRC1_POSZ 1 2 -1 1 EK3_SRC1_VELXY 3 2 -1 1 EK3_SRC1_VELZ 3 2 -1 1 EK3_SRC1_YAW 1 2 -1 1 EK3_SRC2_POSXY 0 2 -1 1 EK3_SRC2_POSZ 1 2 -1 1 EK3_SRC2_VELXY 0 2 -1 1 EK3_SRC2_VELZ 0 2 -1 1 EK3_SRC2_YAW 0 2 -1 1 EK3_SRC3_POSXY 0 2 -1 1 EK3_SRC3_POSZ 1 2 -1 1 EK3_SRC3_VELXY 0 2 -1 1 EK3_SRC3_VELZ 0 2 -1 1 EK3_SRC3_YAW 0 2 -1 1 EK3_SRC_OPTIONS 1 4 -1 1 EK3_TAU_OUTPUT 25 2 -1 1 EK3_TERR_GRAD 0.100000001490116119 9 -1 1 EK3_VELD_M_NSE 0.699999988079071045 9 -1 1 EK3_VELNE_M_NSE 0.500000000000000000 9 -1 1 EK3_VEL_I_GATE 500 4 -1 1 EK3_VIS_VERR_MAX 0.899999976158142090 9 -1 1 EK3_VIS_VERR_MIN 0.100000001490116119 9 -1 1 EK3_WENC_VERR 0.100000001490116119 9 -1 1 EK3_WIND_PSCALE 1.000000000000000000 9 -1 1 EK3_WIND_P_NSE 0.100000001490116119 9 -1 1 EK3_YAW_I_GATE 300 4 -1 1 EK3_YAW_M_NSE 0.500000000000000000 9 -1 1 ESC_TLM_MAV_OFS 0 2 +1 1 CRUISE_SPEED 2.000000000000000000 9 +1 1 CRUISE_THROTTLE 50 2 +1 1 EK2_ABIAS_P_NSE 0.004999999888241291 9 +1 1 EK2_ACC_P_NSE 0.600000023841857910 9 +1 1 EK2_ALT_M_NSE 3.000000000000000000 9 +1 1 EK2_ALT_SOURCE 0 2 +1 1 EK2_BCN_DELAY 50 2 +1 1 EK2_BCN_I_GTE 500 4 +1 1 EK2_BCN_M_NSE 1.000000000000000000 9 +1 1 EK2_CHECK_SCALE 100 4 +1 1 EK2_EAS_I_GATE 400 4 +1 1 EK2_EAS_M_NSE 1.399999976158142090 9 +1 1 EK2_ENABLE 1 2 +1 1 EK2_EXTNAV_DELAY 10 2 +1 1 EK2_FLOW_DELAY 10 2 +1 1 EK2_FLOW_I_GATE 300 4 +1 1 EK2_FLOW_M_NSE 0.250000000000000000 9 +1 1 EK2_FLOW_USE 1 2 +1 1 EK2_GBIAS_P_NSE 0.000099999997473788 9 +1 1 EK2_GLITCH_RAD 25 2 +1 1 EK2_GPS_CHECK 31 2 +1 1 EK2_GPS_TYPE 0 2 +1 1 EK2_GSCL_P_NSE 0.000500000023748726 9 +1 1 EK2_GYRO_P_NSE 0.029999999329447746 9 +1 1 EK2_HGT_DELAY 60 4 +1 1 EK2_HGT_I_GATE 500 4 +1 1 EK2_HRT_FILT 2.000000000000000000 9 +1 1 EK2_IMU_MASK 3 2 +1 1 EK2_LOG_MASK 1 2 +1 1 EK2_MAGB_P_NSE 0.000099999997473788 9 +1 1 EK2_MAGE_P_NSE 0.001000000047497451 9 +1 1 EK2_MAG_CAL 2 2 +1 1 EK2_MAG_EF_LIM 50 4 +1 1 EK2_MAG_I_GATE 300 4 +1 1 EK2_MAG_MASK 0 2 +1 1 EK2_MAG_M_NSE 0.050000000745058060 9 +1 1 EK2_MAX_FLOW 2.500000000000000000 9 +1 1 EK2_NOAID_M_NSE 10.000000000000000000 9 +1 1 EK2_OGN_HGT_MASK 0 2 +1 1 EK2_POSNE_M_NSE 1.000000000000000000 9 +1 1 EK2_POS_I_GATE 500 4 +1 1 EK2_RNG_I_GATE 500 4 +1 1 EK2_RNG_M_NSE 0.500000000000000000 9 +1 1 EK2_RNG_USE_HGT -1 2 +1 1 EK2_RNG_USE_SPD 2.000000000000000000 9 +1 1 EK2_TAU_OUTPUT 25 2 +1 1 EK2_TERR_GRAD 0.100000001490116119 9 +1 1 EK2_VELD_M_NSE 0.699999988079071045 9 +1 1 EK2_VELNE_M_NSE 0.500000000000000000 9 +1 1 EK2_VEL_I_GATE 500 4 +1 1 EK2_WIND_PSCALE 0.500000000000000000 9 +1 1 EK2_WIND_P_NSE 0.100000001490116119 9 +1 1 EK2_YAW_I_GATE 300 4 +1 1 EK2_YAW_M_NSE 0.500000000000000000 9 +1 1 EK3_ENABLE 0 2 1 1 FENCE_ACTION 1 2 1 1 FENCE_ENABLE 0 2 1 1 FENCE_MARGIN 2.000000000000000000 9 1 1 FENCE_RADIUS 300.000000000000000000 9 1 1 FENCE_TOTAL 0 2 1 1 FENCE_TYPE 6 2 -1 1 FFT_ENABLE 0 2 -1 1 FILT1_TYPE 0 2 -1 1 FILT2_TYPE 0 2 -1 1 FILT3_TYPE 0 2 -1 1 FILT4_TYPE 0 2 -1 1 FILT5_TYPE 0 2 -1 1 FILT6_TYPE 0 2 -1 1 FILT7_TYPE 0 2 -1 1 FILT8_TYPE 0 2 -1 1 FLOW_TYPE 0 2 -1 1 FLTMODE_GCSBLOCK 0 6 1 1 FOLL_ENABLE 0 2 1 1 FORMAT_VERSION 16 4 1 1 FRAME_CLASS 1 2 1 1 FRAME_TYPE 0 2 -1 1 FRSKY_DNLINK1_ID 20 2 -1 1 FRSKY_DNLINK2_ID 7 2 -1 1 FRSKY_DNLINK_ID 27 2 -1 1 FRSKY_OPTIONS 0 2 -1 1 FRSKY_UPLINK_ID 13 2 1 1 FS_ACTION 2 2 1 1 FS_CRASH_CHECK 0 2 1 1 FS_EKF_ACTION 1 2 1 1 FS_EKF_THRESH 0.800000011920928955 9 1 1 FS_GCS_ENABLE 0 2 -1 1 FS_GCS_TIMEOUT 5.000000000000000000 9 1 1 FS_OPTIONS 0 6 1 1 FS_THR_ENABLE 1 2 1 1 FS_THR_VALUE 910 4 1 1 FS_TIMEOUT 1.500000000000000000 9 1 1 GCS_PID_MASK 0 4 -1 1 GEN_TYPE 0 2 -1 1 GPS1_CAN_NODEID 0 6 -1 1 GPS1_CAN_OVRIDE 0 6 -1 1 GPS1_COM_PORT 1 2 -1 1 GPS1_DELAY_MS 0 4 -1 1 GPS1_GNSS_MODE 0 2 -1 1 GPS1_MB_TYPE 0 2 -1 1 GPS1_POS_X 0.000000000000000000 9 -1 1 GPS1_POS_Y 0.000000000000000000 9 -1 1 GPS1_POS_Z 0.000000000000000000 9 -1 1 GPS1_RATE_MS 200 4 -1 1 GPS1_TYPE 1 2 -1 1 GPS2_TYPE 0 2 +1 1 GND_ABS_PRESS 85525.578125000000000000 9 +1 1 GND_ABS_PRESS2 85537.359375000000000000 9 +1 1 GND_ABS_PRESS3 0.000000000000000000 9 +1 1 GND_ALT_OFFSET 0.000000000000000000 9 +1 1 GND_EXT_BUS -1 2 +1 1 GND_FLTR_RNG 0 2 +1 1 GND_PRIMARY 0 2 +1 1 GND_PROBE_EXT 0 6 +1 1 GND_TEMP 0.000000000000000000 9 1 1 GPS_AUTO_CONFIG 1 2 1 1 GPS_AUTO_SWITCH 1 2 1 1 GPS_BLEND_MASK 5 2 +1 1 GPS_BLEND_TC 10.000000000000000000 9 +1 1 GPS_DELAY_MS 0 4 +1 1 GPS_DELAY_MS2 0 4 1 1 GPS_DRV_OPTIONS 0 4 +1 1 GPS_GNSS_MODE 0 2 +1 1 GPS_GNSS_MODE2 0 2 1 1 GPS_INJECT_TO 127 2 +1 1 GPS_MIN_DGPS 100 2 1 1 GPS_MIN_ELEV -100 2 1 1 GPS_NAVFILTER 8 2 -1 1 GPS_PRIMARY 0 2 +1 1 GPS_POS1_X 0.000000000000000000 9 +1 1 GPS_POS1_Y 0.000000000000000000 9 +1 1 GPS_POS1_Z 0.000000000000000000 9 +1 1 GPS_POS2_X 0.000000000000000000 9 +1 1 GPS_POS2_Y 0.000000000000000000 9 +1 1 GPS_POS2_Z 0.000000000000000000 9 +1 1 GPS_RATE_MS 200 4 +1 1 GPS_RATE_MS2 200 4 1 1 GPS_RAW_DATA 0 2 1 1 GPS_SAVE_CFG 2 2 1 1 GPS_SBAS_MODE 2 2 1 1 GPS_SBP_LOGMASK -256 4 +1 1 GPS_TYPE 1 2 +1 1 GPS_TYPE2 0 2 1 1 GRIP_ENABLE 0 2 -1 1 GUID_OPTIONS 0 6 1 1 INITIAL_MODE 0 2 -1 1 INS_ACC1_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2OFFS_X 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Y 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Z 0.001000000047497451 9 -1 1 INS_ACC2SCAL_X 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Y 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Z 1.001000046730041504 9 -1 1 INS_ACC2_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2_ID 2753036 6 +1 1 INS_ACC2OFFS_X 0.000000000000000000 9 +1 1 INS_ACC2OFFS_Y 0.000000000000000000 9 +1 1 INS_ACC2OFFS_Z 0.000000000000000000 9 +1 1 INS_ACC2SCAL_X 1.000000000000000000 9 +1 1 INS_ACC2SCAL_Y 1.000000000000000000 9 +1 1 INS_ACC2SCAL_Z 1.000000000000000000 9 +1 1 INS_ACC2_ID 2883874 6 1 1 INS_ACC3OFFS_X 0.000000000000000000 9 1 1 INS_ACC3OFFS_Y 0.000000000000000000 9 1 1 INS_ACC3OFFS_Z 0.000000000000000000 9 1 1 INS_ACC3SCAL_X 1.000000000000000000 9 1 1 INS_ACC3SCAL_Y 1.000000000000000000 9 1 1 INS_ACC3SCAL_Z 1.000000000000000000 9 -1 1 INS_ACC3_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC3_ID 0 6 +1 1 INS_ACC3_ID 3015690 6 1 1 INS_ACCEL_FILTER 10 4 -1 1 INS_ACCOFFS_X 0.001000000047497451 9 -1 1 INS_ACCOFFS_Y 0.001000000047497451 9 -1 1 INS_ACCOFFS_Z 0.001000000047497451 9 -1 1 INS_ACCSCAL_X 1.001000046730041504 9 -1 1 INS_ACCSCAL_Y 1.001000046730041504 9 -1 1 INS_ACCSCAL_Z 1.001000046730041504 9 +1 1 INS_ACCOFFS_X 0.000000000000000000 9 +1 1 INS_ACCOFFS_Y 0.000000000000000000 9 +1 1 INS_ACCOFFS_Z 0.000000000000000000 9 +1 1 INS_ACCSCAL_X 1.000000000000000000 9 +1 1 INS_ACCSCAL_Y 1.000000000000000000 9 +1 1 INS_ACCSCAL_Z 1.000000000000000000 9 1 1 INS_ACC_BODYFIX 2 2 -1 1 INS_ACC_ID 2753028 6 +1 1 INS_ACC_ID 3081250 6 1 1 INS_ENABLE_MASK 127 2 -1 1 INS_FAST_SAMPLE 1 2 -1 1 INS_GYR1_CALTEMP 25.018352508544921875 9 -1 1 INS_GYR2OFFS_X -0.000019538194464985 9 -1 1 INS_GYR2OFFS_Y 0.000107690095319413 9 -1 1 INS_GYR2OFFS_Z 0.000016318013877026 9 -1 1 INS_GYR2_CALTEMP 25.018352508544921875 9 -1 1 INS_GYR2_ID 2752780 6 -1 1 INS_GYR3OFFS_X 0.000000000000000000 9 -1 1 INS_GYR3OFFS_Y 0.000000000000000000 9 -1 1 INS_GYR3OFFS_Z 0.000000000000000000 9 -1 1 INS_GYR3_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR3_ID 0 6 -1 1 INS_GYROFFS_X -0.000007733098755125 9 -1 1 INS_GYROFFS_Y 0.000048677153245080 9 -1 1 INS_GYROFFS_Z 0.000043519139580894 9 +1 1 INS_FAST_SAMPLE 7 2 +1 1 INS_GYR2OFFS_X 0.001012568362057209 9 +1 1 INS_GYR2OFFS_Y -0.013606202788650990 9 +1 1 INS_GYR2OFFS_Z 0.016851436346769333 9 +1 1 INS_GYR2_ID 2883874 6 +1 1 INS_GYR3OFFS_X -0.014680972322821617 9 +1 1 INS_GYR3OFFS_Y -0.000609529088251293 9 +1 1 INS_GYR3OFFS_Z 0.013640789315104485 9 +1 1 INS_GYR3_ID 3015690 6 +1 1 INS_GYROFFS_X 0.020715419203042984 9 +1 1 INS_GYROFFS_Y -0.024957574903964996 9 +1 1 INS_GYROFFS_Z 0.019266281276941299 9 1 1 INS_GYRO_FILTER 4 4 -1 1 INS_GYRO_RATE 0 2 1 1 INS_GYR_CAL 1 2 -1 1 INS_GYR_ID 2752772 6 -1 1 INS_HNTC2_ENABLE 0 2 +1 1 INS_GYR_ID 3081250 6 1 1 INS_HNTCH_ENABLE 0 2 1 1 INS_LOG_BAT_CNT 1024 4 1 1 INS_LOG_BAT_LGCT 32 4 1 1 INS_LOG_BAT_LGIN 20 2 -1 1 INS_LOG_BAT_MASK 127 2 +1 1 INS_LOG_BAT_MASK 0 2 1 1 INS_LOG_BAT_OPT 0 2 +1 1 INS_NOTCH_ENABLE 0 2 1 1 INS_POS1_X 0.000000000000000000 9 1 1 INS_POS1_Y 0.000000000000000000 9 1 1 INS_POS1_Z 0.000000000000000000 9 @@ -471,47 +377,54 @@ 1 1 INS_POS3_X 0.000000000000000000 9 1 1 INS_POS3_Y 0.000000000000000000 9 1 1 INS_POS3_Z 0.000000000000000000 9 -1 1 INS_RAW_LOG_OPT 0 4 1 1 INS_STILL_THRESH 0.100000001490116119 9 -1 1 INS_TCAL1_ENABLE 0 2 -1 1 INS_TCAL2_ENABLE 0 2 -1 1 INS_TCAL3_ENABLE 0 2 -1 1 INS_TCAL_OPTIONS 0 6 1 1 INS_TRIM_OPTION 1 2 1 1 INS_USE 1 2 1 1 INS_USE2 1 2 1 1 INS_USE3 1 2 -1 1 KDE_NPOLE 14 2 1 1 LOG_BACKEND_TYPE 1 2 1 1 LOG_BITMASK 65535 6 -1 1 LOG_BLK_RATEMAX 0.000000000000000000 9 -1 1 LOG_DARM_RATEMAX 0.000000000000000000 9 1 1 LOG_DISARMED 0 2 -1 1 LOG_FILE_BUFSIZE 200 4 +1 1 LOG_FILE_BUFSIZE 50 2 1 1 LOG_FILE_DSRMROT 0 2 -1 1 LOG_FILE_MB_FREE 500 4 -1 1 LOG_FILE_RATEMAX 0.000000000000000000 9 1 1 LOG_FILE_TIMEOUT 5 4 1 1 LOG_MAV_BUFSIZE 8 2 -1 1 LOG_MAV_RATEMAX 0.000000000000000000 9 -1 1 LOG_MAX_FILES 500 4 1 1 LOG_REPLAY 0 2 1 1 LOIT_RADIUS 2.000000000000000000 9 1 1 LOIT_SPEED_GAIN 0.500000000000000000 9 1 1 LOIT_TYPE 0 2 -1 1 MANUAL_OPTIONS 0 6 -1 1 MANUAL_STR_EXPO 0.000000000000000000 9 1 1 MIS_DONE_BEHAVE 0 2 1 1 MIS_OPTIONS 0 4 1 1 MIS_RESTART 0 2 1 1 MIS_TOTAL 0 4 -1 1 MNT1_TYPE 0 2 -1 1 MNT2_TYPE 0 2 +1 1 MNT_ANGMAX_PAN 4500 4 +1 1 MNT_ANGMAX_ROL 4500 4 +1 1 MNT_ANGMAX_TIL 4500 4 +1 1 MNT_ANGMIN_PAN -4500 4 +1 1 MNT_ANGMIN_ROL -4500 4 +1 1 MNT_ANGMIN_TIL -4500 4 +1 1 MNT_DEFLT_MODE 3 2 +1 1 MNT_JSTICK_SPD 0 2 +1 1 MNT_LEAD_PTCH 0.000000000000000000 9 +1 1 MNT_LEAD_RLL 0.000000000000000000 9 +1 1 MNT_NEUTRAL_X 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Y 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Z 0.000000000000000000 9 +1 1 MNT_RC_IN_PAN 0 2 +1 1 MNT_RC_IN_ROLL 0 2 +1 1 MNT_RC_IN_TILT 0 2 +1 1 MNT_RETRACT_X 0.000000000000000000 9 +1 1 MNT_RETRACT_Y 0.000000000000000000 9 +1 1 MNT_RETRACT_Z 0.000000000000000000 9 +1 1 MNT_STAB_PAN 0 2 +1 1 MNT_STAB_ROLL 0 2 +1 1 MNT_STAB_TILT 0 2 +1 1 MNT_TYPE 0 2 1 1 MODE1 0 2 1 1 MODE2 0 2 -1 1 MODE3 11 2 -1 1 MODE4 10 2 -1 1 MODE5 2 2 +1 1 MODE3 0 2 +1 1 MODE4 0 2 +1 1 MODE5 0 2 1 1 MODE6 0 2 1 1 MODE_CH 8 2 1 1 MOT_PWM_FREQ 16 2 @@ -519,49 +432,40 @@ 1 1 MOT_SAFE_DISARM 0 2 1 1 MOT_SLEWRATE 100 4 1 1 MOT_SPD_SCA_BASE 1.000000000000000000 9 -1 1 MOT_STR_THR_MIX 0.500000000000000000 9 1 1 MOT_THR_MAX 100 2 1 1 MOT_THR_MIN 0 2 -1 1 MOT_THST_ASYM 1.000000000000000000 9 1 1 MOT_THST_EXPO 0.000000000000000000 9 -1 1 MOT_VEC_ANGLEMAX 0.000000000000000000 9 -1 1 MSP_OPTIONS 0 2 -1 1 MSP_OSD_NCELLS 0 2 -1 1 NET_ENABLE 0 2 -1 1 NET_P1_TYPE 0 2 -1 1 NET_P2_TYPE 0 2 -1 1 NET_P3_TYPE 0 2 -1 1 NET_P4_TYPE 0 2 -1 1 NMEA_MSG_EN 3 4 -1 1 NMEA_RATE_MS 100 4 +1 1 MOT_VEC_THR_BASE 0.000000000000000000 9 +1 1 NAVL1_DAMPING 0.750000000000000000 9 +1 1 NAVL1_PERIOD 8.000000000000000000 9 +1 1 NAVL1_XTRACK_I 0.019999999552965164 9 +1 1 NTF_BUZZ_ENABLE 1 2 1 1 NTF_BUZZ_ON_LVL 1 2 -1 1 NTF_BUZZ_PIN -1 2 -1 1 NTF_BUZZ_TYPES 5 2 +1 1 NTF_BUZZ_PIN 0 2 1 1 NTF_BUZZ_VOLUME 100 2 1 1 NTF_DISPLAY_TYPE 0 2 1 1 NTF_LED_BRIGHT 3 2 1 1 NTF_LED_LEN 1 2 1 1 NTF_LED_OVERRIDE 0 2 -1 1 NTF_LED_TYPES 123079 6 +1 1 NTF_LED_TYPES 199 6 +1 1 NTF_OREO_THEME 0 2 1 1 OA_TYPE 0 2 -1 1 OSD_TYPE 0 2 1 1 PILOT_STEER_TYPE 0 2 -1 1 PLND_ENABLED 0 2 -1 1 PRX1_TYPE 0 2 -1 1 PRX2_TYPE 0 2 -1 1 PRX3_TYPE 0 2 -1 1 PRX4_TYPE 0 2 -1 1 PRX5_TYPE 0 2 -1 1 PRX_FILT 0.250000000000000000 9 -1 1 PRX_LOG_RAW 0 2 -1 1 PSC_POS_P 0.200000002980232239 9 -1 1 PSC_VEL_D 0.000000000000000000 9 -1 1 PSC_VEL_FF 0.000000000000000000 9 -1 1 PSC_VEL_FLTD 5.000000000000000000 9 -1 1 PSC_VEL_FLTE 5.000000000000000000 9 -1 1 PSC_VEL_I 0.000000000000000000 9 -1 1 PSC_VEL_IMAX 1.000000000000000000 9 -1 1 PSC_VEL_P 1.000000000000000000 9 +1 1 PRX_IGN_ANG1 0 4 +1 1 PRX_IGN_ANG2 0 4 +1 1 PRX_IGN_ANG3 0 4 +1 1 PRX_IGN_ANG4 0 4 +1 1 PRX_IGN_ANG5 0 4 +1 1 PRX_IGN_ANG6 0 4 +1 1 PRX_IGN_WID1 0 2 +1 1 PRX_IGN_WID2 0 2 +1 1 PRX_IGN_WID3 0 2 +1 1 PRX_IGN_WID4 0 2 +1 1 PRX_IGN_WID5 0 2 +1 1 PRX_IGN_WID6 0 2 +1 1 PRX_ORIENT 0 2 +1 1 PRX_TYPE 0 2 +1 1 PRX_YAW_CORR 0 4 1 1 RALLY_INCL_HOME 1 2 1 1 RALLY_LIMIT_KM 0.500000000000000000 9 1 1 RALLY_TOTAL 0 2 @@ -608,8 +512,8 @@ 1 1 RC16_REVERSED 0 2 1 1 RC16_TRIM 1500 4 1 1 RC1_DZ 30 4 -1 1 RC1_MAX 2000 4 -1 1 RC1_MIN 1000 4 +1 1 RC1_MAX 1900 4 +1 1 RC1_MIN 1100 4 1 1 RC1_OPTION 0 4 1 1 RC1_REVERSED 0 2 1 1 RC1_TRIM 1500 4 @@ -620,8 +524,8 @@ 1 1 RC2_REVERSED 0 2 1 1 RC2_TRIM 1500 4 1 1 RC3_DZ 30 4 -1 1 RC3_MAX 2000 4 -1 1 RC3_MIN 1000 4 +1 1 RC3_MAX 1900 4 +1 1 RC3_MIN 1100 4 1 1 RC3_OPTION 0 4 1 1 RC3_REVERSED 0 2 1 1 RC3_TRIM 1500 4 @@ -665,64 +569,211 @@ 1 1 RCMAP_ROLL 1 2 1 1 RCMAP_THROTTLE 3 2 1 1 RCMAP_YAW 4 2 -1 1 RC_OPTIONS 32 6 +1 1 RC_OPTIONS 0 6 1 1 RC_OVERRIDE_TIME 3.000000000000000000 9 -1 1 RC_PROTOCOLS 1 6 -1 1 RELAY1_DEFAULT 0 2 -1 1 RELAY1_FUNCTION 1 2 -1 1 RELAY1_INVERTED 0 2 -1 1 RELAY1_PIN 1 4 -1 1 RELAY2_DEFAULT 0 2 -1 1 RELAY2_FUNCTION 1 2 -1 1 RELAY2_INVERTED 0 2 -1 1 RELAY2_PIN 2 4 -1 1 RELAY3_FUNCTION 0 2 -1 1 RELAY4_FUNCTION 0 2 -1 1 RELAY5_FUNCTION 0 2 -1 1 RELAY6_FUNCTION 0 2 +1 1 RELAY_DEFAULT 0 2 +1 1 RELAY_PIN 54 2 +1 1 RELAY_PIN2 55 2 +1 1 RELAY_PIN3 -1 2 +1 1 RELAY_PIN4 -1 2 +1 1 RELAY_PIN5 -1 2 +1 1 RELAY_PIN6 -1 2 +1 1 RNGFND1_ADDR 0 2 +1 1 RNGFND1_FUNCTION 0 2 +1 1 RNGFND1_GNDCLEAR 10 2 +1 1 RNGFND1_MAX_CM 700 4 +1 1 RNGFND1_MIN_CM 20 4 +1 1 RNGFND1_OFFSET 0.000000000000000000 9 +1 1 RNGFND1_ORIENT 0 2 +1 1 RNGFND1_PIN -1 2 +1 1 RNGFND1_POS_X 0.000000000000000000 9 +1 1 RNGFND1_POS_Y 0.000000000000000000 9 +1 1 RNGFND1_POS_Z 0.000000000000000000 9 +1 1 RNGFND1_PWRRNG 0 4 +1 1 RNGFND1_RMETRIC 1 2 +1 1 RNGFND1_SCALING 3.000000000000000000 9 +1 1 RNGFND1_STOP_PIN -1 2 1 1 RNGFND1_TYPE 0 2 +1 1 RNGFND2_ADDR 0 2 +1 1 RNGFND2_FUNCTION 0 2 +1 1 RNGFND2_GNDCLEAR 10 2 +1 1 RNGFND2_MAX_CM 700 4 +1 1 RNGFND2_MIN_CM 20 4 +1 1 RNGFND2_OFFSET 0.000000000000000000 9 +1 1 RNGFND2_ORIENT 0 2 +1 1 RNGFND2_PIN -1 2 +1 1 RNGFND2_POS_X 0.000000000000000000 9 +1 1 RNGFND2_POS_Y 0.000000000000000000 9 +1 1 RNGFND2_POS_Z 0.000000000000000000 9 +1 1 RNGFND2_PWRRNG 0 4 +1 1 RNGFND2_RMETRIC 1 2 +1 1 RNGFND2_SCALING 3.000000000000000000 9 +1 1 RNGFND2_STOP_PIN -1 2 1 1 RNGFND2_TYPE 0 2 +1 1 RNGFND3_ADDR 0 2 +1 1 RNGFND3_FUNCTION 0 2 +1 1 RNGFND3_GNDCLEAR 10 2 +1 1 RNGFND3_MAX_CM 700 4 +1 1 RNGFND3_MIN_CM 20 4 +1 1 RNGFND3_OFFSET 0.000000000000000000 9 +1 1 RNGFND3_ORIENT 0 2 +1 1 RNGFND3_PIN -1 2 +1 1 RNGFND3_POS_X 0.000000000000000000 9 +1 1 RNGFND3_POS_Y 0.000000000000000000 9 +1 1 RNGFND3_POS_Z 0.000000000000000000 9 +1 1 RNGFND3_PWRRNG 0 4 +1 1 RNGFND3_RMETRIC 1 2 +1 1 RNGFND3_SCALING 3.000000000000000000 9 +1 1 RNGFND3_STOP_PIN -1 2 1 1 RNGFND3_TYPE 0 2 +1 1 RNGFND4_ADDR 0 2 +1 1 RNGFND4_FUNCTION 0 2 +1 1 RNGFND4_GNDCLEAR 10 2 +1 1 RNGFND4_MAX_CM 700 4 +1 1 RNGFND4_MIN_CM 20 4 +1 1 RNGFND4_OFFSET 0.000000000000000000 9 +1 1 RNGFND4_ORIENT 0 2 +1 1 RNGFND4_PIN -1 2 +1 1 RNGFND4_POS_X 0.000000000000000000 9 +1 1 RNGFND4_POS_Y 0.000000000000000000 9 +1 1 RNGFND4_POS_Z 0.000000000000000000 9 +1 1 RNGFND4_PWRRNG 0 4 +1 1 RNGFND4_RMETRIC 1 2 +1 1 RNGFND4_SCALING 3.000000000000000000 9 +1 1 RNGFND4_STOP_PIN -1 2 1 1 RNGFND4_TYPE 0 2 +1 1 RNGFND5_ADDR 0 2 +1 1 RNGFND5_FUNCTION 0 2 +1 1 RNGFND5_GNDCLEAR 10 2 +1 1 RNGFND5_MAX_CM 700 4 +1 1 RNGFND5_MIN_CM 20 4 +1 1 RNGFND5_OFFSET 0.000000000000000000 9 +1 1 RNGFND5_ORIENT 0 2 +1 1 RNGFND5_PIN -1 2 +1 1 RNGFND5_POS_X 0.000000000000000000 9 +1 1 RNGFND5_POS_Y 0.000000000000000000 9 +1 1 RNGFND5_POS_Z 0.000000000000000000 9 +1 1 RNGFND5_PWRRNG 0 4 +1 1 RNGFND5_RMETRIC 1 2 +1 1 RNGFND5_SCALING 3.000000000000000000 9 +1 1 RNGFND5_STOP_PIN -1 2 1 1 RNGFND5_TYPE 0 2 +1 1 RNGFND6_ADDR 0 2 +1 1 RNGFND6_FUNCTION 0 2 +1 1 RNGFND6_GNDCLEAR 10 2 +1 1 RNGFND6_MAX_CM 700 4 +1 1 RNGFND6_MIN_CM 20 4 +1 1 RNGFND6_OFFSET 0.000000000000000000 9 +1 1 RNGFND6_ORIENT 0 2 +1 1 RNGFND6_PIN -1 2 +1 1 RNGFND6_POS_X 0.000000000000000000 9 +1 1 RNGFND6_POS_Y 0.000000000000000000 9 +1 1 RNGFND6_POS_Z 0.000000000000000000 9 +1 1 RNGFND6_PWRRNG 0 4 +1 1 RNGFND6_RMETRIC 1 2 +1 1 RNGFND6_SCALING 3.000000000000000000 9 +1 1 RNGFND6_STOP_PIN -1 2 1 1 RNGFND6_TYPE 0 2 +1 1 RNGFND7_ADDR 0 2 +1 1 RNGFND7_FUNCTION 0 2 +1 1 RNGFND7_GNDCLEAR 10 2 +1 1 RNGFND7_MAX_CM 700 4 +1 1 RNGFND7_MIN_CM 20 4 +1 1 RNGFND7_OFFSET 0.000000000000000000 9 +1 1 RNGFND7_ORIENT 0 2 +1 1 RNGFND7_PIN -1 2 +1 1 RNGFND7_POS_X 0.000000000000000000 9 +1 1 RNGFND7_POS_Y 0.000000000000000000 9 +1 1 RNGFND7_POS_Z 0.000000000000000000 9 +1 1 RNGFND7_PWRRNG 0 4 +1 1 RNGFND7_RMETRIC 1 2 +1 1 RNGFND7_SCALING 3.000000000000000000 9 +1 1 RNGFND7_STOP_PIN -1 2 1 1 RNGFND7_TYPE 0 2 +1 1 RNGFND8_ADDR 0 2 +1 1 RNGFND8_FUNCTION 0 2 +1 1 RNGFND8_GNDCLEAR 10 2 +1 1 RNGFND8_MAX_CM 700 4 +1 1 RNGFND8_MIN_CM 20 4 +1 1 RNGFND8_OFFSET 0.000000000000000000 9 +1 1 RNGFND8_ORIENT 0 2 +1 1 RNGFND8_PIN -1 2 +1 1 RNGFND8_POS_X 0.000000000000000000 9 +1 1 RNGFND8_POS_Y 0.000000000000000000 9 +1 1 RNGFND8_POS_Z 0.000000000000000000 9 +1 1 RNGFND8_PWRRNG 0 4 +1 1 RNGFND8_RMETRIC 1 2 +1 1 RNGFND8_SCALING 3.000000000000000000 9 +1 1 RNGFND8_STOP_PIN -1 2 1 1 RNGFND8_TYPE 0 2 +1 1 RNGFND9_ADDR 0 2 +1 1 RNGFND9_FUNCTION 0 2 +1 1 RNGFND9_GNDCLEAR 10 2 +1 1 RNGFND9_MAX_CM 700 4 +1 1 RNGFND9_MIN_CM 20 4 +1 1 RNGFND9_OFFSET 0.000000000000000000 9 +1 1 RNGFND9_ORIENT 0 2 +1 1 RNGFND9_PIN -1 2 +1 1 RNGFND9_POS_X 0.000000000000000000 9 +1 1 RNGFND9_POS_Y 0.000000000000000000 9 +1 1 RNGFND9_POS_Z 0.000000000000000000 9 +1 1 RNGFND9_PWRRNG 0 4 +1 1 RNGFND9_RMETRIC 1 2 +1 1 RNGFND9_SCALING 3.000000000000000000 9 +1 1 RNGFND9_STOP_PIN -1 2 1 1 RNGFND9_TYPE 0 2 +1 1 RNGFNDA_ADDR 0 2 +1 1 RNGFNDA_FUNCTION 0 2 +1 1 RNGFNDA_GNDCLEAR 10 2 +1 1 RNGFNDA_MAX_CM 700 4 +1 1 RNGFNDA_MIN_CM 20 4 +1 1 RNGFNDA_OFFSET 0.000000000000000000 9 +1 1 RNGFNDA_ORIENT 0 2 +1 1 RNGFNDA_PIN -1 2 +1 1 RNGFNDA_POS_X 0.000000000000000000 9 +1 1 RNGFNDA_POS_Y 0.000000000000000000 9 +1 1 RNGFNDA_POS_Z 0.000000000000000000 9 +1 1 RNGFNDA_PWRRNG 0 4 +1 1 RNGFNDA_RMETRIC 1 2 +1 1 RNGFNDA_SCALING 3.000000000000000000 9 +1 1 RNGFNDA_STOP_PIN -1 2 1 1 RNGFNDA_TYPE 0 2 -1 1 RPM1_TYPE 0 2 +1 1 RPM2_PIN -1 2 +1 1 RPM2_SCALING 1.000000000000000000 9 1 1 RPM2_TYPE 0 2 +1 1 RPM_MAX 100000.000000000000000000 9 +1 1 RPM_MIN 10.000000000000000000 9 +1 1 RPM_MIN_QUAL 0.500000000000000000 9 +1 1 RPM_PIN 54 2 +1 1 RPM_SCALING 1.000000000000000000 9 +1 1 RPM_TYPE 0 2 1 1 RSSI_TYPE 0 2 1 1 RST_SWITCH_CH 0 2 1 1 RTL_SPEED 0.000000000000000000 9 1 1 SAIL_ENABLE 0 2 1 1 SCHED_DEBUG 0 2 1 1 SCHED_LOOP_RATE 50 4 -1 1 SCHED_OPTIONS 0 2 1 1 SCR_ENABLE 0 2 1 1 SERIAL0_BAUD 115 6 1 1 SERIAL0_PROTOCOL 2 2 1 1 SERIAL1_BAUD 57 6 1 1 SERIAL1_OPTIONS 0 4 -1 1 SERIAL1_PROTOCOL 2 2 +1 1 SERIAL1_PROTOCOL 1 2 1 1 SERIAL2_BAUD 57 6 1 1 SERIAL2_OPTIONS 0 4 -1 1 SERIAL2_PROTOCOL 2 2 -1 1 SERIAL3_BAUD 230 6 +1 1 SERIAL2_PROTOCOL 1 2 +1 1 SERIAL3_BAUD 38 6 1 1 SERIAL3_OPTIONS 0 4 1 1 SERIAL3_PROTOCOL 5 2 -1 1 SERIAL4_BAUD 230 6 +1 1 SERIAL4_BAUD 38 6 1 1 SERIAL4_OPTIONS 0 4 1 1 SERIAL4_PROTOCOL 5 2 1 1 SERIAL5_BAUD 57 6 1 1 SERIAL5_OPTIONS 0 4 -1 1 SERIAL5_PROTOCOL -1 2 -1 1 SERIAL6_BAUD 57 6 +1 1 SERIAL5_PROTOCOL 1 2 +1 1 SERIAL6_BAUD 115200 6 1 1 SERIAL6_OPTIONS 0 4 -1 1 SERIAL6_PROTOCOL -1 2 -1 1 SERIAL7_BAUD 57 6 -1 1 SERIAL7_OPTIONS 0 4 -1 1 SERIAL7_PROTOCOL -1 2 +1 1 SERIAL6_PROTOCOL 22 2 1 1 SERIAL_PASS1 0 2 1 1 SERIAL_PASS2 -1 2 1 1 SERIAL_PASSTIMO 15 2 @@ -762,8 +813,8 @@ 1 1 SERVO16_REVERSED 0 2 1 1 SERVO16_TRIM 1500 4 1 1 SERVO1_FUNCTION 26 4 -1 1 SERVO1_MAX 2000 4 -1 1 SERVO1_MIN 1000 4 +1 1 SERVO1_MAX 1900 4 +1 1 SERVO1_MIN 1100 4 1 1 SERVO1_REVERSED 0 2 1 1 SERVO1_TRIM 1500 4 1 1 SERVO2_FUNCTION 0 4 @@ -772,8 +823,8 @@ 1 1 SERVO2_REVERSED 0 2 1 1 SERVO2_TRIM 1500 4 1 1 SERVO3_FUNCTION 70 4 -1 1 SERVO3_MAX 2000 4 -1 1 SERVO3_MIN 1000 4 +1 1 SERVO3_MAX 1900 4 +1 1 SERVO3_MIN 1100 4 1 1 SERVO3_REVERSED 0 2 1 1 SERVO3_TRIM 1500 4 1 1 SERVO4_FUNCTION 0 4 @@ -806,398 +857,33 @@ 1 1 SERVO9_MIN 1100 4 1 1 SERVO9_REVERSED 0 2 1 1 SERVO9_TRIM 1500 4 -1 1 SERVO_32_ENABLE 0 2 -1 1 SERVO_DSHOT_ESC 0 2 -1 1 SERVO_DSHOT_RATE 0 2 -1 1 SERVO_FTW_MASK 0 6 -1 1 SERVO_FTW_POLES 14 2 -1 1 SERVO_FTW_RVMASK 0 6 -1 1 SERVO_GPIO_MASK 0 6 +1 1 SERVO_BLH_DEBUG 0 2 +1 1 SERVO_BLH_MASK 0 6 +1 1 SERVO_BLH_OTYPE 0 2 +1 1 SERVO_BLH_POLES 14 2 +1 1 SERVO_BLH_PORT 0 2 +1 1 SERVO_BLH_REMASK 0 6 +1 1 SERVO_BLH_TEST 0 2 +1 1 SERVO_BLH_TMOUT 0 4 +1 1 SERVO_BLH_TRATE 10 4 1 1 SERVO_RATE 50 4 -1 1 SERVO_RC_FS_MSK 0 6 1 1 SERVO_ROB_POSMAX 4095 6 1 1 SERVO_ROB_POSMIN 0 6 1 1 SERVO_SBUS_RATE 50 4 1 1 SERVO_VOLZ_MASK 0 6 -1 1 SERVO_VOLZ_RANGE 200 4 1 1 SIMPLE_TYPE 0 2 -1 1 SIM_ACC1_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC1_RND 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC2_RND 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC3_RND 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACCEL1_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL2_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL3_FAIL 0.000000000000000000 9 -1 1 SIM_ACC_FAIL_MSK 0 2 -1 1 SIM_ACC_FILE_RW 0 2 -1 1 SIM_ACC_TRIM_X 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Y 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Z 0.000000000000000000 9 -1 1 SIM_ADSB_ALT 1000.000000000000000000 9 -1 1 SIM_ADSB_COUNT -1 4 -1 1 SIM_ADSB_RADIUS 10000.000000000000000000 9 -1 1 SIM_ADSB_TX 0 2 -1 1 SIM_ADSB_TYPES 1 2 -1 1 SIM_ARSPD2_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD2_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD2_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD2_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD2_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD2_RND 2.000000000000000000 9 -1 1 SIM_ARSPD2_SIGN 0 2 -1 1 SIM_ARSPD_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD_RND 2.000000000000000000 9 -1 1 SIM_ARSPD_SIGN 0 2 -1 1 SIM_BAR2_DELAY 0 4 -1 1 SIM_BAR2_DISABLE 0 2 -1 1 SIM_BAR2_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR2_FREEZE 0 2 -1 1 SIM_BAR2_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR2_RND 0.200000002980232239 9 -1 1 SIM_BAR2_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_UP 0.000000000000000000 9 -1 1 SIM_BAR3_DELAY 0 4 -1 1 SIM_BAR3_DISABLE 0 2 -1 1 SIM_BAR3_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR3_FREEZE 0 2 -1 1 SIM_BAR3_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR3_RND 0.200000002980232239 9 -1 1 SIM_BAR3_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_UP 0.000000000000000000 9 -1 1 SIM_BARO_COUNT 2 2 -1 1 SIM_BARO_DELAY 0 4 -1 1 SIM_BARO_DISABLE 0 2 -1 1 SIM_BARO_DRIFT 0.000000000000000000 9 -1 1 SIM_BARO_FREEZE 0 2 -1 1 SIM_BARO_GLITCH 0.000000000000000000 9 -1 1 SIM_BARO_RND 0.200000002980232239 9 -1 1 SIM_BARO_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BARO_WCF_DN 0.000000000000000000 9 -1 1 SIM_BARO_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BARO_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_UP 0.000000000000000000 9 -1 1 SIM_BATT_CAP_AH 0.000000000000000000 9 -1 1 SIM_BATT_VOLTAGE 12.600000381469726563 9 -1 1 SIM_BAUDLIMIT_EN 0 2 -1 1 SIM_CAN_SRV_MSK 0 6 -1 1 SIM_CAN_TYPE1 1 2 -1 1 SIM_CAN_TYPE2 1 2 -1 1 SIM_CLAMP_CH 0 2 -1 1 SIM_DRIFT_SPEED 0.050000000745058060 9 -1 1 SIM_DRIFT_TIME 5.000000000000000000 9 -1 1 SIM_EFI_TYPE 0 2 -1 1 SIM_ENGINE_FAIL 0 2 -1 1 SIM_ENGINE_MUL 1.000000000000000000 9 -1 1 SIM_ESC_ARM_RPM 0.000000000000000000 9 -1 1 SIM_ESC_TELEM 1 2 -1 1 SIM_FLOAT_EXCEPT 1 2 -1 1 SIM_FLOW_DELAY 0 2 -1 1 SIM_FLOW_ENABLE 0 2 -1 1 SIM_FLOW_POS_X 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Y 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Z 0.000000000000000000 9 -1 1 SIM_FLOW_RATE 10 4 -1 1 SIM_FLOW_RND 0.050000000745058060 9 -1 1 SIM_FTOWESC_ENA 0 2 -1 1 SIM_FTOWESC_POW 4095 6 -1 1 SIM_GND_BEHAV -1 2 -1 1 SIM_GPS2_ACC 0.300000011920928955 9 -1 1 SIM_GPS2_ALT_OFS 0 4 -1 1 SIM_GPS2_BYTELOS 0.000000000000000000 9 -1 1 SIM_GPS2_DISABLE 1 2 -1 1 SIM_GPS2_DRFTALT 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_X 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Y 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Z 0.000000000000000000 9 -1 1 SIM_GPS2_HDG 0 2 -1 1 SIM_GPS2_HZ 5 2 -1 1 SIM_GPS2_JAM 0 2 -1 1 SIM_GPS2_LAG_MS 100 4 -1 1 SIM_GPS2_LCKTIME 0 4 -1 1 SIM_GPS2_NOISE 0.000000000000000000 9 -1 1 SIM_GPS2_NUMSATS 10 2 -1 1 SIM_GPS2_POS_X 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS2_TYPE 1 2 -1 1 SIM_GPS2_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Z 0.000000000000000000 9 -1 1 SIM_GPS_ACC 0.300000011920928955 9 -1 1 SIM_GPS_ALT_OFS 0 4 -1 1 SIM_GPS_BYTELOSS 0.000000000000000000 9 -1 1 SIM_GPS_DISABLE 0 2 -1 1 SIM_GPS_DRIFTALT 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_X 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Y 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Z 0.000000000000000000 9 -1 1 SIM_GPS_HDG 0 2 -1 1 SIM_GPS_HZ 5 2 -1 1 SIM_GPS_JAM 0 2 -1 1 SIM_GPS_LAG_MS 100 4 -1 1 SIM_GPS_LOCKTIME 0 4 -1 1 SIM_GPS_LOG_NUM 0 4 -1 1 SIM_GPS_NOISE 0.000000000000000000 9 -1 1 SIM_GPS_NUMSATS 10 2 -1 1 SIM_GPS_POS_X 0.000000000000000000 9 -1 1 SIM_GPS_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS_TYPE 1 2 -1 1 SIM_GPS_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Z 0.000000000000000000 9 -1 1 SIM_GRPE_ENABLE 0 2 -1 1 SIM_GRPE_PIN -1 2 -1 1 SIM_GRPS_ENABLE 0 2 -1 1 SIM_GRPS_GRAB 2000 4 -1 1 SIM_GRPS_PIN -1 2 -1 1 SIM_GRPS_RELEASE 1000 4 -1 1 SIM_GRPS_REVERSE 0 2 -1 1 SIM_GYR1_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR1_RND 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR2_RND 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR3_RND 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR_FAIL_MSK 0 2 -1 1 SIM_GYR_FILE_RW 0 2 -1 1 SIM_IE24_ENABLE 0 2 -1 1 SIM_IE24_ERROR 0 6 -1 1 SIM_IE24_STATE -1 2 -1 1 SIM_IMUT1_ENABLE 0 2 -1 1 SIM_IMUT2_ENABLE 0 2 -1 1 SIM_IMUT3_ENABLE 0 2 -1 1 SIM_IMUT_END 45.000000000000000000 9 -1 1 SIM_IMUT_FIXED 0.000000000000000000 9 -1 1 SIM_IMUT_START 25.000000000000000000 9 -1 1 SIM_IMUT_TCONST 300.000000000000000000 9 -1 1 SIM_IMU_COUNT 2 2 -1 1 SIM_IMU_ORIENT 0 2 -1 1 SIM_IMU_POS_X 0.000000000000000000 9 -1 1 SIM_IMU_POS_Y 0.000000000000000000 9 -1 1 SIM_IMU_POS_Z 0.000000000000000000 9 -1 1 SIM_INIT_ALT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LAT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LON_OFS 0.000000000000000000 9 -1 1 SIM_INS_THR_MIN 0.100000001490116119 9 -1 1 SIM_JSON_MASTER 0 2 -1 1 SIM_LED_LAYOUT 0 2 -1 1 SIM_LOOP_DELAY 0 6 -1 1 SIM_MAG1_DEVID 97539 6 -1 1 SIM_MAG1_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG1_FAIL 0 2 -1 1 SIM_MAG1_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG1_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG1_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG1_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG1_ORIENT 0 2 -1 1 SIM_MAG1_SCALING 1.000000000000000000 9 -1 1 SIM_MAG2_DEVID 131874 6 -1 1 SIM_MAG2_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG2_FAIL 0 2 -1 1 SIM_MAG2_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG2_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG2_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG2_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG2_ORIENT 0 2 -1 1 SIM_MAG2_SCALING 1.000000000000000000 9 -1 1 SIM_MAG3_DEVID 263178 6 -1 1 SIM_MAG3_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG3_FAIL 0 2 -1 1 SIM_MAG3_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG3_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG3_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG3_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG3_ORIENT 0 2 -1 1 SIM_MAG3_SCALING 1.000000000000000000 9 -1 1 SIM_MAG4_DEVID 97283 6 -1 1 SIM_MAG5_DEVID 97795 6 -1 1 SIM_MAG6_DEVID 98051 6 -1 1 SIM_MAG7_DEVID 0 6 -1 1 SIM_MAG8_DEVID 0 6 -1 1 SIM_MAG_ALY_HGT 1.000000000000000000 9 -1 1 SIM_MAG_ALY_X 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Y 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Z 0.000000000000000000 9 -1 1 SIM_MAG_DELAY 0 4 -1 1 SIM_MAG_MOT_X 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Y 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Z 0.000000000000000000 9 -1 1 SIM_MAG_RND 0.000000000000000000 9 -1 1 SIM_MAG_SAVE_IDS 1 2 -1 1 SIM_ODOM_ENABLE 0 2 -1 1 SIM_OH_MASK 0 6 -1 1 SIM_OH_RELAY_MSK -1 4 -1 1 SIM_OPOS_ALT 584.000000000000000000 9 -1 1 SIM_OPOS_HDG 353.000000000000000000 9 -1 1 SIM_OPOS_LAT -35.363262176513671875 9 -1 1 SIM_OPOS_LNG 149.165237426757812500 9 -1 1 SIM_PARA_ENABLE 0 2 -1 1 SIM_PARA_PIN -1 2 -1 1 SIM_PIN_MASK 121 4 -1 1 SIM_PLD_ALT_LMT 15.000000000000000000 9 -1 1 SIM_PLD_DIST_LMT 10.000000000000000000 9 -1 1 SIM_PLD_ENABLE 0 2 -1 1 SIM_PLD_HEIGHT 0.000000000000000000 9 -1 1 SIM_PLD_LAT 0.000000000000000000 9 -1 1 SIM_PLD_LON 0.000000000000000000 9 -1 1 SIM_PLD_OPTIONS 0 2 -1 1 SIM_PLD_ORIENT 24 2 -1 1 SIM_PLD_RATE 100 6 -1 1 SIM_PLD_SHIP 0 2 -1 1 SIM_PLD_TYPE 0 2 -1 1 SIM_PLD_YAW 0 4 -1 1 SIM_RATE_HZ 1200 4 -1 1 SIM_RC_CHANCOUNT 16 2 -1 1 SIM_RC_FAIL 0 2 -1 1 SIM_RICH_CTRL -1 2 -1 1 SIM_RICH_ENABLE 0 2 -1 1 SIM_SAIL_TYPE 0 2 -1 1 SIM_SERVO_DELAY 0.000000000000000000 9 -1 1 SIM_SERVO_FILTER 0.000000000000000000 9 -1 1 SIM_SERVO_SPEED 0.140000000596046448 9 -1 1 SIM_SHIP_DSIZE 10.000000000000000000 9 -1 1 SIM_SHIP_ENABLE 0 2 -1 1 SIM_SHIP_OFS_X 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Y 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Z 0.000000000000000000 9 -1 1 SIM_SHIP_PSIZE 1000.000000000000000000 9 -1 1 SIM_SHIP_SPEED 3.000000000000000000 9 -1 1 SIM_SHIP_SYSID 17 2 -1 1 SIM_SHOVE_TIME 0 6 -1 1 SIM_SHOVE_X 0.000000000000000000 9 -1 1 SIM_SHOVE_Y 0.000000000000000000 9 -1 1 SIM_SHOVE_Z 0.000000000000000000 9 -1 1 SIM_SLUP_ENABLE 0 2 -1 1 SIM_SONAR_GLITCH 0.000000000000000000 9 -1 1 SIM_SONAR_POS_X 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Y 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Z 0.000000000000000000 9 -1 1 SIM_SONAR_RND 0.000000000000000000 9 -1 1 SIM_SONAR_ROT 25 2 -1 1 SIM_SONAR_SCALE 12.121199607849121094 9 -1 1 SIM_SPEEDUP 1.000000000000000000 9 -1 1 SIM_SPR_ENABLE 0 2 -1 1 SIM_SPR_PUMP -1 2 -1 1 SIM_SPR_SPIN -1 2 -1 1 SIM_TA_ENABLE 1 2 -1 1 SIM_TEMP_BFACTOR 0.000000000000000000 9 -1 1 SIM_TEMP_BRD_OFF 20.000000000000000000 9 -1 1 SIM_TEMP_START 25.000000000000000000 9 -1 1 SIM_TEMP_TCONST 30.000000000000000000 9 -1 1 SIM_TERRAIN 1 2 -1 1 SIM_THML_SCENARI 0 2 -1 1 SIM_TIDE_DIR 0.000000000000000000 9 -1 1 SIM_TIDE_SPEED 0.000000000000000000 9 -1 1 SIM_TIME_JITTER 0 4 -1 1 SIM_TWIST_TIME 0 6 -1 1 SIM_TWIST_X 0.000000000000000000 9 -1 1 SIM_TWIST_Y 0.000000000000000000 9 -1 1 SIM_TWIST_Z 0.000000000000000000 9 -1 1 SIM_UART_LOSS 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_X 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Y 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Z 0.000000000000000000 9 -1 1 SIM_VIB_MOT_HMNC 1 4 -1 1 SIM_VIB_MOT_MASK 0 6 -1 1 SIM_VIB_MOT_MAX 0.000000000000000000 9 -1 1 SIM_VIB_MOT_MULT 1.000000000000000000 9 -1 1 SIM_VICON_FAIL 0 2 -1 1 SIM_VICON_GLIT_X 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Y 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Z 0.000000000000000000 9 -1 1 SIM_VICON_POS_X 0.000000000000000000 9 -1 1 SIM_VICON_POS_Y 0.000000000000000000 9 -1 1 SIM_VICON_POS_Z 0.000000000000000000 9 -1 1 SIM_VICON_TMASK 3 2 -1 1 SIM_VICON_VGLI_X 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Y 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Z 0.000000000000000000 9 -1 1 SIM_VICON_YAW 0 4 -1 1 SIM_VICON_YAWERR 0 4 -1 1 SIM_WAVE_AMP 0.500000000000000000 9 -1 1 SIM_WAVE_DIR 0.000000000000000000 9 -1 1 SIM_WAVE_ENABLE 0 2 -1 1 SIM_WAVE_LENGTH 10.000000000000000000 9 -1 1 SIM_WAVE_SPEED 0.500000000000000000 9 -1 1 SIM_WIND_DIR 180.000000000000000000 9 -1 1 SIM_WIND_DIR_Z 0.000000000000000000 9 -1 1 SIM_WIND_SPD 0.000000000000000000 9 -1 1 SIM_WIND_T 0 2 -1 1 SIM_WIND_TC 5.000000000000000000 9 -1 1 SIM_WIND_TURB 0.000000000000000000 9 -1 1 SIM_WIND_T_ALT 60.000000000000000000 9 -1 1 SIM_WIND_T_COEF 0.009999999776482582 9 -1 1 SIM_WOW_PIN -1 2 1 1 SPEED_MAX 0.000000000000000000 9 1 1 SPRAY_ENABLE 0 2 -1 1 SR0_ADSB 4 4 -1 1 SR0_EXTRA1 4 4 -1 1 SR0_EXTRA2 4 4 -1 1 SR0_EXTRA3 4 4 -1 1 SR0_EXT_STAT 4 4 +1 1 SR0_ADSB 0 4 +1 1 SR0_EXTRA1 1 4 +1 1 SR0_EXTRA2 1 4 +1 1 SR0_EXTRA3 1 4 +1 1 SR0_EXT_STAT 1 4 1 1 SR0_PARAMS 10 4 -1 1 SR0_POSITION 4 4 -1 1 SR0_RAW_CTRL 4 4 -1 1 SR0_RAW_SENS 4 4 -1 1 SR0_RC_CHAN 4 4 +1 1 SR0_POSITION 1 4 +1 1 SR0_RAW_CTRL 1 4 +1 1 SR0_RAW_SENS 1 4 +1 1 SR0_RC_CHAN 1 4 1 1 SR1_ADSB 0 4 1 1 SR1_EXTRA1 1 4 1 1 SR1_EXTRA2 1 4 @@ -1228,64 +914,30 @@ 1 1 SR3_RAW_CTRL 1 4 1 1 SR3_RAW_SENS 1 4 1 1 SR3_RC_CHAN 1 4 -1 1 SR4_ADSB 0 4 -1 1 SR4_EXTRA1 1 4 -1 1 SR4_EXTRA2 1 4 -1 1 SR4_EXTRA3 1 4 -1 1 SR4_EXT_STAT 1 4 -1 1 SR4_PARAMS 10 4 -1 1 SR4_POSITION 1 4 -1 1 SR4_RAW_CTRL 1 4 -1 1 SR4_RAW_SENS 1 4 -1 1 SR4_RC_CHAN 1 4 -1 1 SR5_ADSB 0 4 -1 1 SR5_EXTRA1 1 4 -1 1 SR5_EXTRA2 1 4 -1 1 SR5_EXTRA3 1 4 -1 1 SR5_EXT_STAT 1 4 -1 1 SR5_PARAMS 10 4 -1 1 SR5_POSITION 1 4 -1 1 SR5_RAW_CTRL 1 4 -1 1 SR5_RAW_SENS 1 4 -1 1 SR5_RC_CHAN 1 4 -1 1 SR6_ADSB 0 4 -1 1 SR6_EXTRA1 1 4 -1 1 SR6_EXTRA2 1 4 -1 1 SR6_EXTRA3 1 4 -1 1 SR6_EXT_STAT 1 4 -1 1 SR6_PARAMS 10 4 -1 1 SR6_POSITION 1 4 -1 1 SR6_RAW_CTRL 1 4 -1 1 SR6_RAW_SENS 1 4 -1 1 SR6_RC_CHAN 1 4 1 1 SRTL_ACCURACY 2.000000000000000000 9 -1 1 SRTL_OPTIONS 0 6 1 1 SRTL_POINTS 300 4 1 1 STAT_BOOTCNT 1 4 1 1 STAT_FLTTIME 0 6 -1 1 STAT_RESET 1 6 -1 1 STAT_RUNTIME 30 6 +1 1 STAT_RESET 0 6 +1 1 STAT_RUNTIME 0 6 1 1 STICK_MIXING 0 2 1 1 SYSID_ENFORCE 0 2 1 1 SYSID_MYGCS 255 4 1 1 SYSID_THISMAV 1 4 1 1 TELEM_DELAY 0 2 -1 1 TEMP1_TYPE 0 2 -1 1 TEMP2_TYPE 0 2 -1 1 TEMP3_TYPE 0 2 -1 1 TEMP_LOG 0 2 -1 1 TRQ1_TYPE 0 2 -1 1 TRQ2_TYPE 0 2 +1 1 TURN_MAX_G 0.600000023841857910 9 1 1 TURN_RADIUS 0.899999976158142090 9 +1 1 VISO_ORIENT 0 2 +1 1 VISO_POS_X 0.000000000000000000 9 +1 1 VISO_POS_Y 0.000000000000000000 9 +1 1 VISO_POS_Z 0.000000000000000000 9 1 1 VISO_TYPE 0 2 -1 1 VTX_ENABLE 0 2 1 1 WENC_TYPE 0 2 1 1 WNDVN_TYPE 0 2 -1 1 WP_ACCEL 0.000000000000000000 9 -1 1 WP_JERK 0.000000000000000000 9 +1 1 WP_OVERSHOOT 2.000000000000000000 9 1 1 WP_PIVOT_ANGLE 60 4 -1 1 WP_PIVOT_DELAY 0.000000000000000000 9 -1 1 WP_PIVOT_RATE 60 4 -1 1 WP_RADIUS 3.000000000000000000 9 -1 1 WP_SPEED 5.000000000000000000 9 +1 1 WP_PIVOT_RATE 90 4 +1 1 WP_RADIUS 2.000000000000000000 9 +1 1 WP_SPEED 2.000000000000000000 9 +1 1 WP_SPEED_MIN 0.000000000000000000 9 1 1 WRC_ENABLE 0 2 diff --git a/src/FirmwarePlugin/APM/Sub.OfflineEditing.params b/src/FirmwarePlugin/APM/Sub.OfflineEditing.params index ec18d0b5296b..c47093ed8f5c 100644 --- a/src/FirmwarePlugin/APM/Sub.OfflineEditing.params +++ b/src/FirmwarePlugin/APM/Sub.OfflineEditing.params @@ -1,9 +1,9 @@ # Onboard parameters for Vehicle 1 # # Stack: ArduPilot -# Vehicle: Submarine -# Version: 4.6.0 dev -# Git Revision: 1ebd4d99 +# Vehicle: Sub +# Version: 3.7.0 dev +# Git Revision: b319b27 # # Vehicle-Id Component-Id Name Value Type 1 1 ACRO_BAL_PITCH 1.000000000000000000 9 @@ -13,11 +13,13 @@ 1 1 ACRO_TRAINER 2 2 1 1 ACRO_YAW_P 3.375000000000000000 9 1 1 AHRS_COMP_BETA 0.100000001490116119 9 -1 1 AHRS_EKF_TYPE 3 2 +1 1 AHRS_CUSTOM_PIT 0.000000000000000000 9 +1 1 AHRS_CUSTOM_ROLL 0.000000000000000000 9 +1 1 AHRS_CUSTOM_YAW 0.000000000000000000 9 +1 1 AHRS_EKF_TYPE 2 2 1 1 AHRS_GPS_GAIN 1.000000000000000000 9 1 1 AHRS_GPS_MINSATS 6 2 1 1 AHRS_GPS_USE 1 2 -1 1 AHRS_OPTIONS 0 4 1 1 AHRS_ORIENTATION 0 2 1 1 AHRS_RP_P 0.200000002980232239 9 1 1 AHRS_TRIM_X 0.000000000000000000 9 @@ -28,10 +30,7 @@ 1 1 ANGLE_MAX 4500 4 1 1 ARMING_ACCTHRESH 0.750000000000000000 9 1 1 ARMING_CHECK 448 6 -1 1 ARMING_MAGTHRESH 100 4 1 1 ARMING_MIS_ITEMS 0 6 -1 1 ARMING_OPTIONS 0 6 -1 1 ARSPD_ENABLE 0 2 1 1 ATC_ACCEL_P_MAX 110000.000000000000000000 9 1 1 ATC_ACCEL_R_MAX 110000.000000000000000000 9 1 1 ATC_ACCEL_Y_MAX 110000.000000000000000000 9 @@ -41,72 +40,38 @@ 1 1 ATC_ANG_RLL_P 6.000000000000000000 9 1 1 ATC_ANG_YAW_P 6.000000000000000000 9 1 1 ATC_INPUT_TC 0.150000005960464478 9 -1 1 ATC_LAND_P_MULT 1.000000000000000000 9 -1 1 ATC_LAND_R_MULT 1.000000000000000000 9 -1 1 ATC_LAND_Y_MULT 1.000000000000000000 9 1 1 ATC_RATE_FF_ENAB 1 2 1 1 ATC_RATE_P_MAX 0.000000000000000000 9 1 1 ATC_RATE_R_MAX 0.000000000000000000 9 -1 1 ATC_RATE_Y_MAX 180.000000000000000000 9 +1 1 ATC_RATE_Y_MAX 0.000000000000000000 9 1 1 ATC_RAT_PIT_D 0.003599999938160181 9 -1 1 ATC_RAT_PIT_D_FF 0.000000000000000000 9 1 1 ATC_RAT_PIT_FF 0.000000000000000000 9 1 1 ATC_RAT_PIT_FLTD 30.000000000000000000 9 1 1 ATC_RAT_PIT_FLTE 0.000000000000000000 9 1 1 ATC_RAT_PIT_FLTT 30.000000000000000000 9 1 1 ATC_RAT_PIT_I 0.090000003576278687 9 1 1 ATC_RAT_PIT_IMAX 0.444000005722045898 9 -1 1 ATC_RAT_PIT_NEF 0 2 -1 1 ATC_RAT_PIT_NTF 0 2 1 1 ATC_RAT_PIT_P 0.135000005364418030 9 -1 1 ATC_RAT_PIT_PDMX 0.000000000000000000 9 -1 1 ATC_RAT_PIT_SMAX 0.000000000000000000 9 1 1 ATC_RAT_RLL_D 0.003599999938160181 9 -1 1 ATC_RAT_RLL_D_FF 0.000000000000000000 9 1 1 ATC_RAT_RLL_FF 0.000000000000000000 9 1 1 ATC_RAT_RLL_FLTD 30.000000000000000000 9 1 1 ATC_RAT_RLL_FLTE 0.000000000000000000 9 1 1 ATC_RAT_RLL_FLTT 30.000000000000000000 9 1 1 ATC_RAT_RLL_I 0.090000003576278687 9 1 1 ATC_RAT_RLL_IMAX 0.444000005722045898 9 -1 1 ATC_RAT_RLL_NEF 0 2 -1 1 ATC_RAT_RLL_NTF 0 2 1 1 ATC_RAT_RLL_P 0.135000005364418030 9 -1 1 ATC_RAT_RLL_PDMX 0.000000000000000000 9 -1 1 ATC_RAT_RLL_SMAX 0.000000000000000000 9 1 1 ATC_RAT_YAW_D 0.000000000000000000 9 -1 1 ATC_RAT_YAW_D_FF 0.000000000000000000 9 1 1 ATC_RAT_YAW_FF 0.000000000000000000 9 1 1 ATC_RAT_YAW_FLTD 5.000000000000000000 9 1 1 ATC_RAT_YAW_FLTE 0.000000000000000000 9 1 1 ATC_RAT_YAW_FLTT 5.000000000000000000 9 1 1 ATC_RAT_YAW_I 0.017999999225139618 9 1 1 ATC_RAT_YAW_IMAX 0.222000002861022949 9 -1 1 ATC_RAT_YAW_NEF 0 2 -1 1 ATC_RAT_YAW_NTF 0 2 1 1 ATC_RAT_YAW_P 0.180000007152557373 9 -1 1 ATC_RAT_YAW_PDMX 0.000000000000000000 9 -1 1 ATC_RAT_YAW_SMAX 0.000000000000000000 9 1 1 ATC_SLEW_YAW 6000.000000000000000000 9 1 1 ATC_THR_MIX_MAN 0.100000001490116119 9 1 1 ATC_THR_MIX_MAX 0.500000000000000000 9 1 1 ATC_THR_MIX_MIN 0.100000001490116119 9 -1 1 BARO1_DEVID 65540 6 -1 1 BARO1_GND_PRESS -5761763.500000000000000000 9 -1 1 BARO1_WCF_ENABLE 0 2 -1 1 BARO2_DEVID 65796 6 -1 1 BARO2_GND_PRESS -5762477.500000000000000000 9 -1 1 BARO2_WCF_ENABLE 0 2 -1 1 BARO3_DEVID 0 6 -1 1 BARO3_GND_PRESS 0.000000000000000000 9 -1 1 BARO3_WCF_ENABLE 0 2 -1 1 BARO_ALT_OFFSET 0.000000000000000000 9 -1 1 BARO_EXT_BUS 1 2 -1 1 BARO_FLTR_RNG 0 2 -1 1 BARO_GND_TEMP 0.000000000000000000 9 -1 1 BARO_PRIMARY 0 2 -1 1 BARO_PROBE_EXT 768 6 -1 1 BARO_SPEC_GRAV 1.000000000000000000 9 1 1 BATT2_MONITOR 0 2 1 1 BATT3_MONITOR 0 2 1 1 BATT4_MONITOR 0 2 @@ -115,42 +80,34 @@ 1 1 BATT7_MONITOR 0 2 1 1 BATT8_MONITOR 0 2 1 1 BATT9_MONITOR 0 2 -1 1 BATT_AMP_OFFSET 0.000000000000000000 9 -1 1 BATT_AMP_PERVLT 17.000000000000000000 9 -1 1 BATT_ARM_MAH 0 6 -1 1 BATT_ARM_VOLT 0.000000000000000000 9 -1 1 BATT_CAPACITY 3300 6 -1 1 BATT_CRT_MAH 0.000000000000000000 9 -1 1 BATT_CRT_VOLT 0.000000000000000000 9 -1 1 BATT_CURR_PIN 12 2 -1 1 BATT_FS_CRT_ACT 0 2 -1 1 BATT_FS_LOW_ACT 0 2 -1 1 BATT_FS_VOLTSRC 0 2 -1 1 BATT_LOW_MAH 0.000000000000000000 9 -1 1 BATT_LOW_TIMER 10 2 -1 1 BATT_LOW_VOLT 0.000000000000000000 9 -1 1 BATT_MONITOR 4 2 -1 1 BATT_OPTIONS 0 6 -1 1 BATT_SERIAL_NUM -1 6 -1 1 BATT_VLT_OFFSET 0.000000000000000000 9 -1 1 BATT_VOLT_MULT 10.100000381469726563 9 -1 1 BATT_VOLT_PIN 13 2 +1 1 BATT_MONITOR 0 2 +1 1 BRD_ALT_CONFIG 0 2 1 1 BRD_BOOT_DELAY 0 4 -1 1 BRD_OPTIONS 0 6 +1 1 BRD_IMUHEAT_I 0.070000000298023224 9 +1 1 BRD_IMUHEAT_IMAX 70.000000000000000000 9 +1 1 BRD_IMUHEAT_P 50.000000000000000000 9 +1 1 BRD_IMU_TARGTEMP 45 2 +1 1 BRD_IO_ENABLE 1 2 +1 1 BRD_OPTIONS 1 6 +1 1 BRD_PWM_COUNT 4 2 +1 1 BRD_PWM_VOLT_SEL 0 2 1 1 BRD_RTC_TYPES 1 2 1 1 BRD_RTC_TZ_MIN 0 4 +1 1 BRD_SAFETYENABLE 0 2 1 1 BRD_SAFETYOPTION 3 4 -1 1 BRD_SAFETY_DEFLT 0 2 1 1 BRD_SAFETY_MASK 0 6 -1 1 BRD_SD_FENCE 0 4 -1 1 BRD_SD_MISSION 0 4 -1 1 BRD_SERIAL_NUM 0 6 +1 1 BRD_SBUS_OUT 0 2 +1 1 BRD_SD_SLOWDOWN 0 2 +1 1 BRD_SER1_RTSCTS 2 2 +1 1 BRD_SER2_RTSCTS 2 2 +1 1 BRD_SERIAL_NUM 0 4 +1 1 BRD_TYPE 3 2 1 1 BRD_VBUS_MIN 4.300000190734863281 9 1 1 BRD_VSERVO_MIN 0.000000000000000000 9 1 1 BTN0_FUNCTION 0 2 1 1 BTN0_SFUNCTION 0 2 1 1 BTN10_FUNCTION 22 2 -1 1 BTN10_SFUNCTION 0 2 +1 1 BTN10_SFUNCTION 26 2 1 1 BTN11_FUNCTION 42 2 1 1 BTN11_SFUNCTION 47 2 1 1 BTN12_FUNCTION 43 2 @@ -161,43 +118,11 @@ 1 1 BTN14_SFUNCTION 44 2 1 1 BTN15_FUNCTION 0 2 1 1 BTN15_SFUNCTION 0 2 -1 1 BTN16_FUNCTION 0 2 -1 1 BTN16_SFUNCTION 0 2 -1 1 BTN17_FUNCTION 0 2 -1 1 BTN17_SFUNCTION 0 2 -1 1 BTN18_FUNCTION 0 2 -1 1 BTN18_SFUNCTION 0 2 -1 1 BTN19_FUNCTION 0 2 -1 1 BTN19_SFUNCTION 0 2 -1 1 BTN1_FUNCTION 6 2 +1 1 BTN1_FUNCTION 5 2 1 1 BTN1_SFUNCTION 0 2 -1 1 BTN20_FUNCTION 0 2 -1 1 BTN20_SFUNCTION 0 2 -1 1 BTN21_FUNCTION 0 2 -1 1 BTN21_SFUNCTION 0 2 -1 1 BTN22_FUNCTION 0 2 -1 1 BTN22_SFUNCTION 0 2 -1 1 BTN23_FUNCTION 0 2 -1 1 BTN23_SFUNCTION 0 2 -1 1 BTN24_FUNCTION 0 2 -1 1 BTN24_SFUNCTION 0 2 -1 1 BTN25_FUNCTION 0 2 -1 1 BTN25_SFUNCTION 0 2 -1 1 BTN26_FUNCTION 0 2 -1 1 BTN26_SFUNCTION 0 2 -1 1 BTN27_FUNCTION 0 2 -1 1 BTN27_SFUNCTION 0 2 -1 1 BTN28_FUNCTION 0 2 -1 1 BTN28_SFUNCTION 0 2 -1 1 BTN29_FUNCTION 0 2 -1 1 BTN29_SFUNCTION 0 2 -1 1 BTN2_FUNCTION 8 2 +1 1 BTN2_FUNCTION 7 2 1 1 BTN2_SFUNCTION 0 2 -1 1 BTN30_FUNCTION 0 2 -1 1 BTN30_SFUNCTION 0 2 -1 1 BTN31_FUNCTION 0 2 -1 1 BTN31_SFUNCTION 0 2 -1 1 BTN3_FUNCTION 7 2 +1 1 BTN3_FUNCTION 6 2 1 1 BTN3_SFUNCTION 0 2 1 1 BTN4_FUNCTION 4 2 1 1 BTN4_SFUNCTION 0 2 @@ -210,48 +135,45 @@ 1 1 BTN8_FUNCTION 48 2 1 1 BTN8_SFUNCTION 0 2 1 1 BTN9_FUNCTION 23 2 -1 1 BTN9_SFUNCTION 0 2 -1 1 CAM1_TYPE 0 2 -1 1 CAM2_TYPE 0 2 -1 1 CAM_AUTO_ONLY 0 2 -1 1 CAM_MAX_ROLL 0 4 -1 1 CAM_RC_TYPE 0 2 +1 1 BTN9_SFUNCTION 27 2 1 1 CAN_D1_PROTOCOL 1 2 -1 1 CAN_D1_PROTOCOL2 0 2 1 1 CAN_D2_PROTOCOL 1 2 -1 1 CAN_D2_PROTOCOL2 0 2 -1 1 CAN_LOGLEVEL 0 2 1 1 CAN_P1_DRIVER 0 2 1 1 CAN_P2_DRIVER 0 2 -1 1 CIRCLE_OPTIONS 1 4 +1 1 CAN_SLCAN_CPORT 0 2 +1 1 CAN_SLCAN_SERNUM -1 2 +1 1 CAN_SLCAN_TIMOUT 0 4 +1 1 CIRCLE_CONTROL 1 4 1 1 CIRCLE_RADIUS 1000.000000000000000000 9 1 1 CIRCLE_RATE 2.000000000000000000 9 1 1 COMPASS_AUTODEC 1 2 1 1 COMPASS_AUTO_ROT 2 2 1 1 COMPASS_CAL_FIT 16.000000000000000000 9 -1 1 COMPASS_DEC 0.223357215523719788 9 -1 1 COMPASS_DEV_ID 97539 6 -1 1 COMPASS_DEV_ID2 131874 6 -1 1 COMPASS_DEV_ID3 263178 6 -1 1 COMPASS_DEV_ID4 97283 6 -1 1 COMPASS_DEV_ID5 97795 6 -1 1 COMPASS_DEV_ID6 98051 6 +1 1 COMPASS_CUS_PIT 0.000000000000000000 9 +1 1 COMPASS_CUS_ROLL 0.000000000000000000 9 +1 1 COMPASS_CUS_YAW 0.000000000000000000 9 +1 1 COMPASS_DEC 0.000000000000000000 9 +1 1 COMPASS_DEV_ID 590114 6 +1 1 COMPASS_DEV_ID2 0 6 +1 1 COMPASS_DEV_ID3 0 6 +1 1 COMPASS_DEV_ID4 0 6 +1 1 COMPASS_DEV_ID5 0 6 +1 1 COMPASS_DEV_ID6 0 6 1 1 COMPASS_DEV_ID7 0 6 1 1 COMPASS_DEV_ID8 0 6 -1 1 COMPASS_DIA2_X 1.000000000000000000 9 -1 1 COMPASS_DIA2_Y 1.000000000000000000 9 -1 1 COMPASS_DIA2_Z 1.000000000000000000 9 -1 1 COMPASS_DIA3_X 1.000000000000000000 9 -1 1 COMPASS_DIA3_Y 1.000000000000000000 9 -1 1 COMPASS_DIA3_Z 1.000000000000000000 9 +1 1 COMPASS_DIA2_X 0.000000000000000000 9 +1 1 COMPASS_DIA2_Y 0.000000000000000000 9 +1 1 COMPASS_DIA2_Z 0.000000000000000000 9 +1 1 COMPASS_DIA3_X 0.000000000000000000 9 +1 1 COMPASS_DIA3_Y 0.000000000000000000 9 +1 1 COMPASS_DIA3_Z 0.000000000000000000 9 1 1 COMPASS_DIA_X 1.000000000000000000 9 1 1 COMPASS_DIA_Y 1.000000000000000000 9 1 1 COMPASS_DIA_Z 1.000000000000000000 9 -1 1 COMPASS_DISBLMSK 0 6 1 1 COMPASS_ENABLE 1 2 1 1 COMPASS_EXTERN2 0 2 1 1 COMPASS_EXTERN3 0 2 -1 1 COMPASS_EXTERNAL 1 2 +1 1 COMPASS_EXTERNAL 0 2 1 1 COMPASS_FLTR_RNG 0 2 1 1 COMPASS_LEARN 0 2 1 1 COMPASS_MOT2_X 0.000000000000000000 9 @@ -274,118 +196,83 @@ 1 1 COMPASS_ODI_Y 0.000000000000000000 9 1 1 COMPASS_ODI_Z 0.000000000000000000 9 1 1 COMPASS_OFFS_MAX 1000 4 -1 1 COMPASS_OFS2_X 5.000000000000000000 9 -1 1 COMPASS_OFS2_Y 13.000000000000000000 9 -1 1 COMPASS_OFS2_Z -18.000000000000000000 9 -1 1 COMPASS_OFS3_X 5.000000000000000000 9 -1 1 COMPASS_OFS3_Y 13.000000000000000000 9 -1 1 COMPASS_OFS3_Z -18.000000000000000000 9 -1 1 COMPASS_OFS_X 5.000000000000000000 9 -1 1 COMPASS_OFS_Y 13.000000000000000000 9 -1 1 COMPASS_OFS_Z -18.000000000000000000 9 +1 1 COMPASS_OFS2_X 0.000000000000000000 9 +1 1 COMPASS_OFS2_Y 0.000000000000000000 9 +1 1 COMPASS_OFS2_Z 0.000000000000000000 9 +1 1 COMPASS_OFS3_X 0.000000000000000000 9 +1 1 COMPASS_OFS3_Y 0.000000000000000000 9 +1 1 COMPASS_OFS3_Z 0.000000000000000000 9 +1 1 COMPASS_OFS_X 0.000000000000000000 9 +1 1 COMPASS_OFS_Y 0.000000000000000000 9 +1 1 COMPASS_OFS_Z 0.000000000000000000 9 1 1 COMPASS_OPTIONS 0 4 1 1 COMPASS_ORIENT 0 2 1 1 COMPASS_ORIENT2 0 2 1 1 COMPASS_ORIENT3 0 2 1 1 COMPASS_PMOT_EN 0 2 -1 1 COMPASS_PRIO1_ID 97539 6 -1 1 COMPASS_PRIO2_ID 131874 6 -1 1 COMPASS_PRIO3_ID 263178 6 -1 1 COMPASS_SCALE 1.000000000000000000 9 -1 1 COMPASS_SCALE2 1.000000000000000000 9 -1 1 COMPASS_SCALE3 1.000000000000000000 9 +1 1 COMPASS_PRIO1_ID 590114 6 +1 1 COMPASS_PRIO2_ID 0 6 +1 1 COMPASS_PRIO3_ID 0 6 +1 1 COMPASS_SCALE 0.000000000000000000 9 +1 1 COMPASS_SCALE2 0.000000000000000000 9 +1 1 COMPASS_SCALE3 0.000000000000000000 9 +1 1 COMPASS_TYPEMASK 32 6 1 1 COMPASS_USE 1 2 1 1 COMPASS_USE2 1 2 1 1 COMPASS_USE3 1 2 -1 1 CUST_ROT_ENABLE 0 2 -1 1 DID_ENABLE 0 2 -1 1 EAHRS_TYPE 0 2 -1 1 EFI_TYPE 0 2 -1 1 EK2_ENABLE 0 2 -1 1 EK3_ABIAS_P_NSE 0.019999999552965164 9 -1 1 EK3_ACC_BIAS_LIM 1.000000000000000000 9 -1 1 EK3_ACC_P_NSE 0.349999994039535522 9 -1 1 EK3_AFFINITY 0 6 -1 1 EK3_ALT_M_NSE 0.100000001490116119 9 -1 1 EK3_BCN_DELAY 50 2 -1 1 EK3_BCN_I_GTE 500 4 -1 1 EK3_BCN_M_NSE 1.000000000000000000 9 -1 1 EK3_BETA_MASK 0 2 -1 1 EK3_CHECK_SCALE 100 4 -1 1 EK3_DRAG_BCOEF_X 0.000000000000000000 9 -1 1 EK3_DRAG_BCOEF_Y 0.000000000000000000 9 -1 1 EK3_DRAG_MCOEF 0.000000000000000000 9 -1 1 EK3_DRAG_M_NSE 0.500000000000000000 9 -1 1 EK3_EAS_I_GATE 400 4 -1 1 EK3_EAS_M_NSE 1.399999976158142090 9 -1 1 EK3_ENABLE 1 2 -1 1 EK3_ERR_THRESH 0.200000002980232239 9 -1 1 EK3_FLOW_DELAY 10 2 -1 1 EK3_FLOW_I_GATE 300 4 -1 1 EK3_FLOW_M_NSE 0.250000000000000000 9 -1 1 EK3_FLOW_USE 1 2 -1 1 EK3_GBIAS_P_NSE 0.001000000047497451 9 -1 1 EK3_GLITCH_RAD 25 2 -1 1 EK3_GND_EFF_DZ 4.000000000000000000 9 -1 1 EK3_GPS_CHECK 31 2 -1 1 EK3_GPS_VACC_MAX 0.000000000000000000 9 -1 1 EK3_GSF_RST_MAX 2 2 -1 1 EK3_GSF_RUN_MASK 3 2 -1 1 EK3_GSF_USE_MASK 3 2 -1 1 EK3_GYRO_P_NSE 0.014999999664723873 9 -1 1 EK3_HGT_DELAY 60 4 -1 1 EK3_HGT_I_GATE 500 4 -1 1 EK3_HRT_FILT 2.000000000000000000 9 -1 1 EK3_IMU_MASK 3 2 -1 1 EK3_LOG_LEVEL 0 2 -1 1 EK3_MAGB_P_NSE 0.000099999997473788 9 -1 1 EK3_MAGE_P_NSE 0.001000000047497451 9 -1 1 EK3_MAG_CAL 3 2 -1 1 EK3_MAG_EF_LIM 50 4 -1 1 EK3_MAG_I_GATE 300 4 -1 1 EK3_MAG_MASK 0 2 -1 1 EK3_MAG_M_NSE 0.050000000745058060 9 -1 1 EK3_MAX_FLOW 2.500000000000000000 9 -1 1 EK3_NOAID_M_NSE 10.000000000000000000 9 -1 1 EK3_OGNM_TEST_SF 2.000000000000000000 9 -1 1 EK3_OGN_HGT_MASK 0 2 -1 1 EK3_OPTIONS 0 6 -1 1 EK3_POSNE_M_NSE 0.500000000000000000 9 -1 1 EK3_POS_I_GATE 500 4 -1 1 EK3_PRIMARY 0 2 -1 1 EK3_RNG_I_GATE 500 4 -1 1 EK3_RNG_M_NSE 0.500000000000000000 9 -1 1 EK3_RNG_USE_HGT -1 2 -1 1 EK3_RNG_USE_SPD 2.000000000000000000 9 -1 1 EK3_SRC1_POSXY 3 2 -1 1 EK3_SRC1_POSZ 1 2 -1 1 EK3_SRC1_VELXY 3 2 -1 1 EK3_SRC1_VELZ 0 2 -1 1 EK3_SRC1_YAW 1 2 -1 1 EK3_SRC2_POSXY 0 2 -1 1 EK3_SRC2_POSZ 1 2 -1 1 EK3_SRC2_VELXY 0 2 -1 1 EK3_SRC2_VELZ 0 2 -1 1 EK3_SRC2_YAW 0 2 -1 1 EK3_SRC3_POSXY 0 2 -1 1 EK3_SRC3_POSZ 1 2 -1 1 EK3_SRC3_VELXY 0 2 -1 1 EK3_SRC3_VELZ 0 2 -1 1 EK3_SRC3_YAW 0 2 -1 1 EK3_SRC_OPTIONS 1 4 -1 1 EK3_TAU_OUTPUT 25 2 -1 1 EK3_TERR_GRAD 0.100000001490116119 9 -1 1 EK3_VELD_M_NSE 0.699999988079071045 9 -1 1 EK3_VELNE_M_NSE 0.500000000000000000 9 -1 1 EK3_VEL_I_GATE 500 4 -1 1 EK3_VIS_VERR_MAX 0.899999976158142090 9 -1 1 EK3_VIS_VERR_MIN 0.100000001490116119 9 -1 1 EK3_WENC_VERR 0.100000001490116119 9 -1 1 EK3_WIND_PSCALE 1.000000000000000000 9 -1 1 EK3_WIND_P_NSE 0.100000001490116119 9 -1 1 EK3_YAW_I_GATE 300 4 -1 1 EK3_YAW_M_NSE 0.500000000000000000 9 -1 1 ESC_TLM_MAV_OFS 0 2 +1 1 EK2_ABIAS_P_NSE 0.004999999888241291 9 +1 1 EK2_ACC_P_NSE 0.600000023841857910 9 +1 1 EK2_ALT_M_NSE 10.000000000000000000 9 +1 1 EK2_ALT_SOURCE 0 2 +1 1 EK2_BCN_DELAY 50 2 +1 1 EK2_BCN_I_GTE 500 4 +1 1 EK2_BCN_M_NSE 1.000000000000000000 9 +1 1 EK2_CHECK_SCALE 100 4 +1 1 EK2_EAS_I_GATE 400 4 +1 1 EK2_EAS_M_NSE 1.399999976158142090 9 +1 1 EK2_ENABLE 1 2 +1 1 EK2_EXTNAV_DELAY 10 2 +1 1 EK2_FLOW_DELAY 10 2 +1 1 EK2_FLOW_I_GATE 300 4 +1 1 EK2_FLOW_M_NSE 0.250000000000000000 9 +1 1 EK2_FLOW_USE 1 2 +1 1 EK2_GBIAS_P_NSE 0.000099999997473788 9 +1 1 EK2_GLITCH_RAD 25 2 +1 1 EK2_GPS_CHECK 31 2 +1 1 EK2_GPS_TYPE 0 2 +1 1 EK2_GSCL_P_NSE 0.000500000023748726 9 +1 1 EK2_GYRO_P_NSE 0.029999999329447746 9 +1 1 EK2_HGT_DELAY 60 4 +1 1 EK2_HGT_I_GATE 500 4 +1 1 EK2_HRT_FILT 2.000000000000000000 9 +1 1 EK2_IMU_MASK 3 2 +1 1 EK2_LOG_MASK 1 2 +1 1 EK2_MAGB_P_NSE 0.000099999997473788 9 +1 1 EK2_MAGE_P_NSE 0.001000000047497451 9 +1 1 EK2_MAG_CAL 3 2 +1 1 EK2_MAG_EF_LIM 50 4 +1 1 EK2_MAG_I_GATE 300 4 +1 1 EK2_MAG_MASK 0 2 +1 1 EK2_MAG_M_NSE 0.050000000745058060 9 +1 1 EK2_MAX_FLOW 2.500000000000000000 9 +1 1 EK2_NOAID_M_NSE 10.000000000000000000 9 +1 1 EK2_OGN_HGT_MASK 0 2 +1 1 EK2_POSNE_M_NSE 1.000000000000000000 9 +1 1 EK2_POS_I_GATE 500 4 +1 1 EK2_RNG_I_GATE 500 4 +1 1 EK2_RNG_M_NSE 0.500000000000000000 9 +1 1 EK2_RNG_USE_HGT -1 2 +1 1 EK2_RNG_USE_SPD 2.000000000000000000 9 +1 1 EK2_TAU_OUTPUT 25 2 +1 1 EK2_TERR_GRAD 0.100000001490116119 9 +1 1 EK2_VELD_M_NSE 0.699999988079071045 9 +1 1 EK2_VELNE_M_NSE 0.500000000000000000 9 +1 1 EK2_VEL_I_GATE 500 4 +1 1 EK2_WIND_PSCALE 0.500000000000000000 9 +1 1 EK2_WIND_P_NSE 0.100000001490116119 9 +1 1 EK2_YAW_I_GATE 300 4 +1 1 EK2_YAW_M_NSE 0.500000000000000000 9 +1 1 EK3_ENABLE 0 2 1 1 FENCE_ACTION 1 2 1 1 FENCE_ALT_MAX 100.000000000000000000 9 1 1 FENCE_ALT_MIN -10.000000000000000000 9 @@ -394,28 +281,11 @@ 1 1 FENCE_RADIUS 300.000000000000000000 9 1 1 FENCE_TOTAL 0 2 1 1 FENCE_TYPE 7 2 -1 1 FFT_ENABLE 0 2 -1 1 FILT1_TYPE 0 2 -1 1 FILT2_TYPE 0 2 -1 1 FILT3_TYPE 0 2 -1 1 FILT4_TYPE 0 2 -1 1 FILT5_TYPE 0 2 -1 1 FILT6_TYPE 0 2 -1 1 FILT7_TYPE 0 2 -1 1 FILT8_TYPE 0 2 -1 1 FLOW_TYPE 0 2 -1 1 FORMAT_VERSION 1 4 1 1 FRAME_CONFIG 1 2 -1 1 FRSKY_DNLINK1_ID 20 2 -1 1 FRSKY_DNLINK2_ID 7 2 -1 1 FRSKY_DNLINK_ID 27 2 -1 1 FRSKY_OPTIONS 0 2 -1 1 FRSKY_UPLINK_ID 13 2 1 1 FS_CRASH_CHECK 0 2 1 1 FS_EKF_ACTION 0 2 1 1 FS_EKF_THRESH 0.800000011920928955 9 1 1 FS_GCS_ENABLE 2 2 -1 1 FS_GCS_TIMEOUT 5.000000000000000000 9 1 1 FS_LEAK_ENABLE 1 2 1 1 FS_PILOT_INPUT 2 2 1 1 FS_PILOT_TIMEOUT 3.000000000000000000 9 @@ -425,85 +295,89 @@ 1 1 FS_TEMP_MAX 62 2 1 1 FS_TERRAIN_ENAB 0 2 1 1 GCS_PID_MASK 0 4 -1 1 GEN_TYPE 0 2 -1 1 GPS1_CAN_NODEID 0 6 -1 1 GPS1_CAN_OVRIDE 0 6 -1 1 GPS1_COM_PORT 1 2 -1 1 GPS1_DELAY_MS 0 4 -1 1 GPS1_GNSS_MODE 0 2 -1 1 GPS1_MB_TYPE 0 2 -1 1 GPS1_POS_X 0.000000000000000000 9 -1 1 GPS1_POS_Y 0.000000000000000000 9 -1 1 GPS1_POS_Z 0.000000000000000000 9 -1 1 GPS1_RATE_MS 200 4 -1 1 GPS1_TYPE 1 2 -1 1 GPS2_TYPE 0 2 +1 1 GND_ABS_PRESS 85512.687500000000000000 9 +1 1 GND_ABS_PRESS2 85533.437500000000000000 9 +1 1 GND_ABS_PRESS3 0.000000000000000000 9 +1 1 GND_ALT_OFFSET 0.000000000000000000 9 +1 1 GND_EXT_BUS 0 2 +1 1 GND_FLTR_RNG 0 2 +1 1 GND_PRIMARY 0 2 +1 1 GND_PROBE_EXT 0 6 +1 1 GND_SPEC_GRAV 1.000000000000000000 9 +1 1 GND_TEMP 0.000000000000000000 9 1 1 GPS_AUTO_CONFIG 1 2 1 1 GPS_AUTO_SWITCH 1 2 1 1 GPS_BLEND_MASK 5 2 +1 1 GPS_BLEND_TC 10.000000000000000000 9 +1 1 GPS_DELAY_MS 0 4 +1 1 GPS_DELAY_MS2 0 4 1 1 GPS_DRV_OPTIONS 0 4 +1 1 GPS_GNSS_MODE 0 2 +1 1 GPS_GNSS_MODE2 0 2 1 1 GPS_INJECT_TO 127 2 +1 1 GPS_MIN_DGPS 100 2 1 1 GPS_MIN_ELEV -100 2 1 1 GPS_NAVFILTER 8 2 -1 1 GPS_PRIMARY 0 2 +1 1 GPS_POS1_X 0.000000000000000000 9 +1 1 GPS_POS1_Y 0.000000000000000000 9 +1 1 GPS_POS1_Z 0.000000000000000000 9 +1 1 GPS_POS2_X 0.000000000000000000 9 +1 1 GPS_POS2_Y 0.000000000000000000 9 +1 1 GPS_POS2_Z 0.000000000000000000 9 +1 1 GPS_RATE_MS 200 4 +1 1 GPS_RATE_MS2 200 4 1 1 GPS_RAW_DATA 0 2 1 1 GPS_SAVE_CFG 2 2 1 1 GPS_SBAS_MODE 2 2 1 1 GPS_SBP_LOGMASK -256 4 -1 1 GRIP_ENABLE 0 2 -1 1 INS_ACC1_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2OFFS_X 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Y 0.001000000047497451 9 -1 1 INS_ACC2OFFS_Z 0.001000000047497451 9 -1 1 INS_ACC2SCAL_X 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Y 1.001000046730041504 9 -1 1 INS_ACC2SCAL_Z 1.001000046730041504 9 -1 1 INS_ACC2_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC2_ID 2753036 6 +1 1 GPS_TYPE 1 2 +1 1 GPS_TYPE2 0 2 +1 1 INS_ACC2OFFS_X 0.000000000000000000 9 +1 1 INS_ACC2OFFS_Y 0.000000000000000000 9 +1 1 INS_ACC2OFFS_Z 0.000000000000000000 9 +1 1 INS_ACC2SCAL_X 1.000000000000000000 9 +1 1 INS_ACC2SCAL_Y 1.000000000000000000 9 +1 1 INS_ACC2SCAL_Z 1.000000000000000000 9 +1 1 INS_ACC2_ID 2883874 6 1 1 INS_ACC3OFFS_X 0.000000000000000000 9 1 1 INS_ACC3OFFS_Y 0.000000000000000000 9 1 1 INS_ACC3OFFS_Z 0.000000000000000000 9 1 1 INS_ACC3SCAL_X 1.000000000000000000 9 1 1 INS_ACC3SCAL_Y 1.000000000000000000 9 1 1 INS_ACC3SCAL_Z 1.000000000000000000 9 -1 1 INS_ACC3_CALTEMP -300.000000000000000000 9 -1 1 INS_ACC3_ID 0 6 +1 1 INS_ACC3_ID 3015690 6 1 1 INS_ACCEL_FILTER 20 4 -1 1 INS_ACCOFFS_X 0.001000000047497451 9 -1 1 INS_ACCOFFS_Y 0.001000000047497451 9 -1 1 INS_ACCOFFS_Z 0.001000000047497451 9 -1 1 INS_ACCSCAL_X 1.001000046730041504 9 -1 1 INS_ACCSCAL_Y 1.001000046730041504 9 -1 1 INS_ACCSCAL_Z 1.001000046730041504 9 +1 1 INS_ACCOFFS_X 0.000000000000000000 9 +1 1 INS_ACCOFFS_Y 0.000000000000000000 9 +1 1 INS_ACCOFFS_Z 0.000000000000000000 9 +1 1 INS_ACCSCAL_X 1.000000000000000000 9 +1 1 INS_ACCSCAL_Y 1.000000000000000000 9 +1 1 INS_ACCSCAL_Z 1.000000000000000000 9 1 1 INS_ACC_BODYFIX 2 2 -1 1 INS_ACC_ID 2753028 6 +1 1 INS_ACC_ID 3081250 6 1 1 INS_ENABLE_MASK 127 2 -1 1 INS_FAST_SAMPLE 1 2 -1 1 INS_GYR1_CALTEMP -300.000000000000000000 9 +1 1 INS_FAST_SAMPLE 7 2 1 1 INS_GYR2OFFS_X 0.000000000000000000 9 1 1 INS_GYR2OFFS_Y 0.000000000000000000 9 1 1 INS_GYR2OFFS_Z 0.000000000000000000 9 -1 1 INS_GYR2_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR2_ID 2752780 6 +1 1 INS_GYR2_ID 2883874 6 1 1 INS_GYR3OFFS_X 0.000000000000000000 9 1 1 INS_GYR3OFFS_Y 0.000000000000000000 9 1 1 INS_GYR3OFFS_Z 0.000000000000000000 9 -1 1 INS_GYR3_CALTEMP -300.000000000000000000 9 -1 1 INS_GYR3_ID 0 6 +1 1 INS_GYR3_ID 3015690 6 1 1 INS_GYROFFS_X 0.000000000000000000 9 1 1 INS_GYROFFS_Y 0.000000000000000000 9 1 1 INS_GYROFFS_Z 0.000000000000000000 9 1 1 INS_GYRO_FILTER 20 4 -1 1 INS_GYRO_RATE 0 2 1 1 INS_GYR_CAL 0 2 -1 1 INS_GYR_ID 2752772 6 -1 1 INS_HNTC2_ENABLE 0 2 +1 1 INS_GYR_ID 3081250 6 1 1 INS_HNTCH_ENABLE 0 2 1 1 INS_LOG_BAT_CNT 1024 4 1 1 INS_LOG_BAT_LGCT 32 4 1 1 INS_LOG_BAT_LGIN 20 2 1 1 INS_LOG_BAT_MASK 0 2 1 1 INS_LOG_BAT_OPT 0 2 +1 1 INS_NOTCH_ENABLE 0 2 1 1 INS_POS1_X 0.000000000000000000 9 1 1 INS_POS1_Y 0.000000000000000000 9 1 1 INS_POS1_Z 0.000000000000000000 9 @@ -513,12 +387,7 @@ 1 1 INS_POS3_X 0.000000000000000000 9 1 1 INS_POS3_Y 0.000000000000000000 9 1 1 INS_POS3_Z 0.000000000000000000 9 -1 1 INS_RAW_LOG_OPT 0 4 1 1 INS_STILL_THRESH 0.100000001490116119 9 -1 1 INS_TCAL1_ENABLE 0 2 -1 1 INS_TCAL2_ENABLE 0 2 -1 1 INS_TCAL3_ENABLE 0 2 -1 1 INS_TCAL_OPTIONS 0 6 1 1 INS_TRIM_OPTION 1 2 1 1 INS_USE 1 2 1 1 INS_USE2 1 2 @@ -529,29 +398,19 @@ 1 1 JS_GAIN_STEPS 4 2 1 1 JS_LIGHTS_STEPS 8 4 1 1 JS_THR_GAIN 1.000000000000000000 9 -1 1 KDE_NPOLE 14 2 1 1 LEAK1_LOGIC 0 2 1 1 LEAK1_PIN -1 2 -1 1 LEAK1_TYPE -1 2 1 1 LEAK2_LOGIC 0 2 1 1 LEAK2_PIN -1 2 -1 1 LEAK2_TYPE -1 2 1 1 LEAK3_LOGIC 0 2 1 1 LEAK3_PIN -1 2 -1 1 LEAK3_TYPE -1 2 1 1 LOG_BACKEND_TYPE 1 2 -1 1 LOG_BITMASK 180222 6 -1 1 LOG_BLK_RATEMAX 0.000000000000000000 9 -1 1 LOG_DARM_RATEMAX 0.000000000000000000 9 +1 1 LOG_BITMASK 176126 6 1 1 LOG_DISARMED 0 2 -1 1 LOG_FILE_BUFSIZE 200 4 +1 1 LOG_FILE_BUFSIZE 50 2 1 1 LOG_FILE_DSRMROT 0 2 -1 1 LOG_FILE_MB_FREE 500 4 -1 1 LOG_FILE_RATEMAX 0.000000000000000000 9 1 1 LOG_FILE_TIMEOUT 5 4 1 1 LOG_MAV_BUFSIZE 8 2 -1 1 LOG_MAV_RATEMAX 0.000000000000000000 9 -1 1 LOG_MAX_FILES 500 4 1 1 LOG_REPLAY 0 2 1 1 LOIT_ACC_MAX 500.000000000000000000 9 1 1 LOIT_ANG_MAX 0.000000000000000000 9 @@ -562,27 +421,29 @@ 1 1 MIS_OPTIONS 0 4 1 1 MIS_RESTART 0 2 1 1 MIS_TOTAL 0 4 -1 1 MNT1_DEFLT_MODE 3 2 -1 1 MNT1_DEVID 0 6 -1 1 MNT1_LEAD_PTCH 0.000000000000000000 9 -1 1 MNT1_LEAD_RLL 0.000000000000000000 9 -1 1 MNT1_NEUTRAL_X 0.000000000000000000 9 -1 1 MNT1_NEUTRAL_Y 0.000000000000000000 9 -1 1 MNT1_NEUTRAL_Z 0.000000000000000000 9 -1 1 MNT1_OPTIONS 0 2 -1 1 MNT1_PITCH_MAX 45 4 -1 1 MNT1_PITCH_MIN -45 4 -1 1 MNT1_RC_RATE 30 4 -1 1 MNT1_RETRACT_X 0.000000000000000000 9 -1 1 MNT1_RETRACT_Y 0.000000000000000000 9 -1 1 MNT1_RETRACT_Z 0.000000000000000000 9 -1 1 MNT1_ROLL_MAX 30 4 -1 1 MNT1_ROLL_MIN -30 4 -1 1 MNT1_SYSID_DFLT 0 2 -1 1 MNT1_TYPE 7 2 -1 1 MNT1_YAW_MAX 180 4 -1 1 MNT1_YAW_MIN -180 4 -1 1 MNT2_TYPE 0 2 +1 1 MNT_ANGMAX_PAN 4500 4 +1 1 MNT_ANGMAX_ROL 4500 4 +1 1 MNT_ANGMAX_TIL 4500 4 +1 1 MNT_ANGMIN_PAN -4500 4 +1 1 MNT_ANGMIN_ROL -4500 4 +1 1 MNT_ANGMIN_TIL -4500 4 +1 1 MNT_DEFLT_MODE 3 2 +1 1 MNT_JSTICK_SPD 100 2 +1 1 MNT_LEAD_PTCH 0.000000000000000000 9 +1 1 MNT_LEAD_RLL 0.000000000000000000 9 +1 1 MNT_NEUTRAL_X 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Y 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Z 0.000000000000000000 9 +1 1 MNT_RC_IN_PAN 7 2 +1 1 MNT_RC_IN_ROLL 0 2 +1 1 MNT_RC_IN_TILT 8 2 +1 1 MNT_RETRACT_X 0.000000000000000000 9 +1 1 MNT_RETRACT_Y 0.000000000000000000 9 +1 1 MNT_RETRACT_Z 0.000000000000000000 9 +1 1 MNT_STAB_PAN 0 2 +1 1 MNT_STAB_ROLL 0 2 +1 1 MNT_STAB_TILT 0 2 +1 1 MNT_TYPE 0 2 1 1 MOT_10_DIRECTION 1 2 1 1 MOT_11_DIRECTION 1 2 1 1 MOT_12_DIRECTION 1 2 @@ -603,9 +464,8 @@ 1 1 MOT_BOOST_SCALE 0.000000000000000000 9 1 1 MOT_FV_CPLNG_K 1.000000000000000000 9 1 1 MOT_HOVER_LEARN 2 2 -1 1 MOT_OPTIONS 0 4 -1 1 MOT_PWM_MAX 1900 4 -1 1 MOT_PWM_MIN 1100 4 +1 1 MOT_PWM_MAX 0 4 +1 1 MOT_PWM_MIN 0 4 1 1 MOT_PWM_TYPE 0 2 1 1 MOT_SAFE_DISARM 0 2 1 1 MOT_SAFE_TIME 1.000000000000000000 9 @@ -615,77 +475,42 @@ 1 1 MOT_SPIN_MAX 0.949999988079071045 9 1 1 MOT_SPIN_MIN 0.150000005960464478 9 1 1 MOT_SPOOL_TIME 0.500000000000000000 9 -1 1 MOT_SPOOL_TIM_DN 0.000000000000000000 9 1 1 MOT_THST_EXPO 0.649999976158142090 9 -1 1 MOT_THST_HOVER 0.500000000000000000 9 +1 1 MOT_THST_HOVER 0.349999994039535522 9 1 1 MOT_YAW_HEADROOM 200 4 -1 1 MSP_OPTIONS 0 2 -1 1 MSP_OSD_NCELLS 0 2 -1 1 NET_ENABLE 0 2 -1 1 NET_P1_TYPE 0 2 -1 1 NET_P2_TYPE 0 2 -1 1 NET_P3_TYPE 0 2 -1 1 NET_P4_TYPE 0 2 -1 1 NMEA_MSG_EN 3 4 -1 1 NMEA_RATE_MS 100 4 +1 1 NTF_BUZZ_ENABLE 1 2 1 1 NTF_BUZZ_ON_LVL 1 2 -1 1 NTF_BUZZ_PIN -1 2 -1 1 NTF_BUZZ_TYPES 5 2 +1 1 NTF_BUZZ_PIN 0 2 1 1 NTF_BUZZ_VOLUME 100 2 1 1 NTF_DISPLAY_TYPE 0 2 1 1 NTF_LED_BRIGHT 3 2 1 1 NTF_LED_LEN 1 2 1 1 NTF_LED_OVERRIDE 0 2 -1 1 NTF_LED_TYPES 123079 6 -1 1 ORIGIN_ALT 0.000000000000000000 9 -1 1 ORIGIN_LAT 0.000000000000000000 9 -1 1 ORIGIN_LON 0.000000000000000000 9 -1 1 PILOT_ACCEL_Z 200 4 +1 1 NTF_LED_TYPES 199 6 +1 1 NTF_OREO_THEME 0 2 +1 1 PILOT_ACCEL_Z 100 4 1 1 PILOT_SPEED_DN 0 4 -1 1 PILOT_SPEED_UP 200 4 +1 1 PILOT_SPEED_UP 500 4 1 1 PILOT_THR_FILT 0.000000000000000000 9 -1 1 PRX1_TYPE 0 2 -1 1 PRX2_TYPE 0 2 -1 1 PRX3_TYPE 0 2 -1 1 PRX4_TYPE 0 2 -1 1 PRX5_TYPE 0 2 -1 1 PRX_FILT 0.250000000000000000 9 -1 1 PRX_LOG_RAW 0 2 1 1 PSC_ACCZ_D 0.000000000000000000 9 -1 1 PSC_ACCZ_D_FF 0.000000000000000000 9 -1 1 PSC_ACCZ_FF 0.750000000000000000 9 +1 1 PSC_ACCZ_FF 0.000000000000000000 9 1 1 PSC_ACCZ_FLTD 0.000000000000000000 9 1 1 PSC_ACCZ_FLTE 20.000000000000000000 9 1 1 PSC_ACCZ_FLTT 0.000000000000000000 9 -1 1 PSC_ACCZ_I 4.000000000000000000 9 +1 1 PSC_ACCZ_I 0.100000001490116119 9 1 1 PSC_ACCZ_IMAX 100.000000000000000000 9 -1 1 PSC_ACCZ_NEF 0 2 -1 1 PSC_ACCZ_NTF 0 2 -1 1 PSC_ACCZ_P 2.000000000000000000 9 -1 1 PSC_ACCZ_PDMX 0.000000000000000000 9 -1 1 PSC_ACCZ_SMAX 0.000000000000000000 9 +1 1 PSC_ACCZ_P 0.500000000000000000 9 +1 1 PSC_ACC_XY_FILT 2.000000000000000000 9 1 1 PSC_ANGLE_MAX 0.000000000000000000 9 -1 1 PSC_JERK_XY 5.000000000000000000 9 -1 1 PSC_JERK_Z 8.000000000000000000 9 -1 1 PSC_POSXY_P 2.500000000000000000 9 +1 1 PSC_POSXY_P 1.000000000000000000 9 1 1 PSC_POSZ_P 3.000000000000000000 9 -1 1 PSC_VELXY_D 0.800000011920928955 9 -1 1 PSC_VELXY_FF 0.000000000000000000 9 -1 1 PSC_VELXY_FLTD 5.000000000000000000 9 -1 1 PSC_VELXY_FLTE 5.000000000000000000 9 +1 1 PSC_VELXY_D 0.000000000000000000 9 +1 1 PSC_VELXY_D_FILT 5.000000000000000000 9 +1 1 PSC_VELXY_FILT 5.000000000000000000 9 1 1 PSC_VELXY_I 0.500000000000000000 9 1 1 PSC_VELXY_IMAX 1000.000000000000000000 9 -1 1 PSC_VELXY_P 5.000000000000000000 9 -1 1 PSC_VELZ_D 0.000000000000000000 9 -1 1 PSC_VELZ_FF 0.000000000000000000 9 -1 1 PSC_VELZ_FLTD 5.000000000000000000 9 -1 1 PSC_VELZ_FLTE 5.000000000000000000 9 -1 1 PSC_VELZ_I 0.000000000000000000 9 -1 1 PSC_VELZ_IMAX 1000.000000000000000000 9 +1 1 PSC_VELXY_P 1.000000000000000000 9 1 1 PSC_VELZ_P 8.000000000000000000 9 -1 1 RALLY_INCL_HOME 0 2 -1 1 RALLY_LIMIT_KM 1.000000000000000000 9 -1 1 RALLY_TOTAL 0 2 1 1 RC10_DZ 0 4 1 1 RC10_MAX 1900 4 1 1 RC10_MIN 1100 4 @@ -766,78 +591,204 @@ 1 1 RC9_MIN 1100 4 1 1 RC9_REVERSED 0 2 1 1 RC9_TRIM 1500 4 -1 1 RC_OPTIONS 32 6 +1 1 RC_OPTIONS 0 6 1 1 RC_OVERRIDE_TIME 3.000000000000000000 9 -1 1 RC_PROTOCOLS 1 6 1 1 RC_SPEED 200 4 -1 1 RELAY1_DEFAULT 0 2 -1 1 RELAY1_FUNCTION 1 2 -1 1 RELAY1_INVERTED 0 2 -1 1 RELAY1_PIN 13 4 -1 1 RELAY2_FUNCTION 0 2 -1 1 RELAY3_FUNCTION 0 2 -1 1 RELAY4_FUNCTION 0 2 -1 1 RELAY5_FUNCTION 0 2 -1 1 RELAY6_FUNCTION 0 2 +1 1 RELAY_DEFAULT 0 2 +1 1 RELAY_PIN 54 2 +1 1 RELAY_PIN2 55 2 +1 1 RELAY_PIN3 -1 2 +1 1 RELAY_PIN4 -1 2 +1 1 RELAY_PIN5 -1 2 +1 1 RELAY_PIN6 -1 2 1 1 RNGFND1_ADDR 0 2 1 1 RNGFND1_FUNCTION 0 2 1 1 RNGFND1_GNDCLEAR 10 2 -1 1 RNGFND1_MAX_CM 3000 4 +1 1 RNGFND1_MAX_CM 700 4 1 1 RNGFND1_MIN_CM 20 4 1 1 RNGFND1_OFFSET 0.000000000000000000 9 1 1 RNGFND1_ORIENT 25 2 -1 1 RNGFND1_PIN 0 2 +1 1 RNGFND1_PIN -1 2 1 1 RNGFND1_POS_X 0.000000000000000000 9 1 1 RNGFND1_POS_Y 0.000000000000000000 9 1 1 RNGFND1_POS_Z 0.000000000000000000 9 1 1 RNGFND1_PWRRNG 0 4 1 1 RNGFND1_RMETRIC 1 2 -1 1 RNGFND1_SCALING 12.119999885559082031 9 +1 1 RNGFND1_SCALING 3.000000000000000000 9 1 1 RNGFND1_STOP_PIN -1 2 -1 1 RNGFND1_TYPE 1 2 +1 1 RNGFND1_TYPE 10 2 +1 1 RNGFND2_ADDR 0 2 +1 1 RNGFND2_FUNCTION 0 2 +1 1 RNGFND2_GNDCLEAR 10 2 +1 1 RNGFND2_MAX_CM 700 4 +1 1 RNGFND2_MIN_CM 20 4 +1 1 RNGFND2_OFFSET 0.000000000000000000 9 +1 1 RNGFND2_ORIENT 25 2 +1 1 RNGFND2_PIN -1 2 +1 1 RNGFND2_POS_X 0.000000000000000000 9 +1 1 RNGFND2_POS_Y 0.000000000000000000 9 +1 1 RNGFND2_POS_Z 0.000000000000000000 9 +1 1 RNGFND2_PWRRNG 0 4 +1 1 RNGFND2_RMETRIC 1 2 +1 1 RNGFND2_SCALING 3.000000000000000000 9 +1 1 RNGFND2_STOP_PIN -1 2 1 1 RNGFND2_TYPE 0 2 +1 1 RNGFND3_ADDR 0 2 +1 1 RNGFND3_FUNCTION 0 2 +1 1 RNGFND3_GNDCLEAR 10 2 +1 1 RNGFND3_MAX_CM 700 4 +1 1 RNGFND3_MIN_CM 20 4 +1 1 RNGFND3_OFFSET 0.000000000000000000 9 +1 1 RNGFND3_ORIENT 25 2 +1 1 RNGFND3_PIN -1 2 +1 1 RNGFND3_POS_X 0.000000000000000000 9 +1 1 RNGFND3_POS_Y 0.000000000000000000 9 +1 1 RNGFND3_POS_Z 0.000000000000000000 9 +1 1 RNGFND3_PWRRNG 0 4 +1 1 RNGFND3_RMETRIC 1 2 +1 1 RNGFND3_SCALING 3.000000000000000000 9 +1 1 RNGFND3_STOP_PIN -1 2 1 1 RNGFND3_TYPE 0 2 +1 1 RNGFND4_ADDR 0 2 +1 1 RNGFND4_FUNCTION 0 2 +1 1 RNGFND4_GNDCLEAR 10 2 +1 1 RNGFND4_MAX_CM 700 4 +1 1 RNGFND4_MIN_CM 20 4 +1 1 RNGFND4_OFFSET 0.000000000000000000 9 +1 1 RNGFND4_ORIENT 25 2 +1 1 RNGFND4_PIN -1 2 +1 1 RNGFND4_POS_X 0.000000000000000000 9 +1 1 RNGFND4_POS_Y 0.000000000000000000 9 +1 1 RNGFND4_POS_Z 0.000000000000000000 9 +1 1 RNGFND4_PWRRNG 0 4 +1 1 RNGFND4_RMETRIC 1 2 +1 1 RNGFND4_SCALING 3.000000000000000000 9 +1 1 RNGFND4_STOP_PIN -1 2 1 1 RNGFND4_TYPE 0 2 +1 1 RNGFND5_ADDR 0 2 +1 1 RNGFND5_FUNCTION 0 2 +1 1 RNGFND5_GNDCLEAR 10 2 +1 1 RNGFND5_MAX_CM 700 4 +1 1 RNGFND5_MIN_CM 20 4 +1 1 RNGFND5_OFFSET 0.000000000000000000 9 +1 1 RNGFND5_ORIENT 25 2 +1 1 RNGFND5_PIN -1 2 +1 1 RNGFND5_POS_X 0.000000000000000000 9 +1 1 RNGFND5_POS_Y 0.000000000000000000 9 +1 1 RNGFND5_POS_Z 0.000000000000000000 9 +1 1 RNGFND5_PWRRNG 0 4 +1 1 RNGFND5_RMETRIC 1 2 +1 1 RNGFND5_SCALING 3.000000000000000000 9 +1 1 RNGFND5_STOP_PIN -1 2 1 1 RNGFND5_TYPE 0 2 +1 1 RNGFND6_ADDR 0 2 +1 1 RNGFND6_FUNCTION 0 2 +1 1 RNGFND6_GNDCLEAR 10 2 +1 1 RNGFND6_MAX_CM 700 4 +1 1 RNGFND6_MIN_CM 20 4 +1 1 RNGFND6_OFFSET 0.000000000000000000 9 +1 1 RNGFND6_ORIENT 25 2 +1 1 RNGFND6_PIN -1 2 +1 1 RNGFND6_POS_X 0.000000000000000000 9 +1 1 RNGFND6_POS_Y 0.000000000000000000 9 +1 1 RNGFND6_POS_Z 0.000000000000000000 9 +1 1 RNGFND6_PWRRNG 0 4 +1 1 RNGFND6_RMETRIC 1 2 +1 1 RNGFND6_SCALING 3.000000000000000000 9 +1 1 RNGFND6_STOP_PIN -1 2 1 1 RNGFND6_TYPE 0 2 +1 1 RNGFND7_ADDR 0 2 +1 1 RNGFND7_FUNCTION 0 2 +1 1 RNGFND7_GNDCLEAR 10 2 +1 1 RNGFND7_MAX_CM 700 4 +1 1 RNGFND7_MIN_CM 20 4 +1 1 RNGFND7_OFFSET 0.000000000000000000 9 +1 1 RNGFND7_ORIENT 25 2 +1 1 RNGFND7_PIN -1 2 +1 1 RNGFND7_POS_X 0.000000000000000000 9 +1 1 RNGFND7_POS_Y 0.000000000000000000 9 +1 1 RNGFND7_POS_Z 0.000000000000000000 9 +1 1 RNGFND7_PWRRNG 0 4 +1 1 RNGFND7_RMETRIC 1 2 +1 1 RNGFND7_SCALING 3.000000000000000000 9 +1 1 RNGFND7_STOP_PIN -1 2 1 1 RNGFND7_TYPE 0 2 +1 1 RNGFND8_ADDR 0 2 +1 1 RNGFND8_FUNCTION 0 2 +1 1 RNGFND8_GNDCLEAR 10 2 +1 1 RNGFND8_MAX_CM 700 4 +1 1 RNGFND8_MIN_CM 20 4 +1 1 RNGFND8_OFFSET 0.000000000000000000 9 +1 1 RNGFND8_ORIENT 25 2 +1 1 RNGFND8_PIN -1 2 +1 1 RNGFND8_POS_X 0.000000000000000000 9 +1 1 RNGFND8_POS_Y 0.000000000000000000 9 +1 1 RNGFND8_POS_Z 0.000000000000000000 9 +1 1 RNGFND8_PWRRNG 0 4 +1 1 RNGFND8_RMETRIC 1 2 +1 1 RNGFND8_SCALING 3.000000000000000000 9 +1 1 RNGFND8_STOP_PIN -1 2 1 1 RNGFND8_TYPE 0 2 +1 1 RNGFND9_ADDR 0 2 +1 1 RNGFND9_FUNCTION 0 2 +1 1 RNGFND9_GNDCLEAR 10 2 +1 1 RNGFND9_MAX_CM 700 4 +1 1 RNGFND9_MIN_CM 20 4 +1 1 RNGFND9_OFFSET 0.000000000000000000 9 +1 1 RNGFND9_ORIENT 25 2 +1 1 RNGFND9_PIN -1 2 +1 1 RNGFND9_POS_X 0.000000000000000000 9 +1 1 RNGFND9_POS_Y 0.000000000000000000 9 +1 1 RNGFND9_POS_Z 0.000000000000000000 9 +1 1 RNGFND9_PWRRNG 0 4 +1 1 RNGFND9_RMETRIC 1 2 +1 1 RNGFND9_SCALING 3.000000000000000000 9 +1 1 RNGFND9_STOP_PIN -1 2 1 1 RNGFND9_TYPE 0 2 +1 1 RNGFNDA_ADDR 0 2 +1 1 RNGFNDA_FUNCTION 0 2 +1 1 RNGFNDA_GNDCLEAR 10 2 +1 1 RNGFNDA_MAX_CM 700 4 +1 1 RNGFNDA_MIN_CM 20 4 +1 1 RNGFNDA_OFFSET 0.000000000000000000 9 +1 1 RNGFNDA_ORIENT 25 2 +1 1 RNGFNDA_PIN -1 2 +1 1 RNGFNDA_POS_X 0.000000000000000000 9 +1 1 RNGFNDA_POS_Y 0.000000000000000000 9 +1 1 RNGFNDA_POS_Z 0.000000000000000000 9 +1 1 RNGFNDA_PWRRNG 0 4 +1 1 RNGFNDA_RMETRIC 1 2 +1 1 RNGFNDA_SCALING 3.000000000000000000 9 +1 1 RNGFNDA_STOP_PIN -1 2 1 1 RNGFNDA_TYPE 0 2 -1 1 RNGFND_SQ_MIN 90 2 -1 1 RPM1_TYPE 0 2 -1 1 RPM2_TYPE 0 2 +1 1 RNGFND_GAIN 0.800000011920928955 9 1 1 SCHED_DEBUG 0 2 1 1 SCHED_LOOP_RATE 400 4 -1 1 SCHED_OPTIONS 0 2 1 1 SCR_ENABLE 0 2 1 1 SERIAL0_BAUD 115 6 1 1 SERIAL0_PROTOCOL 2 2 1 1 SERIAL1_BAUD 57 6 1 1 SERIAL1_OPTIONS 0 4 -1 1 SERIAL1_PROTOCOL 2 2 +1 1 SERIAL1_PROTOCOL 1 2 1 1 SERIAL2_BAUD 57 6 1 1 SERIAL2_OPTIONS 0 4 -1 1 SERIAL2_PROTOCOL 2 2 -1 1 SERIAL3_BAUD 230 6 +1 1 SERIAL2_PROTOCOL 1 2 +1 1 SERIAL3_BAUD 38 6 1 1 SERIAL3_OPTIONS 0 4 1 1 SERIAL3_PROTOCOL 5 2 -1 1 SERIAL4_BAUD 230 6 +1 1 SERIAL4_BAUD 38 6 1 1 SERIAL4_OPTIONS 0 4 1 1 SERIAL4_PROTOCOL 5 2 1 1 SERIAL5_BAUD 57 6 1 1 SERIAL5_OPTIONS 0 4 -1 1 SERIAL5_PROTOCOL -1 2 -1 1 SERIAL6_BAUD 57 6 +1 1 SERIAL5_PROTOCOL 1 2 +1 1 SERIAL6_BAUD 115200 6 1 1 SERIAL6_OPTIONS 0 4 -1 1 SERIAL6_PROTOCOL -1 2 -1 1 SERIAL7_BAUD 57 6 -1 1 SERIAL7_OPTIONS 0 4 -1 1 SERIAL7_PROTOCOL -1 2 +1 1 SERIAL6_PROTOCOL 22 2 1 1 SERIAL_PASS1 0 2 1 1 SERIAL_PASS2 -1 2 1 1 SERIAL_PASSTIMO 15 2 -1 1 SERVO10_FUNCTION 7 4 +1 1 SERVO10_FUNCTION 0 4 1 1 SERVO10_MAX 1900 4 1 1 SERVO10_MIN 1100 4 1 1 SERVO10_REVERSED 0 2 @@ -907,480 +858,73 @@ 1 1 SERVO7_MIN 1100 4 1 1 SERVO7_REVERSED 0 2 1 1 SERVO7_TRIM 1500 4 -1 1 SERVO8_FUNCTION 7 4 +1 1 SERVO8_FUNCTION 0 4 1 1 SERVO8_MAX 1900 4 1 1 SERVO8_MIN 1100 4 1 1 SERVO8_REVERSED 0 2 1 1 SERVO8_TRIM 1500 4 -1 1 SERVO9_FUNCTION 59 4 +1 1 SERVO9_FUNCTION 0 4 1 1 SERVO9_MAX 1900 4 1 1 SERVO9_MIN 1100 4 1 1 SERVO9_REVERSED 0 2 1 1 SERVO9_TRIM 1500 4 -1 1 SERVO_32_ENABLE 0 2 -1 1 SERVO_DSHOT_ESC 0 2 -1 1 SERVO_DSHOT_RATE 0 2 -1 1 SERVO_FTW_MASK 0 6 -1 1 SERVO_FTW_POLES 14 2 -1 1 SERVO_FTW_RVMASK 0 6 -1 1 SERVO_GPIO_MASK 0 6 +1 1 SERVO_BLH_DEBUG 0 2 +1 1 SERVO_BLH_MASK 0 6 +1 1 SERVO_BLH_OTYPE 0 2 +1 1 SERVO_BLH_POLES 14 2 +1 1 SERVO_BLH_PORT 0 2 +1 1 SERVO_BLH_REMASK 0 6 +1 1 SERVO_BLH_TEST 0 2 +1 1 SERVO_BLH_TMOUT 0 4 +1 1 SERVO_BLH_TRATE 10 4 1 1 SERVO_RATE 50 4 -1 1 SERVO_RC_FS_MSK 0 6 1 1 SERVO_ROB_POSMAX 4095 6 1 1 SERVO_ROB_POSMIN 0 6 1 1 SERVO_SBUS_RATE 50 4 1 1 SERVO_VOLZ_MASK 0 6 -1 1 SERVO_VOLZ_RANGE 200 4 -1 1 SIM_ACC1_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC1_RND 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC1_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC2_RND 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC2_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_X 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_ACC3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_ACC3_RND 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_X 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Y 0.000000000000000000 9 -1 1 SIM_ACC3_SCAL_Z 0.000000000000000000 9 -1 1 SIM_ACCEL1_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL2_FAIL 0.000000000000000000 9 -1 1 SIM_ACCEL3_FAIL 0.000000000000000000 9 -1 1 SIM_ACC_FAIL_MSK 0 2 -1 1 SIM_ACC_FILE_RW 0 2 -1 1 SIM_ACC_TRIM_X 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Y 0.000000000000000000 9 -1 1 SIM_ACC_TRIM_Z 0.000000000000000000 9 -1 1 SIM_ADSB_ALT 1000.000000000000000000 9 -1 1 SIM_ADSB_COUNT -1 4 -1 1 SIM_ADSB_RADIUS 10000.000000000000000000 9 -1 1 SIM_ADSB_TX 0 2 -1 1 SIM_ADSB_TYPES 1 2 -1 1 SIM_ARSPD2_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD2_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD2_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD2_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD2_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD2_RND 2.000000000000000000 9 -1 1 SIM_ARSPD2_SIGN 0 2 -1 1 SIM_ARSPD_FAIL 0.000000000000000000 9 -1 1 SIM_ARSPD_FAILP 0.000000000000000000 9 -1 1 SIM_ARSPD_OFS 2013.000000000000000000 9 -1 1 SIM_ARSPD_PITOT 0.000000000000000000 9 -1 1 SIM_ARSPD_RATIO 1.990000009536743164 9 -1 1 SIM_ARSPD_RND 2.000000000000000000 9 -1 1 SIM_ARSPD_SIGN 0 2 -1 1 SIM_BAR2_DELAY 0 4 -1 1 SIM_BAR2_DISABLE 0 2 -1 1 SIM_BAR2_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR2_FREEZE 0 2 -1 1 SIM_BAR2_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR2_RND 0.200000002980232239 9 -1 1 SIM_BAR2_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR2_WCF_UP 0.000000000000000000 9 -1 1 SIM_BAR3_DELAY 0 4 -1 1 SIM_BAR3_DISABLE 0 2 -1 1 SIM_BAR3_DRIFT 0.000000000000000000 9 -1 1 SIM_BAR3_FREEZE 0 2 -1 1 SIM_BAR3_GLITCH 0.000000000000000000 9 -1 1 SIM_BAR3_RND 0.200000002980232239 9 -1 1 SIM_BAR3_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_DN 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BAR3_WCF_UP 0.000000000000000000 9 -1 1 SIM_BARO_COUNT 2 2 -1 1 SIM_BARO_DELAY 0 4 -1 1 SIM_BARO_DISABLE 0 2 -1 1 SIM_BARO_DRIFT 0.000000000000000000 9 -1 1 SIM_BARO_FREEZE 0 2 -1 1 SIM_BARO_GLITCH 0.000000000000000000 9 -1 1 SIM_BARO_RND 0.019999999552965164 9 -1 1 SIM_BARO_WCF_BAK 0.000000000000000000 9 -1 1 SIM_BARO_WCF_DN 0.000000000000000000 9 -1 1 SIM_BARO_WCF_FWD 0.000000000000000000 9 -1 1 SIM_BARO_WCF_LFT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_RGT 0.000000000000000000 9 -1 1 SIM_BARO_WCF_UP 0.000000000000000000 9 -1 1 SIM_BATT_CAP_AH 0.000000000000000000 9 -1 1 SIM_BATT_VOLTAGE 12.600000381469726563 9 -1 1 SIM_BAUDLIMIT_EN 0 2 -1 1 SIM_BUOYANCY 1.000000000000000000 9 -1 1 SIM_CAN_SRV_MSK 0 6 -1 1 SIM_CAN_TYPE1 1 2 -1 1 SIM_CAN_TYPE2 1 2 -1 1 SIM_CLAMP_CH 0 2 -1 1 SIM_DRIFT_SPEED 0.050000000745058060 9 -1 1 SIM_DRIFT_TIME 5.000000000000000000 9 -1 1 SIM_EFI_TYPE 0 2 -1 1 SIM_ENGINE_FAIL 0 2 -1 1 SIM_ENGINE_MUL 1.000000000000000000 9 -1 1 SIM_ESC_ARM_RPM 0.000000000000000000 9 -1 1 SIM_ESC_TELEM 1 2 -1 1 SIM_FLOAT_EXCEPT 1 2 -1 1 SIM_FLOW_DELAY 0 2 -1 1 SIM_FLOW_ENABLE 0 2 -1 1 SIM_FLOW_POS_X 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Y 0.000000000000000000 9 -1 1 SIM_FLOW_POS_Z 0.000000000000000000 9 -1 1 SIM_FLOW_RATE 10 4 -1 1 SIM_FLOW_RND 0.050000000745058060 9 -1 1 SIM_FTOWESC_ENA 0 2 -1 1 SIM_FTOWESC_POW 4095 6 -1 1 SIM_GND_BEHAV -1 2 -1 1 SIM_GPS2_ACC 0.300000011920928955 9 -1 1 SIM_GPS2_ALT_OFS 0 4 -1 1 SIM_GPS2_BYTELOS 0.000000000000000000 9 -1 1 SIM_GPS2_DISABLE 1 2 -1 1 SIM_GPS2_DRFTALT 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_X 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Y 0.000000000000000000 9 -1 1 SIM_GPS2_GLTCH_Z 0.000000000000000000 9 -1 1 SIM_GPS2_HDG 0 2 -1 1 SIM_GPS2_HZ 5 2 -1 1 SIM_GPS2_JAM 0 2 -1 1 SIM_GPS2_LAG_MS 100 4 -1 1 SIM_GPS2_LCKTIME 0 4 -1 1 SIM_GPS2_NOISE 0.000000000000000000 9 -1 1 SIM_GPS2_NUMSATS 10 2 -1 1 SIM_GPS2_POS_X 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS2_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS2_TYPE 1 2 -1 1 SIM_GPS2_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS2_VERR_Z 0.000000000000000000 9 -1 1 SIM_GPS_ACC 0.300000011920928955 9 -1 1 SIM_GPS_ALT_OFS 0 4 -1 1 SIM_GPS_BYTELOSS 0.000000000000000000 9 -1 1 SIM_GPS_DISABLE 0 2 -1 1 SIM_GPS_DRIFTALT 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_X 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Y 0.000000000000000000 9 -1 1 SIM_GPS_GLITCH_Z 0.000000000000000000 9 -1 1 SIM_GPS_HDG 0 2 -1 1 SIM_GPS_HZ 5 2 -1 1 SIM_GPS_JAM 0 2 -1 1 SIM_GPS_LAG_MS 100 4 -1 1 SIM_GPS_LOCKTIME 0 4 -1 1 SIM_GPS_LOG_NUM 0 4 -1 1 SIM_GPS_NOISE 0.000000000000000000 9 -1 1 SIM_GPS_NUMSATS 10 2 -1 1 SIM_GPS_POS_X 0.000000000000000000 9 -1 1 SIM_GPS_POS_Y 0.000000000000000000 9 -1 1 SIM_GPS_POS_Z 0.000000000000000000 9 -1 1 SIM_GPS_TYPE 1 2 -1 1 SIM_GPS_VERR_X 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Y 0.000000000000000000 9 -1 1 SIM_GPS_VERR_Z 0.000000000000000000 9 -1 1 SIM_GRPE_ENABLE 0 2 -1 1 SIM_GRPE_PIN -1 2 -1 1 SIM_GRPS_ENABLE 0 2 -1 1 SIM_GRPS_GRAB 2000 4 -1 1 SIM_GRPS_PIN -1 2 -1 1 SIM_GRPS_RELEASE 1000 4 -1 1 SIM_GRPS_REVERSE 0 2 -1 1 SIM_GYR1_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR1_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR1_RND 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR1_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR2_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR2_RND 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR2_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_X 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Y 0.000000000000000000 9 -1 1 SIM_GYR3_BIAS_Z 0.000000000000000000 9 -1 1 SIM_GYR3_RND 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_X 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Y 0.000000000000000000 9 -1 1 SIM_GYR3_SCALE_Z 0.000000000000000000 9 -1 1 SIM_GYR_FAIL_MSK 0 2 -1 1 SIM_GYR_FILE_RW 0 2 -1 1 SIM_IE24_ENABLE 0 2 -1 1 SIM_IE24_ERROR 0 6 -1 1 SIM_IE24_STATE -1 2 -1 1 SIM_IMUT1_ENABLE 0 2 -1 1 SIM_IMUT2_ENABLE 0 2 -1 1 SIM_IMUT3_ENABLE 0 2 -1 1 SIM_IMUT_END 45.000000000000000000 9 -1 1 SIM_IMUT_FIXED 0.000000000000000000 9 -1 1 SIM_IMUT_START 25.000000000000000000 9 -1 1 SIM_IMUT_TCONST 300.000000000000000000 9 -1 1 SIM_IMU_COUNT 2 2 -1 1 SIM_IMU_ORIENT 0 2 -1 1 SIM_IMU_POS_X 0.000000000000000000 9 -1 1 SIM_IMU_POS_Y 0.000000000000000000 9 -1 1 SIM_IMU_POS_Z 0.000000000000000000 9 -1 1 SIM_INIT_ALT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LAT_OFS 0.000000000000000000 9 -1 1 SIM_INIT_LON_OFS 0.000000000000000000 9 -1 1 SIM_INS_THR_MIN 0.100000001490116119 9 -1 1 SIM_JSON_MASTER 0 2 -1 1 SIM_LED_LAYOUT 0 2 -1 1 SIM_LOOP_DELAY 0 6 -1 1 SIM_MAG1_DEVID 97539 6 -1 1 SIM_MAG1_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG1_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG1_FAIL 0 2 -1 1 SIM_MAG1_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG1_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG1_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG1_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG1_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG1_ORIENT 0 2 -1 1 SIM_MAG1_SCALING 1.000000000000000000 9 -1 1 SIM_MAG2_DEVID 131874 6 -1 1 SIM_MAG2_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG2_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG2_FAIL 0 2 -1 1 SIM_MAG2_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG2_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG2_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG2_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG2_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG2_ORIENT 0 2 -1 1 SIM_MAG2_SCALING 1.000000000000000000 9 -1 1 SIM_MAG3_DEVID 263178 6 -1 1 SIM_MAG3_DIA_X 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Y 0.000000000000000000 9 -1 1 SIM_MAG3_DIA_Z 0.000000000000000000 9 -1 1 SIM_MAG3_FAIL 0 2 -1 1 SIM_MAG3_ODI_X 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Y 0.000000000000000000 9 -1 1 SIM_MAG3_ODI_Z 0.000000000000000000 9 -1 1 SIM_MAG3_OFS_X 5.000000000000000000 9 -1 1 SIM_MAG3_OFS_Y 13.000000000000000000 9 -1 1 SIM_MAG3_OFS_Z -18.000000000000000000 9 -1 1 SIM_MAG3_ORIENT 0 2 -1 1 SIM_MAG3_SCALING 1.000000000000000000 9 -1 1 SIM_MAG4_DEVID 97283 6 -1 1 SIM_MAG5_DEVID 97795 6 -1 1 SIM_MAG6_DEVID 98051 6 -1 1 SIM_MAG7_DEVID 0 6 -1 1 SIM_MAG8_DEVID 0 6 -1 1 SIM_MAG_ALY_HGT 1.000000000000000000 9 -1 1 SIM_MAG_ALY_X 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Y 0.000000000000000000 9 -1 1 SIM_MAG_ALY_Z 0.000000000000000000 9 -1 1 SIM_MAG_DELAY 0 4 -1 1 SIM_MAG_MOT_X 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Y 0.000000000000000000 9 -1 1 SIM_MAG_MOT_Z 0.000000000000000000 9 -1 1 SIM_MAG_RND 0.000000000000000000 9 -1 1 SIM_MAG_SAVE_IDS 1 2 -1 1 SIM_ODOM_ENABLE 0 2 -1 1 SIM_OH_MASK 0 6 -1 1 SIM_OH_RELAY_MSK -1 4 -1 1 SIM_OPOS_ALT 584.000000000000000000 9 -1 1 SIM_OPOS_HDG 353.000000000000000000 9 -1 1 SIM_OPOS_LAT -35.363262176513671875 9 -1 1 SIM_OPOS_LNG 149.165237426757812500 9 -1 1 SIM_PARA_ENABLE 0 2 -1 1 SIM_PARA_PIN -1 2 -1 1 SIM_PIN_MASK 0 4 -1 1 SIM_PLD_ALT_LMT 15.000000000000000000 9 -1 1 SIM_PLD_DIST_LMT 10.000000000000000000 9 -1 1 SIM_PLD_ENABLE 0 2 -1 1 SIM_PLD_HEIGHT 0.000000000000000000 9 -1 1 SIM_PLD_LAT 0.000000000000000000 9 -1 1 SIM_PLD_LON 0.000000000000000000 9 -1 1 SIM_PLD_OPTIONS 0 2 -1 1 SIM_PLD_ORIENT 24 2 -1 1 SIM_PLD_RATE 100 6 -1 1 SIM_PLD_SHIP 0 2 -1 1 SIM_PLD_TYPE 0 2 -1 1 SIM_PLD_YAW 0 4 -1 1 SIM_RATE_HZ 1200 4 -1 1 SIM_RC_CHANCOUNT 16 2 -1 1 SIM_RC_FAIL 0 2 -1 1 SIM_RICH_CTRL -1 2 -1 1 SIM_RICH_ENABLE 0 2 -1 1 SIM_SERVO_DELAY 0.000000000000000000 9 -1 1 SIM_SERVO_FILTER 0.000000000000000000 9 -1 1 SIM_SERVO_SPEED 0.140000000596046448 9 -1 1 SIM_SHIP_DSIZE 10.000000000000000000 9 -1 1 SIM_SHIP_ENABLE 0 2 -1 1 SIM_SHIP_OFS_X 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Y 0.000000000000000000 9 -1 1 SIM_SHIP_OFS_Z 0.000000000000000000 9 -1 1 SIM_SHIP_PSIZE 1000.000000000000000000 9 -1 1 SIM_SHIP_SPEED 3.000000000000000000 9 -1 1 SIM_SHIP_SYSID 17 2 -1 1 SIM_SHOVE_TIME 0 6 -1 1 SIM_SHOVE_X 0.000000000000000000 9 -1 1 SIM_SHOVE_Y 0.000000000000000000 9 -1 1 SIM_SHOVE_Z 0.000000000000000000 9 -1 1 SIM_SLUP_ENABLE 0 2 -1 1 SIM_SONAR_GLITCH 0.000000000000000000 9 -1 1 SIM_SONAR_POS_X 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Y 0.000000000000000000 9 -1 1 SIM_SONAR_POS_Z 0.000000000000000000 9 -1 1 SIM_SONAR_RND 0.000000000000000000 9 -1 1 SIM_SONAR_ROT 25 2 -1 1 SIM_SONAR_SCALE 12.121199607849121094 9 -1 1 SIM_SPEEDUP 1.000000000000000000 9 -1 1 SIM_SPR_ENABLE 0 2 -1 1 SIM_SPR_PUMP -1 2 -1 1 SIM_SPR_SPIN -1 2 -1 1 SIM_TA_ENABLE 1 2 -1 1 SIM_TEMP_BFACTOR 0.000000000000000000 9 -1 1 SIM_TEMP_BRD_OFF 20.000000000000000000 9 -1 1 SIM_TEMP_START 25.000000000000000000 9 -1 1 SIM_TEMP_TCONST 30.000000000000000000 9 -1 1 SIM_TERRAIN 1 2 -1 1 SIM_THML_SCENARI 0 2 -1 1 SIM_TIDE_DIR 0.000000000000000000 9 -1 1 SIM_TIDE_SPEED 0.000000000000000000 9 -1 1 SIM_TIME_JITTER 0 4 -1 1 SIM_TWIST_TIME 0 6 -1 1 SIM_TWIST_X 0.000000000000000000 9 -1 1 SIM_TWIST_Y 0.000000000000000000 9 -1 1 SIM_TWIST_Z 0.000000000000000000 9 -1 1 SIM_UART_LOSS 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_X 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Y 0.000000000000000000 9 -1 1 SIM_VIB_FREQ_Z 0.000000000000000000 9 -1 1 SIM_VIB_MOT_HMNC 1 4 -1 1 SIM_VIB_MOT_MASK 0 6 -1 1 SIM_VIB_MOT_MAX 0.000000000000000000 9 -1 1 SIM_VIB_MOT_MULT 1.000000000000000000 9 -1 1 SIM_VICON_FAIL 0 2 -1 1 SIM_VICON_GLIT_X 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Y 0.000000000000000000 9 -1 1 SIM_VICON_GLIT_Z 0.000000000000000000 9 -1 1 SIM_VICON_POS_X 0.000000000000000000 9 -1 1 SIM_VICON_POS_Y 0.000000000000000000 9 -1 1 SIM_VICON_POS_Z 0.000000000000000000 9 -1 1 SIM_VICON_TMASK 3 2 -1 1 SIM_VICON_VGLI_X 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Y 0.000000000000000000 9 -1 1 SIM_VICON_VGLI_Z 0.000000000000000000 9 -1 1 SIM_VICON_YAW 0 4 -1 1 SIM_VICON_YAWERR 0 4 -1 1 SIM_WAVE_AMP 0.500000000000000000 9 -1 1 SIM_WAVE_DIR 0.000000000000000000 9 -1 1 SIM_WAVE_ENABLE 0 2 -1 1 SIM_WAVE_LENGTH 10.000000000000000000 9 -1 1 SIM_WAVE_SPEED 0.500000000000000000 9 -1 1 SIM_WIND_DIR 180.000000000000000000 9 -1 1 SIM_WIND_DIR_Z 0.000000000000000000 9 -1 1 SIM_WIND_SPD 0.000000000000000000 9 -1 1 SIM_WIND_T 0 2 -1 1 SIM_WIND_TC 5.000000000000000000 9 -1 1 SIM_WIND_TURB 0.000000000000000000 9 -1 1 SIM_WIND_T_ALT 60.000000000000000000 9 -1 1 SIM_WIND_T_COEF 0.009999999776482582 9 -1 1 SIM_WOW_PIN -1 2 -1 1 SR0_EXTRA1 4 4 -1 1 SR0_EXTRA2 4 4 -1 1 SR0_EXTRA3 4 4 -1 1 SR0_EXT_STAT 4 4 +1 1 SR0_EXTRA1 0 4 +1 1 SR0_EXTRA2 0 4 +1 1 SR0_EXTRA3 0 4 +1 1 SR0_EXT_STAT 0 4 1 1 SR0_PARAMS 0 4 -1 1 SR0_POSITION 4 4 -1 1 SR0_RAW_SENS 4 4 -1 1 SR0_RC_CHAN 4 4 -1 1 SR1_EXTRA1 10 4 -1 1 SR1_EXTRA2 10 4 -1 1 SR1_EXTRA3 3 4 -1 1 SR1_EXT_STAT 2 4 +1 1 SR0_POSITION 0 4 +1 1 SR0_RAW_SENS 2 4 +1 1 SR0_RC_CHAN 0 4 +1 1 SR1_EXTRA1 0 4 +1 1 SR1_EXTRA2 0 4 +1 1 SR1_EXTRA3 0 4 +1 1 SR1_EXT_STAT 0 4 1 1 SR1_PARAMS 0 4 -1 1 SR1_POSITION 3 4 -1 1 SR1_RAW_SENS 2 4 -1 1 SR1_RC_CHAN 2 4 -1 1 SR2_EXTRA1 10 4 -1 1 SR2_EXTRA2 10 4 -1 1 SR2_EXTRA3 3 4 -1 1 SR2_EXT_STAT 2 4 +1 1 SR1_POSITION 0 4 +1 1 SR1_RAW_SENS 0 4 +1 1 SR1_RC_CHAN 0 4 +1 1 SR2_EXTRA1 0 4 +1 1 SR2_EXTRA2 0 4 +1 1 SR2_EXTRA3 0 4 +1 1 SR2_EXT_STAT 0 4 1 1 SR2_PARAMS 0 4 -1 1 SR2_POSITION 3 4 -1 1 SR2_RAW_SENS 2 4 -1 1 SR2_RC_CHAN 2 4 -1 1 SR3_EXTRA1 10 4 -1 1 SR3_EXTRA2 10 4 -1 1 SR3_EXTRA3 3 4 -1 1 SR3_EXT_STAT 2 4 +1 1 SR2_POSITION 0 4 +1 1 SR2_RAW_SENS 0 4 +1 1 SR2_RC_CHAN 0 4 +1 1 SR3_EXTRA1 0 4 +1 1 SR3_EXTRA2 0 4 +1 1 SR3_EXTRA3 0 4 +1 1 SR3_EXT_STAT 0 4 1 1 SR3_PARAMS 0 4 -1 1 SR3_POSITION 3 4 -1 1 SR3_RAW_SENS 2 4 -1 1 SR3_RC_CHAN 2 4 -1 1 SR4_EXTRA1 10 4 -1 1 SR4_EXTRA2 10 4 -1 1 SR4_EXTRA3 3 4 -1 1 SR4_EXT_STAT 2 4 -1 1 SR4_PARAMS 0 4 -1 1 SR4_POSITION 3 4 -1 1 SR4_RAW_SENS 2 4 -1 1 SR4_RC_CHAN 2 4 -1 1 SR5_EXTRA1 10 4 -1 1 SR5_EXTRA2 10 4 -1 1 SR5_EXTRA3 3 4 -1 1 SR5_EXT_STAT 2 4 -1 1 SR5_PARAMS 0 4 -1 1 SR5_POSITION 3 4 -1 1 SR5_RAW_SENS 2 4 -1 1 SR5_RC_CHAN 2 4 -1 1 SR6_EXTRA1 10 4 -1 1 SR6_EXTRA2 10 4 -1 1 SR6_EXTRA3 3 4 -1 1 SR6_EXT_STAT 2 4 -1 1 SR6_PARAMS 0 4 -1 1 SR6_POSITION 3 4 -1 1 SR6_RAW_SENS 2 4 -1 1 SR6_RC_CHAN 2 4 -1 1 STAT_BOOTCNT 1 4 -1 1 STAT_FLTTIME 0 6 -1 1 STAT_RESET 1 6 -1 1 STAT_RUNTIME 60 6 +1 1 SR3_POSITION 0 4 +1 1 SR3_RAW_SENS 0 4 +1 1 SR3_RC_CHAN 0 4 1 1 SURFACE_DEPTH -10.000000000000000000 9 -1 1 SURFTRAK_DEPTH -50.000000000000000000 9 1 1 SYSID_MYGCS 255 4 +1 1 SYSID_SW_MREV 1 4 1 1 SYSID_THISMAV 1 4 -1 1 TEMP1_ADDR 119 2 -1 1 TEMP1_BUS 1 2 -1 1 TEMP1_SRC 0 2 -1 1 TEMP1_SRC_ID -1 6 -1 1 TEMP1_TYPE 1 2 -1 1 TEMP2_TYPE 0 2 -1 1 TEMP3_TYPE 0 2 -1 1 TEMP_LOG 0 2 -1 1 TERRAIN_ENABLE 0 2 1 1 THR_DZ 100 4 -1 1 VISO_TYPE 0 2 -1 1 VTX_ENABLE 0 2 -1 1 WPNAV_ACCEL 250.000000000000000000 9 -1 1 WPNAV_ACCEL_C 0.000000000000000000 9 +1 1 WPNAV_ACCEL 0.000000000000000000 9 1 1 WPNAV_ACCEL_Z 100.000000000000000000 9 -1 1 WPNAV_JERK 1.000000000000000000 9 1 1 WPNAV_RADIUS 200.000000000000000000 9 1 1 WPNAV_RFND_USE 1 2 -1 1 WPNAV_SPEED 100.000000000000000000 9 +1 1 WPNAV_SPEED 500.000000000000000000 9 1 1 WPNAV_SPEED_DN 150.000000000000000000 9 1 1 WPNAV_SPEED_UP 250.000000000000000000 9 -1 1 WPNAV_TER_MARGIN 10.000000000000000000 9 1 1 WP_YAW_BEHAVIOR 4 2 1 1 XTRACK_ANG_LIM 45 2 diff --git a/src/FirmwarePlugin/CMakeLists.txt b/src/FirmwarePlugin/CMakeLists.txt index dff0c2525c85..00b68277facb 100644 --- a/src/FirmwarePlugin/CMakeLists.txt +++ b/src/FirmwarePlugin/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Firmware Plugin Module +# Provides autopilot-specific firmware handling and communication +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE FirmwarePlugin.cc @@ -10,5 +15,8 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# Firmware-Specific Plugins +# ---------------------------------------------------------------------------- add_subdirectory(APM) add_subdirectory(PX4) diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index efb61c43f70f..73aef0074126 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -22,7 +22,7 @@ #include #include -QGC_LOGGING_CATEGORY(FirmwarePluginLog, "qgc.firmwareplugin.firmwareplugin") +QGC_LOGGING_CATEGORY(FirmwarePluginLog, "FirmwarePlugin.FirmwarePlugin") static const QString guided_mode_not_supported_by_vehicle = QObject::tr("Guided mode not supported by Vehicle."); @@ -203,7 +203,6 @@ const QVariantList &FirmwarePlugin::toolIndicators(const Vehicle*) //-- Default list of indicators for all vehicles. if (_toolIndicatorList.isEmpty()) { _toolIndicatorList = QVariantList({ - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Controls/FlightModeIndicator.qml")), QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/VehicleGPSIndicator.qml")), QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/TelemetryRSSIIndicator.qml")), QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/RCRSSIIndicator.qml")), @@ -211,6 +210,8 @@ const QVariantList &FirmwarePlugin::toolIndicators(const Vehicle*) QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/RemoteIDIndicator.qml")), QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/GimbalIndicator.qml")), QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/EscIndicator.qml")), + QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/JoystickIndicator.qml")), + QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/MultiVehicleSelector.qml")), #ifdef QT_DEBUG // ControlIndicator is only available in debug builds for the moment QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/GCSControlIndicator.qml")), @@ -221,19 +222,6 @@ const QVariantList &FirmwarePlugin::toolIndicators(const Vehicle*) return _toolIndicatorList; } -const QVariantList &FirmwarePlugin::modeIndicators(const Vehicle*) -{ - //-- Default list of indicators for all vehicles. - if (_modeIndicatorList.isEmpty()) { - _modeIndicatorList = QVariantList({ - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/MultiVehicleSelector.qml")), - QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/Toolbar/LinkIndicator.qml")), - }); - } - - return _modeIndicatorList; -} - bool FirmwarePlugin::_armVehicleAndValidate(Vehicle *vehicle) const { if (vehicle->armed()) { diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 25d224fd0b3c..a170d517423b 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -340,19 +340,13 @@ class FirmwarePlugin : public QObject /// Return the resource file which contains the vehicle icon used in the flight view when the view is light (Map for instance) virtual QString vehicleImageOutline(const Vehicle* /*vehicle*/) const { return QStringLiteral("/qmlimages/vehicleArrowOutline.svg"); } - // This is the content item for the expanded portion of the main status indicator - virtual QVariant mainStatusIndicatorContentItem(const Vehicle* /*vehicle*/) const { return QVariant(); } + virtual QVariant expandedToolbarIndicatorSource(const Vehicle* /*vehicle*/, const QString& /*indicatorName*/) const { return QVariant(); } /// Returns the list of toolbar tool indicators associated with a vehicle /// signals toolIndicatorsChanged /// @return A list of QUrl with the indicators virtual const QVariantList &toolIndicators(const Vehicle *vehicle); - /// Returns the list of toolbar mode indicators associated with a vehicle - /// signals modeIndicatorsChanged - /// @return A list of QUrl with the indicators - virtual const QVariantList &modeIndicators(const Vehicle *vehicle); - /// Creates vehicle camera manager. virtual QGCCameraManager *createCameraManager(Vehicle *vehicle) const; @@ -408,7 +402,6 @@ class FirmwarePlugin : public QObject signals: void toolIndicatorsChanged(); - void modeIndicatorsChanged(); protected: /// Arms the vehicle with validation and retries diff --git a/src/FirmwarePlugin/FirmwarePluginFactory.cc b/src/FirmwarePlugin/FirmwarePluginFactory.cc index 235a45d8e999..dea8f53916a2 100644 --- a/src/FirmwarePlugin/FirmwarePluginFactory.cc +++ b/src/FirmwarePlugin/FirmwarePluginFactory.cc @@ -13,7 +13,7 @@ #include -QGC_LOGGING_CATEGORY(FirmwarePluginFactoryLog, "qgc.firmwareplugin.firmwarepluginfactory"); +QGC_LOGGING_CATEGORY(FirmwarePluginFactoryLog, "FirmwarePlugin.FirmwarePluginFactory"); /*===========================================================================*/ diff --git a/src/FirmwarePlugin/FirmwarePluginManager.cc b/src/FirmwarePlugin/FirmwarePluginManager.cc index 419c0c9709bd..db7d6913c2c4 100644 --- a/src/FirmwarePlugin/FirmwarePluginManager.cc +++ b/src/FirmwarePlugin/FirmwarePluginManager.cc @@ -14,7 +14,7 @@ #include -QGC_LOGGING_CATEGORY(FirmwarePluginManagerLog, "qgc.firmwareplugin.firmwarepluginmanager"); +QGC_LOGGING_CATEGORY(FirmwarePluginManagerLog, "FirmwarePlugin.FirmwarePluginManager"); Q_GLOBAL_STATIC(FirmwarePluginManager, _firmwarePluginManagerInstance); diff --git a/src/FirmwarePlugin/PX4/CMakeLists.txt b/src/FirmwarePlugin/PX4/CMakeLists.txt index daa603dbbcd4..72563fabbeb8 100644 --- a/src/FirmwarePlugin/PX4/CMakeLists.txt +++ b/src/FirmwarePlugin/PX4/CMakeLists.txt @@ -47,7 +47,7 @@ qt_add_qml_module(PX4FirmwareModule QML_FILES PX4BatteryIndicator.qml PX4FlightModeIndicator.qml - PX4MainStatusIndicatorContentItem.qml + PX4MainStatusIndicator.qml NO_PLUGIN ) diff --git a/src/FirmwarePlugin/PX4/PX4BatteryIndicator.qml b/src/FirmwarePlugin/PX4/PX4BatteryIndicator.qml index b04f6bda5858..587f4fa6f421 100644 --- a/src/FirmwarePlugin/PX4/PX4BatteryIndicator.qml +++ b/src/FirmwarePlugin/PX4/PX4BatteryIndicator.qml @@ -12,48 +12,38 @@ import QtQuick.Layouts import QGroundControl import QGroundControl.Controls +import QGroundControl.FactControls +SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("Low Battery Failsafe") + FactPanelController { id: controller } + LabelledFactComboBox { + label: qsTr("Vehicle Action") + fact: controller.getParameterFact(-1, "COM_LOW_BAT_ACT") + indexModel: false + } -import QGroundControl.FactControls + FactSlider { + Layout.fillWidth: true + label: qsTr("Warning Level") + fact: controller.getParameterFact(-1, "BAT_LOW_THR") + majorTickStepSize: 5 + } + + FactSlider { + Layout.fillWidth: true + label: qsTr("Critical Level") + fact: controller.getParameterFact(-1, "BAT_CRIT_THR") + majorTickStepSize: 5 + } -BatteryIndicator { - waitForParameters: true - - expandedPageComponent: Component { - SettingsGroupLayout { - Layout.fillWidth: true - heading: qsTr("Low Battery Failsafe") - - FactPanelController { id: controller } - - LabelledFactComboBox { - label: qsTr("Vehicle Action") - fact: controller.getParameterFact(-1, "COM_LOW_BAT_ACT") - indexModel: false - } - - FactSlider { - Layout.fillWidth: true - label: qsTr("Warning Level") - fact: controller.getParameterFact(-1, "BAT_LOW_THR") - majorTickStepSize: 5 - } - - FactSlider { - Layout.fillWidth: true - label: qsTr("Critical Level") - fact: controller.getParameterFact(-1, "BAT_CRIT_THR") - majorTickStepSize: 5 - } - - FactSlider { - Layout.fillWidth: true - label: qsTr("Emergency Level") - fact: controller.getParameterFact(-1, "BAT_EMERGEN_THR") - majorTickStepSize: 5 - } - } + FactSlider { + Layout.fillWidth: true + label: qsTr("Emergency Level") + fact: controller.getParameterFact(-1, "BAT_EMERGEN_THR") + majorTickStepSize: 5 } } diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index e14850b1b3f4..5c9736f4d0c5 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -17,6 +17,7 @@ #include "Vehicle.h" #include +#include #include "px4_custom_mode.h" @@ -28,65 +29,67 @@ PX4FirmwarePluginInstanceData::PX4FirmwarePluginInstanceData(QObject* parent) } PX4FirmwarePlugin::PX4FirmwarePlugin() - : _manualFlightMode (tr("Manual")) - , _acroFlightMode (tr("Acro")) - , _stabilizedFlightMode (tr("Stabilized")) - , _rattitudeFlightMode (tr("Rattitude")) - , _altCtlFlightMode (tr("Altitude")) - , _posCtlFlightMode (tr("Position")) - , _offboardFlightMode (tr("Offboard")) - , _readyFlightMode (tr("Ready")) - , _takeoffFlightMode (tr("Takeoff")) - , _holdFlightMode (tr("Hold")) - , _missionFlightMode (tr("Mission")) - , _rtlFlightMode (tr("Return")) - , _landingFlightMode (tr("Land")) - , _preclandFlightMode (tr("Precision Land")) - , _rtgsFlightMode (tr("Return to Groundstation")) - , _followMeFlightMode (tr("Follow Me")) - , _simpleFlightMode (tr("Simple")) - , _orbitFlightMode (tr("Orbit")) { + const QString manualFlightModeName = tr("Manual"); + const QString acroFlightModeName = tr("Acro"); + const QString stabilizedFlightModeName = tr("Stabilized"); + const QString rattitudeFlightModeName = tr("Rattitude"); + const QString altCtlFlightModeName = tr("Altitude"); + const QString posCtlFlightModeName = tr("Position"); + const QString offboardFlightModeName = tr("Offboard"); + const QString readyFlightModeName = tr("Ready"); + const QString takeoffFlightModeName = tr("Takeoff"); + const QString holdFlightModeName = tr("Hold"); + const QString missionFlightModeName = tr("Mission"); + const QString rtlFlightModeName = tr("Return"); + const QString landingFlightModeName = tr("Land"); + const QString preclandFlightModeName = tr("Precision Land"); + const QString rtgsFlightModeName = tr("Return to Groundstation"); + const QString followMeFlightModeName = tr("Follow Me"); + const QString simpleFlightModeName = tr("Simple"); + const QString orbitFlightModeName = tr("Orbit"); + _setModeEnumToModeStringMapping({ - { PX4CustomMode::MANUAL , _manualFlightMode }, - { PX4CustomMode::STABILIZED , _stabilizedFlightMode }, - { PX4CustomMode::ACRO , _acroFlightMode }, - { PX4CustomMode::RATTITUDE , _rattitudeFlightMode }, - { PX4CustomMode::ALTCTL , _altCtlFlightMode }, - { PX4CustomMode::OFFBOARD , _offboardFlightMode }, - { PX4CustomMode::SIMPLE , _simpleFlightMode }, - { PX4CustomMode::POSCTL_POSCTL , _posCtlFlightMode }, - { PX4CustomMode::POSCTL_ORBIT , _orbitFlightMode }, - { PX4CustomMode::AUTO_LOITER , _holdFlightMode }, - { PX4CustomMode::AUTO_MISSION , _missionFlightMode }, - { PX4CustomMode::AUTO_RTL , _rtlFlightMode }, - { PX4CustomMode::AUTO_LAND , _landingFlightMode }, - { PX4CustomMode::AUTO_PRECLAND , _preclandFlightMode }, - { PX4CustomMode::AUTO_READY , _readyFlightMode }, - { PX4CustomMode::AUTO_RTGS , _rtgsFlightMode }, - { PX4CustomMode::AUTO_TAKEOFF , _takeoffFlightMode }, + { PX4CustomMode::MANUAL, manualFlightModeName }, + { PX4CustomMode::STABILIZED, stabilizedFlightModeName }, + { PX4CustomMode::ACRO, acroFlightModeName }, + { PX4CustomMode::RATTITUDE, rattitudeFlightModeName }, + { PX4CustomMode::ALTCTL, altCtlFlightModeName }, + { PX4CustomMode::OFFBOARD, offboardFlightModeName }, + { PX4CustomMode::SIMPLE, simpleFlightModeName }, + { PX4CustomMode::POSCTL_POSCTL, posCtlFlightModeName }, + { PX4CustomMode::POSCTL_ORBIT, orbitFlightModeName }, + { PX4CustomMode::AUTO_LOITER, holdFlightModeName }, + { PX4CustomMode::AUTO_MISSION, missionFlightModeName }, + { PX4CustomMode::AUTO_RTL, rtlFlightModeName }, + { PX4CustomMode::AUTO_LAND, landingFlightModeName }, + { PX4CustomMode::AUTO_PRECLAND, preclandFlightModeName }, + { PX4CustomMode::AUTO_READY, readyFlightModeName }, + { PX4CustomMode::AUTO_RTGS, rtgsFlightModeName }, + { PX4CustomMode::AUTO_TAKEOFF, takeoffFlightModeName }, }); static FlightModeList availableFlightModes = { - // Mode Name , Custom Mode CanBeSet adv - { _manualFlightMode , PX4CustomMode::MANUAL , true , true }, - { _stabilizedFlightMode , PX4CustomMode::STABILIZED , true , true }, - { _acroFlightMode , PX4CustomMode::ACRO , true , true }, - { _rattitudeFlightMode , PX4CustomMode::RATTITUDE , true , false}, - { _altCtlFlightMode , PX4CustomMode::ALTCTL , true , false}, - { _offboardFlightMode , PX4CustomMode::OFFBOARD , true , true }, - { _simpleFlightMode , PX4CustomMode::SIMPLE , false, false}, - { _posCtlFlightMode , PX4CustomMode::POSCTL_POSCTL , true , false}, - { _orbitFlightMode , PX4CustomMode::POSCTL_ORBIT , false, true }, - { _holdFlightMode , PX4CustomMode::AUTO_LOITER , true , true }, - { _missionFlightMode , PX4CustomMode::AUTO_MISSION , true , true }, - { _rtlFlightMode , PX4CustomMode::AUTO_RTL , true , true }, - { _landingFlightMode , PX4CustomMode::AUTO_LAND , false, true }, - { _preclandFlightMode , PX4CustomMode::AUTO_PRECLAND , true , true }, - { _readyFlightMode , PX4CustomMode::AUTO_READY , false, false}, - { _rtgsFlightMode , PX4CustomMode::AUTO_RTGS , false, false}, - { _takeoffFlightMode , PX4CustomMode::AUTO_TAKEOFF , false, false}, + // Mode Name Custom Mode CanBeSet adv + { manualFlightModeName, PX4CustomMode::MANUAL, true, true }, + { stabilizedFlightModeName, PX4CustomMode::STABILIZED, true, true }, + { acroFlightModeName, PX4CustomMode::ACRO, true, true }, + { rattitudeFlightModeName, PX4CustomMode::RATTITUDE, true, false}, + { altCtlFlightModeName, PX4CustomMode::ALTCTL, true, false}, + { offboardFlightModeName, PX4CustomMode::OFFBOARD, true, true }, + { simpleFlightModeName, PX4CustomMode::SIMPLE, false, false}, + { posCtlFlightModeName, PX4CustomMode::POSCTL_POSCTL, true, false}, + { orbitFlightModeName, PX4CustomMode::POSCTL_ORBIT, false, true }, + { holdFlightModeName, PX4CustomMode::AUTO_LOITER, true, true }, + { missionFlightModeName, PX4CustomMode::AUTO_MISSION, true, true }, + { rtlFlightModeName, PX4CustomMode::AUTO_RTL, true, true }, + { landingFlightModeName, PX4CustomMode::AUTO_LAND, false, true }, + { preclandFlightModeName, PX4CustomMode::AUTO_PRECLAND, true, true }, + { readyFlightModeName, PX4CustomMode::AUTO_READY, false, false}, + { rtgsFlightModeName, PX4CustomMode::AUTO_RTGS, false, false}, + { takeoffFlightModeName, PX4CustomMode::AUTO_TAKEOFF, false, false}, }; + updateAvailableFlightModes(availableFlightModes); } @@ -297,12 +300,12 @@ void PX4FirmwarePlugin::pauseVehicle(Vehicle* vehicle) const void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle, bool smartRTL) const { Q_UNUSED(smartRTL); - _setFlightModeAndValidate(vehicle, _rtlFlightMode); + _setFlightModeAndValidate(vehicle, rtlFlightMode()); } void PX4FirmwarePlugin::guidedModeLand(Vehicle* vehicle) const { - _setFlightModeAndValidate(vehicle, _landingFlightMode); + _setFlightModeAndValidate(vehicle, landFlightMode()); } void PX4FirmwarePlugin::_mavCommandResult(int vehicleId, int component, int command, int result, int failureCode) @@ -596,54 +599,53 @@ void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) const QString PX4FirmwarePlugin::pauseFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_LOITER, _holdFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_LOITER); } QString PX4FirmwarePlugin::missionFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_MISSION, _missionFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_MISSION); } QString PX4FirmwarePlugin::rtlFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_RTL, _rtlFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_RTL); } QString PX4FirmwarePlugin::landFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_LAND, _landingFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_LAND); } QString PX4FirmwarePlugin::takeControlFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::MANUAL, _manualFlightMode); + return _modeEnumToString.value(PX4CustomMode::MANUAL); } QString PX4FirmwarePlugin::gotoFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_LOITER, _holdFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_LOITER); } QString PX4FirmwarePlugin::followFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_FOLLOW_TARGET, _followMeFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_FOLLOW_TARGET); } QString PX4FirmwarePlugin::takeOffFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::AUTO_TAKEOFF, _takeoffFlightMode); + return _modeEnumToString.value(PX4CustomMode::AUTO_TAKEOFF); } QString PX4FirmwarePlugin::stabilizedFlightMode() const { - return _modeEnumToString.value(PX4CustomMode::STABILIZED, _stabilizedFlightMode); + return _modeEnumToString.value(PX4CustomMode::STABILIZED); } bool PX4FirmwarePlugin::isGuidedMode(const Vehicle* vehicle) const { // Not supported by generic vehicle - return (vehicle->flightMode() == _holdFlightMode || vehicle->flightMode() == _takeoffFlightMode - || vehicle->flightMode() == _landingFlightMode); + return (vehicle->flightMode() == pauseFlightMode() || vehicle->flightMode() == takeOffFlightMode() || vehicle->flightMode() == landFlightMode()); } bool PX4FirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message) @@ -759,37 +761,6 @@ bool PX4FirmwarePlugin::hasGripper(const Vehicle* vehicle) const return false; } -QVariant PX4FirmwarePlugin::mainStatusIndicatorContentItem(const Vehicle*) const -{ - return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4MainStatusIndicatorContentItem.qml")); -} - -const QVariantList& PX4FirmwarePlugin::toolIndicators(const Vehicle* vehicle) -{ - if (_toolIndicatorList.size() == 0) { - // First call the base class to get the standard QGC list - _toolIndicatorList = FirmwarePlugin::toolIndicators(vehicle); - - // Find the generic flight mode indicator and replace with the custom one - for (int i=0; i<_toolIndicatorList.size(); i++) { - if (_toolIndicatorList.at(i).toUrl().toString().contains("FlightModeIndicator.qml")) { - _toolIndicatorList[i] = QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml")); - break; - } - } - - // Find the generic battery indicator and replace with the custom one - for (int i=0; i<_toolIndicatorList.size(); i++) { - if (_toolIndicatorList.at(i).toUrl().toString().contains("BatteryIndicator.qml")) { - _toolIndicatorList[i] = QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4BatteryIndicator.qml")); - break; - } - } - } - - return _toolIndicatorList; -} - void PX4FirmwarePlugin::updateAvailableFlightModes(FlightModeList &modeList) { for(auto &mode: modeList){ @@ -849,3 +820,16 @@ void PX4FirmwarePlugin::updateAvailableFlightModes(FlightModeList &modeList) } _updateFlightModeList(modeList); } + +QVariant PX4FirmwarePlugin::expandedToolbarIndicatorSource(const Vehicle* vehicle, const QString& indicatorName) const +{ + if (indicatorName == "Battery") { + return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4BatteryIndicator.qml")); + } else if (indicatorName == "FlightMode") { + return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml")); + } else if (indicatorName == "MainStatus") { + return QVariant::fromValue(QUrl::fromUserInput("qrc:/qml/QGroundControl/FirmwarePlugin/PX4/PX4MainStatusIndicator.qml")); + } + + return QVariant(); +} diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index 05dd01a768ba..9ed4aca996a1 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -75,33 +75,10 @@ class PX4FirmwarePlugin : public FirmwarePlugin bool supportsNegativeThrust (Vehicle* vehicle) const override; QString getHobbsMeter (Vehicle* vehicle) const override; bool hasGripper (const Vehicle* vehicle) const override; - QVariant mainStatusIndicatorContentItem (const Vehicle* vehicle) const override; - const QVariantList& toolIndicators (const Vehicle* vehicle) override; + QVariant expandedToolbarIndicatorSource (const Vehicle* vehicle, const QString& indicatorName) const override; void updateAvailableFlightModes (FlightModeList &modeList) override; -protected: - - // If plugin superclass wants to change a mode name, then set a new name for the flight mode in the superclass constructor - QString _manualFlightMode; - QString _acroFlightMode; - QString _stabilizedFlightMode; - QString _rattitudeFlightMode; - QString _altCtlFlightMode; - QString _posCtlFlightMode; - QString _offboardFlightMode; - QString _readyFlightMode; - QString _takeoffFlightMode; - QString _holdFlightMode; - QString _missionFlightMode; - QString _rtlFlightMode; - QString _landingFlightMode; - QString _preclandFlightMode; - QString _rtgsFlightMode; - QString _followMeFlightMode; - QString _simpleFlightMode; - QString _orbitFlightMode; - private slots: void _mavCommandResult(int vehicleId, int component, int command, int result, int failureCode); diff --git a/src/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml b/src/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml index 45bff5e4f2a3..7a067fe102fb 100644 --- a/src/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml +++ b/src/FirmwarePlugin/PX4/PX4FlightModeIndicator.qml @@ -13,110 +13,100 @@ import QtQuick.Layouts import QGroundControl import QGroundControl.Controls +import QGroundControl.FactControls +ColumnLayout { + Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 60 + spacing: margins / 2 + + property Fact mpcLandSpeedFact: controller.getParameterFact(-1, "MPC_LAND_SPEED", false) + property Fact precisionLandingFact: controller.getParameterFact(-1, "RTL_PLD_MD", false) + property Fact sys_vehicle_resp: controller.getParameterFact(-1, "SYS_VEHICLE_RESP", false) + property Fact mpc_xy_vel_all: controller.getParameterFact(-1, "MPC_XY_VEL_ALL", false) + property Fact mpc_z_vel_all: controller.getParameterFact(-1, "MPC_Z_VEL_ALL", false) + property var qgcPal: QGroundControl.globalPalette + property real margins: ScreenTools.defaultFontPixelHeight + property real sliderWidth: ScreenTools.defaultFontPixelWidth * 40 + property var flyViewSettings: QGroundControl.settingsManager.flyViewSettings + + FactPanelController { id: controller } + + SettingsGroupLayout { + Layout.fillWidth: true + + FactSlider { + Layout.fillWidth: true + Layout.preferredWidth: sliderWidth + label: qsTr("RTL Altitude") + fact: controller.getParameterFact(-1, "RTL_RETURN_ALT") + to: fact.maxIsDefaultForType ? QGroundControl.unitsConversion.metersToAppSettingsVerticalDistanceUnits(121.92) : fact.max + majorTickStepSize: 10 + } + } + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("GeoFence") + LabelledFactComboBox { + Layout.fillWidth: true + label: qsTr("Breach Action") + fact: controller.getParameterFact(-1, "GF_ACTION") + } -import QGroundControl.FactControls - -FlightModeIndicator { - waitForParameters: true - - expandedPageComponent: Component { ColumnLayout { - Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 60 - spacing: margins / 2 - - property Fact mpcLandSpeedFact: controller.getParameterFact(-1, "MPC_LAND_SPEED", false) - property Fact precisionLandingFact: controller.getParameterFact(-1, "RTL_PLD_MD", false) - property Fact sys_vehicle_resp: controller.getParameterFact(-1, "SYS_VEHICLE_RESP", false) - property Fact mpc_xy_vel_all: controller.getParameterFact(-1, "MPC_XY_VEL_ALL", false) - property Fact mpc_z_vel_all: controller.getParameterFact(-1, "MPC_Z_VEL_ALL", false) - property var qgcPal: QGroundControl.globalPalette - property real margins: ScreenTools.defaultFontPixelHeight - property real sliderWidth: ScreenTools.defaultFontPixelWidth * 40 - property var flyViewSettings: QGroundControl.settingsManager.flyViewSettings - - FactPanelController { id: controller } - - SettingsGroupLayout { - Layout.fillWidth: true - - FactSlider { - Layout.fillWidth: true - Layout.preferredWidth: sliderWidth - label: qsTr("RTL Altitude") - fact: controller.getParameterFact(-1, "RTL_RETURN_ALT") - to: fact.maxIsDefaultForType ? QGroundControl.unitsConversion.metersToAppSettingsVerticalDistanceUnits(121.92) : fact.max - majorTickStepSize: 10 + QGCCheckBoxSlider { + Layout.fillWidth: true + text: qsTr("Max Distance") + checked: maxDistanceSlider.value > 0 + + onClicked: { + if (checked) { + maxDistanceSlider.setValue(prevValue != 0 ? prevValue : maxDistanceSlider.to) + } else { + prevValue = maxDistanceSlider.value + maxDistanceSlider.setValue(0) + } } + + property real prevValue: 0 } - SettingsGroupLayout { + FactSlider { + id: maxDistanceSlider Layout.fillWidth: true - heading: qsTr("GeoFence") - - LabelledFactComboBox { - Layout.fillWidth: true - label: qsTr("Breach Action") - fact: controller.getParameterFact(-1, "GF_ACTION") - } - - ColumnLayout { - QGCCheckBoxSlider { - Layout.fillWidth: true - text: qsTr("Max Distance") - checked: maxDistanceSlider.value > 0 - - onClicked: { - if (checked) { - maxDistanceSlider.setValue(prevValue != 0 ? prevValue : maxDistanceSlider.to) - } else { - prevValue = maxDistanceSlider.value - maxDistanceSlider.setValue(0) - } - } - - property real prevValue: 0 - } + fact: controller.getParameterFact(-1, "GF_MAX_HOR_DIST") + to: flyViewSettings.maxGoToLocationDistance.value + majorTickStepSize: 500 + enabled: fact.value > 0 + } + } - FactSlider { - id: maxDistanceSlider - Layout.fillWidth: true - fact: controller.getParameterFact(-1, "GF_MAX_HOR_DIST") - to: flyViewSettings.maxGoToLocationDistance.value - majorTickStepSize: 500 - enabled: fact.value > 0 + ColumnLayout { + QGCCheckBoxSlider { + Layout.fillWidth: true + text: qsTr("Max Altitude") + checked: maxAltitudeSlider.value > 0 + + onClicked: { + if (checked) { + maxAltitudeSlider.setValue(prevValue != 0 ? prevValue : maxAltitudeSlider.to) + } else { + prevValue = maxAltitudeSlider.value + maxAltitudeSlider.setValue(0) } } - ColumnLayout { - QGCCheckBoxSlider { - Layout.fillWidth: true - text: qsTr("Max Altitude") - checked: maxAltitudeSlider.value > 0 - - onClicked: { - if (checked) { - maxAltitudeSlider.setValue(prevValue != 0 ? prevValue : maxAltitudeSlider.to) - } else { - prevValue = maxAltitudeSlider.value - maxAltitudeSlider.setValue(0) - } - } - - property real prevValue: 0 - } + property real prevValue: 0 + } - FactSlider { - id: maxAltitudeSlider - Layout.fillWidth: true - fact: controller.getParameterFact(-1, "GF_MAX_VER_DIST") - to: flyViewSettings.guidedMaximumAltitude.value - majorTickStepSize: 10 - enabled: fact.value > 0 - } - } + FactSlider { + id: maxAltitudeSlider + Layout.fillWidth: true + fact: controller.getParameterFact(-1, "GF_MAX_VER_DIST") + to: flyViewSettings.guidedMaximumAltitude.value + majorTickStepSize: 10 + enabled: fact.value > 0 } } } diff --git a/src/FirmwarePlugin/PX4/PX4MainStatusIndicatorContentItem.qml b/src/FirmwarePlugin/PX4/PX4MainStatusIndicator.qml similarity index 99% rename from src/FirmwarePlugin/PX4/PX4MainStatusIndicatorContentItem.qml rename to src/FirmwarePlugin/PX4/PX4MainStatusIndicator.qml index 2f38944d8f14..5476cb970d85 100644 --- a/src/FirmwarePlugin/PX4/PX4MainStatusIndicatorContentItem.qml +++ b/src/FirmwarePlugin/PX4/PX4MainStatusIndicator.qml @@ -13,10 +13,6 @@ import QtQuick.Layouts import QGroundControl import QGroundControl.Controls - - - - import QGroundControl.FactControls ColumnLayout { diff --git a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc index e560a6cf2c05..8a4953869cbb 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc +++ b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc @@ -19,7 +19,7 @@ #include #include -QGC_LOGGING_CATEGORY(PX4ParameterMetaDataLog, "PX4ParameterMetaDataLog") +QGC_LOGGING_CATEGORY(PX4ParameterMetaDataLog, "FirmwarePlugin.PX4ParameterMetaData") PX4ParameterMetaData::PX4ParameterMetaData(QObject* parent) : QObject(parent) @@ -70,9 +70,9 @@ QVariant PX4ParameterMetaData::_stringToTypedVariant(const QString& string, Fact convertTo = QMetaType::QByteArray; break; } - + *convertOk = var.convert(QMetaType(convertTo)); - + return var; } @@ -94,43 +94,43 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData qWarning() << "Internal error: metaDataFile mission" << metaDataFile; return; } - + if (!xmlFile.open(QIODevice::ReadOnly)) { qWarning() << "Internal error: Unable to open parameter file:" << metaDataFile << xmlFile.errorString(); return; } - + QXmlStreamReader xml(xmlFile.readAll()); xmlFile.close(); if (xml.hasError()) { qWarning() << "Badly formed XML" << xml.errorString(); return; } - + QString factGroup; QString errorString; FactMetaData* metaData = nullptr; int xmlState = XmlStateNone; bool badMetaData = true; - + while (!xml.atEnd()) { if (xml.isStartElement()) { QString elementName = xml.name().toString(); - + if (elementName == "parameters") { if (xmlState != XmlStateNone) { qWarning() << "Badly formed XML"; return; } xmlState = XmlStateFoundParameters; - + } else if (elementName == "version") { if (xmlState != XmlStateFoundParameters) { qWarning() << "Badly formed XML"; return; } xmlState = XmlStateFoundVersion; - + bool convertOk; QString strVersion = xml.readElementText(); int intVersion = strVersion.toInt(&convertOk); @@ -143,7 +143,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData qDebug() << "Parameter version stamp too old, skipping load. Found:" << intVersion << "Want: 3 File:" << metaDataFile; return; } - + } else if (elementName == "parameter_version_major") { // Just skip over for now } else if (elementName == "parameter_version_minor") { @@ -156,30 +156,30 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData return; } xmlState = XmlStateFoundGroup; - + if (!xml.attributes().hasAttribute("name")) { qWarning() << "Badly formed XML"; return; } factGroup = xml.attributes().value("name").toString(); qCDebug(PX4ParameterMetaDataLog) << "Found group: " << factGroup; - + } else if (elementName == "parameter") { if (xmlState != XmlStateFoundGroup) { qWarning() << "Badly formed XML"; return; } xmlState = XmlStateFoundParameter; - + if (!xml.attributes().hasAttribute("name") || !xml.attributes().hasAttribute("type")) { qWarning() << "Badly formed XML"; return; } - + QString name = xml.attributes().value("name").toString(); QString type = xml.attributes().value("type").toString(); QString strDefault = xml.attributes().value("default").toString(); - + QString category = xml.attributes().value("category").toString(); if (category.isEmpty()) { category = QStringLiteral("Standard"); @@ -208,7 +208,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData qWarning() << "Parameter meta data with bad type:" << type << " name:" << name; return; } - + // Now that we know type we can create meta data object and add it to the system metaData = new FactMetaData(foundType, this); if (_mapParameterName2FactMetaData.contains(name)) { @@ -224,10 +224,10 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData metaData->setGroup(factGroup); metaData->setReadOnly(readOnly); metaData->setVolatileValue(volatileValue); - + if (xml.attributes().hasAttribute("default") && !strDefault.isEmpty()) { QVariant varDefault; - + if (metaData->convertAndValidateRaw(strDefault, false, varDefault, errorString)) { metaData->setRawDefaultValue(varDefault); } else { @@ -235,7 +235,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData } } } - + } else { // We should be getting meta data now if (xmlState != XmlStateFoundParameter) { @@ -276,7 +276,10 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData if (metaData->convertAndValidateRaw(text, false /* convertOnly */, varMax, errorString)) { metaData->setRawMax(varMax); } else { - qCWarning(PX4ParameterMetaDataLog) << "Invalid max value, name:" << metaData->name() << " type:" << metaData->type() << " max:" << text << " error:" << errorString; + // PX4 firmware has a metadata generation bug for VTQ_TELEM_IDS_* parameters + if (!metaData->name().startsWith("VTQ_TELEM_IDS_")) { + qCWarning(PX4ParameterMetaDataLog) << "Invalid max value, name:" << metaData->name() << " type:" << metaData->type() << " max:" << text << " error:" << errorString; + } } } else if (elementName == "unit") { @@ -350,7 +353,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData qCDebug(PX4ParameterMetaDataLog) << "parameter value:" << "index:" << bit << "description:" << bitDescription; - if (bit < 31) { + if (bit < 32) { QVariant bitmaskRawValue = 1 << bit; QVariant bitmaskValue; QString errorString; @@ -362,7 +365,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData << " error:" << errorString; } } else { - qCWarning(PX4ParameterMetaDataLog) << "Invalid value for bitmask, bit:" << bit; + qCWarning(PX4ParameterMetaDataLog) << "Invalid value for bitmask bit, name:" << metaData->name() << " bit:" << bit; } } } else { diff --git a/src/FlightDisplay/CMakeLists.txt b/src/FlightDisplay/CMakeLists.txt index e273aac9369c..e21520bccc07 100644 --- a/src/FlightDisplay/CMakeLists.txt +++ b/src/FlightDisplay/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Flight Display Module +# In-flight view, telemetry displays, and vehicle control UI +# ============================================================================ + qt_add_library(FlightDisplayModule STATIC) qt_add_qml_module(FlightDisplayModule @@ -22,6 +27,8 @@ qt_add_qml_module(FlightDisplayModule FlyViewCustomLayer.qml FlyViewInsetViewer.qml FlyViewInstrumentPanel.qml + FlyViewGripperButton.qml + FlyViewGripperDropPanel.qml FlyViewMap.qml FlyViewMissionCompleteDialog.qml FlyViewPreFlightChecklistPopup.qml @@ -31,9 +38,7 @@ qt_add_qml_module(FlightDisplayModule FlyViewTopRightPanel.qml FlyViewVideo.qml FlyViewWidgetLayer.qml - GripperMenu.qml GuidedActionConfirm.qml - GuidedActionGripper.qml GuidedActionLand.qml GuidedActionPause.qml GuidedActionRTL.qml diff --git a/src/FlightDisplay/CustomGuidedActionsController.qml b/src/FlightDisplay/CustomGuidedActionsController.qml index 8cd37bf376ad..8958ace816f2 100644 --- a/src/FlightDisplay/CustomGuidedActionsController.qml +++ b/src/FlightDisplay/CustomGuidedActionsController.qml @@ -19,4 +19,4 @@ QtObject { function customExecuteAction(actionCode, actionData, sliderOutputValue, optionCheckedode) { return false // false = action not handled here } -} \ No newline at end of file +} diff --git a/src/FlightDisplay/DefaultChecklist.qml b/src/FlightDisplay/DefaultChecklist.qml index 06494676ff64..ddfc05838b85 100644 --- a/src/FlightDisplay/DefaultChecklist.qml +++ b/src/FlightDisplay/DefaultChecklist.qml @@ -89,4 +89,3 @@ Item { } } } - diff --git a/src/FlightDisplay/FixedWingChecklist.qml b/src/FlightDisplay/FixedWingChecklist.qml index 093f8d034707..69dc8e247299 100644 --- a/src/FlightDisplay/FixedWingChecklist.qml +++ b/src/FlightDisplay/FixedWingChecklist.qml @@ -89,4 +89,3 @@ Item { } } } - diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index 3afe8f0ff9c3..3723c527d842 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -85,7 +85,7 @@ Item { visible: QGroundControl.videoManager.decoding function getWidth() { if(_ar != 0.0){ - if(_isMode_FIT_HEIGHT + if(_isMode_FIT_HEIGHT || (_isMode_FILL && (root.width/root.height < _ar)) || (_isMode_NO_CROP && (root.width/root.height > _ar))){ // This return value has different implications depending on the mode @@ -100,8 +100,8 @@ Item { } function getHeight() { if(_ar != 0.0){ - if(_isMode_FIT_WIDTH - || (_isMode_FILL && (root.width/root.height > _ar)) + if(_isMode_FIT_WIDTH + || (_isMode_FILL && (root.width/root.height > _ar)) || (_isMode_NO_CROP && (root.width/root.height < _ar))){ // This return value has different implications depending on the mode // For FIT_WIDTH and FILL diff --git a/src/FlightDisplay/FlyView.qml b/src/FlightDisplay/FlyView.qml index 55ab7ce48159..748ff76db939 100644 --- a/src/FlightDisplay/FlyView.qml +++ b/src/FlightDisplay/FlyView.qml @@ -59,6 +59,7 @@ Item { property rect _centerViewport: Qt.rect(0, 0, width, height) property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 30 property var _mapControl: mapControl + property real _widgetMargin: ScreenTools.defaultFontPixelWidth * 0.75 property real _fullItemZorder: 0 property real _pipItemZorder: QGroundControl.zOrderWidgets @@ -74,21 +75,16 @@ Item { QGCToolInsets { id: _toolInsets + topEdgeLeftInset: toolbar.height + topEdgeCenterInset: topEdgeLeftInset + topEdgeRightInset: topEdgeLeftInset leftEdgeBottomInset: _pipView.leftEdgeBottomInset bottomEdgeLeftInset: _pipView.bottomEdgeLeftInset } - FlyViewToolBar { - id: toolbar - visible: !QGroundControl.videoManager.fullScreen - } - Item { id: mapHolder - anchors.top: toolbar.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right + anchors.fill: parent FlyViewMap { id: mapControl @@ -128,6 +124,8 @@ Item { anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: guidedValueSlider.visible ? guidedValueSlider.left : parent.right + anchors.margins: _widgetMargin + anchors.topMargin: toolbar.height + _widgetMargin z: _fullItemZorder + 2 // we need to add one extra layer for map 3d viewer (normally was 1) parentToolInsets: _toolInsets mapControl: _mapControl @@ -189,4 +187,9 @@ Item { _root.utmspSendActTrigger = value } } + + FlyViewToolBar { + id: toolbar + visible: !QGroundControl.videoManager.fullScreen + } } diff --git a/src/FlightDisplay/FlyViewAdditionalActionsButton.qml b/src/FlightDisplay/FlyViewAdditionalActionsButton.qml index 58b717fc328a..ea283c2fb74f 100644 --- a/src/FlightDisplay/FlyViewAdditionalActionsButton.qml +++ b/src/FlightDisplay/FlyViewAdditionalActionsButton.qml @@ -25,7 +25,7 @@ ToolStripAction { property var _guidedController: globals.guidedControllerFlyView - property var _additionalActions: FlyViewAdditionalActionsList { + property var _additionalActions: FlyViewAdditionalActionsList { guidedController: _guidedController } @@ -40,7 +40,7 @@ ToolStripAction { } dropPanelComponent: Component { - FlyViewAdditionalActionsPanel { + FlyViewAdditionalActionsPanel { additionalActions: _additionalActions mavlinkActions: _mavlinkActions.actions customActions: _customActions diff --git a/src/FlightDisplay/FlyViewAdditionalActionsList.qml b/src/FlightDisplay/FlyViewAdditionalActionsList.qml index 7c3c232d63e6..e833f5f4f730 100644 --- a/src/FlightDisplay/FlyViewAdditionalActionsList.qml +++ b/src/FlightDisplay/FlyViewAdditionalActionsList.qml @@ -12,9 +12,8 @@ import QtQml QtObject { property var guidedController - property bool anyActionAvailable: guidedController.showStartMission || guidedController.showContinueMission || guidedController.showChangeAlt || - guidedController.showChangeLoiterRadius || guidedController.showLandAbort || guidedController.showChangeSpeed || - guidedController.showGripper + property bool anyActionAvailable: guidedController.showStartMission || guidedController.showContinueMission || guidedController.showChangeAlt || + guidedController.showChangeLoiterRadius || guidedController.showLandAbort || guidedController.showChangeSpeed property var model: [ { title: guidedController.startMissionTitle, @@ -51,12 +50,6 @@ QtObject { text: guidedController.changeSpeedMessage, action: guidedController.actionChangeSpeed, visible: guidedController.showChangeSpeed - }, - { - title: guidedController.gripperTitle, - text: guidedController.gripperMessage, - action: guidedController.actionGripper, - visible: guidedController.showGripper } ] } diff --git a/src/FlightDisplay/FlyViewGripperButton.qml b/src/FlightDisplay/FlyViewGripperButton.qml new file mode 100644 index 000000000000..9a85776e55b4 --- /dev/null +++ b/src/FlightDisplay/FlyViewGripperButton.qml @@ -0,0 +1,26 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QGroundControl +import QGroundControl.FlightDisplay + +ToolStripAction { + id: action + text: qsTr("Gripper") + iconSource: "/res/Gripper.svg" + visible: _gripperAvailable + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _gripperAvailable: _activeVehicle ? _activeVehicle.hasGripper : false + + dropPanelComponent: Component { + FlyViewGripperDropPanel { + } + } +} diff --git a/src/FlightDisplay/FlyViewGripperDropPanel.qml b/src/FlightDisplay/FlyViewGripperDropPanel.qml new file mode 100644 index 000000000000..e15f28f5b81b --- /dev/null +++ b/src/FlightDisplay/FlyViewGripperDropPanel.qml @@ -0,0 +1,36 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.Controls + +ColumnLayout { + spacing: ScreenTools.defaultFontHeight / 2 + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property var _buttonTitles: [qsTr("Open"), qsTr("Close"), qsTr("Stop")] + property var _buttonActions: [QGCMAVLink.GripperActionOpen, QGCMAVLink.GripperActionClose, QGCMAVLink.GripperActionStop] + + Repeater { + model: _buttonTitles + + QGCDelayButton { + Layout.fillWidth: true + text: _buttonTitles[index] + + onActivated: { + _activeVehicle.sendGripperAction(_buttonActions[index]) + dropPanel.hide() + } + } + } +} diff --git a/src/FlightDisplay/FlyViewMap.qml b/src/FlightDisplay/FlyViewMap.qml index 12bd0cab8cf8..b55f5e858e79 100644 --- a/src/FlightDisplay/FlyViewMap.qml +++ b/src/FlightDisplay/FlyViewMap.qml @@ -656,7 +656,7 @@ FlightMap { QGCButton { Layout.fillWidth: true text: qsTr("Edit Position") - onClicked: { + onClicked: { roiEditPositionDialogComponent.createObject(mainWindow, { showSetPositionFromVehicle: false }).open() roiEditDropPanel.close() } @@ -757,7 +757,7 @@ FlightMap { } onMapClicked: (position) => { - if (!globals.guidedControllerFlyView.guidedUIVisible && + if (!globals.guidedControllerFlyView.guidedUIVisible && (globals.guidedControllerFlyView.showGotoLocation || globals.guidedControllerFlyView.showOrbit || globals.guidedControllerFlyView.showROI || globals.guidedControllerFlyView.showSetHome || globals.guidedControllerFlyView.showSetEstimatorOrigin)) { diff --git a/src/FlightDisplay/FlyViewToolStripActionList.qml b/src/FlightDisplay/FlyViewToolStripActionList.qml index f4ae49ece84b..860dca4f54db 100644 --- a/src/FlightDisplay/FlyViewToolStripActionList.qml +++ b/src/FlightDisplay/FlyViewToolStripActionList.qml @@ -50,6 +50,6 @@ ToolStripActionList { GuidedActionRTL { }, GuidedActionPause { }, FlyViewAdditionalActionsButton { }, - GuidedActionGripper { } + FlyViewGripperButton { } ] } diff --git a/src/FlightDisplay/FlyViewTopRightColumnLayout.qml b/src/FlightDisplay/FlyViewTopRightColumnLayout.qml index 3e1b2086e2bc..151bc5e02aaa 100644 --- a/src/FlightDisplay/FlyViewTopRightColumnLayout.qml +++ b/src/FlightDisplay/FlyViewTopRightColumnLayout.qml @@ -25,13 +25,13 @@ ColumnLayout { Layout.preferredWidth: _rightPanelWidth } - // We use a Loader to load the photoVideoControlComponent only when the active vehicle is not null + // We use a Loader to load the photoVideoControlComponent only when we have an active vehicle and a camera manager. // This make it easier to implement PhotoVideoControl without having to check for the mavlink camera // to be null all over the place Loader { id: photoVideoControlLoader Layout.alignment: Qt.AlignTop | Qt.AlignRight - sourceComponent: globals.activeVehicle ? photoVideoControlComponent : undefined + sourceComponent: globals.activeVehicle && globals.activeVehicle.cameraManager ? photoVideoControlComponent : undefined property real rightEdgeCenterInset: visible ? parent.width - x : 0 diff --git a/src/FlightDisplay/FlyViewTopRightPanel.qml b/src/FlightDisplay/FlyViewTopRightPanel.qml index afdbd63f2f47..b6acb28d6ba5 100644 --- a/src/FlightDisplay/FlyViewTopRightPanel.qml +++ b/src/FlightDisplay/FlyViewTopRightPanel.qml @@ -252,4 +252,3 @@ Rectangle { } } } - diff --git a/src/FlightDisplay/FlyViewVideo.qml b/src/FlightDisplay/FlyViewVideo.qml index 0dc656622e5a..ed512ff0e96e 100644 --- a/src/FlightDisplay/FlyViewVideo.qml +++ b/src/FlightDisplay/FlyViewVideo.qml @@ -151,7 +151,7 @@ Item { } onReleased: (mouse) => { onScreenGimbalController.releaseControl() - + //if there is already a selection, delete it if (trackingROI !== null) { trackingROI.destroy(); diff --git a/src/FlightDisplay/FlyViewWidgetLayer.qml b/src/FlightDisplay/FlyViewWidgetLayer.qml index 6a54052300d3..138686751007 100644 --- a/src/FlightDisplay/FlyViewWidgetLayer.qml +++ b/src/FlightDisplay/FlyViewWidgetLayer.qml @@ -23,9 +23,6 @@ import QGroundControl.Controls import QGroundControl.FlightDisplay import QGroundControl.FlightMap - - - // This is the ui overlay layer for the widgets/tools for Fly View Item { id: _root @@ -45,7 +42,6 @@ Item { property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75 property rect _centerViewport: Qt.rect(0, 0, width, height) property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 30 - property alias _gripperMenu: gripperOptions property real _layoutMargin: ScreenTools.defaultFontPixelWidth * 0.75 property bool _layoutSpacing: ScreenTools.defaultFontPixelWidth property bool _showSingleVehicleUI: true @@ -72,9 +68,7 @@ Item { id: topRightPanel anchors.top: parent.top anchors.right: parent.right - anchors.topMargin: _layoutMargin - anchors.rightMargin: _layoutMargin - maximumHeight: parent.height - (bottomRightRowLayout.height + _margins * 5) + maximumHeight: parent.height - (bottomRightRowLayout.height + _margins * 4) property real topEdgeRightInset: height + _layoutMargin property real rightEdgeTopInset: width + _layoutMargin @@ -83,7 +77,6 @@ Item { FlyViewTopRightColumnLayout { id: topRightColumnLayout - anchors.margins: _layoutMargin anchors.top: parent.top anchors.bottom: bottomRightRowLayout.top anchors.right: parent.right @@ -97,7 +90,6 @@ Item { FlyViewBottomRightRowLayout { id: bottomRightRowLayout - anchors.margins: _layoutMargin anchors.bottom: parent.bottom anchors.right: parent.right spacing: _layoutSpacing @@ -114,7 +106,6 @@ Item { } GuidedActionConfirm { - anchors.margins: _toolsMargin anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter z: QGroundControl.zOrderTopMost @@ -133,8 +124,8 @@ Item { visible: _virtualJoystickEnabled && !QGroundControl.videoManager.fullScreen && !(_activeVehicle ? _activeVehicle.usingHighLatencyLink : false) anchors.bottom: parent.bottom anchors.bottomMargin: bottomLoaderMargin - anchors.left: parent.left - anchors.leftMargin: ( y > toolStrip.y + toolStrip.height ? toolStrip.width / 2 : toolStrip.width * 1.05 + toolStrip.x) + anchors.left: parent.left + anchors.leftMargin: ( y > toolStrip.y + toolStrip.height ? toolStrip.width / 2 : toolStrip.width * 1.05 + toolStrip.x) source: "qrc:/qml/QGroundControl/FlightDisplay/VirtualJoystick.qml" active: _virtualJoystickEnabled && !(_activeVehicle ? _activeVehicle.usingHighLatencyLink : false) @@ -143,7 +134,7 @@ Item { property bool leftHandedMode: QGroundControl.settingsManager.appSettings.virtualJoystickLeftHandedMode.rawValue property bool _virtualJoystickEnabled: QGroundControl.settingsManager.appSettings.virtualJoystick.rawValue property real bottomEdgeRightInset: parent.height-y - property var _pipViewMargin: _pipView.visible ? parentToolInsets.bottomEdgeLeftInset + ScreenTools.defaultFontPixelHeight * 2 : + property var _pipViewMargin: _pipView.visible ? parentToolInsets.bottomEdgeLeftInset + ScreenTools.defaultFontPixelHeight * 2 : bottomRightRowLayout.height + ScreenTools.defaultFontPixelHeight * 1.5 property var bottomLoaderMargin: _pipViewMargin >= parent.height / 2 ? parent.height / 2 : _pipViewMargin @@ -160,7 +151,7 @@ Item { //Loader status logic onLoaded: { if (virtualJoystickMultiTouch.visible) { - virtualJoystickMultiTouch.item.calibration = true + virtualJoystickMultiTouch.item.calibration = true virtualJoystickMultiTouch.item.uiTotalWidth = rootWidth virtualJoystickMultiTouch.item.uiRealX = itemX } else { @@ -171,8 +162,6 @@ Item { FlyViewToolStrip { id: toolStrip - anchors.leftMargin: _toolsMargin + parentToolInsets.leftEdgeCenterInset - anchors.topMargin: _toolsMargin + parentToolInsets.topEdgeLeftInset anchors.left: parent.left anchors.top: parent.top z: QGroundControl.zOrderWidgets @@ -191,10 +180,6 @@ Item { property real leftEdgeCenterInset: leftEdgeTopInset } - GripperMenu { - id: gripperOptions - } - VehicleWarnings { anchors.centerIn: parent z: QGroundControl.zOrderTopMost @@ -202,11 +187,12 @@ Item { MapScale { id: mapScale - anchors.margins: _toolsMargin anchors.left: toolStrip.right anchors.top: parent.top mapControl: _mapControl buttonsOnLeft: true + zoomButtonsVisible: false + autoHide: true visible: !ScreenTools.isTinyScreen && QGroundControl.corePlugin.options.flyView.showMapScale && !isViewer3DOpen && mapControl.pipState.state === mapControl.pipState.fullState property real topEdgeCenterInset: visible ? y + height : 0 diff --git a/src/FlightDisplay/GripperMenu.qml b/src/FlightDisplay/GripperMenu.qml deleted file mode 100644 index 67083a52e14c..000000000000 --- a/src/FlightDisplay/GripperMenu.qml +++ /dev/null @@ -1,67 +0,0 @@ - -import QtQuick -import QtQuick.Controls -import QtQuick.Dialogs -import QtLocation -import QtPositioning -import QtQuick.Layouts - -import QGroundControl - -import QGroundControl.Controls - - -import QGroundControl.FlightMap - -QGCPopupDialog { - title: "Select one action" - property var acceptFunction: null - buttons: Dialog.Cancel - - onRejected:{ - _guidedController._gripperFunction = Vehicle.Invalid_option - _guidedController.closeAll() - close() - } - - onAccepted: { - if (acceptFunction) { - _guidedController._gripperFunction = Vehicle.Invalid_option - close() - } - } - - RowLayout { - QGCColumnButton { - id: grabButton - text: "Grab" - iconSource: "/res/GripperGrab.svg" - font.pointSize: ScreenTools.defaultFontPointSize * 3.5 - backRadius: width / 40 - heightFactor: 0.75 - Layout.preferredHeight: releaseButton.height - Layout.preferredWidth: releaseButton.width - - onClicked: { - _guidedController._gripperFunction = Vehicle.Gripper_grab - close() - } - } - - QGCColumnButton { - id: releaseButton - text: "Release" - iconSource: "/res/GripperRelease.svg" - font.pointSize: ScreenTools.defaultFontPointSize * 3.5 - backRadius: width / 40 - heightFactor: 0.75 - Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 27 - Layout.preferredHeight: Layout.preferredWidth / 1.20 - - onClicked: { - _guidedController._gripperFunction = Vehicle.Gripper_release - close() - } - } - } -} diff --git a/src/FlightDisplay/GuidedActionConfirm.qml b/src/FlightDisplay/GuidedActionConfirm.qml index 7ca10ce811fc..60f500e35d13 100644 --- a/src/FlightDisplay/GuidedActionConfirm.qml +++ b/src/FlightDisplay/GuidedActionConfirm.qml @@ -18,8 +18,8 @@ import QGroundControl.Controls import QGroundControl.UTMSP Rectangle { - id: _root - width: ScreenTools.defaultFontPixelWidth * 35 + id: control + width: mainLayout.width + (_margins * 2) height: mainLayout.height + (_margins * 2) radius: ScreenTools.defaultFontPixelWidth / 2 color: qgcPal.window @@ -27,8 +27,8 @@ Rectangle { property var guidedController property var guidedValueSlider - property string title // Currently unused - property alias message: messageText.text + property string title + property string message property int action property var actionData property bool hideTrigger: false @@ -36,7 +36,7 @@ Rectangle { property alias optionText: optionCheckBox.text property alias optionChecked: optionCheckBox.checked - property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property real _margins: ScreenTools.defaultFontPixelHeight / 2 property bool _emergencyAction: action === guidedController.actionEmergencyStop // Properties of UTM adapter @@ -45,12 +45,6 @@ Rectangle { Component.onCompleted: guidedController.confirmDialog = this - onVisibleChanged: { - if (visible) { - slider.focus = true - } - } - onHideTriggerChanged: { if (hideTrigger) { confirmCancelled() @@ -88,18 +82,17 @@ Rectangle { QGCPalette { id: qgcPal } ColumnLayout { - id: mainLayout - anchors.centerIn: parent - width: parent.width - (_margins * 2) - spacing: _margins + id: mainLayout + x: control._margins + y: control._margins + spacing: control._margins QGCLabel { - id: messageText Layout.fillWidth: true + Layout.leftMargin: closeButton.width + closeButton.anchors.rightMargin + Layout.rightMargin: Layout.leftMargin + text: control.message horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - font.pointSize: ScreenTools.defaultFontPointSize - font.bold: true } QGCCheckBox { @@ -109,57 +102,48 @@ Rectangle { visible: text !== "" } - RowLayout { + QGCDelayButton { Layout.fillWidth: true - spacing: ScreenTools.defaultFontPixelWidth - - SliderSwitch { - id: slider - confirmText: ScreenTools.isMobile ? qsTr("Slide to confirm") : qsTr("Slide or hold spacebar") - Layout.fillWidth: true - enabled: _utmspEnabled === true? utmspSliderTrigger : true - opacity: if(_utmspEnabled){utmspSliderTrigger === true ? 1 : 0.5} else{1} - - onAccept: { - _root.visible = false - var sliderOutputValue = 0 - if (guidedValueSlider.visible) { - sliderOutputValue = guidedValueSlider.getOutputValue() - guidedValueSlider.visible = false - } - hideTrigger = false - guidedController.executeAction(_root.action, _root.actionData, sliderOutputValue, _root.optionChecked) - if (mapIndicator) { - mapIndicator.actionConfirmed() - mapIndicator = undefined - } - - UTMSPStateStorage.indicatorOnMissionStatus = true - UTMSPStateStorage.currentNotificationIndex = 7 - UTMSPStateStorage.currentStateIndex = 3 + text: control.title + enabled: _utmspEnabled === true? utmspSliderTrigger : true + opacity: if(_utmspEnabled){utmspSliderTrigger === true ? 1 : 0.5} else{1} + + onActivated: { + control.visible = false + var sliderOutputValue = 0 + if (guidedValueSlider.visible) { + sliderOutputValue = guidedValueSlider.getOutputValue() + guidedValueSlider.visible = false } - } - - Rectangle { - height: slider.height * 0.75 - width: height - radius: height / 2 - color: qgcPal.primaryButton - - QGCColoredImage { - anchors.margins: parent.height / 4 - anchors.fill: parent - source: "/res/XDelete.svg" - fillMode: Image.PreserveAspectFit - color: qgcPal.text + hideTrigger = false + guidedController.executeAction(control.action, control.actionData, sliderOutputValue, control.optionChecked) + if (mapIndicator) { + mapIndicator.actionConfirmed() + mapIndicator = undefined } - QGCMouseArea { - fillItem: parent - onClicked: confirmCancelled() - } + UTMSPStateStorage.indicatorOnMissionStatus = true + UTMSPStateStorage.currentNotificationIndex = 7 + UTMSPStateStorage.currentStateIndex = 3 } } } -} + QGCColoredImage { + id: closeButton + anchors.topMargin: _margins / 2 + anchors.rightMargin: _margins / 2 + anchors.top: parent.top + anchors.right: parent.right + height: ScreenTools.defaultFontPixelHeight * 0.5 + width: height + source: "/res/XDelete.svg" + fillMode: Image.PreserveAspectFit + color: qgcPal.text + + QGCMouseArea { + fillItem: parent + onClicked: confirmCancelled() + } + } +} diff --git a/src/FlightDisplay/GuidedActionGripper.qml b/src/FlightDisplay/GuidedActionGripper.qml deleted file mode 100644 index 71960d4fe88f..000000000000 --- a/src/FlightDisplay/GuidedActionGripper.qml +++ /dev/null @@ -1,24 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2020 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -import QGroundControl.FlightDisplay -import QGroundControl - -GuidedToolStripAction { - property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _initialConnectComplete: activeVehicle ? activeVehicle.initialConnectComplete : false - property bool _grip_enable: _initialConnectComplete ? activeVehicle.hasGripper : false - property bool _isVehicleArmed: _initialConnectComplete ? activeVehicle.armed : false - - text: "Gripper" - iconSource: "/res/Gripper.svg" - visible: !_isVehicleArmed && _grip_enable // in this way if the pilot it's on the ground can release the cargo without actions tool - enabled: _grip_enable - actionID: _guidedController.actionGripper -} diff --git a/src/FlightDisplay/GuidedActionsController.qml b/src/FlightDisplay/GuidedActionsController.qml index cffc8fb7b4a4..e3232cbba266 100644 --- a/src/FlightDisplay/GuidedActionsController.qml +++ b/src/FlightDisplay/GuidedActionsController.qml @@ -40,7 +40,6 @@ Item { readonly property string mvDisarmTitle: qsTr("Disarm (MV)") readonly property string rtlTitle: qsTr("Return") readonly property string takeoffTitle: qsTr("Takeoff") - readonly property string gripperTitle: qsTr("Gripper Function") readonly property string landTitle: qsTr("Land") readonly property string startMissionTitle: qsTr("Start Mission") readonly property string mvStartMissionTitle: qsTr("Start Mission (MV)") @@ -56,7 +55,6 @@ Item { readonly property string landAbortTitle: qsTr("Land Abort") readonly property string setWaypointTitle: qsTr("Set Waypoint") readonly property string gotoTitle: qsTr("Go To Location") - readonly property string vtolTransitionTitle: qsTr("VTOL Transition") readonly property string roiTitle: qsTr("ROI") readonly property string setHomeTitle: qsTr("Set Home") readonly property string setEstimatorOriginTitle: qsTr("Set Estimator origin") @@ -70,7 +68,6 @@ Item { readonly property string mvDisarmMessage: qsTr("Disarm selected vehicles.") readonly property string emergencyStopMessage: qsTr("WARNING: THIS WILL STOP ALL MOTORS. IF VEHICLE IS CURRENTLY IN THE AIR IT WILL CRASH.") readonly property string takeoffMessage: qsTr("Takeoff from ground and hold position.") - readonly property string gripperMessage: qsTr("Grab or Release the cargo") readonly property string startMissionMessage: qsTr("Takeoff from ground and start the current mission.") readonly property string mvStartMissionMessage: qsTr("Takeoff from ground and start the current mission for selected vehicles.") readonly property string continueMissionMessage: qsTr("Continue the mission from the current waypoint.") @@ -87,8 +84,6 @@ Item { readonly property string landAbortMessage: qsTr("Abort the landing sequence.") readonly property string pauseMessage: qsTr("Pause the vehicle at it's current position, adjusting altitude up or down as needed.") readonly property string mvPauseMessage: qsTr("Pause selected vehicles at their current position.") - readonly property string vtolTransitionFwdMessage: qsTr("Transition VTOL to fixed wing flight.") - readonly property string vtolTransitionMRMessage: qsTr("Transition VTOL to multi-rotor flight.") readonly property string roiMessage: qsTr("Make the specified location a Region Of Interest.") readonly property string setHomeMessage: qsTr("Set vehicle home as the specified location. This will affect Return to Home position") readonly property string setEstimatorOriginMessage: qsTr("Make the specified location the estimator origin.") @@ -114,21 +109,16 @@ Item { readonly property int actionPause: 17 readonly property int actionMVPause: 18 readonly property int actionMVStartMission: 19 - readonly property int actionVtolTransitionToFwdFlight: 20 - readonly property int actionVtolTransitionToMRFlight: 21 - readonly property int actionROI: 22 - readonly property int actionForceArm: 24 - readonly property int actionChangeSpeed: 25 - readonly property int actionGripper: 26 - readonly property int actionSetHome: 27 - readonly property int actionSetEstimatorOrigin: 28 - readonly property int actionSetFlightMode: 29 - readonly property int actionChangeHeading: 30 - readonly property int actionMVArm: 31 - readonly property int actionMVDisarm: 32 - readonly property int actionChangeLoiterRadius: 33 - - + readonly property int actionROI: 20 + readonly property int actionForceArm: 21 + readonly property int actionChangeSpeed: 22 + readonly property int actionSetHome: 24 + readonly property int actionSetEstimatorOrigin: 25 + readonly property int actionSetFlightMode: 26 + readonly property int actionChangeHeading: 27 + readonly property int actionMVArm: 28 + readonly property int actionMVDisarm: 29 + readonly property int actionChangeLoiterRadius: 30 readonly property int customActionStart: 10000 // Custom actions ids should start here so that they don't collide with the built in actions @@ -161,7 +151,6 @@ Item { property bool showLandAbort: _guidedActionsEnabled && _vehicleFlying && _fixedWingOnApproach property bool showGotoLocation: _guidedActionsEnabled && _vehicleFlying property bool showSetHome: _guidedActionsEnabled - property bool showGripper: _initialConnectComplete ? _activeVehicle.hasGripper : false property bool showSetEstimatorOrigin: _activeVehicle && !(_activeVehicle.sensorsPresentBits & Vehicle.SysStatusSensorGPS) property bool showChangeHeading: _guidedActionsEnabled && _vehicleFlying @@ -197,7 +186,6 @@ Item { property bool _fixedWingOnApproach: _activeVehicle ? _activeVehicle.fixedWing && _vehicleLanding : false property bool _vehicleInFwdFlight: _activeVehicle ? _activeVehicle.inFwdFlight : false property bool _speedLimitsAvailable: _activeVehicle && ((_vehicleInFwdFlight && _activeVehicle.haveFWSpeedLimits) || (!_vehicleInFwdFlight && _activeVehicle.haveMRSpeedLimits)) - property var _gripperFunction: undefined // You can turn on log output for GuidedActionsController by turning on GuidedActionsControllerLog category property bool __guidedModeSupported: _activeVehicle ? _activeVehicle.guidedModeSupported : false @@ -370,8 +358,6 @@ Item { function onArmVehicleRequest() { armVehicleRequest() } function onForceArmVehicleRequest() { forceArmVehicleRequest() } function onDisarmVehicleRequest() { disarmVehicleRequest() } - function onVtolTransitionToFwdFlightRequest() { vtolTransitionToFwdFlightRequest() } - function onVtolTransitionToMRFlightRequest() { vtolTransitionToMRFlightRequest() } } function armVehicleRequest() { @@ -391,14 +377,6 @@ Item { } - function vtolTransitionToFwdFlightRequest() { - confirmAction(actionVtolTransitionToFwdFlight) - } - - function vtolTransitionToMRFlightRequest() { - confirmAction(actionVtolTransitionToMRFlight) - } - function closeAll() { confirmDialog.visible = false guidedValueSlider.visible = false @@ -543,16 +521,6 @@ Item { confirmDialog.message = mvPauseMessage confirmDialog.hideTrigger = true break; - case actionVtolTransitionToFwdFlight: - confirmDialog.title = vtolTransitionTitle - confirmDialog.message = vtolTransitionFwdMessage - confirmDialog.hideTrigger = true - break - case actionVtolTransitionToMRFlight: - confirmDialog.title = vtolTransitionTitle - confirmDialog.message = vtolTransitionMRMessage - confirmDialog.hideTrigger = true - break case actionROI: confirmDialog.title = roiTitle confirmDialog.message = roiMessage @@ -564,12 +532,6 @@ Item { confirmDialog.message = changeSpeedMessage guidedValueSlider.visible = true break - case actionGripper: - confirmDialog.hideTrigger = true - confirmDialog.title = gripperTitle - confirmDialog.message = gripperMessage - _widgetLayer._gripperMenu.createObject(mainWindow).open() - break case actionSetHome: confirmDialog.title = setHomeTitle confirmDialog.message = setHomeMessage @@ -697,12 +659,6 @@ Item { selectedVehicles.get(i).pauseVehicle() } break - case actionVtolTransitionToFwdFlight: - _activeVehicle.vtolInFwdFlight = true - break - case actionVtolTransitionToMRFlight: - _activeVehicle.vtolInFwdFlight = false - break case actionROI: _activeVehicle.guidedModeROI(actionData) break @@ -717,9 +673,6 @@ Item { } } break - case actionGripper: - _gripperFunction === undefined ? _activeVehicle.sendGripperAction(Vehicle.Invalid_option) : _activeVehicle.sendGripperAction(_gripperFunction) - break case actionSetHome: _activeVehicle.doSetHome(actionData) break diff --git a/src/FlightDisplay/GuidedValueSlider.qml b/src/FlightDisplay/GuidedValueSlider.qml index 366e3c8faebd..464f4b30083b 100644 --- a/src/FlightDisplay/GuidedValueSlider.qml +++ b/src/FlightDisplay/GuidedValueSlider.qml @@ -63,7 +63,7 @@ Item { property int _majorTickAdjustment: _majorTicksVisibleAboveIndicator * _majorTickValueStep // Calculate the next major tick above/below min/max - property int _majorTickMaxValue: Math.ceil((_sliderMaxVal + _majorTickAdjustment)/ _majorTickValueStep) * _majorTickValueStep + property int _majorTickMaxValue: Math.ceil((_sliderMaxVal + _majorTickAdjustment)/ _majorTickValueStep) * _majorTickValueStep property int _majorTickMinValue: Math.floor((_sliderMinVal - _majorTickAdjustment)/ _majorTickValueStep) * _majorTickValueStep // Now calculate the position we draw the first tick mark such that we are not allowed to flick above the max value @@ -131,7 +131,7 @@ Item { font.pointSize: ScreenTools.smallFontPointSize text: _displayText } - + QGCFlickable { id: sliderFlickable Layout.fillWidth: true @@ -250,6 +250,14 @@ Item { ctx.stroke() } + // Repaint when palette changes + Connections { + target: _qgcPal + function onPaletteChanged() { + indicatorCanvas.requestPaint() + } + } + QGCLabel { id: valueLabel anchors.margins: indicatorCanvas.indicatorValueMargins @@ -260,8 +268,8 @@ Item { text: _clampedSliderValueString(_sliderValue) + " " + unitsString font.pointSize: ScreenTools.largeFontPointSize - property var unitsString: _sliderType === GuidedValueSlider.Speed ? - QGroundControl.unitsConversion.appSettingsSpeedUnitsString : + property var unitsString: _sliderType === GuidedValueSlider.Speed ? + QGroundControl.unitsConversion.appSettingsSpeedUnitsString : QGroundControl.unitsConversion.appSettingsVerticalDistanceUnitsString } diff --git a/src/FlightDisplay/MultiRotorChecklist.qml b/src/FlightDisplay/MultiRotorChecklist.qml index f0a2779339e0..b5e87a8cd385 100644 --- a/src/FlightDisplay/MultiRotorChecklist.qml +++ b/src/FlightDisplay/MultiRotorChecklist.qml @@ -84,4 +84,3 @@ Item { } } } - diff --git a/src/FlightDisplay/ObstacleDistanceOverlayMap.qml b/src/FlightDisplay/ObstacleDistanceOverlayMap.qml index 8a28f91f8efb..6c3a5b7ff977 100644 --- a/src/FlightDisplay/ObstacleDistanceOverlayMap.qml +++ b/src/FlightDisplay/ObstacleDistanceOverlayMap.qml @@ -111,4 +111,3 @@ Item { id: obstacleDistance } } - diff --git a/src/FlightDisplay/ProximityRadarValues.qml b/src/FlightDisplay/ProximityRadarValues.qml index 20e912b87eba..bb92183f1a1e 100644 --- a/src/FlightDisplay/ProximityRadarValues.qml +++ b/src/FlightDisplay/ProximityRadarValues.qml @@ -51,4 +51,3 @@ QtObject { onRotationYaw270ValueChanged: rotationValueChanged() onRotationYaw315ValueChanged: rotationValueChanged() } - diff --git a/src/FlightDisplay/ProximityRadarVideoView.qml b/src/FlightDisplay/ProximityRadarVideoView.qml index 8f1024a14244..da3d8c4dcfb0 100644 --- a/src/FlightDisplay/ProximityRadarVideoView.qml +++ b/src/FlightDisplay/ProximityRadarVideoView.qml @@ -87,4 +87,3 @@ Item { } } - diff --git a/src/FlightDisplay/VTOLChecklist.qml b/src/FlightDisplay/VTOLChecklist.qml index 8893c7cbd47d..b50b6f526385 100644 --- a/src/FlightDisplay/VTOLChecklist.qml +++ b/src/FlightDisplay/VTOLChecklist.qml @@ -89,4 +89,3 @@ Item { } } } - diff --git a/src/FlightDisplay/VirtualJoystick.qml b/src/FlightDisplay/VirtualJoystick.qml index 8522f36dcdc0..5ff900b110cd 100644 --- a/src/FlightDisplay/VirtualJoystick.qml +++ b/src/FlightDisplay/VirtualJoystick.qml @@ -29,7 +29,7 @@ Item { property var calibration: false property var uiTotalWidth property var uiRealX - + Timer { interval: 40 // 25Hz, same as real joystick rate running: QGroundControl.settingsManager.appSettings.virtualJoystick.value diff --git a/src/FlightMap/CMakeLists.txt b/src/FlightMap/CMakeLists.txt index c942298a0fa0..58adacc03a5e 100644 --- a/src/FlightMap/CMakeLists.txt +++ b/src/FlightMap/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Flight Map Module +# Map widgets, vehicle indicators, and mission visualization +# ============================================================================ + qt_add_library(FlightMapModule STATIC) qt_add_qml_module(FlightMapModule diff --git a/src/FlightMap/FlightMap.qml b/src/FlightMap/FlightMap.qml index 484852a1de24..4f0619cba469 100644 --- a/src/FlightMap/FlightMap.qml +++ b/src/FlightMap/FlightMap.qml @@ -120,7 +120,7 @@ Map { signal mapPanStart signal mapPanStop signal mapClicked(var position) - + PinchHandler { id: pinchHandler target: null diff --git a/src/FlightMap/MapItems/ProximityRadarMapView.qml b/src/FlightMap/MapItems/ProximityRadarMapView.qml index ac5d4eff44e4..0ad79b478136 100644 --- a/src/FlightMap/MapItems/ProximityRadarMapView.qml +++ b/src/FlightMap/MapItems/ProximityRadarMapView.qml @@ -121,4 +121,3 @@ MapQuickItem { } } - diff --git a/src/FlightMap/MapItems/QGCMapCircleVisuals.qml b/src/FlightMap/MapItems/QGCMapCircleVisuals.qml index 598a22429721..5999c259ee9e 100644 --- a/src/FlightMap/MapItems/QGCMapCircleVisuals.qml +++ b/src/FlightMap/MapItems/QGCMapCircleVisuals.qml @@ -285,4 +285,3 @@ Item { } } } - diff --git a/src/FlightMap/MapItems/QGCMapPolygonVisuals.qml b/src/FlightMap/MapItems/QGCMapPolygonVisuals.qml index 12e22c6437d3..59002467ef1c 100644 --- a/src/FlightMap/MapItems/QGCMapPolygonVisuals.qml +++ b/src/FlightMap/MapItems/QGCMapPolygonVisuals.qml @@ -63,8 +63,8 @@ Item { function addEditingVisuals() { if (_objMgrEditingVisuals.empty) { _objMgrEditingVisuals.createObjects( - [ dragHandlesComponent, splitHandlesComponent, centerDragHandleComponent, edgeLengthHandlesComponent ], - mapControl, + [ dragHandlesComponent, splitHandlesComponent, centerDragHandleComponent, edgeLengthHandlesComponent ], + mapControl, false /* addToMap */) } } @@ -738,4 +738,3 @@ Item { } } } - diff --git a/src/FlightMap/MapItems/QGCMapPolylineVisuals.qml b/src/FlightMap/MapItems/QGCMapPolylineVisuals.qml index b58ab68e2a7f..296711fa1815 100644 --- a/src/FlightMap/MapItems/QGCMapPolylineVisuals.qml +++ b/src/FlightMap/MapItems/QGCMapPolylineVisuals.qml @@ -376,4 +376,3 @@ Item { } } } - diff --git a/src/FlightMap/MapItems/VehicleMapItem.qml b/src/FlightMap/MapItems/VehicleMapItem.qml index fc3342fa27e6..5a6940a1d5b7 100644 --- a/src/FlightMap/MapItems/VehicleMapItem.qml +++ b/src/FlightMap/MapItems/VehicleMapItem.qml @@ -56,10 +56,10 @@ MapQuickItem { blurMax: 32 blurMultiplier: .1 } - + Repeater { - model: vehicle ? vehicle.gimbalController.gimbals : [] - + model: vehicle ? vehicle.gimbalController.gimbals : [] + Item { id: canvasItem anchors.centerIn: vehicleItem diff --git a/src/FlightMap/MapScale.qml b/src/FlightMap/MapScale.qml index b9b76b0cea22..76a3f388278e 100644 --- a/src/FlightMap/MapScale.qml +++ b/src/FlightMap/MapScale.qml @@ -17,7 +17,7 @@ import QGroundControl.Controls /// Map scale control Item { - id: scale + id: control width: buttonsOnLeft || !_zoomButtonsVisible ? rightEnd.x + rightEnd.width : zoomDownButton.x + zoomDownButton.width height: rightEnd.y + rightEnd.height @@ -26,6 +26,7 @@ Item { property alias terrainButtonChecked: terrainButton.checked property bool zoomButtonsVisible: true property bool buttonsOnLeft: true ///< Buttons to left/right of scale bar + property bool autoHide: false ///< true: disappears after a timeout on scale change signal terrainButtonClicked @@ -118,8 +119,8 @@ Item { function calculateScale() { if(mapControl) { var scaleLinePixelLength = 100 - var leftCoord = mapControl.toCoordinate(Qt.point(0, scale.y), false /* clipToViewPort */) - var rightCoord = mapControl.toCoordinate(Qt.point(scaleLinePixelLength, scale.y), false /* clipToViewPort */) + var leftCoord = mapControl.toCoordinate(Qt.point(0, control.y), false /* clipToViewPort */) + var rightCoord = mapControl.toCoordinate(Qt.point(scaleLinePixelLength, control.y), false /* clipToViewPort */) var scaleLineMeters = Math.round(leftCoord.distanceTo(rightCoord)) if (QGroundControl.settingsManager.unitsSettings.horizontalDistanceUnits.value === UnitsSettings.HorizontalDistanceUnitsFeet) { calculateFeetRatio(scaleLineMeters, scaleLinePixelLength) @@ -129,19 +130,38 @@ Item { } } + function triggerRecalc() { + calculateScale() + if (control.autoHide) { + autoHideTimer.restart() + autoHideAnimation.stop() + control.opacity = 1 + } + } + + + Component.onCompleted: calculateScale() + Connections { - target: mapControl - function onWidthChanged() { scaleTimer.restart() } - function onHeightChanged() { scaleTimer.restart() } - function onZoomLevelChanged() { scaleTimer.restart() } + target: mapControl + function onWidthChanged() { triggerRecalc() } + function onHeightChanged() { triggerRecalc() } + function onZoomLevelChanged() { triggerRecalc() } + } + + PropertyAnimation { + id: autoHideAnimation + target: control + property: "opacity" + from: 1 + to: 0 + duration: 500 } Timer { - id: scaleTimer - interval: 100 - running: false - repeat: false - onTriggered: calculateScale() + id: autoHideTimer + interval: 3000 + onTriggered: autoHideAnimation.start() } QGCMapLabel { @@ -223,10 +243,4 @@ Item { visible: _zoomButtonsVisible onClicked: mapControl.zoomLevel -= 0.5 } - - Component.onCompleted: { - if (scale.visible) { - calculateScale(); - } - } } diff --git a/src/FlightMap/Widgets/CompassDial.qml b/src/FlightMap/Widgets/CompassDial.qml index 81e6ca960110..b5a3b416bb5b 100644 --- a/src/FlightMap/Widgets/CompassDial.qml +++ b/src/FlightMap/Widgets/CompassDial.qml @@ -23,7 +23,7 @@ Item { function translateCenterToAngleX(radius, angle) { return radius * Math.sin(angle * (Math.PI / 180)) - } + } function translateCenterToAngleY(radius, angle) { return -radius * Math.cos(angle * (Math.PI / 180)) @@ -108,5 +108,5 @@ Item { angle: 45 / 2 + (45 * index) } } - } + } } diff --git a/src/FlightMap/Widgets/PhotoVideoControl.qml b/src/FlightMap/Widgets/PhotoVideoControl.qml index 74df33cba472..fc220da114ce 100644 --- a/src/FlightMap/Widgets/PhotoVideoControl.qml +++ b/src/FlightMap/Widgets/PhotoVideoControl.qml @@ -14,20 +14,15 @@ import QtQuick.Controls import QtQuick.Dialogs import QGroundControl - import QGroundControl.Controls - - - - import QGroundControl.FactControls Rectangle { - width: mainLayout.width + (_margins * 2) - height: mainLayout.height + (_margins * 2) + width: mainLayout.width + (_smallMargins * 2) + height: mainLayout.height + (_smallMargins * 2) color: Qt.rgba(qgcPal.window.r, qgcPal.window.g, qgcPal.window.b, 0.5) radius: _margins - visible: _camera.capturesVideo || _camera.capturesPhotos + visible: _camera.capturesVideo || _camera.capturesPhotos || _camera.hasTracking || _camera.hasVideoStream property real _margins: ScreenTools.defaultFontPixelHeight / 2 property real _smallMargins: ScreenTools.defaultFontPixelWidth / 2 @@ -40,6 +35,67 @@ Rectangle { property bool _photoCaptureSingleIdle: _camera.photoCaptureStatus === MavlinkCameraControl.PHOTO_CAPTURE_IDLE property bool _photoCaptureIntervalIdle: _camera.photoCaptureStatus === MavlinkCameraControl.PHOTO_CAPTURE_INTERVAL_IDLE property bool _photoCaptureIdle: _photoCaptureSingleIdle || _photoCaptureIntervalIdle + property color _captureButtonColor: _cameraInPhotoMode ? qgcPal.photoCaptureButtonColor : qgcPal.videoCaptureButtonColor + +/* + // Used for testing camera ui options. Set _camera to testCamera to use. + QtObject { + id: testCamera + + property bool capturesVideo: false + property bool capturesPhotos: true + property bool hasModes: true + property bool hasZoom: true + property bool hasTracking: true + property string modelName: "Test Camera" + property int cameraMode: MavlinkCameraControl.CAM_MODE_PHOTO + property int videoCaptureStatus: MavlinkCameraControl.VIDEO_CAPTURE_STATUS_STOPPED + property int photoCaptureStatus: MavlinkCameraControl.PHOTO_CAPTURE_IDLE + property int zoomLevel: 0 + property int photoCaptureMode: MavlinkCameraControl.PHOTO_CAPTURE_SINGLE + property int photoLapse: 5 + property int thermalMode: MavlinkCameraControl.THERMAL_OFF + property int thermalOpacity: 50 + property int storageStatus: MavlinkCameraControl.STORAGE_READY + property int batteryRemaining: 75 + property int recordTime: 0 + property string storageFreeStr: "32 GB" + property string batteryRemainingStr: "75 %" + property string recordTimeStr: "00:00:00" + property bool trackingEnabled: false + + function setCameraModeVideo() { + cameraMode = MavlinkCameraControl.CAM_MODE_VIDEO; + videoCaptureStatus = MavlinkCameraControl.VIDEO_CAPTURE_STATUS_STOPPED; + photoCaptureStatus = MavlinkCameraControl.PHOTO_CAPTURE_IDLE; + } + + function setCameraModePhoto() { + cameraMode = MavlinkCameraControl.CAM_MODE_PHOTO; + videoCaptureStatus = MavlinkCameraControl.VIDEO_CAPTURE_STATUS_STOPPED; + photoCaptureStatus = MavlinkCameraControl.PHOTO_CAPTURE_IDLE; + } + + function takePhoto() { + photoCaptureStatus = MavlinkCameraControl.PHOTO_CAPTURE_IN_PROGRESS; + takePhotoTimer.start(); + } + + function toggleVideoRecording() { + if (videoCaptureStatus === MavlinkCameraControl.VIDEO_CAPTURE_STATUS_RUNNING) { + videoCaptureStatus = MavlinkCameraControl.VIDEO_CAPTURE_STATUS_STOPPED; + } else { + videoCaptureStatus = MavlinkCameraControl.VIDEO_CAPTURE_STATUS_RUNNING; + } + } + } + + Timer { + id: takePhotoTimer + interval: 500 + onTriggered: testCamera.photoCaptureStatus = MavlinkCameraControl.PHOTO_CAPTURE_IDLE + } +*/ QGCPalette { id: qgcPal; colorGroupEnabled: enabled } @@ -47,14 +103,14 @@ Rectangle { RowLayout { id: mainLayout - anchors.margins: _margins + anchors.margins: _smallMargins anchors.top: parent.top anchors.left: parent.left spacing: _margins ColumnLayout { Layout.fillHeight: true - spacing: _margins + spacing: 0 visible: _camera.hasZoom QGCLabel { @@ -74,108 +130,122 @@ Rectangle { onValueChanged: _camera.zoomLevel = value } } - + ColumnLayout { - spacing: _margins * 2 + spacing: _margins - ColumnLayout { - spacing: _margins + // Camera name + QGCLabel { + Layout.alignment: Qt.AlignHCenter + text: _camera.modelName + visible: _cameraManager.cameras.length > 1 + } - // Camera name - QGCLabel { - Layout.alignment: Qt.AlignHCenter - text: _camera.modelName - visible: _cameraManager.cameras.length > 1 - } + // Photo/Video Mode Selector + Rectangle { + Layout.alignment: Qt.AlignHCenter + width: ScreenTools.defaultFontPixelWidth * 10 + height: width / 2 + color: qgcPal.windowShadeLight + radius: height * 0.5 + visible: _camera.hasModes - // Photo/Video Mode Selector + //-- Video Mode Rectangle { - Layout.alignment: Qt.AlignHCenter - width: ScreenTools.defaultFontPixelWidth * 10 - height: width / 2 - color: qgcPal.windowShadeLight - radius: height * 0.5 - visible: _camera.hasModes + anchors.verticalCenter: parent.verticalCenter + width: parent.height + height: parent.height + color: _cameraInVideoMode ? qgcPal.window : qgcPal.windowShadeLight + radius: height * 0.5 + anchors.left: parent.left + border.color: qgcPal.text + border.width: _cameraInPhotoMode ? 0 : 1 - //-- Video Mode - Rectangle { - anchors.verticalCenter: parent.verticalCenter - width: parent.height - height: parent.height - color: _cameraInVideoMode ? qgcPal.window : qgcPal.windowShadeLight - radius: height * 0.5 - anchors.left: parent.left - border.color: qgcPal.text - border.width: _cameraInPhotoMode ? 0 : 1 - - QGCColoredImage { - height: parent.height * 0.5 - width: height - anchors.centerIn: parent - source: "/qmlimages/camera_video.svg" - fillMode: Image.PreserveAspectFit - sourceSize.height: height - color: _cameraInVideoMode ? qgcPal.colorGreen : qgcPal.text - - MouseArea { - anchors.fill: parent - enabled: _cameraInPhotoMode ? _photoCaptureIdle : true - onClicked: _camera.setCameraModeVideo() - } + QGCColoredImage { + height: parent.height * 0.5 + width: height + anchors.centerIn: parent + source: "/qmlimages/camera_video.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + color: _cameraInVideoMode ? qgcPal.colorGreen : qgcPal.text + + MouseArea { + anchors.fill: parent + enabled: _cameraInPhotoMode ? _photoCaptureIdle : true + onClicked: _camera.setCameraModeVideo() } } - - //-- Photo Mode - Rectangle { - anchors.verticalCenter: parent.verticalCenter - width: parent.height - height: parent.height - color: _cameraInPhotoMode ? qgcPal.window : qgcPal.windowShadeLight - radius: height * 0.5 - anchors.right: parent.right - border.color: qgcPal.text - border.width: _cameraInPhotoMode ? 1 : 0 - - QGCColoredImage { - height: parent.height * 0.5 - width: height - anchors.centerIn: parent - source: "/qmlimages/camera_photo.svg" - fillMode: Image.PreserveAspectFit - sourceSize.height: height - color: _cameraInPhotoMode ? qgcPal.colorGreen : qgcPal.text - - MouseArea { - anchors.fill: parent - enabled: _cameraInVideoMode ? _videoCaptureIdle : true - onClicked: _camera.setCameraModePhoto() - } + } + + //-- Photo Mode + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: parent.height + height: parent.height + color: _cameraInPhotoMode ? qgcPal.window : qgcPal.windowShadeLight + radius: height * 0.5 + anchors.right: parent.right + border.color: qgcPal.text + border.width: _cameraInPhotoMode ? 1 : 0 + + QGCColoredImage { + height: parent.height * 0.5 + width: height + anchors.centerIn: parent + source: "/qmlimages/camera_photo.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + color: _cameraInPhotoMode ? qgcPal.colorGreen : qgcPal.text + + MouseArea { + anchors.fill: parent + enabled: _cameraInVideoMode ? _videoCaptureIdle : true + onClicked: _camera.setCameraModePhoto() } } } + } + + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + spacing: _smallMargins + visible: _camera.capturesVideo || _camera.capturesPhotos // Take Photo, Start/Stop Video button Rectangle { Layout.alignment: Qt.AlignHCenter - color: Qt.rgba(0,0,0,0) + color: qgcPal.button width: ScreenTools.defaultFontPixelWidth * 6 height: width radius: width * 0.5 - border.color: qgcPal.buttonText - border.width: 3 + border.width: 1 + border.color: qgcPal.buttonBorder + + Rectangle { + anchors.centerIn: parent + anchors.alignWhenCentered: false // Prevents anchors.centerIn from snapping to integer coordinates, which can throw off centering. + color: qgcPal.buttonBorder + width: parent.width * 0.75 + height: width + radius: width * 0.5 + } Rectangle { - anchors.centerIn: parent - width: parent.width * (_isShootingInCurrentMode ? 0.5 : 0.75) - height: width - radius: _isShootingInCurrentMode ? 0 : width * 0.5 - color: _isShootingInCurrentMode || _canShootInCurrentMode ? qgcPal.colorRed : qgcPal.colorGrey + anchors.centerIn: parent + anchors.alignWhenCentered: false // Prevents anchors.centerIn from snapping to integer coordinates, which can throw off centering. + width: parent.width * (_isShootingInCurrentMode ? 0.5 : 0.75) + height: width + radius: _isShootingInCurrentMode ? ScreenTools.defaultFontPixelWidth * 0.5 : width * 0.5 + color: _captureButtonColor + border.width: 1 + border.color: qgcPal.buttonBorder property bool _isShootingInPhotoMode: _cameraInPhotoMode && _camera.photoCaptureStatus === MavlinkCameraControl.PHOTO_CAPTURE_IN_PROGRESS property bool _isShootingInVideoMode: (!_cameraInPhotoMode && _camera.videoCaptureStatus === MavlinkCameraControl.VIDEO_CAPTURE_STATUS_RUNNING) property bool _isShootingInCurrentMode: _cameraInPhotoMode ? _isShootingInPhotoMode : _isShootingInVideoMode property bool _isShootingInOtherMode: _cameraInPhotoMode ? _isShootingInVideoMode : _isShootingInPhotoMode - property bool _canShootInCurrentMode: _isShootingInOtherMode ? + property bool _canShootInCurrentMode: _isShootingInOtherMode ? (_cameraInPhotoMode ? _camera.photosInVideoMode : _camera.videoInPhotoMode) : true } @@ -201,10 +271,10 @@ Rectangle { // Record time / Capture count Rectangle { Layout.alignment: Qt.AlignHCenter - color: !_videoCaptureIdle && !_photoCaptureIdle ? "transparent" : qgcPal.colorRed + color: _videoCaptureIdle && _photoCaptureIdle ? "transparent" : _captureButtonColor Layout.preferredWidth: (_cameraInVideoMode ? videoRecordTime.width : photoCaptureCount.width) + (_smallMargins * 2) Layout.preferredHeight: (_cameraInVideoMode ? videoRecordTime.height : photoCaptureCount.height) - radius: _margins / 2 + radius: _smallMargins // Video record time QGCLabel { @@ -213,7 +283,6 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top text: _videoCaptureIdle ? "00:00:00" : _camera.recordTimeStr - font.pointSize: ScreenTools.largeFontPointSize visible: _cameraInVideoMode } @@ -224,37 +293,39 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top text: _activeVehicle ? ('00000' + _activeVehicle.cameraTriggerPoints.count).slice(-5) : "00000" - font.pointSize: ScreenTools.largeFontPointSize visible: _cameraInPhotoMode } } + } - //-- Status Information - ColumnLayout { - Layout.alignment: Qt.AlignHCenter - spacing: 0 + //-- Status Information + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + spacing: 0 + visible: storageStatus.visible || batteryStatus.visible - QGCLabel { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Free Space: ") + _camera.storageFreeStr - font.pointSize: ScreenTools.defaultFontPointSize - visible: _camera.storageStatus === MavlinkCameraControl.STORAGE_READY - } + QGCLabel { + id: storageStatus + Layout.alignment: Qt.AlignHCenter + text: qsTr("Free: ") + _camera.storageFreeStr + font.pointSize: ScreenTools.defaultFontPointSize + visible: _camera.storageStatus === MavlinkCameraControl.STORAGE_READY + } - QGCLabel { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Battery: ") + _camera.batteryRemainingStr - font.pointSize: ScreenTools.defaultFontPointSize - visible: _camera.batteryRemaining >= 0 - } + QGCLabel { + id: batteryStatus + Layout.alignment: Qt.AlignHCenter + text: qsTr("Battery: ") + _camera.batteryRemainingStr + font.pointSize: ScreenTools.defaultFontPointSize + visible: _camera.batteryRemaining >= 0 } } ColumnLayout { id: trackingControls Layout.alignment: Qt.AlignHCenter - spacing: _margins - visible: _camera && _camera.hasTracking + spacing: 0 + visible: _camera.hasTracking Rectangle { Layout.alignment: Qt.AlignHCenter @@ -263,7 +334,7 @@ Rectangle { Layout.preferredHeight: Layout.preferredWidth border.color: qgcPal.buttonText border.width: 3 - + QGCColoredImage { height: parent.height * 0.5 width: height @@ -288,8 +359,7 @@ Rectangle { QGCLabel { Layout.alignment: Qt.AlignHCenter text: qsTr("Camera Tracking") - font.pointSize: ScreenTools.defaultFontPointSize - visible: _camera && _camera.hasTracking + font.pointSize: ScreenTools.smallFontPointSize } } diff --git a/src/FlightMap/Widgets/QGCCompassWidget.qml b/src/FlightMap/Widgets/QGCCompassWidget.qml index aa3113348499..5d5cd5cfe28a 100644 --- a/src/FlightMap/Widgets/QGCCompassWidget.qml +++ b/src/FlightMap/Widgets/QGCCompassWidget.qml @@ -59,7 +59,7 @@ Rectangle { function translateCenterToAngleX(radius, angle) { return radius * Math.sin(angle * (Math.PI / 180)) - } + } function translateCenterToAngleY(radius, angle) { return -radius * Math.cos(angle * (Math.PI / 180)) diff --git a/src/FollowMe/CMakeLists.txt b/src/FollowMe/CMakeLists.txt index d5ad7bae7269..d7f127e164e2 100644 --- a/src/FollowMe/CMakeLists.txt +++ b/src/FollowMe/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Follow Me Module +# Enables vehicles to follow a ground station's GPS position +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE FollowMe.cc diff --git a/src/FollowMe/FollowMe.cc b/src/FollowMe/FollowMe.cc index 41a540d43681..8e903bd7c5f4 100644 --- a/src/FollowMe/FollowMe.cc +++ b/src/FollowMe/FollowMe.cc @@ -18,7 +18,7 @@ #include -QGC_LOGGING_CATEGORY(FollowMeLog, "qgc.followme") +QGC_LOGGING_CATEGORY(FollowMeLog, "API.FollowMe") Q_APPLICATION_STATIC(FollowMe, _followMeInstance); @@ -29,7 +29,7 @@ FollowMe::FollowMe(QObject *parent) // qCDebug(FollowMeLog) << Q_FUNC_INFO << this; _gcsMotionReportTimer->setSingleShot(false); - // We set the update interval to a fixed amount of time. + // We set the update interval to a fixed amount of time. // Even if the device can't update this quickly we'll pick up a new value on the next time around. // We can't trust the value for update interval which comes from the actual device. _gcsMotionReportTimer->setInterval(kMotionUpdateInterval); diff --git a/src/GPS/CMakeLists.txt b/src/GPS/CMakeLists.txt index d101c261e08f..1d745b488994 100644 --- a/src/GPS/CMakeLists.txt +++ b/src/GPS/CMakeLists.txt @@ -1,3 +1,9 @@ +# ============================================================================ +# GPS Module +# GPS/GNSS positioning and RTK support +# ============================================================================ + +# GPS requires serial link support if(QGC_NO_SERIAL_LINK) return() endif() @@ -12,6 +18,8 @@ target_sources(${CMAKE_PROJECT_NAME} GPSRtk.h GPSRTKFactGroup.cc GPSRTKFactGroup.h + NTRIP.cc + NTRIP.h RTCMMavlink.cc RTCMMavlink.h satellite_info.h @@ -23,7 +31,9 @@ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt6::Core) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -#===========================================================================# +# ============================================================================ +# PX4 GPS Drivers Integration +# ============================================================================ CPMAddPackage( NAME px4-gpsdrivers @@ -32,14 +42,23 @@ CPMAddPackage( SOURCE_SUBDIR src ) -file(GLOB GPS_DRIVERS_SOURCES "${px4-gpsdrivers_SOURCE_DIR}/src/*") +# NOTE: Using file(GLOB) for external dependency sources +# CONFIGURE_DEPENDS ensures CMake re-runs if files are added/removed +file(GLOB GPS_DRIVERS_SOURCES + CONFIGURE_DEPENDS + "${px4-gpsdrivers_SOURCE_DIR}/src/*.c" + "${px4-gpsdrivers_SOURCE_DIR}/src/*.cpp" + "${px4-gpsdrivers_SOURCE_DIR}/src/*.h" +) target_sources(${CMAKE_PROJECT_NAME} PRIVATE definitions.h ${GPS_DRIVERS_SOURCES} ) -# Add JSON files +# ---------------------------------------------------------------------------- +# GPS/RTK Configuration Resources +# ---------------------------------------------------------------------------- qt_add_resources(${CMAKE_PROJECT_NAME} json_gps_drivers PREFIX "/json/Vehicle" FILES GPSRTKFact.json diff --git a/src/GPS/GPSManager.cc b/src/GPS/GPSManager.cc index fb00f7ab3a83..00f533ffdcf1 100644 --- a/src/GPS/GPSManager.cc +++ b/src/GPS/GPSManager.cc @@ -13,7 +13,7 @@ #include -QGC_LOGGING_CATEGORY(GPSManagerLog, "qgc.gps.gpsmanager") +QGC_LOGGING_CATEGORY(GPSManagerLog, "GPS.GPSManager") Q_APPLICATION_STATIC(GPSManager, _gpsManager); diff --git a/src/GPS/GPSProvider.cc b/src/GPS/GPSProvider.cc index 6eec729ac270..f22b6efe2509 100644 --- a/src/GPS/GPSProvider.cc +++ b/src/GPS/GPSProvider.cc @@ -24,8 +24,8 @@ #include #endif -QGC_LOGGING_CATEGORY(GPSProviderLog, "qgc.gps.gpsprovider") -QGC_LOGGING_CATEGORY(GPSDriversLog, "qgc.gps.drivers") +QGC_LOGGING_CATEGORY(GPSProviderLog, "GPS.GPSProvider") +QGC_LOGGING_CATEGORY(GPSDriversLog, "GPS.Drivers") GPSProvider::GPSProvider(const QString &device, GPSType type, const rtk_data_s &rtkData, const std::atomic_bool &requestStop, QObject *parent) : QThread(parent) diff --git a/src/GPS/GPSRTKFactGroup.cc b/src/GPS/GPSRTKFactGroup.cc index ac5018c5495b..774f0ef2f0ee 100644 --- a/src/GPS/GPSRTKFactGroup.cc +++ b/src/GPS/GPSRTKFactGroup.cc @@ -10,7 +10,7 @@ #include "GPSRTKFactGroup.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(GPSRTKFactGroupLog, "qgc.gps.gpsrtkfactgroup") +QGC_LOGGING_CATEGORY(GPSRTKFactGroupLog, "GPS.GPSRTKFactGroup") GPSRTKFactGroup::GPSRTKFactGroup(QObject *parent) : FactGroup(1000, QStringLiteral(":/json/Vehicle/GPSRTKFact.json"), parent) diff --git a/src/GPS/GPSRtk.cc b/src/GPS/GPSRtk.cc index 0d2d38b0d421..0c38055bbce0 100644 --- a/src/GPS/GPSRtk.cc +++ b/src/GPS/GPSRtk.cc @@ -15,7 +15,7 @@ #include "RTKSettings.h" #include "SettingsManager.h" -QGC_LOGGING_CATEGORY(GPSRtkLog, "qgc.gps.gpsrtk") +QGC_LOGGING_CATEGORY(GPSRtkLog, "GPS.GPSRtk") GPSRtk::GPSRtk(QObject *parent) : QObject(parent) diff --git a/src/GPS/NTRIP.cc b/src/GPS/NTRIP.cc new file mode 100644 index 000000000000..72121fab3389 --- /dev/null +++ b/src/GPS/NTRIP.cc @@ -0,0 +1,1304 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "NTRIP.h" +#include "NTRIPSettings.h" +#include "Fact.h" +#include "FactGroup.h" +#include "QGCApplication.h" +#include "QGCLoggingCategory.h" +#include "RTCMMavlink.h" +#include "SettingsManager.h" +#include +#include + +#include "MultiVehicleManager.h" +#include "Vehicle.h" +#include "PositionManager.h" +#include +#include + +#include +#include +#include + +#include +#include + +QGC_LOGGING_CATEGORY(NTRIPLog, "qgc.ntrip") + +// Register the QML type without constructing the singleton up-front. +// This avoids creating NTRIPManager during a temporary Q(Core)Application +// used for command-line parsing, which could lead to it being destroyed +// early and leaving a dangling static pointer. +static QObject* _ntripManagerQmlProvider(QQmlEngine*, QJSEngine*) +{ + return NTRIPManager::instance(); +} + +static void _ntripManagerRegisterQmlTypes() +{ + qmlRegisterSingletonType("QGroundControl.NTRIP", 1, 0, "NTRIPManager", _ntripManagerQmlProvider); +} +Q_COREAPP_STARTUP_FUNCTION(_ntripManagerRegisterQmlTypes) + +NTRIPManager* NTRIPManager::_instance = nullptr; + +// constructor +NTRIPManager::NTRIPManager(QObject* parent) + : QObject(parent) + , _ntripStatus(tr("Disconnected")) +{ + qCDebug(NTRIPLog) << "NTRIPManager created"; + _startupTimer.start(); + + _rtcmMavlink = qgcApp() ? qgcApp()->findChild() : nullptr; + if (!_rtcmMavlink) { + QObject* parentObj = qgcApp() ? static_cast(qgcApp()) : static_cast(this); + // Ensure an RTCMMavlink helper exists for forwarding RTCM messages + _rtcmMavlink = new RTCMMavlink(parentObj); + _rtcmMavlink->setObjectName(QStringLiteral("RTCMMavlink")); + qCDebug(NTRIPLog) << "NTRIP Created RTCMMavlink helper"; + } + + // Force NTRIP checkbox OFF during app startup and keep it OFF until first settings tick. + { + NTRIPSettings* settings = SettingsManager::instance()->ntripSettings(); + if (settings && settings->ntripServerConnectEnabled()) { + Fact* fact = settings->ntripServerConnectEnabled(); + + // Force OFF immediately + fact->setRawValue(false); + + // While !_forcedOffOnce, if something tries to flip it ON, flip it back OFF. + _ntripEnableConn = connect(fact, &Fact::rawValueChanged, + this, [this, fact]() { + if (!_forcedOffOnce) { + const bool wantOn = fact->rawValue().toBool(); + if (wantOn) { + qCDebug(NTRIPLog) << "NTRIP Startup: coercing ntripServerConnectEnabled back to false"; + fact->setRawValue(false); + } + } + }); + } + } + + // Check settings periodically + _settingsCheckTimer = new QTimer(this); + connect(_settingsCheckTimer, &QTimer::timeout, this, &NTRIPManager::_checkSettings); + _settingsCheckTimer->start(1000); // Check every second + + _ggaTimer = new QTimer(this); + _ggaTimer->setInterval(5000); + connect(_ggaTimer, &QTimer::timeout, this, &NTRIPManager::_sendGGA); + + connect(qApp, &QCoreApplication::aboutToQuit, this, &NTRIPManager::stopNTRIP, Qt::QueuedConnection); + + _checkSettings(); +} + +// destructor +NTRIPManager::~NTRIPManager() +{ + qCDebug(NTRIPLog) << "NTRIPManager destroyed"; + stopNTRIP(); + // Clear singleton pointer in case we were destroyed (e.g., if created under + // a short-lived Q(Core)Application). + if (_instance == this) { + _instance = nullptr; + } +} + +RTCMParser::RTCMParser() +{ + reset(); +} + +void RTCMParser::reset() +{ + _state = WaitingForPreamble; + _messageLength = 0; + _bytesRead = 0; + _lengthBytesRead = 0; + _crcBytesRead = 0; +} + +bool RTCMParser::addByte(uint8_t byte) +{ + switch (_state) { + case WaitingForPreamble: + if (byte == RTCM3_PREAMBLE) { + _buffer[0] = byte; + _bytesRead = 1; + _state = ReadingLength; + _lengthBytesRead = 0; + } + break; + + case ReadingLength: + _lengthBytes[_lengthBytesRead++] = byte; + _buffer[_bytesRead++] = byte; + if (_lengthBytesRead == 2) { + // Extract 10-bit length from bytes (6 reserved bits + 10 length bits) + _messageLength = ((_lengthBytes[0] & 0x03) << 8) | _lengthBytes[1]; + if (_messageLength > 0 && _messageLength < 1021) { // Valid RTCM3 length + _state = ReadingMessage; + } else { + reset(); // Invalid length, restart + } + } + break; + + case ReadingMessage: + _buffer[_bytesRead++] = byte; + if (_bytesRead >= _messageLength + 3) { // +3 for header + _state = ReadingCRC; + _crcBytesRead = 0; + } + break; + + case ReadingCRC: + _crcBytes[_crcBytesRead++] = byte; + if (_crcBytesRead == 3) { + // Message complete - for simplicity, we'll skip CRC validation + return true; + } + break; + } + return false; +} + +uint16_t RTCMParser::messageId() +{ + if (_messageLength >= 2) { + // Message ID is in bits 14-25 of the message (after the 24-bit header) + return ((_buffer[3] << 4) | (_buffer[4] >> 4)) & 0xFFF; + } + return 0; +} + +NTRIPTCPLink::NTRIPTCPLink(const QString& hostAddress, + int port, + const QString& username, + const QString& password, + const QString& mountpoint, + const QString& whitelist, + bool useSpartn, + QObject* parent) + : QObject(parent) + , _hostAddress(hostAddress) + , _port(port) + , _username(username) + , _password(password) + , _mountpoint(mountpoint) + , _useSpartn(useSpartn) +{ + + + if (_useSpartn) { + // SPARTN path does not use RTCM whitelist or parser + if (!whitelist.isEmpty()) { + qCDebug(NTRIPLog) << "NTRIP SPARTN enabled; ignoring RTCM whitelist:" << whitelist; + } + // Initialize SPARTN header-strip guard + _spartnBuf.clear(); + _spartnNeedHeaderStrip = true; + + // Helpful hint if user left the default RTCM port + if (_port != 2102) { + qCWarning(NTRIPLog) << "NTRIP SPARTN is enabled but port is" << _port << "(expected 2102 for TLS)"; + } + } else { + // RTCM path: build whitelist and parser as before + for (const auto& msg : whitelist.split(',')) { + int msg_int = msg.toInt(); + if (msg_int) + _whitelist.append(msg_int); + } + qCDebug(NTRIPLog) << "NTRIP whitelist:" << _whitelist; + if (_whitelist.empty()) { + qCDebug(NTRIPLog) << "NTRIP whitelist is empty; all RTCM message IDs will be forwarded."; + } + if (!_rtcmParser) { + _rtcmParser = new RTCMParser(); + } + _rtcmParser->reset(); + } + + _state = NTRIPState::uninitialised; + + // Start the thread only after all members are initialized to avoid races + start(); +} + +NTRIPTCPLink::~NTRIPTCPLink() +{ + _stopping.store(true); + + if (_socket) { + QObject::disconnect(_readyReadConn); + _socket->disconnectFromHost(); + _socket->close(); + delete _socket; + _socket = nullptr; + } + + if (_rtcmParser) { + delete _rtcmParser; + _rtcmParser = nullptr; + } +} + +void NTRIPTCPLink::start() +{ + // This runs in the worker thread after QThread::started is emitted + _hardwareConnect(); +} + +void NTRIPTCPLink::_hardwareConnect() +{ + if (_stopping.load()) { + return; + } + + qCDebug(NTRIPLog) << "NTRIP connectToHost" << _hostAddress << ":" << _port << " mount=" << _mountpoint + << " tls=" << (_useSpartn ? "yes" : "no"); + + // allocate the appropriate socket type + // SPARTN selection (TLS on 2102 or when explicitly enabled) + if (_useSpartn) { + QSslSocket* sslSocket = new QSslSocket(this); + _socket = sslSocket; + } else { + _socket = new QTcpSocket(this); + } + + _socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); + _socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + _socket->setReadBufferSize(0); + + QObject::connect(_socket, &QTcpSocket::errorOccurred, this, [this](QAbstractSocket::SocketError code) { + // Suppress errors generated during intentional shutdown + if (_stopping.load() || !_socket) { + qCDebug(NTRIPLog) << "NTRIP suppressing socket error during intentional stop:" << int(code); + return; + } + + QString msg = _socket->errorString(); + + // If the peer closes before we receive any HTTP headers, try to give a helpful hint. + if (code == QAbstractSocket::RemoteHostClosedError && _state == NTRIPState::waiting_for_http_response) { + const QByteArray leftover = _socket->readAll(); + if (!leftover.isEmpty()) { + qCWarning(NTRIPLog) << "NTRIP peer closed; trailing bytes before close:" << leftover; + msg += QString(" (peer closed after %1 bytes)").arg(leftover.size()); + } else { + if (_port == 2102) { + msg += " (hint: port 2102 expects HTTPS/TLS; current socket is plain TCP)"; + } else if (!_mountpoint.isEmpty()) { + msg += " (peer closed before HTTP response; check mountpoint and credentials)"; + } + } + } + + qCWarning(NTRIPLog) << "NTRIP socket error code:" << int(code) + << " msg:" << msg + << " state:" << int(_socket->state()) + << " peer:" << _socket->peerAddress().toString() << ":" << _socket->peerPort() + << " local:" << _socket->localAddress().toString() << ":" << _socket->localPort(); + emit error(msg); + }, Qt::DirectConnection); + + // If the server cleanly disconnects, capture reason and emit error so NTRIPManager updates status + QObject::connect(_socket, &QTcpSocket::disconnected, this, [this]() { + // Suppress disconnect noise during intentional shutdown + if (_stopping.load() || !_socket) { + qCDebug(NTRIPLog) << "NTRIP suppressing disconnect signal during intentional stop"; + return; + } + + const QByteArray trailing = _socket->readAll(); + QString reason; + if (!trailing.isEmpty()) { + reason = QString::fromUtf8(trailing).trimmed(); + qCWarning(NTRIPLog) << "NTRIP disconnected; trailing bytes:" << trailing; + } else { + reason = "Server disconnected"; + qCWarning(NTRIPLog) << "NTRIP disconnected cleanly by server"; + } + // Emit richer context on disconnect + const qint64 t0 = this->property("ntrip_postok_t0_ms").toLongLong(); + const bool saw = this->property("ntrip_saw_rtcm").toBool(); + const qint64 dt = (t0 > 0) ? (QDateTime::currentMSecsSinceEpoch() - t0) : -1; + qCWarning(NTRIPLog) << "NTRIP disconnect context:" + << "rtcm_received_first_byte=" << (saw ? "yes" : "no") + << "ms_since_200=" << dt + << "peer=" << _socket->peerAddress().toString() << ":" << _socket->peerPort() + << "local=" << _socket->localAddress().toString() << ":" << _socket->localPort(); + emit error(reason); // Will call NTRIPManager::_tcpError() + }, Qt::DirectConnection); + + _readyReadConn = QObject::connect(_socket, &QTcpSocket::readyRead,this, &NTRIPTCPLink::_readBytes,Qt::DirectConnection); + + auto sendHttpRequest = [this]() { + if (!_mountpoint.isEmpty()) { + qCDebug(NTRIPLog) << "NTRIP Sending HTTP request"; + const QByteArray authB64 = QString(_username + ":" + _password).toUtf8().toBase64(); + QString query = + "GET /%1 HTTP/1.1\r\n" + "Host: %2\r\n" + "Ntrip-Version: Ntrip/2.0\r\n" + "User-Agent: QGC-NTRIP\r\n" + "Connection: keep-alive\r\n" + "Accept: */*\r\n" + "Authorization: Basic %3\r\n" + "\r\n"; + const QByteArray req = query.arg(_mountpoint).arg(_hostAddress).arg(QString::fromUtf8(authB64)).toUtf8(); + const qint64 written = _socket->write(req); + _socket->flush(); + + // Extra diagnostics: request line and auth presence (length only) + const QString reqStr = QString::fromUtf8(req); + const int cr = reqStr.indexOf("\r"); + const QString firstLine = (cr > 0) ? reqStr.left(cr).trimmed() : QString(); + const int authIdx = reqStr.indexOf("Authorization: Basic "); + int b64Len = -1; + if (authIdx >= 0) { + const int eol = reqStr.indexOf("\r\n", authIdx); + if (eol > authIdx) b64Len = eol - (authIdx + 21); + } + qCDebug(NTRIPLog) << "NTRIP HTTP request first line:" << firstLine; + qCDebug(NTRIPLog) << "NTRIP HTTP auth present:" << (authIdx >= 0) << "auth_b64_len:" << b64Len; + + qCDebug(NTRIPLog) << "NTRIP HTTP request bytes written:" << written; + _state = NTRIPState::waiting_for_http_response; + } + else { + _state = NTRIPState::waiting_for_rtcm_header; + emit connected(); + } + qCDebug(NTRIPLog) << "NTRIP Socket connected" + << "local" << _socket->localAddress().toString() << ":" << _socket->localPort() + << "-> peer" << _socket->peerAddress().toString() << ":" << _socket->peerPort(); + }; + + if (_useSpartn) { + QSslSocket* sslSocket = qobject_cast(_socket); + Q_ASSERT(sslSocket); + + QObject::connect(sslSocket, &QSslSocket::encrypted, this, [this, sendHttpRequest]() { + qCDebug(NTRIPLog) << "SPARTN TLS connection established"; + sendHttpRequest(); + }, Qt::DirectConnection); + + QObject::connect(sslSocket, QOverload&>::of(&QSslSocket::sslErrors), + this, [this](const QList &errors) { + for (const QSslError &e : errors) { + qCWarning(NTRIPLog) << "TLS Error:" << e.errorString(); + } + }, Qt::DirectConnection); + + sslSocket->connectToHostEncrypted(_hostAddress, static_cast(_port)); + + if (!sslSocket->waitForEncrypted(10000)) { + qCDebug(NTRIPLog) << "NTRIP TLS socket failed to establish encryption"; + emit error(_socket->errorString()); + delete _socket; + _socket = nullptr; + return; + } + + if (sslSocket->isEncrypted()) { + sendHttpRequest(); + } + } else { + QTcpSocket* tcpSocket = qobject_cast(_socket); + Q_ASSERT(tcpSocket); + + tcpSocket->connectToHost(_hostAddress, static_cast(_port)); + + if (!tcpSocket->waitForConnected(10000)) { + qCDebug(NTRIPLog) << "NTRIP Socket failed to connect"; + emit error(_socket->errorString()); + delete _socket; + _socket = nullptr; + return; + } + + sendHttpRequest(); + } +} + +void NTRIPTCPLink::_parse(const QByteArray& buffer) +{ + if (_stopping.load()) { + return; + } + + static bool logged_empty_once = false; + if (!logged_empty_once && _whitelist.empty()) { + qCDebug(NTRIPLog) << "NTRIP whitelist is empty at runtime; forwarding all RTCM messages."; + logged_empty_once = true; + } + + // RTCM v3 transport sizes + constexpr int kRtcmHeaderSize = 3; // preamble (1) + length (2) + + for (char ch : buffer) { + const uint8_t byte = static_cast(static_cast(ch)); + + if (_state == NTRIPState::waiting_for_rtcm_header) { + if (byte != RTCM3_PREAMBLE) { + continue; + } + _state = NTRIPState::accumulating_rtcm_packet; + } + + if (_rtcmParser->addByte(byte)) { + _state = NTRIPState::waiting_for_rtcm_header; + + // Build exact on-wire frame: [D3 | len(2) | payload | CRC(3)] + const int payload_len = static_cast(_rtcmParser->messageLength()); + QByteArray message(reinterpret_cast(_rtcmParser->message()), + kRtcmHeaderSize + payload_len); + + const uint8_t* crc_ptr = _rtcmParser->crcBytes(); + const int crc_len = _rtcmParser->crcSize(); + message.append(reinterpret_cast(crc_ptr), crc_len); + + const uint16_t id = _rtcmParser->messageId(); + + if (_whitelist.empty() || _whitelist.contains(id)) { + qCDebug(NTRIPLog) << "NTRIP RTCM packet id" << id << "len" << message.length(); + emit RTCMDataUpdate(message); + qCDebug(NTRIPLog) << "NTRIP Sending" << id << "of size" << message.length(); + } else { + qCDebug(NTRIPLog) << "NTRIP Ignoring" << id; + } + + _rtcmParser->reset(); + } + } +} + +void NTRIPTCPLink::_handleSpartnData(const QByteArray& dataIn) +{ + if (dataIn.isEmpty()) { + return; + } + + // Accumulate so we can remove a response header exactly once if it ever appears here. + _spartnBuf.append(dataIn); + + if (_spartnNeedHeaderStrip) { + const bool looksHttp = _spartnBuf.startsWith("HTTP/") || _spartnBuf.startsWith("ICY "); + if (looksHttp) { + const int headerEnd = _spartnBuf.indexOf("\r\n\r\n"); + if (headerEnd < 0) { + // Header incomplete; cap buffer to avoid growth on a bad stream. + if (_spartnBuf.size() > 32768) { + _spartnBuf = _spartnBuf.right(32768); + } + return; + } + // Drop the header and keep the payload. + _spartnBuf.remove(0, headerEnd + 4); + } + _spartnNeedHeaderStrip = false; + } + + if (_spartnBuf.isEmpty()) { + return; + } + + // Deliver raw SPARTN bytes to whoever is consuming them (e.g., a GNSS forwarder). + emit SPARTNDataUpdate(_spartnBuf); + + // Clear after handing off. + _spartnBuf.clear(); +} + +void NTRIPTCPLink::_readBytes() +{ + if (_stopping.load()) { + return; + } + + // SPARTN path: after HTTP 200 OK, feed raw bytes through the SPARTN handler + if (_socket && _state == NTRIPState::waiting_for_spartn_data) { + const QByteArray bytes = _socket->readAll(); + if (!bytes.isEmpty()) { + _handleSpartnData(bytes); + } + return; + } + + if (!_socket) { + return; + } + + // Static counters and flags scoped to this function (no header changes needed) + static quint64 s_totalRtcm = 0; + + if (_state == NTRIPState::waiting_for_http_response) { + QByteArray responseData = _socket->readAll(); + if (responseData.isEmpty()) { + return; + } + + QString response = QString::fromUtf8(responseData); + qCDebug(NTRIPLog) << "NTRIP HTTP response received:" << response.left(200); + + // Dump full headers once for diagnostics + const int hdrEnd = response.indexOf("\r\n\r\n"); + const QString headers = (hdrEnd >= 0) ? response.left(hdrEnd) : response; + qCDebug(NTRIPLog) << "NTRIP HTTP response headers BEGIN >>>"; + for (const QString& line : headers.split("\r\n")) { + if (!line.isEmpty()) qCDebug(NTRIPLog) << line; + } + qCDebug(NTRIPLog) << "NTRIP HTTP response headers <<< END"; + + // Parse status line + QStringList lines = response.split('\n'); + bool foundOkResponse = false; + for (const QString& line : lines) { + const QString trimmed = line.trimmed(); + if ((trimmed.startsWith("HTTP/") || trimmed.startsWith("ICY ")) && + (trimmed.contains(" 200 ") || trimmed.contains(" 201 "))) { + foundOkResponse = true; + qCDebug(NTRIPLog) << "NTRIP: Found OK response:" << trimmed; + break; + } else if ((trimmed.startsWith("HTTP/") || trimmed.startsWith("ICY ")) && + (trimmed.contains(" 4") || trimmed.contains(" 5"))) { + qCWarning(NTRIPLog) << "NTRIP: Server error response:" << trimmed; + emit error(QString("NTRIP HTTP error: %1").arg(trimmed)); + + _socket->disconnectFromHost(); + _socket->close(); + delete _socket; + _socket = nullptr; + _state = NTRIPState::uninitialised; + + if (!_stopping.load()) { + QTimer::singleShot(3000, this, [this]() { + if (!_stopping.load()) { + _hardwareConnect(); + } + }); + } + return; + } + } + + if (foundOkResponse) { + // Mark the 200 OK time and reset first-RTCM flag using QObject properties. + const qint64 nowMs = QDateTime::currentMSecsSinceEpoch(); + this->setProperty("ntrip_postok_t0_ms", nowMs); + this->setProperty("ntrip_saw_rtcm", false); + this->setProperty("ntrip_watchdog_fired", false); + + // Fire a one-shot watchdog that only logs if no RTCM arrives by ~28s. + QTimer::singleShot(28000, this, [this]() { + if (this->property("ntrip_watchdog_fired").toBool()) return; + const bool saw = this->property("ntrip_saw_rtcm").toBool(); + if (!saw) { + this->setProperty("ntrip_watchdog_fired", true); + qCWarning(NTRIPLog) + << "NTRIP no RTCM received 28s after 200 OK. Likely caster timeout." + << "Check: duplicate login, entitlement/region, GGA validity."; + } + }); + + _state = _useSpartn ? NTRIPState::waiting_for_spartn_data + : NTRIPState::waiting_for_rtcm_header; + + qCDebug(NTRIPLog) << "NTRIP: HTTP handshake complete, transitioning to data state"; + emit connected(); + + // Process any remaining data after the headers + if (hdrEnd >= 0) { + QByteArray remainingData = responseData.mid(hdrEnd + 4); + if (!remainingData.isEmpty()) { + qCDebug(NTRIPLog) << "NTRIP: Processing data after HTTP headers:" << remainingData.size() << "bytes"; + if (_useSpartn) { + _handleSpartnData(remainingData); + } else { + _parse(remainingData); + } + } + } + } else { + qCDebug(NTRIPLog) << "NTRIP: Waiting for complete HTTP response..."; + } + + return; // done with HTTP response handling + } + + // Data after handshake (RTCM path) + QByteArray bytes = _socket->readAll(); + if (!bytes.isEmpty()) { + // Mark first RTCM arrival and log timing/sample + if (!this->property("ntrip_saw_rtcm").toBool()) { + this->setProperty("ntrip_saw_rtcm", true); + const qint64 t0 = this->property("ntrip_postok_t0_ms").toLongLong(); + const qint64 dt = (t0 > 0) ? (QDateTime::currentMSecsSinceEpoch() - t0) : -1; + const QByteArray sample = bytes.left(48); + const unsigned char b0 = static_cast(sample.isEmpty() ? 0 : sample[0]); + qCDebug(NTRIPLog) << "NTRIP first RTCM bytes at" << dt << "ms after 200 OK"; + qCDebug(NTRIPLog) << "NTRIP rx sample (hex, first 48B):" << sample.toHex(' '); + qCDebug(NTRIPLog) << "NTRIP preamble_check first_byte=0x" << QByteArray::number(b0, 16); + } + + s_totalRtcm += static_cast(bytes.size()); + qCDebug(NTRIPLog) << "NTRIP rx bytes:" << bytes.size() + << "total:" << s_totalRtcm + << "bytesAvailable:" << _socket->bytesAvailable(); + + _parse(bytes); + } +} + +void NTRIPTCPLink::debugFetchSourceTable() +{ + // Build request (include Authorization so the caster can tailor the table to your creds) + const QByteArray authB64 = QString(_username + ":" + _password).toUtf8().toBase64(); + const QByteArray req = + QByteArray("GET / HTTP/1.1\r\n") + + "Host: " + _hostAddress.toUtf8() + "\r\n" + + "User-Agent: QGC-NTRIP\r\n" + + "Ntrip-Version: Ntrip/2.0\r\n" + + "Accept: */*\r\n" + + "Authorization: Basic " + authB64 + "\r\n" + + "Connection: close\r\n\r\n"; + + QByteArray all; + + if (_useSpartn || _port == 2102) { + // TLS (e.g., SPARTN/2102) + QSslSocket sock; + sock.connectToHostEncrypted(_hostAddress, static_cast(_port)); + if (!sock.waitForEncrypted(7000)) { + qCWarning(NTRIPLog) << "SOURCETABLE TLS connect failed:" << sock.errorString(); + return; + } + if (sock.write(req) <= 0 || !sock.waitForBytesWritten(3000)) { + qCWarning(NTRIPLog) << "SOURCETABLE TLS write failed:" << sock.errorString(); + return; + } + while (sock.waitForReadyRead(3000)) { + all += sock.readAll(); + if (sock.bytesAvailable() == 0 && sock.state() != QAbstractSocket::ConnectedState) break; + } + sock.close(); + } else { + // Plain TCP (e.g., RTCM/2101) + QTcpSocket sock; + sock.connectToHost(_hostAddress, static_cast(_port)); + if (!sock.waitForConnected(5000)) { + qCWarning(NTRIPLog) << "SOURCETABLE connect failed:" << sock.errorString(); + return; + } + if (sock.write(req) <= 0 || !sock.waitForBytesWritten(3000)) { + qCWarning(NTRIPLog) << "SOURCETABLE write failed:" << sock.errorString(); + return; + } + while (sock.waitForReadyRead(3000)) { + all += sock.readAll(); + if (sock.bytesAvailable() == 0 && sock.state() != QAbstractSocket::ConnectedState) break; + } + sock.close(); + } + + // Split headers/body and dump the body (the sourcetable) + const int hdrEnd = all.indexOf("\r\n\r\n"); + const QByteArray body = (hdrEnd >= 0) ? all.mid(hdrEnd + 4) : all; + + qCDebug(NTRIPLog) << "----- NTRIP SOURCETABLE BEGIN -----"; + for (const QByteArray& line : body.split('\n')) { + const QByteArray l = line.trimmed(); + if (!l.isEmpty()) qCDebug(NTRIPLog) << l; + } + qCDebug(NTRIPLog) << "------ NTRIP SOURCETABLE END ------"; +} + +void NTRIPTCPLink::sendNMEA(const QByteArray& sentence) +{ + if (_stopping.load()) { + return; + } + if (!_socket || _socket->state() != QAbstractSocket::ConnectedState) { + return; + } + + QByteArray line = sentence; + + // Validate or repair checksum in the form $CORE*XX + if (line.size() >= 5 && line.at(0) == '$') { + int star = line.lastIndexOf('*'); + if (star > 1) { + // Calculate checksum over bytes between '$' and '*' + quint8 calc = 0; + for (int i = 1; i < star; ++i) { + calc ^= static_cast(line.at(i)); + } + + // Format calculated checksum as two uppercase hex digits + QByteArray calcCks = QByteArray::number(calc, 16) + .rightJustified(2, '0') + .toUpper(); + + bool needsRepair = false; + if (star + 3 > line.size()) { + // Not enough chars after '*', definitely needs repair + needsRepair = true; + } else { + QByteArray txCks = line.mid(star + 1, 2).toUpper(); + if (txCks != calcCks) { + needsRepair = true; + } + } + + if (needsRepair) { + // Remove any existing checksum and replace with correct one + line = line.left(star + 1) + calcCks; + } + } else { + // No '*' found, append one and correct checksum + quint8 calc = 0; + for (int i = 1; i < line.size(); ++i) { + calc ^= static_cast(line.at(i)); + } + QByteArray calcCks = QByteArray::number(calc, 16) + .rightJustified(2, '0') + .toUpper(); + line.append('*').append(calcCks); + } + } + + // Ensure CRLF termination + if (!line.endsWith("\r\n")) { + line.append("\r\n"); + } + + qCDebug(NTRIPLog) << "NTRIP Sent NMEA:" << QString::fromUtf8(line.trimmed()); + + const qint64 written = _socket->write(line); + _socket->flush(); + + qCDebug(NTRIPLog) << "NTRIP Socket state:" << (_socket ? _socket->state() : -1); + qCDebug(NTRIPLog) << "NTRIP Bytes written:" << written; +} + + +void NTRIPTCPLink::requestStop() +{ + _stopping.store(true); + + if (_socket) { + QObject::disconnect(_readyReadConn); + _socket->disconnectFromHost(); + _socket->close(); + delete _socket; + _socket = nullptr; + } + + emit finished(); // let NTRIPManager's QThread quit in response +} + +NTRIPManager* NTRIPManager::instance() +{ + if (!_instance) { + // Prefer QGCApplication as parent if available, otherwise fall back to qApp + QObject* parent = qgcApp() ? static_cast(qgcApp()) : static_cast(qApp); + _instance = new NTRIPManager(parent); + } + return _instance; +} + +void NTRIPManager::startNTRIP() +{ + if (_startStopBusy) { + return; + } + _startStopBusy = true; // guard begin + + qCDebug(NTRIPLog) << "startNTRIP: begin"; + + if (_tcpLink) { + _startStopBusy = false; // already started; release guard + return; + } + + _ntripStatus = tr("Connecting..."); + emit ntripStatusChanged(); + + + NTRIPSettings* settings = SettingsManager::instance()->ntripSettings(); + QString host; + int port = 2101; + QString user; + QString pass; + QString mount; + QString whitelist; + + if (settings) { + if (settings->ntripServerHostAddress()) host = settings->ntripServerHostAddress()->rawValue().toString(); + if (settings->ntripServerPort()) port = settings->ntripServerPort()->rawValue().toInt(); + if (settings->ntripUsername()) user = settings->ntripUsername()->rawValue().toString(); + if (settings->ntripPassword()) pass = settings->ntripPassword()->rawValue().toString(); + if (settings->ntripMountpoint()) mount = settings->ntripMountpoint()->rawValue().toString(); + if (settings->ntripWhitelist()) whitelist = settings->ntripWhitelist()->rawValue().toString(); + if (settings->ntripUseSpartn()) _useSpartn = settings->ntripUseSpartn()->rawValue().toBool(); + } else { + qCWarning(NTRIPLog) << "NTRIP settings group is null; starting with defaults"; + } + + qCDebug(NTRIPLog) << "startNTRIP: host=" << host + << " port=" << port + << " mount=" << mount + << " userIsEmpty=" << user.isEmpty(); + + _tcpThread = new QThread(this); + NTRIPTCPLink* link = new NTRIPTCPLink(host, port, user, pass, mount, whitelist, _useSpartn); + _tcpLink = link; + + link->moveToThread(_tcpThread); + + connect(_tcpThread, &QThread::started, link, &NTRIPTCPLink::start); + connect(link, &NTRIPTCPLink::finished, _tcpThread, &QThread::quit); + connect(_tcpThread, &QThread::finished, _tcpThread, &QObject::deleteLater); + connect(_tcpThread, &QThread::finished, link, &QObject::deleteLater); + + // Surface errors (including server disconnect reasons) to status/UI + connect(_tcpLink, &NTRIPTCPLink::error, + this, &NTRIPManager::_tcpError, + Qt::QueuedConnection); + + connect(_tcpLink, &NTRIPTCPLink::connected, this, [this]() { + _casterStatus = CasterStatus::Connected; + emit casterStatusChanged(_casterStatus); + + const QString want = _useSpartn ? tr("Connected (SPARTN)") : tr("Connected"); + if (_ntripStatus != want) { + _ntripStatus = want; + emit ntripStatusChanged(); + } + + // >>> ONE-SHOT SOURCETABLE DUMP (enable with env var QGC_NTRIP_DUMP_TABLE=1) + if (qEnvironmentVariableIsSet("QGC_NTRIP_DUMP_TABLE")) { + static bool dumped = false; + if (!dumped && _tcpLink) { + dumped = true; + QMetaObject::invokeMethod(_tcpLink, "debugFetchSourceTable", Qt::QueuedConnection); + } + } + // <<< + + // Start rapid 1 Hz GGA until we get the first valid coord + if (_ggaTimer && !_ggaTimer->isActive()) { + _ggaTimer->setInterval(1000); // 1 Hz while waiting for fix + _sendGGA(); // try immediately + _ggaTimer->start(); + } + + }, Qt::QueuedConnection); + + if (_useSpartn) { + connect(_tcpLink, &NTRIPTCPLink::SPARTNDataUpdate, this, [this](const QByteArray& data){ + static uint32_t spartn_count = 0; + if ((spartn_count++ % 50) == 0) { + qCDebug(NTRIPLog) << "SPARTN bytes flowing:" << data.size(); + } + if (_ntripStatus != tr("Connected (SPARTN)")) { + _ntripStatus = tr("Connected (SPARTN)"); + emit ntripStatusChanged(); + } + _casterStatus = CasterStatus::Connected; + emit casterStatusChanged(_casterStatus); + + _rtcmDataReceived(data); + }, Qt::QueuedConnection); + } else { + connect(_tcpLink, &NTRIPTCPLink::RTCMDataUpdate, this, &NTRIPManager::_rtcmDataReceived, Qt::QueuedConnection); + } + + _tcpThread->start(); + qCDebug(NTRIPLog) << "NTRIP started"; + + _startStopBusy = false; // guard end +} + +void NTRIPManager::stopNTRIP() +{ + if (_startStopBusy) { + return; + } + _startStopBusy = true; // guard begin + + if (_tcpLink) { + // Prevent spurious "NTRIP error: ..." toasts during user-initiated shutdown + disconnect(_tcpLink, &NTRIPTCPLink::error, this, &NTRIPManager::_tcpError); + + // Ask the worker to stop in its own thread + QMetaObject::invokeMethod(_tcpLink, "requestStop", Qt::QueuedConnection); + + // If we still own the thread, stop and wait for it + if (_tcpThread) { + _tcpThread->quit(); // or just rely on 'finished' from requestStop() + _tcpThread->wait(); + _tcpThread = nullptr; + } + + // _tcpLink will be deleted via deleteLater() when the thread finishes + _tcpLink = nullptr; + + _ntripStatus = tr("Disconnected"); + emit ntripStatusChanged(); + qCDebug(NTRIPLog) << "NTRIP stopped"; + } + + if (_ggaTimer && _ggaTimer->isActive()) { + _ggaTimer->stop(); + } + + _startStopBusy = false; // guard end +} + +void NTRIPManager::_tcpError(const QString& errorMsg) +{ + qCWarning(NTRIPLog) << "NTRIP Server Error:" << errorMsg; + _ntripStatus = errorMsg; + emit ntripStatusChanged(); + _startStopBusy = false; // make sure guard clears on error + + // Check for specific "no location provided" disconnect reason + if (errorMsg.contains("NO_LOCATION_PROVIDED", Qt::CaseInsensitive)) { + _onCasterDisconnected(errorMsg); + } else { + _casterStatus = CasterStatus::OtherError; + emit casterStatusChanged(_casterStatus); + } + + // Show error to user via QGC notification (toast) + if (qgcApp()) { + qgcApp()->showAppMessage(tr("NTRIP error: %1").arg(errorMsg)); + } +} + +void NTRIPManager::_rtcmDataReceived(const QByteArray& data) +{ + + qCDebug(NTRIPLog) << "NTRIP Forwarding RTCM to vehicle:" << data.size() << "bytes"; + + // Lazily resolve the tool if we don't have it yet + if (!_rtcmMavlink && qgcApp()) { + _rtcmMavlink = qgcApp()->findChild(); + } + + if (_rtcmMavlink) { + // Forward raw RTCM bytes to the autopilot via MAVLink GPS_RTCM_DATA + _rtcmMavlink->RTCMDataUpdate(data); + + if (_ntripStatus != tr("Connected")) { + _ntripStatus = tr("Connected"); + emit ntripStatusChanged(); + } + } else { + // Tool not constructed yet — avoid crash, keep status, and don't spam + static uint32_t drop_warn_counter = 0; + if ((drop_warn_counter++ % 50) == 0) { + qCWarning(NTRIPLog) << "RTCMMavlink tool not ready; dropping RTCM bytes:" << data.size(); + } + } + + _startStopBusy = false; // guard clears once bytes flow (or we tried) +} + +static QByteArray _makeGGA(const QGeoCoordinate& coord, double altitude_msl) +{ + // UTC time hhmmss format + const QTime utc = QDateTime::currentDateTimeUtc().time(); + const QString hhmmss = QString("%1%2%3") + .arg(utc.hour(), 2, 10, QChar('0')) + .arg(utc.minute(), 2, 10, QChar('0')) + .arg(utc.second(), 2, 10, QChar('0')); + + // Convert decimal degrees to NMEA DMM with zero-padded minutes: + // lat: ddmm.mmmm, lon: dddmm.mmmm + auto dmm = [](double deg, bool lat) -> QString { + double a = qFabs(deg); + int d = int(a); + double m = (a - d) * 60.0; + + // Round to 4 decimals and handle 59.99995 -> 60.0000 carry into degrees + int m10000 = int(m * 10000.0 + 0.5); + double m_rounded = m10000 / 10000.0; + if (m_rounded >= 60.0) { + m_rounded -= 60.0; + d += 1; + } + + // Ensure minutes have two digits before the decimal (e.g. 08.1234) + QString mm = QString::number(m_rounded, 'f', 4); + if (m_rounded < 10.0) { + mm.prepend("0"); + } + + if (lat) { + return QString("%1%2").arg(d, 2, 10, QChar('0')).arg(mm); + } else { + return QString("%1%2").arg(d, 3, 10, QChar('0')).arg(mm); + } + }; + + const bool latNorth = coord.latitude() >= 0.0; + const bool lonEast = coord.longitude() >= 0.0; + + const QString latField = dmm(coord.latitude(), true); + const QString lonField = dmm(coord.longitude(), false); + + // IMPORTANT: include geoid separation as 0.0 and unit 'M' + // Fields: time,lat,N,lon,E,fix,nsat,hdop,alt,M,geoid,M,age,station + const QString core = QString("GPGGA,%1,%2,%3,%4,%5,1,12,1.0,%6,M,0.0,M,,") + .arg(hhmmss) + .arg(latField) + .arg(latNorth ? "N" : "S") + .arg(lonField) + .arg(lonEast ? "E" : "W") + .arg(QString::number(altitude_msl, 'f', 1)); + + // NMEA checksum over bytes between '$' and '*' + quint8 cksum = 0; + const QByteArray coreBytes = core.toUtf8(); + for (char ch : coreBytes) { + cksum ^= static_cast(ch); + } + + const QString cks = QString("%1").arg(cksum, 2, 16, QChar('0')).toUpper(); + const QByteArray sentence = QByteArray("$") + coreBytes + QByteArray("*") + cks.toUtf8(); + return sentence; +} + +void NTRIPManager::_sendGGA() +{ + if (!_tcpLink) { + return; + } + + QGeoCoordinate coord; + double alt_msl = 0.0; + bool validCoord = false; + QString srcUsed; + + if (qgcApp()) { + // PRIORITY 1: Raw GPS data from vehicle GPS facts (most important for RTK) + MultiVehicleManager* mvm = MultiVehicleManager::instance(); + if (mvm) { + if (Vehicle* veh = mvm->activeVehicle()) { + FactGroup* gps = veh->gpsFactGroup(); + if (gps) { + // Try common GPS fact names + Fact* latF = gps->getFact(QStringLiteral("lat")); + Fact* lonF = gps->getFact(QStringLiteral("lon")); + + // If "lat"/"lon" don't work, try alternative names + if (!latF) latF = gps->getFact(QStringLiteral("latitude")); + if (!lonF) lonF = gps->getFact(QStringLiteral("longitude")); + if (!latF) latF = gps->getFact(QStringLiteral("lat_deg")); + if (!lonF) lonF = gps->getFact(QStringLiteral("lon_deg")); + if (!latF) latF = gps->getFact(QStringLiteral("latitude_deg")); + if (!lonF) lonF = gps->getFact(QStringLiteral("longitude_deg")); + + if (latF && lonF) { + const double glat = latF->rawValue().toDouble(); + const double glon = lonF->rawValue().toDouble(); + + // GPS coordinates might be in 1e-7 degrees format (int32 scaled) + double lat_deg = glat; + double lon_deg = glon; + + // Check if values look like scaled integers (> 1000 suggests 1e-7 format) + if (qAbs(glat) > 1000.0 || qAbs(glon) > 1000.0) { + lat_deg = glat * 1e-7; + lon_deg = glon * 1e-7; + } + + if (qIsFinite(lat_deg) && qIsFinite(lon_deg) && + !(lat_deg == 0.0 && lon_deg == 0.0) && + qAbs(lat_deg) <= 90.0 && qAbs(lon_deg) <= 180.0) { + + coord = QGeoCoordinate(lat_deg, lon_deg); + validCoord = true; + + // Get altitude from GPS if available + Fact* altF = gps->getFact(QStringLiteral("alt")); + if (!altF) altF = gps->getFact(QStringLiteral("altitude")); + if (altF) { + double raw_alt = altF->rawValue().toDouble(); + // Altitude might be in mm or cm, convert to meters + if (qAbs(raw_alt) > 10000.0) { // > 10km suggests mm format + alt_msl = raw_alt * 1e-3; // mm to meters + } else if (qAbs(raw_alt) > 1000.0) { // > 1km suggests cm format + alt_msl = raw_alt * 1e-2; // cm to meters + } else { + alt_msl = raw_alt; // already in meters + } + if (!qIsFinite(alt_msl)) alt_msl = 0.0; + } + + srcUsed = QStringLiteral("GPS Raw"); + qCDebug(NTRIPLog) << "NTRIP: Using raw GPS data for GGA" + << "lat:" << lat_deg << "lon:" << lon_deg << "alt:" << alt_msl; + } + } + } else { + // Vehicle exists but no GPS FactGroup yet + qCDebug(NTRIPLog) << "NTRIP: Vehicle found but no GPS FactGroup available yet"; + } + } else { + // MultiVehicleManager exists but no active vehicle yet + static int no_vehicle_count = 0; + if ((no_vehicle_count++ % 10) == 0) { // Log every 10th time to avoid spam + qCDebug(NTRIPLog) << "NTRIP: MultiVehicleManager found but no active vehicle yet"; + } + } + } else { + // MultiVehicleManager not found yet - this is normal during early startup + static int no_mvm_count = 0; + if ((no_mvm_count++ % 10) == 0) { // Log every 10th time to avoid spam + qCDebug(NTRIPLog) << "NTRIP: MultiVehicleManager not available yet (startup)"; + } + } + + // PRIORITY 2: Vehicle global position estimate (EKF output) + if (!validCoord) { + MultiVehicleManager* mvm2 = MultiVehicleManager::instance(); + if (mvm2) { + if (Vehicle* veh2 = mvm2->activeVehicle()) { + coord = veh2->coordinate(); + if (coord.isValid() && !(coord.latitude() == 0.0 && coord.longitude() == 0.0)) { + validCoord = true; + double a = coord.altitude(); + alt_msl = qIsFinite(a) ? a : 0.0; + srcUsed = QStringLiteral("Vehicle EKF"); + qCDebug(NTRIPLog) << "NTRIP: Using vehicle EKF position for GGA" << coord; + } + } + } + } + + } + + if (!validCoord) { + qCDebug(NTRIPLog) << "NTRIP: No valid position available, skipping GGA."; + return; + } + + // Once we have a real valid coord, slow down GGA to 5 seconds + if (_ggaTimer && _ggaTimer->interval() != 5000) { + _ggaTimer->setInterval(5000); + qCDebug(NTRIPLog) << "NTRIP: Real position acquired, reducing GGA frequency to 5s"; + } + + const QByteArray gga = _makeGGA(coord, alt_msl); + + // Debug: Log the GGA sentence being sent (but reduce spam) + static int gga_send_count = 0; + if ((gga_send_count++ % 5) == 0) { // Log every 5th GGA to reduce spam + qCDebug(NTRIPLog) << "NTRIP: Sending GGA:" << gga; + qCDebug(NTRIPLog) << "NTRIP: GGA coord:" << coord << "alt:" << alt_msl << "source:" << srcUsed; + } + + QMetaObject::invokeMethod(_tcpLink, "sendNMEA", + Qt::QueuedConnection, + Q_ARG(QByteArray, gga)); + + // Update GGA source for UI display + if (!srcUsed.isEmpty() && srcUsed != _ggaSource) { + _ggaSource = srcUsed; + emit ggaSourceChanged(); + } +} + +void NTRIPManager::_checkSettings() +{ + if (_startStopBusy) { + qCDebug(NTRIPLog) << "NTRIP _checkSettings: busy, skipping"; + return; + } + + NTRIPSettings* settings = SettingsManager::instance()->ntripSettings(); + Fact* enableFact = nullptr; + if (settings && settings->ntripServerConnectEnabled()) { + enableFact = settings->ntripServerConnectEnabled(); + } + + // During startup, keep forcing OFF until we observe the Fact stable at false + // for 2 consecutive ticks. This defeats late restores from QML/settings. + if (_startupSuppress) { + if (enableFact) { + if (enableFact->rawValue().toBool()) { + qCDebug(NTRIPLog) << "NTRIP Startup: coercing OFF (late restore detected)"; + enableFact->setRawValue(false); + _startupStableTicks = 0; // reset stability counter on any true + } else { + _startupStableTicks++; + } + } else { + _startupStableTicks = 0; // no fact yet, not stable + } + + // Release once we have seen false for 2 ticks + if (_startupStableTicks >= 2) { + _startupSuppress = false; + if (!_forcedOffOnce) { + _forcedOffOnce = true; + if (_ntripEnableConn) { + disconnect(_ntripEnableConn); + } + } + qCDebug(NTRIPLog) << "NTRIP Startup: suppression released; honoring user setting from now on"; + } else { + return; // keep suppressing this tick + } + } + + bool shouldBeRunning = false; + if (enableFact) { + shouldBeRunning = enableFact->rawValue().toBool(); + } else { + qCWarning(NTRIPLog) << "NTRIP settings missing; defaulting to disabled"; + } + + bool isRunning = (_tcpLink != nullptr); + + qCDebug(NTRIPLog) << "NTRIP _checkSettings:" + << " isRunning=" << isRunning + << " shouldBeRunning=" << shouldBeRunning; + + if (shouldBeRunning && !isRunning) { + qCDebug(NTRIPLog) << "NTRIP _checkSettings: calling startNTRIP()"; + startNTRIP(); + } else if (!shouldBeRunning && isRunning) { + qCDebug(NTRIPLog) << "NTRIP _checkSettings: calling stopNTRIP()"; + stopNTRIP(); + } +} + +void NTRIPManager::_onCasterDisconnected(const QString& reason) +{ + qWarning() << "NTRIP: Disconnected by server:" << reason; + if (reason.contains("NO_LOCATION_PROVIDED", Qt::CaseInsensitive)) { + _casterStatus = CasterStatus::NoLocationError; + } else { + _casterStatus = CasterStatus::OtherError; + } + emit casterStatusChanged(_casterStatus); +} diff --git a/src/GPS/NTRIP.h b/src/GPS/NTRIP.h new file mode 100644 index 000000000000..60b3f747a14d --- /dev/null +++ b/src/GPS/NTRIP.h @@ -0,0 +1,190 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(NTRIPLog) + +class RTCMMavlink; +class NTRIPSettings; +class Vehicle; +class MultiVehicleManager; + +#define RTCM3_PREAMBLE 0xD3 + +class RTCMParser +{ +public: + RTCMParser(); + void reset(); + bool addByte(uint8_t byte); + uint8_t* message() { return _buffer; } + uint16_t messageLength() { return _messageLength; } + uint16_t messageId(); + const uint8_t* crcBytes() const { return _crcBytes; } + int crcSize() const { return 3; } + +private: + enum State { + WaitingForPreamble, + ReadingLength, + ReadingMessage, + ReadingCRC + }; + + State _state; + uint8_t _buffer[1024]; + uint16_t _messageLength; + uint16_t _bytesRead; + uint16_t _lengthBytesRead; + uint8_t _lengthBytes[2]; + uint16_t _crcBytesRead; + uint8_t _crcBytes[3]; +}; + +class NTRIPTCPLink : public QObject +{ + Q_OBJECT + +public: + NTRIPTCPLink(const QString& hostAddress, + int port, + const QString& username, + const QString& password, + const QString& mountpoint, + const QString& whitelist, + bool useSpartn, + QObject* parent = nullptr); + + Q_INVOKABLE void debugFetchSourceTable(); + + ~NTRIPTCPLink(); + +public slots: + void start(); + void requestStop(); + void sendNMEA(const QByteArray& sentence); + +signals: + void finished(); + void error(const QString& errorMsg); + void RTCMDataUpdate(const QByteArray& message); + // Called when SPARTN corrections are received from the NTRIPTCPLink + // These corrections are forwarded to the connected vehicle (PX4/ArduPilot) + // using the same path as RTCM corrections via _rtcmDataReceived(). + void SPARTNDataUpdate(const QByteArray& message); + void connected(); + + +private slots: + void _readBytes(); + +private: + enum class NTRIPState { + uninitialised, + waiting_for_http_response, + waiting_for_spartn_data, + waiting_for_rtcm_header, + accumulating_rtcm_packet, + }; + + void _hardwareConnect(); + void _parse(const QByteArray& buffer); + void _handleSpartnData(const QByteArray& data); + + QTcpSocket* _socket = nullptr; + + QString _hostAddress; + int _port; + QString _username; + QString _password; + QString _mountpoint; + bool _useSpartn = false; + QVector _whitelist; + + RTCMParser* _rtcmParser = nullptr; + NTRIPState _state; + + std::atomic _stopping{false}; + QMetaObject::Connection _readyReadConn; + + // Small buffer to strip a response header only once if needed + QByteArray _spartnBuf; + bool _spartnNeedHeaderStrip = true; + +}; + +class NTRIPManager : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString ntripStatus READ ntripStatus NOTIFY ntripStatusChanged) + Q_PROPERTY(CasterStatus casterStatus READ casterStatus NOTIFY casterStatusChanged) + Q_PROPERTY(QString ggaSource READ ggaSource NOTIFY ggaSourceChanged) + +public: + enum class CasterStatus { Connected, NoLocationError, OtherError }; + + NTRIPManager(QObject* parent = nullptr); + ~NTRIPManager(); + + static NTRIPManager* instance(); + + QString ntripStatus() const { return _ntripStatus; } + CasterStatus casterStatus() const { return _casterStatus; } + QString ggaSource() const { return _ggaSource; } + + void startNTRIP(); + void stopNTRIP(); + +signals: + void ntripStatusChanged(); + void casterStatusChanged(CasterStatus status); + void ggaSourceChanged(); + +public slots: + void _tcpError(const QString& errorMsg); + void _rtcmDataReceived(const QByteArray& data); + +private: + void _checkSettings(); + void _sendGGA(); + void _onCasterDisconnected(const QString& reason); + + QTimer* _ggaTimer = nullptr; + + QString _ntripStatus; + QString _ggaSource; + QMetaObject::Connection _ntripEnableConn; + + NTRIPTCPLink* _tcpLink = nullptr; + QThread* _tcpThread = nullptr; + RTCMMavlink* _rtcmMavlink = nullptr; + QTimer* _settingsCheckTimer = nullptr; + bool _startStopBusy = false; + bool _forcedOffOnce = false; + bool _useSpartn = false; + + QElapsedTimer _startupTimer; + bool _startupSuppress = true; + int _startupStableTicks = 0; + + CasterStatus _casterStatus = CasterStatus::OtherError; + + static NTRIPManager* _instance; +}; diff --git a/src/GPS/RTCMMavlink.cc b/src/GPS/RTCMMavlink.cc index bd34cfeb7e57..1205d0a849f0 100644 --- a/src/GPS/RTCMMavlink.cc +++ b/src/GPS/RTCMMavlink.cc @@ -13,7 +13,7 @@ #include "QGCLoggingCategory.h" #include "Vehicle.h" -QGC_LOGGING_CATEGORY(RTCMMavlinkLog, "qgc.gps.rtcmmavlink") +QGC_LOGGING_CATEGORY(RTCMMavlinkLog, "GPS.RTCMMavlink") RTCMMavlink::RTCMMavlink(QObject *parent) : QObject(parent) diff --git a/src/Gimbal/CMakeLists.txt b/src/Gimbal/CMakeLists.txt index d8fafa30c2cd..f694b50c5f6b 100644 --- a/src/Gimbal/CMakeLists.txt +++ b/src/Gimbal/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Gimbal Module +# Camera gimbal control and stabilization +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE Gimbal.cc @@ -6,7 +11,9 @@ target_sources(${CMAKE_PROJECT_NAME} GimbalController.h ) -# Add JSON files +# ---------------------------------------------------------------------------- +# Gimbal Configuration Resources +# ---------------------------------------------------------------------------- qt_add_resources(${CMAKE_PROJECT_NAME} json_gimbal PREFIX "/json/Vehicle" FILES GimbalFact.json diff --git a/src/Gimbal/Gimbal.cc b/src/Gimbal/Gimbal.cc index d7b7451b0f1b..a66bf8ba5147 100644 --- a/src/Gimbal/Gimbal.cc +++ b/src/Gimbal/Gimbal.cc @@ -11,12 +11,12 @@ #include "GimbalController.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(GimbalLog, "qgc.gimbal.gimbal") +QGC_LOGGING_CATEGORY(GimbalLog, "Gimbal.Gimbal") Gimbal::Gimbal(GimbalController *parent) : FactGroup(1000, QStringLiteral(":/json/Vehicle/GimbalFact.json"), parent) { - // qCDebug(GimbalLog) << Q_FUNC_INFO << this; + qCDebug(GimbalLog) << this; _initFacts(); } @@ -38,9 +38,9 @@ const Gimbal &Gimbal::operator=(const Gimbal &other) _requestInformationRetries = other._requestInformationRetries; _requestStatusRetries = other._requestStatusRetries; _requestAttitudeRetries = other._requestAttitudeRetries; - _receivedInformation = other._receivedInformation; - _receivedStatus = other._receivedStatus; - _receivedAttitude = other._receivedAttitude; + _receivedGimbalManagerInformation = other._receivedGimbalManagerInformation; + _receivedGimbalManagerStatus = other._receivedGimbalManagerStatus; + _receivedGimbalDeviceAttitudeStatus = other._receivedGimbalDeviceAttitudeStatus; _isComplete = other._isComplete; _retracted = other._retracted; _neutral = other._neutral; @@ -74,3 +74,11 @@ void Gimbal::_initFacts() _deviceIdFact.setRawValue(0); _managerCompidFact.setRawValue(0); } + +void Gimbal::setCapabilityFlags(uint32_t flags) +{ + if (_capabilityFlags != flags) { + _capabilityFlags = flags; + emit capabilityFlagsChanged(); + } +} diff --git a/src/Gimbal/Gimbal.h b/src/Gimbal/Gimbal.h index 3d2a35635278..408a0ccc84fb 100644 --- a/src/Gimbal/Gimbal.h +++ b/src/Gimbal/Gimbal.h @@ -12,6 +12,7 @@ #include #include "FactGroup.h" +#include "QGCMAVLink.h" Q_DECLARE_LOGGING_CATEGORY(GimbalLog) @@ -32,6 +33,8 @@ class Gimbal : public FactGroup Q_PROPERTY(bool retracted READ retracted NOTIFY retractedChanged) Q_PROPERTY(bool gimbalHaveControl READ gimbalHaveControl NOTIFY gimbalHaveControlChanged) Q_PROPERTY(bool gimbalOthersHaveControl READ gimbalOthersHaveControl NOTIFY gimbalOthersHaveControlChanged) + Q_PROPERTY(bool supportsRetract READ supportsRetract NOTIFY capabilityFlagsChanged) + Q_PROPERTY(bool supportsYawLock READ supportsYawLock NOTIFY capabilityFlagsChanged) friend class GimbalController; @@ -69,6 +72,10 @@ class Gimbal : public FactGroup void setGimbalHaveControl(bool set) { if (set != _haveControl) { _haveControl = set; emit gimbalHaveControlChanged(); } } void setGimbalOthersHaveControl(bool set) { if (set != _othersHaveControl) { _othersHaveControl = set; emit gimbalOthersHaveControlChanged(); } } + void setCapabilityFlags(uint32_t flags); + bool supportsRetract() const { return (_capabilityFlags & GIMBAL_MANAGER_CAP_FLAGS_HAS_RETRACT) != 0; } + bool supportsYawLock() const { return (_capabilityFlags & GIMBAL_MANAGER_CAP_FLAGS_HAS_YAW_LOCK) != 0; } + signals: void pitchRateChanged(); void yawRateChanged(); @@ -76,6 +83,7 @@ class Gimbal : public FactGroup void retractedChanged(); void gimbalHaveControlChanged(); void gimbalOthersHaveControlChanged(); + void capabilityFlagsChanged(); private: void _initFacts(); @@ -83,11 +91,12 @@ class Gimbal : public FactGroup unsigned _requestInformationRetries = 3; unsigned _requestStatusRetries = 6; unsigned _requestAttitudeRetries = 3; - bool _receivedInformation = false; - bool _receivedStatus = false; - bool _receivedAttitude = false; + bool _receivedGimbalManagerInformation = false; + bool _receivedGimbalManagerStatus = false; + bool _receivedGimbalDeviceAttitudeStatus = false; bool _isComplete = false; bool _neutral = false; + uint32_t _capabilityFlags = 0; // GIMBAL_MANAGER_CAP_FLAGS Fact _absoluteRollFact = Fact(0, QStringLiteral("gimbalRoll"), FactMetaData::valueTypeFloat); Fact _absolutePitchFact = Fact(0, QStringLiteral("gimbalPitch"), FactMetaData::valueTypeFloat); diff --git a/src/Gimbal/GimbalController.cc b/src/Gimbal/GimbalController.cc index 7cb90402158f..1405828c5f4f 100644 --- a/src/Gimbal/GimbalController.cc +++ b/src/Gimbal/GimbalController.cc @@ -16,7 +16,7 @@ #include "SettingsManager.h" #include "Vehicle.h" -QGC_LOGGING_CATEGORY(GimbalControllerLog, "qgc.gimbal.gimbalcontroller") +QGC_LOGGING_CATEGORY(GimbalControllerLog, "Gimbal.GimbalController") GimbalController::GimbalController(Vehicle *vehicle) : QObject(vehicle) @@ -87,7 +87,7 @@ void GimbalController::_handleHeartbeat(const mavlink_message_t &message) // Note that we are working over potential gimbal managers here, instead of potential gimbals. // This is because we address the gimbal manager by compid, but a gimbal device might have an // id different than the message compid it comes from. For more information see https://mavlink.io/en/services/gimbal_v2.html - if (!gimbalManager.receivedInformation && (gimbalManager.requestGimbalManagerInformationRetries > 0)) { + if (!gimbalManager.receivedGimbalManagerInformation && (gimbalManager.requestGimbalManagerInformationRetries > 0)) { _requestGimbalInformation(message.compid); --gimbalManager.requestGimbalManagerInformationRetries; } @@ -116,16 +116,17 @@ void GimbalController::_handleGimbalManagerInformation(const mavlink_message_t & Gimbal *const gimbal = gimbalIt.value(); gimbal->setManagerCompid(message.compid); gimbal->setDeviceId(information.gimbal_device_id); + gimbal->setCapabilityFlags(information.cap_flags); - if (!gimbal->_receivedInformation) { + if (!gimbal->_receivedGimbalManagerInformation) { qCDebug(GimbalControllerLog) << "gimbal manager with compId:" << message.compid << " is responsible for gimbal device:" << information.gimbal_device_id; } - gimbal->_receivedInformation = true; + gimbal->_receivedGimbalManagerInformation = true; // It is important to flag our potential gimbal manager as well, so we stop requesting gimbal_manger_information message PotentialGimbalManager &gimbalManager = _potentialGimbalManagers[message.compid]; - gimbalManager.receivedInformation = true; + gimbalManager.receivedGimbalManagerInformation = true; _checkComplete(*gimbal, pairId); } @@ -164,12 +165,12 @@ void GimbalController::_handleGimbalManagerStatus(const mavlink_message_t &messa } // Only log this message once - if (!gimbal->_receivedStatus) { + if (!gimbal->_receivedGimbalManagerStatus) { qCDebug(GimbalControllerLog) << "_handleGimbalManagerStatus: gimbal manager with compId" << message.compid << "is responsible for gimbal device" << status.gimbal_device_id; } - gimbal->_receivedStatus = true; + gimbal->_receivedGimbalManagerStatus = true; const bool haveControl = (status.primary_control_sysid == MAVLinkProtocol::instance()->getSystemId()) && @@ -261,7 +262,7 @@ void GimbalController::_handleGimbalDeviceAttitudeStatus(const mavlink_message_t gimbal->setAbsoluteYaw(absoluteYaw); } - gimbal->_receivedAttitude = true; + gimbal->_receivedGimbalDeviceAttitudeStatus = true; _checkComplete(*gimbal, pairId); } @@ -285,14 +286,14 @@ void GimbalController::_checkComplete(Gimbal &gimbal, GimbalPairId pairId) return; } - if (!gimbal._receivedInformation && gimbal._requestInformationRetries > 0) { + if (!gimbal._receivedGimbalManagerInformation && gimbal._requestInformationRetries > 0) { _requestGimbalInformation(pairId.managerCompid); --gimbal._requestInformationRetries; } // Limit to 1 second between set message interface requests static qint64 lastRequestStatusMessage = 0; qint64 now = QDateTime::currentMSecsSinceEpoch(); - if (!gimbal._receivedStatus && (gimbal._requestStatusRetries > 0) && (now - lastRequestStatusMessage > 1000)) { + if (!gimbal._receivedGimbalManagerStatus && (gimbal._requestStatusRetries > 0) && (now - lastRequestStatusMessage > 1000)) { lastRequestStatusMessage = now; _vehicle->sendMavCommand(pairId.managerCompid, MAV_CMD_SET_MESSAGE_INTERVAL, @@ -306,8 +307,8 @@ void GimbalController::_checkComplete(Gimbal &gimbal, GimbalPairId pairId) << ", retries remaining:" << gimbal._requestStatusRetries; } - if (!gimbal._receivedAttitude && (gimbal._requestAttitudeRetries > 0) && - gimbal._receivedInformation && (pairId.deviceId != 0)) { + if (!gimbal._receivedGimbalDeviceAttitudeStatus && (gimbal._requestAttitudeRetries > 0) && + gimbal._receivedGimbalManagerInformation && (pairId.deviceId != 0)) { // We request the attitude directly from the gimbal device component. // We can only do that once we have received the gimbal manager information // telling us which gimbal device it is responsible for. @@ -325,7 +326,7 @@ void GimbalController::_checkComplete(Gimbal &gimbal, GimbalPairId pairId) --gimbal._requestAttitudeRetries; } - if (!gimbal._receivedInformation || !gimbal._receivedStatus || !gimbal._receivedAttitude) { + if (!gimbal._receivedGimbalManagerInformation || !gimbal._receivedGimbalManagerStatus || !gimbal._receivedGimbalDeviceAttitudeStatus) { // Not complete yet. return; } @@ -426,7 +427,7 @@ void GimbalController::centerGimbal() qCDebug(GimbalControllerLog) << "gimbalYawStep: active gimbal is nullptr, returning"; return; } - sendPitchBodyYaw(0.0, 0.0); + sendPitchBodyYaw(0.0, 0.0, true); } void GimbalController::gimbalOnScreenControl(float panPct, float tiltPct, bool clickAndPoint, bool clickAndDrag, bool rateControl, bool retract, bool neutral, bool yawlock) @@ -540,7 +541,7 @@ void GimbalController::sendPitchAbsoluteYaw(float pitch, float yaw, bool showErr _activeGimbal->deviceId()->rawValue().toUInt()); } -void GimbalController::toggleGimbalRetracted(bool set) +void GimbalController::setGimbalRetract(bool set) { if (!_tryGetGimbalControl()) { return; @@ -596,7 +597,7 @@ void GimbalController::_rateSenderTimeout() sendRate(); } -void GimbalController::toggleGimbalYawLock(bool set) +void GimbalController::setGimbalYawLock(bool set) { if (!_tryGetGimbalControl()) { return; diff --git a/src/Gimbal/GimbalController.h b/src/Gimbal/GimbalController.h index 4a2601ed587f..e570271e7651 100644 --- a/src/Gimbal/GimbalController.h +++ b/src/Gimbal/GimbalController.h @@ -27,8 +27,9 @@ class GimbalController : public QObject QML_ELEMENT QML_UNCREATABLE("") Q_MOC_INCLUDE("QmlObjectListModel.h") - Q_PROPERTY(Gimbal *activeGimbal READ activeGimbal WRITE setActiveGimbal NOTIFY activeGimbalChanged) - Q_PROPERTY(QmlObjectListModel *gimbals READ gimbals CONSTANT) + + Q_PROPERTY(Gimbal* activeGimbal READ activeGimbal WRITE setActiveGimbal NOTIFY activeGimbalChanged) + Q_PROPERTY(QmlObjectListModel* gimbals READ gimbals CONSTANT) public: GimbalController(Vehicle *vehicle); @@ -43,8 +44,8 @@ class GimbalController : public QObject Q_INVOKABLE void gimbalOnScreenControl(float panpct, float tiltpct, bool clickAndPoint, bool clickAndDrag, bool rateControl, bool retract = false, bool neutral = false, bool yawlock = false); Q_INVOKABLE void sendPitchBodyYaw(float pitch, float yaw, bool showError = true); Q_INVOKABLE void sendPitchAbsoluteYaw(float pitch, float yaw, bool showError = true); - Q_INVOKABLE void toggleGimbalRetracted(bool set = false); - Q_INVOKABLE void toggleGimbalYawLock(bool set = false); + Q_INVOKABLE void setGimbalRetract(bool set); + Q_INVOKABLE void setGimbalYawLock(bool set); Q_INVOKABLE void acquireGimbalControl(); Q_INVOKABLE void releaseGimbalControl(); Q_INVOKABLE void sendRate(); @@ -55,7 +56,7 @@ class GimbalController : public QObject public slots: // These slots are conected with joysticks for button control - void gimbalYawLock(bool yawLock) { toggleGimbalYawLock(yawLock); } + void gimbalYawLock(bool yawLock) { setGimbalYawLock(yawLock); } Q_INVOKABLE void centerGimbal(); void gimbalPitchStart(int direction); void gimbalYawStart(int direction); @@ -110,7 +111,7 @@ private slots: struct PotentialGimbalManager { unsigned requestGimbalManagerInformationRetries = 6; - bool receivedInformation = false; + bool receivedGimbalManagerInformation = false; }; QMap _potentialGimbalManagers; // key is compid diff --git a/src/Joystick/CMakeLists.txt b/src/Joystick/CMakeLists.txt index 160778aa2891..f6e754d849b5 100644 --- a/src/Joystick/CMakeLists.txt +++ b/src/Joystick/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Joystick Module +# Game controller and joystick input support +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE Joystick.cc @@ -8,6 +13,9 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# Android Joystick Implementation +# ---------------------------------------------------------------------------- if(ANDROID) target_sources(${CMAKE_PROJECT_NAME} PRIVATE @@ -17,6 +25,9 @@ if(ANDROID) return() endif() +# ---------------------------------------------------------------------------- +# SDL Joystick Implementation (Desktop) +# ---------------------------------------------------------------------------- target_sources(${CMAKE_PROJECT_NAME} PRIVATE JoystickSDL.cc @@ -29,7 +40,9 @@ if(SDL_GAMECONTROLLERCONFIG) target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE SDL_GAMECONTROLLERCONFIG=\"${SDL_GAMECONTROLLERCONFIG}\") endif() -#===========================================================================# +# ============================================================================ +# SDL Game Controller Database +# ============================================================================ CPMAddPackage( NAME sdl_gamecontrollerdb @@ -48,8 +61,11 @@ qt_add_resources(${CMAKE_PROJECT_NAME} "qgcresources_sdl" "${SDL_GAMECONTROLLERDB_PATH}" ) -#===========================================================================# +# ============================================================================ +# SDL3 Library Integration +# ============================================================================ +# Platform-specific SDL options set(SDL_EXTRA_OPTIONS "") if(WIN32) set(SDL_EXTRA_OPTIONS @@ -64,6 +80,8 @@ if(UNIX) "SDL_UNIX_CONSOLE_BUILD ON" ) endif() + +# Video only needed on macOS if(MACOS) set(SDL_VIDEO_FLAG "ON") else() @@ -72,9 +90,9 @@ endif() CPMAddPackage( NAME SDL3 - VERSION 3.2.16 + VERSION 3.2.20 GITHUB_REPOSITORY libsdl-org/SDL - GIT_TAG release-3.2.16 + GIT_TAG release-3.2.20 OPTIONS "SDL_INSTALL OFF" "SDL_UNINSTALL OFF" diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 07b92d368bed..cf5c39594301 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -24,8 +24,8 @@ #include #include -QGC_LOGGING_CATEGORY(JoystickLog, "qgc.joystick.joystick") -QGC_LOGGING_CATEGORY(JoystickValuesLog, "qgc.joystick.joystickvalues") +QGC_LOGGING_CATEGORY(JoystickLog, "Joystick.joystick") +QGC_LOGGING_CATEGORY(JoystickValuesLog, "Joystick.joystickvalues") /*===========================================================================*/ @@ -61,7 +61,7 @@ Joystick::Joystick(const QString &name, int axisCount, int buttonCount, int hatC , _mavlinkActionManager(new MavlinkActionManager(SettingsManager::instance()->mavlinkActionsSettings()->joystickActionsFile(), this)) , _assignableButtonActions(new QmlObjectListModel(this)) { - // qCDebug(JoystickLog) << Q_FUNC_INFO << this; + qCDebug(JoystickLog) << Q_FUNC_INFO << this << name << "axisCount:buttonCount:hatCount" << axisCount << buttonCount << hatCount; for (int i = 0; i < _axisCount; i++) { _rgAxisValues[i] = 0; @@ -212,10 +212,13 @@ void Joystick::_loadSettings() _circleCorrection = settings.value(_circleCorrectionSettingsKey, false).toBool(); _negativeThrust = settings.value(_negativeThrustSettingsKey, false).toBool(); _throttleMode = static_cast(settings.value(_throttleModeSettingsKey, ThrottleModeDownZero).toInt(&convertOk)); + _enableManualControlExtensions = settings.value(_manualControlExtensionsEnabledKey, false).toBool(); badSettings |= !convertOk; - qCDebug(JoystickLog) << Q_FUNC_INFO << "calibrated:txmode:throttlemode:exponential:deadband:badsettings" << _calibrated << _transmitterMode << _throttleMode << _exponential << _deadband << badSettings; + qCDebug(JoystickLog) << _name; + qCDebug(JoystickLog) << " calibrated:txmode:throttlemode:exponential:deadband:badsettings"; + qCDebug(JoystickLog) << " " << _calibrated << _transmitterMode << _throttleMode << _exponential << _deadband << badSettings; const QString minTpl("Axis%1Min"); const QString maxTpl("Axis%1Max"); @@ -223,6 +226,7 @@ void Joystick::_loadSettings() const QString revTpl("Axis%1Rev"); const QString deadbndTpl("Axis%1Deadbnd"); + qCDebug(JoystickLog) << " axis:min:max:trim:reversed:deadband:badsettings"; for (int axis = 0; axis < _axisCount; axis++) { Calibration_t *const calibration = &_rgCalibration[axis]; @@ -240,11 +244,13 @@ void Joystick::_loadSettings() calibration->reversed = settings.value(revTpl.arg(axis), false).toBool(); - qCDebug(JoystickLog) << Q_FUNC_INFO << "axis:min:max:trim:reversed:deadband:badsettings" << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << calibration->deadband << badSettings; + qCDebug(JoystickLog) << " " << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << calibration->deadband << badSettings; } int workingAxis = 0; - for (int function = 0; function < maxFunction; function++) { + qCDebug(JoystickLog) << "Function Axis mappings"; + qCDebug(JoystickLog) << " function:axis:badsettings"; + for (int function = 0; function < maxAxisFunction; function++) { int functionAxis = settings.value(_rgFunctionSettingsKey[function], -1).toInt(&convertOk); badSettings |= (!convertOk || (functionAxis >= _axisCount)); @@ -256,7 +262,7 @@ void Joystick::_loadSettings() _rgFunctionAxis[function] = functionAxis; } - qCDebug(JoystickLog) << Q_FUNC_INFO << "function:axis:badsettings" << function << functionAxis << badSettings; + qCDebug(JoystickLog) << " " << axisFunctionToString(static_cast(function)) << functionAxis << badSettings; } badSettings |= (workingAxis < 4); @@ -324,8 +330,14 @@ void Joystick::_saveSettings() settings.setValue(_throttleModeSettingsKey, _throttleMode); settings.setValue(_negativeThrustSettingsKey, _negativeThrust); settings.setValue(_circleCorrectionSettingsKey, _circleCorrection); + settings.setValue(_manualControlExtensionsEnabledKey, _enableManualControlExtensions); - qCDebug(JoystickLog) << Q_FUNC_INFO << "calibrated:throttlemode:deadband:txmode" << _calibrated << _throttleMode << _deadband << _circleCorrection << _transmitterMode; + qCDebug(JoystickLog) << _name; + qCDebug(JoystickLog) << " calibrated:exponential:accumulator:circlecorrection:negativeThrust:throttlemode:deadband:axisfrequency:buttonfrequency:txmode"; + qCDebug(JoystickLog) << " " << _calibrated << _exponential << _accumulator + << _circleCorrection << _negativeThrust << _throttleMode + << _deadband << _axisFrequencyHz << _buttonFrequencyHz + << _transmitterMode; const QString minTpl("Axis%1Min"); const QString maxTpl("Axis%1Max"); @@ -333,6 +345,7 @@ void Joystick::_saveSettings() const QString revTpl("Axis%1Rev"); const QString deadbndTpl("Axis%1Deadbnd"); + qCDebug(JoystickLog) << " axis:min:max:trim:reversed:deadband"; for (int axis = 0; axis < _axisCount; axis++) { Calibration_t* calibration = &_rgCalibration[axis]; settings.setValue(trimTpl.arg(axis), calibration->center); @@ -340,24 +353,22 @@ void Joystick::_saveSettings() settings.setValue(maxTpl.arg(axis), calibration->max); settings.setValue(revTpl.arg(axis), calibration->reversed); settings.setValue(deadbndTpl.arg(axis), calibration->deadband); - qCDebug(JoystickLog) << Q_FUNC_INFO - << "name:axis:min:max:trim:reversed:deadband" - << _name - << axis - << calibration->min - << calibration->max - << calibration->center - << calibration->reversed - << calibration->deadband; + qCDebug(JoystickLog) << " " << axis + << calibration->min + << calibration->max + << calibration->center + << calibration->reversed + << calibration->deadband; } // Write mode 2 mappings without changing mapping currently in use - int temp[maxFunction]; + int temp[maxAxisFunction]; _remapAxes(_transmitterMode, 2, temp); - for (int function = 0; function < maxFunction; function++) { + qCDebug(JoystickLog) << "TX Mode mappings"; + for (int function = 0; function < maxAxisFunction; function++) { settings.setValue(_rgFunctionSettingsKey[function], temp[function]); - qCDebug(JoystickLog) << Q_FUNC_INFO << "name:function:axis" << _name << function << _rgFunctionSettingsKey[function]; + qCDebug(JoystickLog) << " " << axisFunctionToString(static_cast(function)) << _rgFunctionSettingsKey[function]; } _saveButtonSettings(); @@ -379,15 +390,15 @@ int Joystick::_mapFunctionMode(int mode, int function) return -1; } -void Joystick::_remapAxes(int currentMode, int newMode, int (&newMapping)[maxFunction]) +void Joystick::_remapAxes(int currentMode, int newMode, int (&newMapping)[maxAxisFunction]) { - int temp[maxFunction]; + int temp[maxAxisFunction]; - for (int function = 0; function < maxFunction; function++) { + for (int function = 0; function < maxAxisFunction; function++) { temp[_mapFunctionMode(newMode, function)] = _rgFunctionAxis[_mapFunctionMode(currentMode, function)]; } - for (int function = 0; function < maxFunction; function++) { + for (int function = 0; function < maxAxisFunction; function++) { newMapping[function] = temp[function]; } } @@ -608,14 +619,14 @@ void Joystick::_handleAxis() axis = _rgFunctionAxis[throttleFunction]; float throttle = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], (_throttleMode == ThrottleModeDownZero) ? false :_deadband); - float gimbalPitch = 0.0f; - if (_axisCount > 4) { + float gimbalPitch = NAN; + if (_enableManualControlExtensions && _axisCount > 4) { axis = _rgFunctionAxis[gimbalPitchFunction]; gimbalPitch = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis],_deadband); } - float gimbalYaw = 0.0f; - if (_axisCount > 5) { + float gimbalYaw = NAN; + if (_enableManualControlExtensions && _axisCount > 5) { axis = _rgFunctionAxis[gimbalYawFunction]; gimbalYaw = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis],_deadband); } @@ -674,7 +685,7 @@ void Joystick::_handleAxis() const uint16_t lowButtons = static_cast(buttonPressedBits & 0xFFFF); const uint16_t highButtons = static_cast((buttonPressedBits >> 16) & 0xFFFF); - _activeVehicle->sendJoystickDataThreadSafe(roll, pitch, yaw, throttle, lowButtons, highButtons); + _activeVehicle->sendJoystickDataThreadSafe(roll, pitch, yaw, throttle, lowButtons, highButtons, gimbalPitch, gimbalYaw); } void Joystick::startPolling(Vehicle* vehicle) @@ -691,7 +702,7 @@ void Joystick::startPolling(Vehicle* vehicle) (void) disconnect(this, &Joystick::gimbalPitchStop, _activeVehicle->gimbalController(), &GimbalController::gimbalPitchStop); (void) disconnect(this, &Joystick::gimbalYawStop, _activeVehicle->gimbalController(), &GimbalController::gimbalYawStop); (void) disconnect(this, &Joystick::emergencyStop, _activeVehicle, &Vehicle::emergencyStop); - (void) disconnect(this, &Joystick::gripperAction, _activeVehicle, &Vehicle::setGripperAction); + (void) disconnect(this, &Joystick::gripperAction, _activeVehicle, &Vehicle::sendGripperAction); (void) disconnect(this, &Joystick::landingGearDeploy, _activeVehicle, &Vehicle::landingGearDeploy); (void) disconnect(this, &Joystick::landingGearRetract, _activeVehicle, &Vehicle::landingGearRetract); (void) disconnect(this, &Joystick::motorInterlock, _activeVehicle, &Vehicle::motorInterlock); @@ -722,7 +733,7 @@ void Joystick::startPolling(Vehicle* vehicle) (void) connect(this, &Joystick::gimbalPitchStop, _activeVehicle->gimbalController(), &GimbalController::gimbalPitchStop); (void) connect(this, &Joystick::gimbalYawStop, _activeVehicle->gimbalController(), &GimbalController::gimbalYawStop); (void) connect(this, &Joystick::emergencyStop, _activeVehicle, &Vehicle::emergencyStop); - (void) connect(this, &Joystick::gripperAction, _activeVehicle, &Vehicle::setGripperAction); + (void) connect(this, &Joystick::gripperAction, _activeVehicle, &Vehicle::sendGripperAction); (void) connect(this, &Joystick::landingGearDeploy, _activeVehicle, &Vehicle::landingGearDeploy); (void) connect(this, &Joystick::landingGearRetract, _activeVehicle, &Vehicle::landingGearRetract); (void) connect(this, &Joystick::motorInterlock, _activeVehicle, &Vehicle::motorInterlock); @@ -753,7 +764,7 @@ void Joystick::stopPolling() (void) disconnect(this, &Joystick::gimbalYawStart, _activeVehicle->gimbalController(), &GimbalController::gimbalYawStart); (void) disconnect(this, &Joystick::gimbalPitchStop, _activeVehicle->gimbalController(), &GimbalController::gimbalPitchStop); (void) disconnect(this, &Joystick::gimbalYawStop, _activeVehicle->gimbalController(), &GimbalController::gimbalYawStop); - (void) disconnect(this, &Joystick::gripperAction, _activeVehicle, &Vehicle::setGripperAction); + (void) disconnect(this, &Joystick::gripperAction, _activeVehicle, &Vehicle::sendGripperAction); (void) disconnect(this, &Joystick::landingGearDeploy, _activeVehicle, &Vehicle::landingGearDeploy); (void) disconnect(this, &Joystick::landingGearRetract, _activeVehicle, &Vehicle::landingGearRetract); (void) disconnect(this, &Joystick::motorInterlock, _activeVehicle, &Vehicle::motorInterlock); @@ -799,7 +810,7 @@ void Joystick::setFunctionAxis(AxisFunction_t function, int axis) int Joystick::getFunctionAxis(AxisFunction_t function) const { - if ((static_cast(function) < 0) || (function >= maxFunction)) { + if ((static_cast(function) < 0) || (function >= maxAxisFunction)) { qCWarning(JoystickLog) << "Invalid function" << function; } @@ -836,7 +847,7 @@ void Joystick::setButtonAction(int button, const QString& action) return; } - qCWarning(JoystickLog) << Q_FUNC_INFO << button << action; + qCDebug(JoystickLog) << "button:action" << button << action; QSettings settings; settings.beginGroup(_settingsGroup); @@ -1094,13 +1105,17 @@ void Joystick::_executeButtonAction(const QString &action, bool buttonDown) if (buttonDown) { emit emergencyStop(); } - } else if (action == _buttonActionGripperGrab) { + } else if (action == _buttonActionGripperClose) { + if (buttonDown) { + emit gripperAction(QGCMAVLink::GripperActionClose); + } + } else if (action == _buttonActionGripperOpen) { if (buttonDown) { - emit gripperAction(GRIPPER_ACTION_GRAB); + emit gripperAction(QGCMAVLink::GripperActionOpen); } - } else if (action == _buttonActionGripperRelease) { + } else if (action == _buttonActionGripperStop) { if (buttonDown) { - emit gripperAction(GRIPPER_ACTION_RELEASE); + emit gripperAction(QGCMAVLink::GripperActionStop); } } else if (action == _buttonActionLandingGearDeploy) { if (buttonDown) { @@ -1204,8 +1219,9 @@ void Joystick::_buildActionList(Vehicle *activeVehicle) _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGimbalYawLock)); _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGimbalYawFollow)); _assignableButtonActions->append(new AssignableButtonAction(_buttonActionEmergencyStop)); - _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGripperGrab)); - _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGripperRelease)); + _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGripperClose)); + _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGripperOpen)); + _assignableButtonActions->append(new AssignableButtonAction(_buttonActionGripperStop)); _assignableButtonActions->append(new AssignableButtonAction(_buttonActionLandingGearDeploy)); _assignableButtonActions->append(new AssignableButtonAction(_buttonActionLandingGearRetract)); #ifndef QGC_NO_ARDUPILOT_DIALECT @@ -1230,3 +1246,32 @@ void Joystick::_buildActionList(Vehicle *activeVehicle) emit assignableActionsChanged(); } + +QString Joystick::axisFunctionToString(AxisFunction_t function) +{ + switch (function) { + case rollFunction: + return QStringLiteral("Roll"); + case pitchFunction: + return QStringLiteral("Pitch"); + case yawFunction: + return QStringLiteral("Yaw"); + case throttleFunction: + return QStringLiteral("Throttle"); + case gimbalPitchFunction: + return QStringLiteral("Gimbal Pitch"); + case gimbalYawFunction: + return QStringLiteral("Gimbal Yaw"); + default: + return QStringLiteral("Unknown"); + } +} + +void Joystick::setEnableManualControlExtensions(bool enable) +{ + if (_enableManualControlExtensions != enable) { + _enableManualControlExtensions = enable; + _saveSettings(); + emit enableManualControlExtensionsChanged(); + } +} diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index 95db3b2ea04a..3c359cee3f2a 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -9,7 +9,7 @@ #pragma once -#include "MAVLinkLib.h" +#include "QGCMAVLink.h" #include #include @@ -82,6 +82,7 @@ class Joystick : public QThread Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QStringList assignableActionTitles READ assignableActionTitles NOTIFY assignableActionsChanged) Q_PROPERTY(QStringList buttonActions READ buttonActions NOTIFY buttonActionsChanged) + Q_PROPERTY(bool enableManualControlExtensions READ enableManualControlExtensions WRITE setEnableManualControlExtensions NOTIFY enableManualControlExtensionsChanged) enum ButtonEvent_t { BUTTON_UP, @@ -108,8 +109,9 @@ class Joystick : public QThread throttleFunction, gimbalPitchFunction, gimbalYawFunction, - maxFunction + maxAxisFunction }; + static QString axisFunctionToString(AxisFunction_t function); enum ThrottleMode_t { ThrottleModeCenterZero, @@ -183,6 +185,9 @@ class Joystick : public QThread /// Set joystick button repeat rate (in Hz) void setButtonFrequency(float val); + bool enableManualControlExtensions() const { return _enableManualControlExtensions; } + void setEnableManualControlExtensions(bool enable); + signals: // The raw signals are only meant for use by calibration void rawAxisValueChanged(int index, int value); @@ -196,6 +201,7 @@ class Joystick : public QThread void accumulatorChanged(bool accumulator); void enabledChanged(bool enabled); void circleCorrectionChanged(bool circleCorrection); + void enableManualControlExtensionsChanged(); void axisValues(float roll, float pitch, float yaw, float throttle); void axisFrequencyHzChanged(); void buttonFrequencyHzChanged(); @@ -218,7 +224,7 @@ class Joystick : public QThread void setVtolInFwdFlight(bool set); void setFlightMode(const QString &flightMode); void emergencyStop(); - void gripperAction(GRIPPER_ACTIONS gripperAction); + void gripperAction(QGCMAVLink::GripperActions gripperAction); void landingGearDeploy(); void landingGearRetract(); void motorInterlock(bool enable); @@ -254,6 +260,7 @@ private slots: /// Adjust the raw axis value to the -1:1 range given calibration information float _adjustRange(int value, const Calibration_t &calibration, bool withDeadbands); + void _executeButtonAction(const QString &action, bool buttonDown); int _findAssignableButtonAction(const QString &action); bool _validAxis(int axis) const; @@ -268,7 +275,7 @@ private slots: int _mapFunctionMode(int mode, int function); /// Remap current axis functions from current TX mode to new TX mode - void _remapAxes(int currentMode, int newMode, int (&newMapping)[maxFunction]); + void _remapAxes(int currentMode, int newMode, int (&newMapping)[maxAxisFunction]); int _hatButtonCount = 0; int _totalButtonCount = 0; @@ -285,10 +292,11 @@ private slots: bool _deadband = false; bool _negativeThrust = false; bool _pollingStartedForCalibration = false; + bool _enableManualControlExtensions = false; float _axisFrequencyHz = _defaultAxisFrequencyHz; float _buttonFrequencyHz = _defaultButtonFrequencyHz; float _exponential = 0; - int _rgFunctionAxis[maxFunction] = {}; + int _rgFunctionAxis[maxAxisFunction] = {}; QElapsedTimer _axisTime; QList _buttonActionArray; QStringList _availableActionTitles; @@ -307,7 +315,7 @@ private slots: static constexpr float _minButtonFrequencyHz = 0.25f; static constexpr float _maxButtonFrequencyHz = 50.0f; - static constexpr const char *_rgFunctionSettingsKey[maxFunction] = { + static constexpr const char *_rgFunctionSettingsKey[maxAxisFunction] = { "RollAxis", "PitchAxis", "YawAxis", @@ -333,6 +341,7 @@ private slots: static constexpr const char *_roverTXModeSettingsKey = "TXMode_Rover"; static constexpr const char *_vtolTXModeSettingsKey = "TXMode_VTOL"; static constexpr const char *_submarineTXModeSettingsKey = "TXMode_Submarine"; + static constexpr const char *_manualControlExtensionsEnabledKey = "ManualControlExtensionsEnabled"; static constexpr const char *_buttonActionNone = QT_TR_NOOP("No Action"); static constexpr const char *_buttonActionArm = QT_TR_NOOP("Arm"); @@ -360,8 +369,9 @@ private slots: static constexpr const char *_buttonActionGimbalYawLock = QT_TR_NOOP("Gimbal Yaw Lock"); static constexpr const char *_buttonActionGimbalYawFollow = QT_TR_NOOP("Gimbal Yaw Follow"); static constexpr const char *_buttonActionEmergencyStop = QT_TR_NOOP("Emergency Stop"); - static constexpr const char *_buttonActionGripperGrab = QT_TR_NOOP("Gripper Close"); - static constexpr const char *_buttonActionGripperRelease = QT_TR_NOOP("Gripper Open"); + static constexpr const char *_buttonActionGripperClose = QT_TR_NOOP("Gripper Close"); + static constexpr const char *_buttonActionGripperOpen = QT_TR_NOOP("Gripper Open"); + static constexpr const char *_buttonActionGripperStop = QT_TR_NOOP("Gripper Stop"); static constexpr const char *_buttonActionLandingGearDeploy= QT_TR_NOOP("Landing gear deploy"); static constexpr const char *_buttonActionLandingGearRetract= QT_TR_NOOP("Landing gear retract"); static constexpr const char *_buttonActionMotorInterlockEnable= QT_TR_NOOP("Motor Interlock enable"); diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index aaa58b882447..0f7c128f65f1 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -15,7 +15,7 @@ #include #include -QGC_LOGGING_CATEGORY(JoystickAndroidLog, "qgc.joystick.joystickandroid") +QGC_LOGGING_CATEGORY(JoystickAndroidLog, "Joystick.joystickandroid") QList JoystickAndroid::_androidBtnList(_androidBtnListCount); int JoystickAndroid::ACTION_DOWN = 0; diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 8a3cfae4a4ff..2bac13c32b53 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -20,7 +20,7 @@ #include #include -QGC_LOGGING_CATEGORY(JoystickManagerLog, "qgc.joystick.joystickmanager") +QGC_LOGGING_CATEGORY(JoystickManagerLog, "Joystick.joystickmanager") Q_APPLICATION_STATIC(JoystickManager, _joystickManager); @@ -29,7 +29,7 @@ JoystickManager::JoystickManager(QObject *parent) { qCDebug(JoystickManagerLog) << this; - _joystickCheckTimer.setInterval(kTimerInterval); + _joystickCheckTimer.setInterval(1000); _joystickCheckTimer.setSingleShot(false); (void) connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); } diff --git a/src/Joystick/JoystickManager.h b/src/Joystick/JoystickManager.h index 640d2c20f36b..e1cf4e78929f 100644 --- a/src/Joystick/JoystickManager.h +++ b/src/Joystick/JoystickManager.h @@ -67,7 +67,6 @@ private slots: int _joystickCheckTimerCounter = 0; QTimer _joystickCheckTimer; - static constexpr int kTimerInterval = 1000; static constexpr int kTimeout = 1000; static constexpr const char *_settingsGroup = "JoystickManager"; static constexpr const char *_settingsKeyActiveJoystick = "ActiveJoystick"; diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index 1bc39e7946fe..7b742f411012 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -16,10 +16,12 @@ #include -QGC_LOGGING_CATEGORY(JoystickSDLLog, "qgc.joystick.joysticksdl") +QGC_LOGGING_CATEGORY(JoystickSDLLog, "Joystick.joysticksdl") -JoystickSDL::JoystickSDL(const QString &name, int axisCount, int buttonCount, int hatCount, int instanceId, bool isGamepad, QObject *parent) - : Joystick(name, axisCount, buttonCount, hatCount, parent) +JoystickSDL::JoystickSDL(const QString &name, QList gamepadAxes, QList nonGamepadAxes, int buttonCount, int hatCount, int instanceId, bool isGamepad, QObject *parent) + : Joystick(name, gamepadAxes.length() + nonGamepadAxes.length(), buttonCount, hatCount, parent) + , _gamepadAxes(gamepadAxes) + , _nonGamepadAxes(nonGamepadAxes) , _isGamepad(isGamepad) , _instanceId(instanceId) { @@ -75,13 +77,57 @@ QMap JoystickSDL::discover() continue; } + QList gamepadAxes; + QSet joyAxesMappedToGamepad; + + if (SDL_IsGamepad(jid)) { + auto tmpGamepad = SDL_OpenGamepad(jid); + if (!tmpGamepad) { + qCWarning(JoystickSDLLog) << "Failed to open gamepad" << jid << SDL_GetError(); + continue; + } + + // Determine if this gamepad axis is one we should show to the user + for (int i = 0; i < SDL_GAMEPAD_AXIS_COUNT; i++) { + if (SDL_GamepadHasAxis(tmpGamepad, static_cast(i))) { + gamepadAxes.append(i); + } + } + + // If a sdlJoystick axis is mapped to a sdlGamepad axis, then the axis is represented + // by both the sdlJoystick interface and the sdlGamepad interface. If this is the case, + // We'll only show the sdlGamepad interface version of the axis to the user. + int count = 0; + SDL_GamepadBinding **bindings = SDL_GetGamepadBindings(tmpGamepad, &count); + if (bindings) { + for (int i = 0; i < count; ++i) { + SDL_GamepadBinding *binding = bindings[i]; + if (binding && binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) { + joyAxesMappedToGamepad.insert(binding->input.axis.axis); + } + } + SDL_free(bindings); + } else { + qCWarning(JoystickSDLLog) << "Failed to get bindings for" << name << "error:" << SDL_GetError(); + } + + SDL_CloseGamepad(tmpGamepad); + } + SDL_Joystick *tmpJoy = SDL_OpenJoystick(jid); if (!tmpJoy) { qCWarning(JoystickSDLLog) << "Failed to open joystick" << jid << SDL_GetError(); continue; } + QList nonGamepadAxes; const int axisCount = SDL_GetNumJoystickAxes(tmpJoy); + for (int i = 0; i < axisCount; i++) { + if (!joyAxesMappedToGamepad.contains(i)) { + nonGamepadAxes.append(i); + } + } + const int buttonCount = SDL_GetNumJoystickButtons(tmpJoy); const int hatCount = SDL_GetNumJoystickHats(tmpJoy); SDL_CloseJoystick(tmpJoy); @@ -95,7 +141,8 @@ QMap JoystickSDL::discover() } current[name] = new JoystickSDL(name, - qMax(0, axisCount), + gamepadAxes, + nonGamepadAxes, qMax(0, buttonCount), qMax(0, hatCount), jid, @@ -162,15 +209,17 @@ bool JoystickSDL::_update() bool JoystickSDL::_getButton(int idx) const { - int button = -1; + // First try the standardized gamepad set if idx is inside that set + if (_sdlGamepad && (idx >= 0) && (idx < SDL_GAMEPAD_BUTTON_COUNT)) { + return SDL_GetGamepadButton(_sdlGamepad, static_cast(idx)); + } - if (_isGamepad) { - button = SDL_GetGamepadButton(_sdlGamepad, static_cast(idx)); - } else { - button = SDL_GetJoystickButton(_sdlJoystick, idx); + // Fall back to raw joystick buttons (covers unmapped/extras) + if (_sdlJoystick && (idx >= 0) && (idx < SDL_GetNumJoystickButtons(_sdlJoystick))) { + return SDL_GetJoystickButton(_sdlJoystick, idx); } - return (button == 1); + return false; } int JoystickSDL::_getAxis(int idx) const @@ -178,7 +227,12 @@ int JoystickSDL::_getAxis(int idx) const int axis = -1; if (_isGamepad) { - axis = SDL_GetGamepadAxis(_sdlGamepad, static_cast(idx)); + if (idx < _gamepadAxes.length()) { + axis = SDL_GetGamepadAxis(_sdlGamepad, static_cast(_gamepadAxes[idx])); + } + else { + axis = SDL_GetJoystickAxis(_sdlJoystick, _nonGamepadAxes[idx - _gamepadAxes.length()]); + } } else { axis = SDL_GetJoystickAxis(_sdlJoystick, idx); } diff --git a/src/Joystick/JoystickSDL.h b/src/Joystick/JoystickSDL.h index 6a2215deef6c..526e0280c57a 100644 --- a/src/Joystick/JoystickSDL.h +++ b/src/Joystick/JoystickSDL.h @@ -24,7 +24,7 @@ Q_DECLARE_LOGGING_CATEGORY(JoystickSDLLog) class JoystickSDL : public Joystick { public: - explicit JoystickSDL(const QString &name, int axisCount, int buttonCount, int hatCount, int instanceId, bool isGamepad, QObject *parent = nullptr); + explicit JoystickSDL(const QString &name, QList gamepadAxes, QList nonGamepadAxes, int buttonCount, int hatCount, int instanceId, bool isGamepad, QObject *parent = nullptr); ~JoystickSDL() override; int instanceId() const { return _instanceId; } @@ -46,6 +46,8 @@ class JoystickSDL : public Joystick static void _loadGamepadMappings(); + QList _gamepadAxes; + QList _nonGamepadAxes; bool _isGamepad = false; int _instanceId = -1; diff --git a/src/MAVLink/CMakeLists.txt b/src/MAVLink/CMakeLists.txt index 15e37b0411fc..c31b7b55196d 100644 --- a/src/MAVLink/CMakeLists.txt +++ b/src/MAVLink/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# MAVLink Module +# MAVLink protocol implementation and communication handling +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE ImageProtocolManager.cc @@ -19,37 +24,31 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -#===========================================================================# - -message(STATUS "Building MAVLink") +# ============================================================================ +# MAVLink Protocol Library Integration +# ============================================================================ -# TODO: -# CPMAddPackage( -# NAME mavlink -# GITHUB_REPOSITORY ${QGC_MAVLINK_GIT_REPO} -# GIT_TAG ${QGC_MAVLINK_GIT_TAG} -# OPTIONS -# "MAVLINK_DIALECT all" -# "MAVLINK_VERSION 2.0" -# ) +message(STATUS "QGC: Building MAVLink") CPMAddPackage( NAME mavlink GIT_REPOSITORY ${QGC_MAVLINK_GIT_REPO} GIT_TAG ${QGC_MAVLINK_GIT_TAG} + OPTIONS + "MAVLINK_DIALECT ${QGC_MAVLINK_DIALECT}" + "MAVLINK_VERSION ${QGC_MAVLINK_VERSION}" ) -# For QGC all dialects means common and development. Though use of development mavlink code should be restricted to debug builds only. -target_include_directories(${CMAKE_PROJECT_NAME} - PRIVATE - ${mavlink_SOURCE_DIR} - ${mavlink_SOURCE_DIR}/all - ${mavlink_SOURCE_DIR}/common - ${mavlink_SOURCE_DIR}/development -) - -# if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") -# target_compile_options(comm PRIVATE -Wno-address-of-packed-member) -# endif() - +if(mavlink_ADDED) + add_dependencies(${CMAKE_PROJECT_NAME} mavlink) + target_include_directories(${CMAKE_PROJECT_NAME} + PRIVATE + ${mavlink_BINARY_DIR}/include/mavlink + ${mavlink_BINARY_DIR}/include/mavlink/${QGC_MAVLINK_DIALECT} + ) +endif() + +# ---------------------------------------------------------------------------- +# LibEvents - MAVLink event handling +# ---------------------------------------------------------------------------- add_subdirectory(LibEvents) diff --git a/src/MAVLink/ImageProtocolManager.cc b/src/MAVLink/ImageProtocolManager.cc index 08f700139c16..ae835913a014 100644 --- a/src/MAVLink/ImageProtocolManager.cc +++ b/src/MAVLink/ImageProtocolManager.cc @@ -10,7 +10,7 @@ #include "ImageProtocolManager.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(ImageProtocolManagerLog, "qgc.mavlink.imageprotocolmanager") +QGC_LOGGING_CATEGORY(ImageProtocolManagerLog, "MAVLink.ImageProtocolManager") ImageProtocolManager::ImageProtocolManager(QObject *parent) : QObject(parent) diff --git a/src/MAVLink/LibEvents/CMakeLists.txt b/src/MAVLink/LibEvents/CMakeLists.txt index 35554453bc85..96d8dfefae26 100644 --- a/src/MAVLink/LibEvents/CMakeLists.txt +++ b/src/MAVLink/LibEvents/CMakeLists.txt @@ -1,22 +1,29 @@ -target_sources(${CMAKE_PROJECT_NAME} - PRIVATE - EventHandler.cc - EventHandler.h - HealthAndArmingCheckReport.cc - HealthAndArmingCheckReport.h - logging.cpp -) - -target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -#===========================================================================# - -CPMAddPackage( - NAME libevents - GITHUB_REPOSITORY mavlink/libevents - GIT_TAG main - SOURCE_SUBDIR libs/cpp -) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE libevents) -target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${libevents_SOURCE_DIR}/libs/cpp) +# ============================================================================ +# LibEvents - MAVLink Event Handling +# Event logging, health reporting, and arming checks +# ============================================================================ + +target_sources(${CMAKE_PROJECT_NAME} + PRIVATE + EventHandler.cc + EventHandler.h + HealthAndArmingCheckReport.cc + HealthAndArmingCheckReport.h + logging.cpp +) + +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +# ============================================================================ +# LibEvents Library Integration +# ============================================================================ + +CPMAddPackage( + NAME libevents + GITHUB_REPOSITORY mavlink/libevents + GIT_TAG main + SOURCE_SUBDIR libs/cpp +) + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE libevents) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${libevents_SOURCE_DIR}/libs/cpp) diff --git a/src/MAVLink/LibEvents/HealthAndArmingCheckReport.h b/src/MAVLink/LibEvents/HealthAndArmingCheckReport.h index 0620777c93b9..193e893a9fdf 100644 --- a/src/MAVLink/LibEvents/HealthAndArmingCheckReport.h +++ b/src/MAVLink/LibEvents/HealthAndArmingCheckReport.h @@ -92,4 +92,3 @@ class HealthAndArmingCheckReport : public QObject QmlObjectListModel* _problemsForCurrentMode = nullptr; ///< list of HealthAndArmingCheckProblem* }; - diff --git a/src/MAVLink/LibEvents/logging.cpp b/src/MAVLink/LibEvents/logging.cpp index 5e074fde7dfc..885548fd8a41 100644 --- a/src/MAVLink/LibEvents/logging.cpp +++ b/src/MAVLink/LibEvents/logging.cpp @@ -11,7 +11,7 @@ #include "libevents_includes.h" -QGC_LOGGING_CATEGORY(EventsLog, "EventsLog"); +QGC_LOGGING_CATEGORY(EventsLog, "API.Events") void qgc_events_parser_debug_printf(const char *fmt, ...) { char msg[256]; @@ -23,4 +23,4 @@ void qgc_events_parser_debug_printf(const char *fmt, ...) { int len = strlen(msg); if (len > 0) msg[len-1] = '\0'; // remove newline qCDebug(EventsLog) << msg; -} \ No newline at end of file +} diff --git a/src/MAVLink/MAVLinkFTP.cc b/src/MAVLink/MAVLinkFTP.cc index 306da0a64d0d..ba56a8a5f92a 100644 --- a/src/MAVLink/MAVLinkFTP.cc +++ b/src/MAVLink/MAVLinkFTP.cc @@ -82,4 +82,3 @@ QString MavlinkFTP::errorCodeToString(ErrorCode_t errorCode) return "Unknown Error"; } - diff --git a/src/MAVLink/MAVLinkStreamConfig.cc b/src/MAVLink/MAVLinkStreamConfig.cc index 79a64fa8f62a..f585afa7d71b 100644 --- a/src/MAVLink/MAVLinkStreamConfig.cc +++ b/src/MAVLink/MAVLinkStreamConfig.cc @@ -11,7 +11,7 @@ #include "MAVLinkLib.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(MAVLinkStreamConfigLog, "qgc.mavlink.mavlinkstreamconfig") +QGC_LOGGING_CATEGORY(MAVLinkStreamConfigLog, "MAVLink.MAVLinkStreamConfig") MAVLinkStreamConfig::MAVLinkStreamConfig(const SetMessageIntervalCb &messageIntervalCb) : _messageIntervalCb(messageIntervalCb) diff --git a/src/MAVLink/QGCMAVLink.cc b/src/MAVLink/QGCMAVLink.cc index 1863cd7b55a6..df9e91d075d7 100644 --- a/src/MAVLink/QGCMAVLink.cc +++ b/src/MAVLink/QGCMAVLink.cc @@ -10,9 +10,11 @@ #include "QGCMAVLink.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(QGCMAVLinkLog, "qgc.mavlink.qgcmavlink") +QGC_LOGGING_CATEGORY(QGCMAVLinkLog, "MAVLink.QGCMAVLink") const QHash QGCMAVLink::mavlinkCompIdHash { + { MAV_COMP_ID_AUTOPILOT1, "Autopilot" }, + { MAV_COMP_ID_ONBOARD_COMPUTER, "Onboard Computer" }, { MAV_COMP_ID_CAMERA, "Camera1" }, { MAV_COMP_ID_CAMERA2, "Camera2" }, { MAV_COMP_ID_CAMERA3, "Camera3" }, @@ -76,7 +78,7 @@ QGCMAVLink::QGCMAVLink(QObject *parent) (void) qRegisterMetaType("mavlink_message_t"); (void) qRegisterMetaType("MAV_TYPE"); (void) qRegisterMetaType("MAV_AUTOPILOT"); - (void) qRegisterMetaType("GRIPPER_ACTIONS"); + (void) qRegisterMetaType("QGCMAVLink::GripperActions"); } QGCMAVLink::~QGCMAVLink() @@ -134,6 +136,18 @@ QString QGCMAVLink::firmwareClassToString(FirmwareClass_t firmwareClass) } } +MAV_AUTOPILOT QGCMAVLink::firmwareTypeFromString(const QString &firmwareTypeStr) +{ + const QString type = firmwareTypeStr.trimmed(); + if (type == QLatin1String("ArduPilot")) { + return MAV_AUTOPILOT_ARDUPILOTMEGA; + } + if (type == QLatin1String("PX4 Pro")) { + return MAV_AUTOPILOT_PX4; + } + return MAV_AUTOPILOT_GENERIC; +} + bool QGCMAVLink::isAirship(MAV_TYPE mavType) { return vehicleClass(mavType) == VehicleClassAirship; @@ -149,6 +163,11 @@ bool QGCMAVLink::isRoverBoat(MAV_TYPE mavType) return vehicleClass(mavType) == VehicleClassRoverBoat; } +bool QGCMAVLink::isSpacecraft(MAV_TYPE mavType) +{ + return vehicleClass(mavType) == VehicleClassSpacecraft; +} + bool QGCMAVLink::isSub(MAV_TYPE mavType) { return vehicleClass(mavType) == VehicleClassSub; @@ -172,6 +191,8 @@ QGCMAVLink::VehicleClass_t QGCMAVLink::vehicleClass(MAV_TYPE mavType) return VehicleClassRoverBoat; case MAV_TYPE_SUBMARINE: return VehicleClassSub; + case MAV_TYPE_SPACECRAFT_ORBITER: + return VehicleClassSpacecraft; case MAV_TYPE_QUADROTOR: case MAV_TYPE_COAXIAL: case MAV_TYPE_HELICOPTER: @@ -207,6 +228,8 @@ QString QGCMAVLink::vehicleClassToUserVisibleString(VehicleClass_t vehicleClass) return QT_TRANSLATE_NOOP("Vehicle Class", "Rover-Boat"); case VehicleClassSub: return QT_TRANSLATE_NOOP("Vehicle Class", "Sub"); + case VehicleClassSpacecraft: + return QT_TRANSLATE_NOOP("Vehicle Class", "Spacecraft"); case VehicleClassMultiRotor: return QT_TRANSLATE_NOOP("Vehicle Class", "Multi-Rotor"); case VehicleClassVTOL: @@ -218,6 +241,24 @@ QString QGCMAVLink::vehicleClassToUserVisibleString(VehicleClass_t vehicleClass) } } +MAV_TYPE QGCMAVLink::vehicleTypeFromString(const QString &vehicleStr) +{ + const QString type = vehicleStr.trimmed(); + if (type == QLatin1String("Multi-Rotor")) { + return MAV_TYPE_QUADROTOR; + } + if (type == QLatin1String("Fixed Wing")) { + return MAV_TYPE_FIXED_WING; + } + if (type == QLatin1String("Rover")) { + return MAV_TYPE_GROUND_ROVER; + } + if (type == QLatin1String("Sub")) { + return MAV_TYPE_SUBMARINE; + } + return MAV_TYPE_GENERIC; +} + QString QGCMAVLink::vehicleClassToInternalString(VehicleClass_t vehicleClass) { switch (vehicleClass) { @@ -229,6 +270,8 @@ QString QGCMAVLink::vehicleClassToInternalString(VehicleClass_t vehicleClass) return QStringLiteral("RoverBoat"); case VehicleClassSub: return QStringLiteral("Sub"); + case VehicleClassSpacecraft: + return QStringLiteral("Spacecraft"); case VehicleClassMultiRotor: return QStringLiteral("MultiRotor"); case VehicleClassVTOL: @@ -240,7 +283,7 @@ QString QGCMAVLink::vehicleClassToInternalString(VehicleClass_t vehicleClass) } } -QString QGCMAVLink::mavResultToString(MAV_RESULT result) +QString QGCMAVLink::mavResultToString(uint8_t result) { switch (result) { case MAV_RESULT_ACCEPTED: @@ -328,6 +371,7 @@ QString QGCMAVLink::mavTypeToString(MAV_TYPE mavType) { { MAV_TYPE_GROUND_ROVER, tr("Ground rover")}, { MAV_TYPE_SURFACE_BOAT, tr("Surface vessel, boat, ship")}, { MAV_TYPE_SUBMARINE, tr("Submarine")}, + { MAV_TYPE_SPACECRAFT_ORBITER, tr("Spacecraft, orbiter")}, { MAV_TYPE_HEXAROTOR, tr("Hexarotor")}, { MAV_TYPE_OCTOROTOR, tr("Octorotor")}, { MAV_TYPE_TRICOPTER, tr("trirotor")}, @@ -365,6 +409,24 @@ QString QGCMAVLink::firmwareVersionTypeToString(FIRMWARE_VERSION_TYPE firmwareVe } } +FIRMWARE_VERSION_TYPE QGCMAVLink::firmwareVersionTypeFromString(const QString &typeStr) +{ + const QString type = typeStr.trimmed(); + if (type == QLatin1String("dev")) { + return FIRMWARE_VERSION_TYPE_DEV; + } + if (type == QLatin1String("alpha")) { + return FIRMWARE_VERSION_TYPE_ALPHA; + } + if (type == QLatin1String("beta")) { + return FIRMWARE_VERSION_TYPE_BETA; + } + if (type == QLatin1String("rc")) { + return FIRMWARE_VERSION_TYPE_RC; + } + return FIRMWARE_VERSION_TYPE_OFFICIAL; +} + int QGCMAVLink::motorCount(MAV_TYPE mavType, uint8_t frameType) { switch (mavType) { @@ -419,6 +481,8 @@ int QGCMAVLink::motorCount(MAV_TYPE mavType, uint8_t frameType) return -1; } } + case MAV_TYPE_SPACECRAFT_ORBITER: + return 8; default: return -1; @@ -453,3 +517,16 @@ uint32_t QGCMAVLink::highLatencyFailuresToMavSysStatus(mavlink_high_latency2_t& return onboardControlSensorsEnabled; } + +QString QGCMAVLink::compIdToString(uint8_t compId) +{ + QString compIdStr; + + if (mavlinkCompIdHash.contains(compId)) { + compIdStr = mavlinkCompIdHash.value(compId); + } else { + compIdStr = QStringLiteral("Unknown"); + } + + return QStringLiteral("%1 (%2)").arg(compIdStr).arg(static_cast(compId)); +} diff --git a/src/MAVLink/QGCMAVLink.h b/src/MAVLink/QGCMAVLink.h index d12ccf3727ea..58d39f7da10b 100644 --- a/src/MAVLink/QGCMAVLink.h +++ b/src/MAVLink/QGCMAVLink.h @@ -44,6 +44,7 @@ class QGCMAVLink : public QObject static constexpr const VehicleClass_t VehicleClassFixedWing = MAV_TYPE_FIXED_WING; static constexpr const VehicleClass_t VehicleClassRoverBoat = MAV_TYPE_GROUND_ROVER; static constexpr const VehicleClass_t VehicleClassSub = MAV_TYPE_SUBMARINE; + static constexpr const VehicleClass_t VehicleClassSpacecraft = MAV_TYPE_SPACECRAFT_ORBITER; static constexpr const VehicleClass_t VehicleClassMultiRotor = MAV_TYPE_QUADROTOR; static constexpr const VehicleClass_t VehicleClassVTOL = MAV_TYPE_VTOL_TAILSITTER_QUADROTOR; static constexpr const VehicleClass_t VehicleClassGeneric = MAV_TYPE_GENERIC; @@ -56,26 +57,32 @@ class QGCMAVLink : public QObject static FirmwareClass_t firmwareClass (MAV_AUTOPILOT autopilot); static MAV_AUTOPILOT firmwareClassToAutopilot (FirmwareClass_t firmwareClass) { return static_cast(firmwareClass); } static QString firmwareClassToString (FirmwareClass_t firmwareClass); - static QList allFirmwareClasses (void); + static MAV_AUTOPILOT firmwareTypeFromString (const QString &firmwareTypeStr); + static QList allFirmwareClasses (); static bool isAirship (MAV_TYPE mavType); static bool isFixedWing (MAV_TYPE mavType); static bool isRoverBoat (MAV_TYPE mavType); static bool isSub (MAV_TYPE mavType); + static bool isSpacecraft (MAV_TYPE mavType); static bool isMultiRotor (MAV_TYPE mavType); static bool isVTOL (MAV_TYPE mavType); static VehicleClass_t vehicleClass (MAV_TYPE mavType); static MAV_TYPE vehicleClassToMavType (VehicleClass_t vehicleClass) { return static_cast(vehicleClass); } static QString vehicleClassToUserVisibleString(VehicleClass_t vehicleClass); static QString vehicleClassToInternalString(VehicleClass_t vehicleClass); + static MAV_TYPE vehicleTypeFromString(const QString &vehicleStr); static QList allVehicleClasses (void); - static QString mavResultToString (MAV_RESULT result); + static QString mavResultToString (uint8_t result); + static QString mavResultToString (MAV_RESULT result) { return mavResultToString(static_cast(result)); } static QString mavSysStatusSensorToString (MAV_SYS_STATUS_SENSOR sysStatusSensor); static QString mavTypeToString (MAV_TYPE mavType); static QString firmwareVersionTypeToString (FIRMWARE_VERSION_TYPE firmwareVersionType); + static FIRMWARE_VERSION_TYPE firmwareVersionTypeFromString(const QString &typeStr); static int motorCount (MAV_TYPE mavType, uint8_t frameType = 0); static uint32_t highLatencyFailuresToMavSysStatus(mavlink_high_latency2_t& highLatency2); + static QString compIdToString (uint8_t compId); // Expose mavlink enums to Qml. I've tried various way to make this work without duping, but haven't found anything that works. @@ -132,12 +139,13 @@ class QGCMAVLink : public QObject }; Q_ENUM(MavlinkSysStatus) - enum GRIPPER_OPTIONS { - Gripper_release = GRIPPER_ACTION_RELEASE, - Gripper_grab = GRIPPER_ACTION_GRAB, - Invalid_option = GRIPPER_ACTIONS_ENUM_END, + enum GripperActions { + GripperActionOpen = GRIPPER_ACTION_OPEN, + GripperActionClose = GRIPPER_ACTION_CLOSE, + GripperActionStop = GRIPPER_ACTION_STOP, + GripperOptionInvalid = GRIPPER_ACTIONS_ENUM_END, }; - Q_ENUM(GRIPPER_OPTIONS) + Q_ENUM(GripperActions) enum CalibrationType { CalibrationNone, @@ -182,4 +190,4 @@ class QGCMAVLink : public QObject static const QHash mavlinkCompIdHash; }; -Q_DECLARE_METATYPE(GRIPPER_ACTIONS) +Q_DECLARE_METATYPE(QGCMAVLink::GripperActions) diff --git a/src/MAVLink/StatusTextHandler.cc b/src/MAVLink/StatusTextHandler.cc index 1ba99dbe638e..8044c38e4d0d 100644 --- a/src/MAVLink/StatusTextHandler.cc +++ b/src/MAVLink/StatusTextHandler.cc @@ -13,7 +13,7 @@ #include #include -QGC_LOGGING_CATEGORY(StatusTextHandlerLog, "qgc.mavlink.statustexthandler") +QGC_LOGGING_CATEGORY(StatusTextHandlerLog, "MAVLink.StatusTextHandler") StatusText::StatusText(MAV_COMPONENT componentid, MAV_SEVERITY severity, const QString &text) : m_compId(componentid) @@ -57,17 +57,17 @@ StatusTextHandler::~StatusTextHandler() QString StatusTextHandler::getMessageText(const mavlink_message_t &message) { - QByteArray b; + // Warning: There is a bug in mavlink which causes mavlink_msg_statustext_get_text to work incorrect. + // It ends up copying crap off the end of the buffer, so don't use it for now. - b.resize(MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN + 1); - (void) mavlink_msg_statustext_get_text(&message, b.data()); + mavlink_statustext_t statusText; + mavlink_msg_statustext_decode(&message, &statusText); - // Ensure NUL-termination - b[b.length()-1] = '\0'; + char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN + 1]; + memcpy(buffer, statusText.text, MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN); + buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = '\0'; - const QString text = QString::fromLocal8Bit(b.constData(), std::strlen(b.constData())); - - return text; + return QString(buffer); } QString StatusTextHandler::formattedMessages() const diff --git a/src/MAVLink/SysStatusSensorInfo.cc b/src/MAVLink/SysStatusSensorInfo.cc index 2b7499c47d06..45153058c1ef 100644 --- a/src/MAVLink/SysStatusSensorInfo.cc +++ b/src/MAVLink/SysStatusSensorInfo.cc @@ -11,7 +11,7 @@ #include "QGCLoggingCategory.h" #include "QGCMAVLink.h" -QGC_LOGGING_CATEGORY(SysStatusSensorInfoLog, "qgc.mavlink.sysstatussensorinfo") +QGC_LOGGING_CATEGORY(SysStatusSensorInfoLog, "MAVLink.SysStatusSensorInfo") SysStatusSensorInfo::SysStatusSensorInfo(QObject *parent) : QObject(parent) diff --git a/src/MissionManager/BlankPlanCreator.h b/src/MissionManager/BlankPlanCreator.h index 7d1701d710b0..9b5d0c8cce7f 100644 --- a/src/MissionManager/BlankPlanCreator.h +++ b/src/MissionManager/BlankPlanCreator.h @@ -14,7 +14,7 @@ class BlankPlanCreator : public PlanCreator { Q_OBJECT - + public: BlankPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); diff --git a/src/MissionManager/CMakeLists.txt b/src/MissionManager/CMakeLists.txt index 8c90c9955c34..3a86a95c481b 100644 --- a/src/MissionManager/CMakeLists.txt +++ b/src/MissionManager/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Mission Manager Module +# Handles mission planning, geofences, rally points, and survey patterns +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE BlankPlanCreator.cc @@ -75,7 +80,9 @@ target_sources(${CMAKE_PROJECT_NAME} VTOLLandingComplexItem.h ) -# Add JSON files +# ---------------------------------------------------------------------------- +# Mission Definition JSON Resources +# ---------------------------------------------------------------------------- file(GLOB_RECURSE JSON_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.json) qt_add_resources(${CMAKE_PROJECT_NAME} json_mission_manager PREFIX "/json" diff --git a/src/MissionManager/CameraSection.cc b/src/MissionManager/CameraSection.cc index 02323924120b..af147eb6d7e0 100644 --- a/src/MissionManager/CameraSection.cc +++ b/src/MissionManager/CameraSection.cc @@ -15,7 +15,7 @@ #include "Vehicle.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(CameraSectionLog, "CameraSectionLog") +QGC_LOGGING_CATEGORY(CameraSectionLog, "Plan.CameraSection") QMap CameraSection::_metaDataMap; diff --git a/src/MissionManager/CameraSection.h b/src/MissionManager/CameraSection.h index 4188144ae46f..827d816684b8 100644 --- a/src/MissionManager/CameraSection.h +++ b/src/MissionManager/CameraSection.h @@ -63,11 +63,9 @@ class CameraSection : public Section void setSpecifyGimbal (bool specifyGimbal); void setSpecifyCameraMode (bool specifyCameraMode); - ///< Signals specifiedGimbalYawChanged ///< @return The gimbal yaw specified by this item, NaN if not specified double specifiedGimbalYaw(void) const; - ///< Signals specifiedGimbalPitchChanged ///< @return The gimbal pitch specified by this item, NaN if not specified double specifiedGimbalPitch(void) const; diff --git a/src/MissionManager/ComplexMissionItem.cc b/src/MissionManager/ComplexMissionItem.cc index aa6011135e0c..8a34e163e96e 100644 --- a/src/MissionManager/ComplexMissionItem.cc +++ b/src/MissionManager/ComplexMissionItem.cc @@ -144,4 +144,3 @@ void ComplexMissionItem::_segmentTerrainCollisionChanged(bool terrainCollision) } emit terrainCollisionChanged(_cTerrainCollisionSegments != 0); } - diff --git a/src/MissionManager/CorridorScanComplexItem.cc b/src/MissionManager/CorridorScanComplexItem.cc index 44267774fbf6..55f0eb2e4a18 100644 --- a/src/MissionManager/CorridorScanComplexItem.cc +++ b/src/MissionManager/CorridorScanComplexItem.cc @@ -17,13 +17,13 @@ #include -QGC_LOGGING_CATEGORY(CorridorScanComplexItemLog, "CorridorScanComplexItemLog") +QGC_LOGGING_CATEGORY(CorridorScanComplexItemLog, "Plan.CorridorScanComplexItemL") const QString CorridorScanComplexItem::name(CorridorScanComplexItem::tr("Corridor Scan")); CorridorScanComplexItem::CorridorScanComplexItem(PlanMasterController* masterController, bool flyView, const QString& kmlOrShpFile) : TransectStyleComplexItem (masterController, flyView, settingsGroup) - , _entryPoint (0) + , _entryPointLocation (EntryPointDefaultOrder) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CorridorScan.SettingsGroup.json"), this)) , _corridorWidthFact (settingsGroup, _metaDataMap[corridorWidthName]) { @@ -76,7 +76,7 @@ void CorridorScanComplexItem::_saveCommon(QJsonObject& saveObject) saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; saveObject[corridorWidthName] = _corridorWidthFact.rawValue().toDouble(); - saveObject[_jsonEntryPointKey] = _entryPoint; + saveObject[_jsonEntryPointKey] = static_cast(_entryPointLocation); _corridorPolyline.saveToJson(saveObject); } @@ -140,7 +140,7 @@ bool CorridorScanComplexItem::_loadWorker(const QJsonObject& complexObject, int _corridorWidthFact.setRawValue(complexObject[corridorWidthName].toDouble()); - _entryPoint = complexObject[_jsonEntryPointKey].toInt(); + _entryPointLocation = static_cast(complexObject[_jsonEntryPointKey].toInt()); _ignoreRecalc = false; @@ -178,11 +178,21 @@ void CorridorScanComplexItem::_polylineDirtyChanged(bool dirty) void CorridorScanComplexItem::rotateEntryPoint(void) { - _entryPoint++; - if (_entryPoint > 3) { - _entryPoint = 0; + int modeAsInt = static_cast(_entryPointLocation); + + if (_calcTransectCount() < 2) { + // A single transect has no "opposite side of center" so we need to bump by 2 to get to the opposite end of the scan + modeAsInt += 2; + } else { + modeAsInt++; } + if (modeAsInt > EntryPointStartOppositeEndOppositeSide) { + modeAsInt = 0; + } + + _entryPointLocation = static_cast(modeAsInt); + _rebuildTransects(); } @@ -292,20 +302,20 @@ void CorridorScanComplexItem::_rebuildTransectsPhase1(void) bool reverseTransects = false; bool reverseVertices = false; - switch (_entryPoint) { - case 0: + switch (_entryPointLocation) { + case EntryPointDefaultOrder: reverseTransects = false; reverseVertices = false; break; - case 1: + case EntryPointStartSameEndOppositeSide: reverseTransects = true; reverseVertices = false; break; - case 2: + case EntryPointStartOppositeEndSameSide: reverseTransects = false; reverseVertices = true; break; - case 3: + case EntryPointStartOppositeEndOppositeSide: reverseTransects = true; reverseVertices = true; break; @@ -384,11 +394,11 @@ double CorridorScanComplexItem::timeBetweenShots(void) double CorridorScanComplexItem::_calcTransectSpacing(void) const { double transectSpacing = _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); - if (transectSpacing < 0.5) { + if (transectSpacing < _minimumTransectSpacingMeters) { // We can't let spacing get too small otherwise we will end up with too many transects. - // So we limit to 0.5 meter spacing as min and set to huge value which will cause a single - // transect to be added. - transectSpacing = 100000; + // So we limit the spacing to be above a small increment and below that value we set to huge spacing + // which will cause a single transect to be added instead of having things blow up. + transectSpacing = _forceLargeTransectSpacingMeters; } return transectSpacing; diff --git a/src/MissionManager/CorridorScanComplexItem.h b/src/MissionManager/CorridorScanComplexItem.h index a0f883d4c397..4f13ddf38349 100644 --- a/src/MissionManager/CorridorScanComplexItem.h +++ b/src/MissionManager/CorridorScanComplexItem.h @@ -29,6 +29,16 @@ class CorridorScanComplexItem : public TransectStyleComplexItem Q_PROPERTY(QGCMapPolyline* corridorPolyline READ corridorPolyline CONSTANT) Q_PROPERTY(Fact* corridorWidth READ corridorWidth CONSTANT) + // Note1: These values are persisted to plan files so they cannot be changed with breaking plan file back compat + // Note2: rotateEntryPoint expects these values in this order + enum EntryPointLocation { + EntryPointDefaultOrder = 0, // Standard transect generation order + EntryPointStartSameEndOppositeSide = 1, // Start at same end, opposite side of center + EntryPointStartOppositeEndSameSide = 2, // Start at opposite end, same side + EntryPointStartOppositeEndOppositeSide = 3, // Start at opposite end, opposite side + }; + Q_ENUM(EntryPointLocation) + Fact* corridorWidth (void) { return &_corridorWidthFact; } QGCMapPolyline* corridorPolyline(void) { return &_corridorPolyline; } @@ -79,7 +89,7 @@ private slots: QGCMapPolyline _corridorPolyline; QList> _transectSegments; ///< Internal transect segments including grid exit, turnaround and internal camera points - int _entryPoint; + EntryPointLocation _entryPointLocation; QMap _metaDataMap; SettingsFact _corridorWidthFact; diff --git a/src/MissionManager/CorridorScanPlanCreator.h b/src/MissionManager/CorridorScanPlanCreator.h index 8b80ec213d36..93bbf8d44a05 100644 --- a/src/MissionManager/CorridorScanPlanCreator.h +++ b/src/MissionManager/CorridorScanPlanCreator.h @@ -14,7 +14,7 @@ class CorridorScanPlanCreator : public PlanCreator { Q_OBJECT - + public: CorridorScanPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index 462c8be2b507..bfc9cfa9e59f 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -17,7 +17,7 @@ #include -QGC_LOGGING_CATEGORY(FixedWingLandingComplexItemLog, "FixedWingLandingComplexItemLog") +QGC_LOGGING_CATEGORY(FixedWingLandingComplexItemLog, "Plan.FixedWingLandingComplexItem") const QString FixedWingLandingComplexItem::name(FixedWingLandingComplexItem::tr("Fixed Wing Landing")); @@ -135,16 +135,6 @@ void FixedWingLandingComplexItem::_calcGlideSlope(void) _glideSlopeFact.setRawValue(qRadiansToDegrees(qAtan(landingAltDifference / landingDistance))); } -void FixedWingLandingComplexItem::moveLandingPosition(const QGeoCoordinate& coordinate) -{ - double savedHeading = landingHeading()->rawValue().toDouble(); - double savedDistance = landingDistance()->rawValue().toDouble(); - - setLandingCoordinate(coordinate); - landingHeading()->setRawValue(savedHeading); - landingDistance()->setRawValue(savedDistance); -} - bool FixedWingLandingComplexItem::_isValidLandItem(const MissionItem& missionItem) { if (missionItem.command() != MAV_CMD_NAV_LAND || diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index 043f5067ab24..5de926d83b19 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -31,8 +31,6 @@ class FixedWingLandingComplexItem : public LandingComplexItem Q_PROPERTY(Fact* valueSetIsDistance READ valueSetIsDistance CONSTANT) Q_PROPERTY(Fact* glideSlope READ glideSlope CONSTANT) - Q_INVOKABLE void moveLandingPosition(const QGeoCoordinate& coordinate); // Maintains the current landing distance and heading - Fact* glideSlope (void) { return &_glideSlopeFact; } Fact* valueSetIsDistance (void) { return &_valueSetIsDistanceFact; } diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index 9bbcee4fda24..72390503daa7 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -26,7 +26,7 @@ #include #include -QGC_LOGGING_CATEGORY(GeoFenceControllerLog, "GeoFenceControllerLog") +QGC_LOGGING_CATEGORY(GeoFenceControllerLog, "PlanManager.GeoFenceController") QMap GeoFenceController::_metaDataMap; @@ -105,17 +105,11 @@ void GeoFenceController::_managerVehicleChanged(Vehicle* managerVehicle) connect(_geoFenceManager, &GeoFenceManager::removeAllComplete, this, &GeoFenceController::_managerRemoveAllComplete); connect(_geoFenceManager, &GeoFenceManager::inProgressChanged, this, &GeoFenceController::syncInProgressChanged); - //-- GeoFenceController::supported() tests both the capability bit AND the protocol version. (void) connect(_managerVehicle, &Vehicle::capabilityBitsChanged, this, [this](uint64_t capabilityBits) { Q_UNUSED(capabilityBits); emit supportedChanged(supported()); }); - (void) connect(_managerVehicle, &Vehicle::requestProtocolVersion, this, [this](unsigned version) { - Q_UNUSED(version); - emit supportedChanged(supported()); - }); - connect(_managerVehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &GeoFenceController::_parametersReady); _parametersReady(); @@ -225,7 +219,7 @@ void GeoFenceController::save(QJsonObject& json) } void GeoFenceController::removeAll(void) -{ +{ setBreachReturnPoint(QGeoCoordinate()); _polygons.clearAndDeleteContents(); _circles.clearAndDeleteContents(); @@ -489,7 +483,7 @@ void GeoFenceController::clearAllInteractive(void) bool GeoFenceController::supported(void) const { - return (_managerVehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_FENCE) && (_managerVehicle->maxProtoVersion() >= 200); + return _managerVehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_FENCE; } /* Returns the radius of the "paramCircularFence" diff --git a/src/MissionManager/GeoFenceManager.cc b/src/MissionManager/GeoFenceManager.cc index 8a46b101c42b..ed60d944f5ff 100644 --- a/src/MissionManager/GeoFenceManager.cc +++ b/src/MissionManager/GeoFenceManager.cc @@ -12,7 +12,7 @@ #include "QmlObjectListModel.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(GeoFenceManagerLog, "GeoFenceManagerLog") +QGC_LOGGING_CATEGORY(GeoFenceManagerLog, "PlanManager.GeoFenceManager") GeoFenceManager::GeoFenceManager(Vehicle* vehicle) : PlanManager (vehicle, MAV_MISSION_TYPE_FENCE) @@ -193,5 +193,5 @@ void GeoFenceManager::_planManagerLoadComplete(bool removeAllRequested) bool GeoFenceManager::supported(void) const { - return (_vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_FENCE) && (_vehicle->maxProtoVersion() >= 200); + return _vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_FENCE; } diff --git a/src/MissionManager/GeoFenceManager.h b/src/MissionManager/GeoFenceManager.h index 65cbdcb5871c..eac26ba1d45a 100644 --- a/src/MissionManager/GeoFenceManager.h +++ b/src/MissionManager/GeoFenceManager.h @@ -27,11 +27,11 @@ Q_DECLARE_LOGGING_CATEGORY(GeoFenceManagerLog) class GeoFenceManager : public PlanManager { Q_OBJECT - + public: GeoFenceManager(Vehicle* vehicle); ~GeoFenceManager(); - + bool supported(void) const; /// Signals sendComplete when done @@ -60,7 +60,7 @@ class GeoFenceManager : public PlanManager BadPolygonItemFormat, ///< Error re-creating polygons from mission items InvalidCircleRadius, } ErrorCode_t; - + signals: void loadComplete (void); void inProgressChanged (bool inProgress); diff --git a/src/MissionManager/LandingComplexItem.cc b/src/MissionManager/LandingComplexItem.cc index 1764059157cf..287294e0ea8a 100644 --- a/src/MissionManager/LandingComplexItem.cc +++ b/src/MissionManager/LandingComplexItem.cc @@ -22,7 +22,7 @@ #include "Vehicle.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(LandingComplexItemLog, "LandingComplexItemLog") +QGC_LOGGING_CATEGORY(LandingComplexItemLog, "Plan.LandingComplexItem") LandingComplexItem::LandingComplexItem(PlanMasterController* masterController, bool flyView) : ComplexMissionItem (masterController, flyView) @@ -643,6 +643,19 @@ void LandingComplexItem::_setDirty(void) setDirty(true); } +void LandingComplexItem::setCoordinate(const QGeoCoordinate& coordinate) { + if (!_landingCoordSet) { + setLandingCoordinate(coordinate); + return; + } + + // Move entire complex item, preserving heading and distance + _ignoreRecalcSignals = true; + setLandingCoordinate(coordinate); + _ignoreRecalcSignals = false; + _recalcFromHeadingAndDistanceChange(); +} + void LandingComplexItem::setSequenceNumber(int sequenceNumber) { if (_sequenceNumber != sequenceNumber) { diff --git a/src/MissionManager/LandingComplexItem.h b/src/MissionManager/LandingComplexItem.h index 0cc10b27a286..e50dadd8fa33 100644 --- a/src/MissionManager/LandingComplexItem.h +++ b/src/MissionManager/LandingComplexItem.h @@ -110,7 +110,7 @@ class LandingComplexItem : public ComplexMissionItem ReadyForSaveState readyForSaveState (void) const final; bool exitCoordinateSameAsEntry (void) const final { return false; } void setDirty (bool dirty) final; - void setCoordinate (const QGeoCoordinate& coordinate) final { setFinalApproachCoordinate(coordinate); } + void setCoordinate (const QGeoCoordinate& coordinate) final; void setSequenceNumber (int sequenceNumber) final; double amslEntryAlt (void) const final; double amslExitAlt (void) const final; diff --git a/src/MissionManager/MavCmdInfoCommon.json b/src/MissionManager/MavCmdInfoCommon.json index a4dd6f45da14..97c00107180d 100644 --- a/src/MissionManager/MavCmdInfoCommon.json +++ b/src/MissionManager/MavCmdInfoCommon.json @@ -300,7 +300,7 @@ "default": 0, "decimalPlaces": 0, "min": 0 - } + } }, { "id": 83, "rawName": "MAV_CMD_NAV_ALTITUDE_WAIT", "friendlyName": "Altitude wait" }, { @@ -1066,7 +1066,7 @@ { "id": 211, "rawName": "MAV_CMD_DO_GRIPPER", - "friendlyName": "Gripper Mechanism", + "friendlyName": "Gripper Action", "description": "Control a gripper mechanism.", "specifiesCoordinate": false, "friendlyEdit": true, @@ -1145,7 +1145,6 @@ { "id": 510, "rawName": "MAV_CMD_GET_MESSAGE_INTERVAL", "friendlyName": "Get message interval" }, { "id": 511, "rawName": "MAV_CMD_SET_MESSAGE_INTERVAL", "friendlyName": "Set message interval" }, { "id": 512, "rawName": "MAV_CMD_REQUEST_MESSAGE" }, - { "id": 519, "rawName": "MAV_CMD_REQUEST_PROTOCOL_VERSION" }, { "id": 520, "rawName": "MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES", "friendlyName": "Get capabilities" }, { "id": 530, diff --git a/src/MissionManager/MissionCommandList.h b/src/MissionManager/MissionCommandList.h index f0a60373916d..f3b39b9959fd 100644 --- a/src/MissionManager/MissionCommandList.h +++ b/src/MissionManager/MissionCommandList.h @@ -21,7 +21,7 @@ class MissionCommandUIInfo; class MissionCommandList : public QObject { Q_OBJECT - + public: /// @param baseCommandList true: bottomost level of mission command hierarchy (partial spec allowed), false: override level of hierarchy MissionCommandList(const QString& jsonFilename, bool baseCommandList, QObject* parent = nullptr); @@ -33,7 +33,7 @@ class MissionCommandList : public QObject MissionCommandUIInfo* getUIInfo(MAV_CMD command) const; const QList& commandIds(void) const { return _ids; } - + static constexpr const char* qgcFileType = "MavCmdInfo"; private: diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index 1867344d9854..ec7700de0505 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -17,7 +17,7 @@ #include -QGC_LOGGING_CATEGORY(MissionCommandTreeLog, "qgc.missionmanager.missioncommandtree"); +QGC_LOGGING_CATEGORY(MissionCommandTreeLog, "Plan.MissionCommandTree"); Q_APPLICATION_STATIC(MissionCommandTree, _missionCommandTreeInstance); diff --git a/src/MissionManager/MissionCommandUIInfo.cc b/src/MissionManager/MissionCommandUIInfo.cc index 80359b4e7a5e..84264cea9826 100644 --- a/src/MissionManager/MissionCommandUIInfo.cc +++ b/src/MissionManager/MissionCommandUIInfo.cc @@ -12,7 +12,7 @@ #include "FactMetaData.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(MissionCommandsLog, "MissionCommandsLog") +QGC_LOGGING_CATEGORY(MissionCommandsLog, "Plan.MissionCommands") MissionCmdParamInfo::MissionCmdParamInfo(QObject* parent) : QObject(parent) diff --git a/src/MissionManager/MissionCommandUIInfo.h b/src/MissionManager/MissionCommandUIInfo.h index 56cc70de88d1..a862ff78255e 100644 --- a/src/MissionManager/MissionCommandUIInfo.h +++ b/src/MissionManager/MissionCommandUIInfo.h @@ -214,9 +214,8 @@ class MissionCommandUIInfo : public QObject { static constexpr const char* _commentJsonKey = "comment"; static constexpr const char* _advancedCategory = "Advanced"; - friend class MissionCommandTree; + friend class MissionCommandTree; #ifdef QGC_UNITTEST_BUILD friend class MissionCommandTreeTest; #endif }; - diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index b8f9e5ca5530..87579c2e5c7d 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -38,7 +38,7 @@ #define UPDATE_TIMEOUT 5000 ///< How often we check for bounding box changes -QGC_LOGGING_CATEGORY(MissionControllerLog, "MissionControllerLog") +QGC_LOGGING_CATEGORY(MissionControllerLog, "PlanManager.MissionController") MissionController::MissionController(PlanMasterController* masterController, QObject *parent) : PlanElementController (masterController, parent) @@ -195,7 +195,7 @@ void MissionController::_newMissionItemsAvailableFromVehicle(bool removeAllReque _visualItems = newControllerMissionItems; _settingsItem = settingsItem; - // We set Altitude mode to mixed, otherwise if we need a non relative altitude frame we won't be able to change it + // We set Altitude mode to mixed, otherwise if we need a non relative altitude frame we won't be able to change it setGlobalAltitudeMode(QGroundControlQmlGlobal::AltitudeModeMixed); MissionController::_scanForAdditionalSettings(_visualItems, _masterController); @@ -966,8 +966,7 @@ bool MissionController::_loadTextMissionFile(QTextStream& stream, QmlObjectListM } else { if (TakeoffMissionItem::isTakeoffCommand(static_cast(item->command()))) { // This needs to be a TakeoffMissionItem - TakeoffMissionItem* takeoffItem = new TakeoffMissionItem(_masterController, _flyView, settingsItem, true /* forLoad */); - takeoffItem->load(stream); + TakeoffMissionItem* takeoffItem = new TakeoffMissionItem(item->missionItem(), _masterController, _flyView, settingsItem, false /* forLoad */); item->deleteLater(); item = takeoffItem; } diff --git a/src/MissionManager/MissionItem.cc b/src/MissionManager/MissionItem.cc index 7cae70a93c9e..83ad6c6d4d83 100644 --- a/src/MissionManager/MissionItem.cc +++ b/src/MissionManager/MissionItem.cc @@ -136,7 +136,7 @@ const MissionItem& MissionItem::operator=(const MissionItem& other) } MissionItem::~MissionItem() -{ +{ } @@ -184,7 +184,7 @@ bool MissionItem::_convertJsonV1ToV2(const QJsonObject& json, QJsonObject& v2Jso if (json.contains(_jsonParamsKey)) { // Already V2 format return true; - } + } QList keyInfoList = { { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, diff --git a/src/MissionManager/MissionItem.h b/src/MissionManager/MissionItem.h index cb8e74b7a541..d887dae4889e 100644 --- a/src/MissionManager/MissionItem.h +++ b/src/MissionManager/MissionItem.h @@ -55,7 +55,7 @@ class MissionItem : public QObject ~MissionItem(); const MissionItem& operator=(const MissionItem& other); - + MAV_CMD command (void) const { return (MAV_CMD)_commandFact.rawValue().toInt(); } bool isCurrentItem (void) const { return _isCurrentItem; } int sequenceNumber (void) const { return _sequenceNumber; } @@ -92,7 +92,7 @@ class MissionItem : public QObject void setParam5 (double param5); void setParam6 (double param6); void setParam7 (double param7); - + void save(QJsonObject& json) const; bool load(QTextStream &loadStream); bool load(const QJsonObject& json, int sequenceNumber, QString& errorString); @@ -142,7 +142,7 @@ private slots: static constexpr const char* _jsonParam2Key = "param2"; static constexpr const char* _jsonParam3Key = "param3"; static constexpr const char* _jsonParam4Key = "param4"; - + // Deprecated V2 format keys static constexpr const char* _jsonCoordinateKey = "coordinate"; diff --git a/src/MissionManager/MissionManager.cc b/src/MissionManager/MissionManager.cc index 0e4e0d0d5bf2..6c660294683b 100644 --- a/src/MissionManager/MissionManager.cc +++ b/src/MissionManager/MissionManager.cc @@ -16,7 +16,7 @@ #include "MissionCommandUIInfo.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog") +QGC_LOGGING_CATEGORY(MissionManagerLog, "PlanManager.MissionManager") MissionManager::MissionManager(Vehicle* vehicle) : PlanManager (vehicle, MAV_MISSION_TYPE_MISSION) @@ -258,14 +258,14 @@ void MissionManager::_updateMissionIndex(int index) } } -void MissionManager::_handleHighLatency(const mavlink_message_t& message) +void MissionManager::_handleHighLatency(const mavlink_message_t& message) { mavlink_high_latency_t highLatency; mavlink_msg_high_latency_decode(&message, &highLatency); _updateMissionIndex(highLatency.wp_num); } -void MissionManager::_handleHighLatency2(const mavlink_message_t& message) +void MissionManager::_handleHighLatency2(const mavlink_message_t& message) { mavlink_high_latency2_t highLatency2; mavlink_msg_high_latency2_decode(&message, &highLatency2); @@ -290,4 +290,3 @@ void MissionManager::_handleHeartbeat(const mavlink_message_t& message) emit lastCurrentIndexChanged(_lastCurrentIndex); } } - diff --git a/src/MissionManager/MissionManager.h b/src/MissionManager/MissionManager.h index 770fdd0812d9..7689660e4a5b 100644 --- a/src/MissionManager/MissionManager.h +++ b/src/MissionManager/MissionManager.h @@ -25,7 +25,7 @@ class MissionManager : public PlanManager public: MissionManager(Vehicle* vehicle); ~MissionManager(); - + /// Current mission item as reported by MISSION_CURRENT int currentIndex(void) const { return _currentMissionIndex; } diff --git a/src/MissionManager/MissionSettingsItem.cc b/src/MissionManager/MissionSettingsItem.cc index c24b8fdce032..8c5d0c2f215b 100644 --- a/src/MissionManager/MissionSettingsItem.cc +++ b/src/MissionManager/MissionSettingsItem.cc @@ -16,7 +16,7 @@ #include -QGC_LOGGING_CATEGORY(MissionSettingsItemLog, "MissionSettingsItemLog") +QGC_LOGGING_CATEGORY(MissionSettingsItemLog, "Plan.MissionSettingsItem") QMap MissionSettingsItem::_metaDataMap; diff --git a/src/MissionManager/PlanCreator.h b/src/MissionManager/PlanCreator.h index 5a9540e69195..712de55394f3 100644 --- a/src/MissionManager/PlanCreator.h +++ b/src/MissionManager/PlanCreator.h @@ -20,7 +20,7 @@ class MissionController; class PlanCreator : public QObject { Q_OBJECT - + public: PlanCreator(PlanMasterController* planMasterController, QString name, QString imageResource, QObject* parent = nullptr); diff --git a/src/MissionManager/PlanElementController.h b/src/MissionManager/PlanElementController.h index b28e15728519..d817162c8bb6 100644 --- a/src/MissionManager/PlanElementController.h +++ b/src/MissionManager/PlanElementController.h @@ -24,7 +24,7 @@ class PlanElementController : public QObject public: PlanElementController(PlanMasterController* masterController, QObject* parent = nullptr); ~PlanElementController(); - + Q_PROPERTY(PlanMasterController* masterController READ masterController CONSTANT) Q_PROPERTY(bool supported READ supported NOTIFY supportedChanged) ///< true: Element is supported by Vehicle Q_PROPERTY(bool containsItems READ containsItems NOTIFY containsItemsChanged) ///< true: Elemement is non-empty diff --git a/src/MissionManager/PlanManager.cc b/src/MissionManager/PlanManager.cc index f433dbd415f9..b98eca85f1da 100644 --- a/src/MissionManager/PlanManager.cc +++ b/src/MissionManager/PlanManager.cc @@ -15,7 +15,7 @@ #include "MissionCommandTree.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(PlanManagerLog, "PlanManagerLog") +QGC_LOGGING_CATEGORY(PlanManagerLog, "PlanManager.PlanManager") PlanManager::PlanManager(Vehicle* vehicle, MAV_MISSION_TYPE planType) : QObject (vehicle) diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index 92bd3d1e2f25..5c513d621d53 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -29,7 +29,7 @@ #include #include -QGC_LOGGING_CATEGORY(PlanMasterControllerLog, "PlanMasterControllerLog") +QGC_LOGGING_CATEGORY(PlanMasterControllerLog, "PlanManager.PlanMasterController") PlanMasterController::PlanMasterController(QObject* parent) : QObject (parent) @@ -615,7 +615,7 @@ void PlanMasterController::_updateOverallDirty(void) if(_previousOverallDirty != dirty()){ _previousOverallDirty = dirty(); emit dirtyChanged(_previousOverallDirty); - } + } } void PlanMasterController::_updatePlanCreatorsList(void) diff --git a/src/MissionManager/PlanMasterController.h b/src/MissionManager/PlanMasterController.h index d12646507866..15c437ec2fae 100644 --- a/src/MissionManager/PlanMasterController.h +++ b/src/MissionManager/PlanMasterController.h @@ -30,7 +30,7 @@ class PlanMasterController : public QObject QML_ELEMENT Q_MOC_INCLUDE("QmlObjectListModel.h") Q_MOC_INCLUDE("Vehicle.h") - + public: PlanMasterController(QObject* parent = nullptr); #ifdef QT_DEBUG diff --git a/src/MissionManager/RallyPoint.cc b/src/MissionManager/RallyPoint.cc index 337f1b3f89ce..939d1fb13efe 100644 --- a/src/MissionManager/RallyPoint.cc +++ b/src/MissionManager/RallyPoint.cc @@ -49,7 +49,7 @@ const RallyPoint& RallyPoint::operator=(const RallyPoint& other) } RallyPoint::~RallyPoint() -{ +{ } diff --git a/src/MissionManager/RallyPoint.h b/src/MissionManager/RallyPoint.h index 6553c2dfed95..bedbe49c4a08 100644 --- a/src/MissionManager/RallyPoint.h +++ b/src/MissionManager/RallyPoint.h @@ -19,7 +19,7 @@ class RallyPoint : public QObject { Q_OBJECT - + public: RallyPoint(const QGeoCoordinate& coordinate, QObject* parent = nullptr); RallyPoint(const RallyPoint& other, QObject* parent = nullptr); @@ -27,7 +27,7 @@ class RallyPoint : public QObject ~RallyPoint(); const RallyPoint& operator=(const RallyPoint& other); - + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) Q_PROPERTY(QVariantList textFieldFacts MEMBER _textFieldFacts CONSTANT) diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index e2d4bcd956f8..614d263647b2 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -20,7 +20,7 @@ #include -QGC_LOGGING_CATEGORY(RallyPointControllerLog, "RallyPointControllerLog") +QGC_LOGGING_CATEGORY(RallyPointControllerLog, "PlanManager.RallyPointController") RallyPointController::RallyPointController(PlanMasterController* masterController, QObject* parent) : PlanElementController (masterController, parent) @@ -66,17 +66,11 @@ void RallyPointController::_managerVehicleChanged(Vehicle* managerVehicle) connect(_rallyPointManager, &RallyPointManager::removeAllComplete, this, &RallyPointController::_managerRemoveAllComplete); connect(_rallyPointManager, &RallyPointManager::inProgressChanged, this, &RallyPointController::syncInProgressChanged); - //-- RallyPointController::supported() tests both the capability bit AND the protocol version. (void) connect(_managerVehicle, &Vehicle::capabilityBitsChanged, this, [this](uint64_t capabilityBits) { Q_UNUSED(capabilityBits); emit supportedChanged(supported()); }); - (void) connect(_managerVehicle, &Vehicle::requestProtocolVersion, this, [this](unsigned version) { - Q_UNUSED(version); - emit supportedChanged(supported()); - }); - emit supportedChanged(supported()); } @@ -261,7 +255,7 @@ void RallyPointController::addPoint(QGeoCoordinate point) bool RallyPointController::supported(void) const { - return (_managerVehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_RALLY) && (_managerVehicle->maxProtoVersion() >= 200); + return _managerVehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_RALLY; } void RallyPointController::removePoint(QObject* rallyPoint) diff --git a/src/MissionManager/RallyPointController.h b/src/MissionManager/RallyPointController.h index 35e51b7c1f5d..e210d6a1ff08 100644 --- a/src/MissionManager/RallyPointController.h +++ b/src/MissionManager/RallyPointController.h @@ -30,7 +30,7 @@ class RallyPointController : public PlanElementController public: explicit RallyPointController(PlanMasterController* masterController, QObject* parent = nullptr); ~RallyPointController(); - + Q_PROPERTY(QmlObjectListModel* points READ points CONSTANT) Q_PROPERTY(QString editorQml READ editorQml CONSTANT) Q_PROPERTY(QObject* currentRallyPoint READ currentRallyPoint WRITE setCurrentRallyPoint NOTIFY currentRallyPointChanged) diff --git a/src/MissionManager/RallyPointManager.cc b/src/MissionManager/RallyPointManager.cc index 514f610b600c..2e975be6f06d 100644 --- a/src/MissionManager/RallyPointManager.cc +++ b/src/MissionManager/RallyPointManager.cc @@ -11,7 +11,7 @@ #include "Vehicle.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(RallyPointManagerLog, "RallyPointManagerLog") +QGC_LOGGING_CATEGORY(RallyPointManagerLog, "PlanManager.RallyPointManager") RallyPointManager::RallyPointManager(Vehicle* vehicle) : PlanManager(vehicle, MAV_MISSION_TYPE_RALLY) @@ -71,7 +71,7 @@ void RallyPointManager::removeAll(void) bool RallyPointManager::supported(void) const { - return (_vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_RALLY) && (_vehicle->maxProtoVersion() >= 200); + return _vehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_RALLY; } void RallyPointManager::_planManagerLoadComplete(bool removeAllRequested) diff --git a/src/MissionManager/RallyPointManager.h b/src/MissionManager/RallyPointManager.h index 7990176cfaa3..78bc589e2562 100644 --- a/src/MissionManager/RallyPointManager.h +++ b/src/MissionManager/RallyPointManager.h @@ -24,11 +24,11 @@ Q_DECLARE_LOGGING_CATEGORY(RallyPointManagerLog) class RallyPointManager : public PlanManager { Q_OBJECT - + public: RallyPointManager(Vehicle* vehicle); ~RallyPointManager(); - + bool supported (void) const; void sendToVehicle (const QList& rgPoints); void removeAll (void); @@ -42,7 +42,7 @@ class RallyPointManager : public PlanManager TooManyPoints, ///< Too many points for valid geofence InvalidCircleRadius, } ErrorCode_t; - + signals: void loadComplete (void); void inProgressChanged (bool inProgress); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index a694a0e064f7..f53359abaafa 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -153,7 +153,7 @@ void SimpleMissionItem::_connectSignals(void) // For NAV_LOITER_X commands, they must emit a radiusChanged signal connect(&_missionItem._param2Fact, &Fact::valueChanged, this, &SimpleMissionItem::_possibleRadiusChanged); connect(&_missionItem._param3Fact, &Fact::valueChanged, this, &SimpleMissionItem::_possibleRadiusChanged); - + // Exit coordinate is the same as entrance coordinate connect(this, &SimpleMissionItem::coordinateChanged, this, &SimpleMissionItem::exitCoordinateChanged); @@ -245,7 +245,7 @@ void SimpleMissionItem::_setupMetaData(void) } SimpleMissionItem::~SimpleMissionItem() -{ +{ } void SimpleMissionItem::save(QJsonArray& missionItems) @@ -406,7 +406,7 @@ QString SimpleMissionItem::abbreviation() const void SimpleMissionItem::_rebuildTextFieldFacts(void) { _textFieldFacts.clear(); - + if (rawEdit()) { _missionItem._param1Fact.setName("Param1"); _missionItem._param1Fact.setMetaData(_defaultParamMetaData); diff --git a/src/MissionManager/SimpleMissionItem.h b/src/MissionManager/SimpleMissionItem.h index 5f281b356c48..f33b9b20dfab 100644 --- a/src/MissionManager/SimpleMissionItem.h +++ b/src/MissionManager/SimpleMissionItem.h @@ -64,7 +64,7 @@ class SimpleMissionItem : public VisualMissionItem bool scanForSections(QmlObjectListModel* visualItems, int scanIndex, PlanMasterController* masterController); // Property accesors - + QString category (void) const; int command (void) const { return _missionItem._commandFact.cookedValue().toInt(); } MAV_CMD mavCommand (void) const { return static_cast(command()); } @@ -87,7 +87,7 @@ class SimpleMissionItem : public VisualMissionItem void setRawEdit(bool rawEdit); void setAltitudeMode(QGroundControlQmlGlobal::AltMode altitudeMode); - + void setCommandByIndex(int index); void setCommand(int command); @@ -193,7 +193,7 @@ private slots: QmlObjectListModel _textFieldFacts; QmlObjectListModel _nanFacts; QmlObjectListModel _comboboxFacts; - + static FactMetaData* _altitudeMetaData; static FactMetaData* _commandMetaData; static FactMetaData* _defaultParamMetaData; diff --git a/src/MissionManager/StructureScanComplexItem.cc b/src/MissionManager/StructureScanComplexItem.cc index d3e1b1c97f3a..288e847520eb 100644 --- a/src/MissionManager/StructureScanComplexItem.cc +++ b/src/MissionManager/StructureScanComplexItem.cc @@ -20,7 +20,7 @@ #include -QGC_LOGGING_CATEGORY(StructureScanComplexItemLog, "StructureScanComplexItemLog") +QGC_LOGGING_CATEGORY(StructureScanComplexItemLog, "Plan.StructureScanComplexItem") const QString StructureScanComplexItem::name(StructureScanComplexItem::tr("Structure Scan")); @@ -29,7 +29,7 @@ StructureScanComplexItem::StructureScanComplexItem(PlanMasterController* masterC , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/StructureScan.SettingsGroup.json"), this /* QObject parent */)) , _sequenceNumber (0) , _entryVertex (0) - , _ignoreRecalc (false) +, _ignoreRecalc (false) , _scanDistance (0.0) , _cameraShots (0) , _cameraCalc (masterController, settingsGroup) @@ -754,4 +754,3 @@ void StructureScanComplexItem::_segmentTerrainCollisionChanged(bool terrainColli ComplexMissionItem::_segmentTerrainCollisionChanged(terrainCollision); _structurePolygon.setShowAltColor(_cTerrainCollisionSegments != 0); } - diff --git a/src/MissionManager/StructureScanPlanCreator.h b/src/MissionManager/StructureScanPlanCreator.h index 8c2abd2d5795..00bb5af48ffb 100644 --- a/src/MissionManager/StructureScanPlanCreator.h +++ b/src/MissionManager/StructureScanPlanCreator.h @@ -14,7 +14,7 @@ class StructureScanPlanCreator : public PlanCreator { Q_OBJECT - + public: StructureScanPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index ca113b69e102..43416165bed7 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -24,7 +24,7 @@ #include #include -QGC_LOGGING_CATEGORY(SurveyComplexItemLog, "SurveyComplexItemLog") +QGC_LOGGING_CATEGORY(SurveyComplexItemLog, "Plan.SurveyComplexItem") const QString SurveyComplexItem::name(SurveyComplexItem::tr("Survey")); @@ -674,11 +674,11 @@ void SurveyComplexItem::_rebuildTransectsPhase1WorkerSinglePolygon(bool refly) double gridAngle = _gridAngleFact.rawValue().toDouble(); double gridSpacing = _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); - if (gridSpacing < 0.5) { - // We can't let gridSpacing get too small otherwise we will end up with too many transects. - // So we limit to 0.5 meter spacing as min and set to huge value which will cause a single - // transect to be added. - gridSpacing = 100000; + if (gridSpacing < _minimumTransectSpacingMeters) { + // We can't let spacing get too small otherwise we will end up with too many transects. + // So we limit the spacing to be above a small increment and below that value we set to huge spacing + // which will cause a single transect to be added instead of having things blow up. + gridSpacing = _forceLargeTransectSpacingMeters; } gridAngle = _clampGridAngle90(gridAngle); diff --git a/src/MissionManager/SurveyPlanCreator.h b/src/MissionManager/SurveyPlanCreator.h index 6ace13330d5c..8bc2dd9040ff 100644 --- a/src/MissionManager/SurveyPlanCreator.h +++ b/src/MissionManager/SurveyPlanCreator.h @@ -14,7 +14,7 @@ class SurveyPlanCreator : public PlanCreator { Q_OBJECT - + public: SurveyPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); diff --git a/src/MissionManager/TakeoffMissionItem.h b/src/MissionManager/TakeoffMissionItem.h index ca8af72838d2..06e8bb67c58b 100644 --- a/src/MissionManager/TakeoffMissionItem.h +++ b/src/MissionManager/TakeoffMissionItem.h @@ -19,7 +19,7 @@ class MissionSettingsItem; class TakeoffMissionItem : public SimpleMissionItem { Q_OBJECT - + public: // Note: forLoad = true indicates that TakeoffMissionItem::load will be called onthe item TakeoffMissionItem(PlanMasterController* masterController, bool flyView, MissionSettingsItem* settingsItem, bool forLoad); diff --git a/src/MissionManager/TransectStyleComplexItem.cc b/src/MissionManager/TransectStyleComplexItem.cc index bd7759938e6c..188c941f9e28 100644 --- a/src/MissionManager/TransectStyleComplexItem.cc +++ b/src/MissionManager/TransectStyleComplexItem.cc @@ -23,7 +23,7 @@ #include -QGC_LOGGING_CATEGORY(TransectStyleComplexItemLog, "TransectStyleComplexItemLog") +QGC_LOGGING_CATEGORY(TransectStyleComplexItemLog, "Plan.TransectStyleComplexItem") TransectStyleComplexItem::TransectStyleComplexItem(PlanMasterController* masterController, bool flyView, QString settingsGroup) : ComplexMissionItem (masterController, flyView) diff --git a/src/MissionManager/TransectStyleComplexItem.h b/src/MissionManager/TransectStyleComplexItem.h index ede0c2905541..10292b8b71fa 100644 --- a/src/MissionManager/TransectStyleComplexItem.h +++ b/src/MissionManager/TransectStyleComplexItem.h @@ -211,6 +211,8 @@ protected slots: static constexpr int _terrainQueryTimeoutMsecs= 1000; static constexpr int _hoverAndCaptureDelaySeconds = 4; + static constexpr double _minimumTransectSpacingMeters = 0.3; + static constexpr double _forceLargeTransectSpacingMeters = 100000; private slots: void _reallyQueryTransectsPathHeightInfo (void); @@ -245,5 +247,5 @@ private slots: QTimer _terrainPolyPathQueryTimer; // Deprecated json keys - static constexpr const char* _jsonTerrainFollowKeyDeprecated = "FollowTerrain"; + static constexpr const char* _jsonTerrainFollowKeyDeprecated = "FollowTerrain"; }; diff --git a/src/MissionManager/VTOLLandingComplexItem.cc b/src/MissionManager/VTOLLandingComplexItem.cc index 0b8ccc71087b..5e5017680a27 100644 --- a/src/MissionManager/VTOLLandingComplexItem.cc +++ b/src/MissionManager/VTOLLandingComplexItem.cc @@ -20,7 +20,7 @@ #include -QGC_LOGGING_CATEGORY(VTOLLandingComplexItemLog, "VTOLLandingComplexItemLog") +QGC_LOGGING_CATEGORY(VTOLLandingComplexItemLog, "Plan.VTOLLandingComplexItem") const QString VTOLLandingComplexItem::name(VTOLLandingComplexItem::tr("VTOL Landing")); diff --git a/src/MissionManager/VisualMissionItem.cc b/src/MissionManager/VisualMissionItem.cc index 2ce54df49823..a3d28e557899 100644 --- a/src/MissionManager/VisualMissionItem.cc +++ b/src/MissionManager/VisualMissionItem.cc @@ -69,7 +69,7 @@ const VisualMissionItem& VisualMissionItem::operator=(const VisualMissionItem& o } VisualMissionItem::~VisualMissionItem() -{ +{ } void VisualMissionItem::setIsCurrentItem(bool isCurrentItem) diff --git a/src/PositionManager/CMakeLists.txt b/src/PositionManager/CMakeLists.txt index f91c277a963d..84eddc713c26 100644 --- a/src/PositionManager/CMakeLists.txt +++ b/src/PositionManager/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# Position Manager Module +# Manages GPS/GNSS position sources and simulated position data +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE PositionManager.cpp diff --git a/src/PositionManager/PositionManager.cpp b/src/PositionManager/PositionManager.cpp index 0713eade4f11..e02b2dd7bd2a 100644 --- a/src/PositionManager/PositionManager.cpp +++ b/src/PositionManager/PositionManager.cpp @@ -20,7 +20,7 @@ #include #include -QGC_LOGGING_CATEGORY(QGCPositionManagerLog, "qgc.positionmanager.positionmanager") +QGC_LOGGING_CATEGORY(QGCPositionManagerLog, "PositionManager.QGCPositionManager") Q_APPLICATION_STATIC(QGCPositionManager, _positionManager); diff --git a/src/PositionManager/SimulatedPosition.cc b/src/PositionManager/SimulatedPosition.cc index 721f2d2aadd0..4741791d77f7 100644 --- a/src/PositionManager/SimulatedPosition.cc +++ b/src/PositionManager/SimulatedPosition.cc @@ -16,7 +16,7 @@ #include #include -QGC_LOGGING_CATEGORY(SimulatedPositionLog, "qgc.positionmanager.simulatedposition") +QGC_LOGGING_CATEGORY(SimulatedPositionLog, "PositionManager.SimulatedPosition") SimulatedPosition::SimulatedPosition(QObject* parent) : QGeoPositionInfoSource(parent) diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 07fd6f5d4ac8..2833583fbaeb 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -22,12 +22,12 @@ #include #include #include +#include #include #include "QGCLogging.h" #include "AudioOutput.h" -#include "CmdLineOptParser.h" #include "FollowMe.h" #include "JoystickManager.h" #include "JsonHelper.h" @@ -36,11 +36,13 @@ #include "MultiVehicleManager.h" #include "ParameterManager.h" #include "PositionManager.h" +#include "QGCCommandLineParser.h" #include "QGCCorePlugin.h" #include "QGCFileDownload.h" #include "QGCImageProvider.h" #include "QGCLoggingCategory.h" #include "SettingsManager.h" +#include "MavlinkSettings.h" #include "AppSettings.h" #include "UDPLink.h" #include "Vehicle.h" @@ -51,34 +53,24 @@ #include "SerialLink.h" #endif -QGC_LOGGING_CATEGORY(QGCApplicationLog, "qgc.qgcapplication") +QGC_LOGGING_CATEGORY(QGCApplicationLog, "API.QGCApplication") -QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool simpleBootTest) +QGCApplication::QGCApplication(int &argc, char *argv[], const QGCCommandLineParser::CommandLineParseResult &cli) : QApplication(argc, argv) - , _runningUnitTests(unitTesting) - , _simpleBootTest(simpleBootTest) + , _runningUnitTests(cli.runningUnitTests) + , _simpleBootTest(cli.simpleBootTest) + , _fakeMobile(cli.fakeMobile) + , _logOutput(cli.logOutput) + , _systemId(cli.systemId.value_or(0)) { _msecsElapsedTime.start(); // Setup for network proxy support QNetworkProxyFactory::setUseSystemConfiguration(true); - // Parse command line options - bool fClearSettingsOptions = false; // Clear stored settings - bool fClearCache = false; // Clear parameter/airframe caches - bool logging = false; // Turn on logging - QString loggingOptions; - - CmdLineOpt_t rgCmdLineOptions[] = { - { "--clear-settings", &fClearSettingsOptions, nullptr }, - { "--clear-cache", &fClearCache, nullptr }, - { "--logging", &logging, &loggingOptions }, - { "--fake-mobile", &_fakeMobile, nullptr }, - { "--log-output", &_logOutput, nullptr }, - // Add additional command line option flags here - }; - - ParseCmdLineOptions(argc, argv, rgCmdLineOptions, std::size(rgCmdLineOptions), false); + bool fClearSettingsOptions = cli.clearSettingsOptions; // Clear stored settings + const bool fClearCache = cli.clearCache; // Clear parameter/airframe caches + const QString loggingOptions = cli.loggingOptions.value_or(QString("")); // Set up timer for delayed missing fact display _missingParamsDelayedDisplayTimer.setSingleShot(true); @@ -87,7 +79,7 @@ QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool s // Set application information QString applicationName; - if (_runningUnitTests || simpleBootTest) { + if (_runningUnitTests || _simpleBootTest) { // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app // name. Also we want to run unit tests with clean settings every time. applicationName = QStringLiteral("%1_unittest").arg(QGC_APP_NAME); @@ -117,7 +109,7 @@ QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool s // The setting will delete all settings on this boot fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey); - if (_runningUnitTests || simpleBootTest) { + if (_runningUnitTests || _simpleBootTest) { // Unit tests run with clean settings fClearSettingsOptions = true; } @@ -152,11 +144,14 @@ QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting, bool s } // Set up our logging filters - QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions); + QGCLoggingCategoryManager::instance()->setFilterRulesFromSettings(loggingOptions); // We need to set language as early as possible prior to loading on JSON files. setLanguage(); + // Force old SVG Tiny 1.2 behavior for compatibility + QSvgRenderer::setDefaultOptions(QtSvg::Tiny12FeaturesOnly); + #ifndef QGC_DAILY_BUILD _checkForNewVersion(); #endif @@ -219,13 +214,17 @@ QGCApplication::~QGCApplication() void QGCApplication::init() { SettingsManager::instance()->init(); + if (_systemId > 0) { + qCDebug(QGCApplicationLog) << "Setting MAVLink System ID to:" << _systemId; + SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->setRawValue(_systemId); + } // Although this should really be in _initForNormalAppBoot putting it here allowws us to create unit tests which pop up more easily - if(QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) { + if (QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) { qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans font"; } - if(QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) { + if (QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) { qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans-demibold font"; } @@ -410,7 +409,8 @@ void QGCApplication::showAppMessage(const QString &message, const QString &title QMetaObject::invokeMethod(rootQmlObject, "_showMessageDialog", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, dialogTitle), Q_ARG(QVariant, varMessage)); } else if (runningUnitTests()) { // Unit tests can run without UI - qCDebug(QGCApplicationLog) << "QGCApplication::showAppMessage unittest title:message" << dialogTitle << message; + // We don't use a logging category to make it easier to debug unit tests + qDebug() << "QGCApplication::showAppMessage unittest title:message" << dialogTitle << message; } else { // UI isn't ready yet _delayedAppMessages.append(QPair(dialogTitle, message)); diff --git a/src/QGCApplication.h b/src/QGCApplication.h index e4cd6ddf0b33..a656d745e37a 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -9,14 +9,18 @@ #pragma once +#include #include #include #include #include #include - #include +namespace QGCCommandLineParser { + struct CommandLineParseResult; +} + class QQmlApplicationEngine; class QQuickWindow; class QGCImageProvider; @@ -38,6 +42,8 @@ class QMetaObject; #define qgcApp() qApp +Q_DECLARE_LOGGING_CATEGORY(QGCApplicationLog) + /// The main application and management class. /// Needs QApplication base to support QtCharts module. /// TODO: Use QtGraphs to convert to QGuiApplication @@ -48,7 +54,7 @@ class QGCApplication : public QApplication /// Unit Test have access to creating and destroying singletons friend class UnitTest; public: - QGCApplication(int &argc, char *argv[], bool unitTesting, bool simpleBootTest); + QGCApplication(int &argc, char *argv[], const QGCCommandLineParser::CommandLineParseResult &args); ~QGCApplication(); /// Sets the persistent flag to delete all settings the next time QGroundControl is started. @@ -96,7 +102,7 @@ class QGCApplication : public QApplication QQmlApplicationEngine *qmlAppEngine() const { return _qmlAppEngine; } signals: - void languageChanged(const QLocale locale); + void languageChanged(const QLocale &locale); public slots: void showVehicleConfig(); @@ -129,7 +135,7 @@ private slots: bool compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) final; void _initVideo(); - + /// Initialize the application for normal application boot. Or in other words we are not going to run unit tests. void _initForNormalAppBoot(); @@ -137,14 +143,16 @@ private slots: void _checkForNewVersion(); bool _runningUnitTests = false; - bool _simpleBootTest = false; + bool _simpleBootTest = false; + bool _fakeMobile = false; ///< true: Fake ui into displaying mobile interface + bool _logOutput = false; ///< true: Log Qt debug output to file + quint8 _systemId = 0; ///< MAVLink system ID, 0 means not set + static constexpr int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display QTimer _missingParamsDelayedDisplayTimer; ///< Timer use to delay missing fact display QList> _missingParams; ///< List of missing parameter component id:name QQmlApplicationEngine *_qmlAppEngine = nullptr; - bool _logOutput = false; ///< true: Log Qt debug output to file - bool _fakeMobile = false; ///< true: Fake ui into displaying mobile interface bool _settingsUpgraded = false; ///< true: Settings format has been upgrade to new version int _majorVersion = 0; int _minorVersion = 0; diff --git a/src/QmlControls/AppMessages.qml b/src/QmlControls/AppLogging.qml similarity index 54% rename from src/QmlControls/AppMessages.qml rename to src/QmlControls/AppLogging.qml index 2b21d9c1aea5..a789b562233f 100644 --- a/src/QmlControls/AppMessages.qml +++ b/src/QmlControls/AppLogging.qml @@ -13,15 +13,11 @@ import QtQuick.Dialogs import QtQuick.Layouts import QGroundControl - import QGroundControl.Controls - import QGroundControl.FactControls - - Item { - id: _root + id: root property bool listViewLoadCompleted: false @@ -62,7 +58,7 @@ Item { clip: true model: debugMessageModel delegate: delegateItem - + function scrollToEnd() { if (listViewLoadCompleted) { if (followTail.checked) { @@ -156,96 +152,127 @@ Item { id: filtersDialogComponent QGCPopupDialog { - title: qsTr("Logging categories") + title: qsTr("Logging") buttons: Dialog.Close - property int enabledCategoryCount: 0 + ColumnLayout { + width: maxContentAvailableWidth - function clearAllLogging() { - var logCategories = QGroundControl.loggingCategories() - for (var category of logCategories) { - QGroundControl.setCategoryLoggingOn(category, false) - } - QGroundControl.updateLoggingFilterRules() - categoryRepeater.model = undefined - categoryRepeater.model = QGroundControl.loggingCategories() - enabledCategoryCount = 0 - enabledCategoryRepeater.model = undefined - enabledCategoryRepeater.model = QGroundControl.loggingCategories() - } + SettingsGroupLayout { + heading: qsTr("Search") + Layout.fillWidth: true - function updateLoggingCategory(logCategory, checked, rebuildCategoryList) { - QGroundControl.setCategoryLoggingOn(logCategory, checked) - QGroundControl.updateLoggingFilterRules() - enabledCategoryCount = 0 - enabledCategoryRepeater.model = undefined - enabledCategoryRepeater.model = QGroundControl.loggingCategories() - if (rebuildCategoryList) { - categoryRepeater.model = undefined - categoryRepeater.model = QGroundControl.loggingCategories() - } - } + RowLayout { + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelHeight / 2 - ColumnLayout { - RowLayout { - spacing: ScreenTools.defaultFontPixelHeight / 2 - Layout.alignment: Qt.AlignVCenter - Layout.fillHeight: true - Layout.fillWidth: true + QGCTextField { + Layout.fillWidth: true + id: searchText + text: "" + enabled: true + } - QGCLabel { - text: qsTr("Search:") + QGCButton { + text: qsTr("Clear") + onClicked: searchText.text = "" + } } + } - QGCTextField { - id: searchText - text: "" - Layout.fillWidth: true - enabled: true - } + SettingsGroupLayout { + heading: qsTr("Enabled Categories") + Layout.fillWidth: true + + Flow { + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelHeight / 2 + + Repeater { + model: QGroundControl.flatLoggingCategoriesModel() + + QGCCheckBoxSlider { + Layout.fillWidth: true + Layout.maximumHeight: visible ? implicitHeight : 0 + text: object.fullCategory + visible: object.enabled + checked: object.enabled + onClicked: object.enabled = checked + } + } - QGCButton { - text: qsTr("Clear") - onClicked: searchText.text = "" + QGCButton { + text: qsTr("Disable All") + onClicked: QGroundControl.disableAllLoggingCategories() + } } } - ColumnLayout { - spacing: ScreenTools.defaultFontPixelHeight / 2 + // Shown when not filtered + Flow { + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelHeight / 2 + visible: searchText.text === "" Repeater { - id: enabledCategoryRepeater - model: QGroundControl.loggingCategories() - - QGCCheckBox { - text: modelData - visible: QGroundControl.categoryLoggingOn(modelData) - checked: QGroundControl.categoryLoggingOn(modelData) - onClicked: updateLoggingCategory(modelData, checked, true /* rebuildCategoryList */) - - Component.onCompleted: enabledCategoryCount += checked ? 1 : 0 + model: QGroundControl.treeLoggingCategoriesModel() + + ColumnLayout { + spacing: ScreenTools.defaultFontPixelHeight / 2 + + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { + Layout.preferredWidth: ScreenTools.defaultFontPixelWidth + text: object.expanded ? qsTr("-") : qsTr("+") + horizontalAlignment: Text.AlignLeft + visible: object.children + + QGCMouseArea { + anchors.fill: parent + onClicked: object.expanded = !object.expanded + } + } + + QGCCheckBoxSlider { + Layout.fillWidth: true + text: object.shortCategory + checked: object.enabled + onClicked: object.enabled = checked + } + } + + Repeater { + model: object.expanded ? object.children : undefined + + QGCCheckBoxSlider { + Layout.fillWidth: true + text: " " + object.shortCategory + checked: object.enabled + onClicked: object.enabled = checked + } + } } } - - QGCButton { - text: qsTr("Clear All") - visible: enabledCategoryCount > 0 - onClicked: clearAllLogging() - } } - ColumnLayout { - spacing: ScreenTools.defaultFontPixelHeight / 2 + // Shown when filtered + Flow { + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelHeight / 2 + visible: searchText.text !== "" Repeater { - id: categoryRepeater - model: QGroundControl.loggingCategories() - - QGCCheckBox { - text: modelData - visible: searchText.text ? text.match(`(${searchText.text})`, "i") : true - checked: QGroundControl.categoryLoggingOn(modelData) - onClicked: updateLoggingCategory(modelData, checked, false /* rebuildCategoryList */) + model: QGroundControl.flatLoggingCategoriesModel() + + QGCCheckBoxSlider { + Layout.fillWidth: true + Layout.maximumHeight: visible ? implicitHeight : 0 + text: object.fullCategory + visible: text.match(`(${searchText.text})`, "i") + checked: object.enabled + onClicked: object.enabled = checked } } } @@ -253,4 +280,3 @@ Item { } } } - diff --git a/src/QmlControls/AppSettings.qml b/src/QmlControls/AppSettings.qml index 572a6ebe9eee..aa695912f3d4 100644 --- a/src/QmlControls/AppSettings.qml +++ b/src/QmlControls/AppSettings.qml @@ -43,6 +43,11 @@ Rectangle { } } + // This need to block click event leakage to underlying map. + DeadMouseArea { + anchors.fill: parent + } + QGCPalette { id: qgcPal } Component.onCompleted: { @@ -141,4 +146,3 @@ Rectangle { anchors.bottom: parent.bottom } } - diff --git a/src/QmlControls/BatteryIndicator.qml b/src/QmlControls/BatteryIndicator.qml index 9ce21cb9e06c..e1273936e833 100644 --- a/src/QmlControls/BatteryIndicator.qml +++ b/src/QmlControls/BatteryIndicator.qml @@ -12,10 +12,6 @@ import QtQuick.Layouts import QGroundControl import QGroundControl.Controls - - - - import QGroundControl.FactControls //------------------------------------------------------------------------- @@ -26,8 +22,8 @@ Item { anchors.bottom: parent.bottom width: batteryIndicatorRow.width - property bool showIndicator: true - property bool waitForParameters: false // UI won't show until parameters are ready + property bool showIndicator: _activeVehicle && _activeVehicle.batteries.count > 0 + property bool waitForParameters: true // UI won't show until parameters are ready property Component expandedPageComponent property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle @@ -36,33 +32,169 @@ Item { property bool _showPercentage: _indicatorDisplay.rawValue === 0 property bool _showVoltage: _indicatorDisplay.rawValue === 1 property bool _showBoth: _indicatorDisplay.rawValue === 2 + property int _lowestBatteryId: -1 // -1: show all batteries, otherwise show only battery with this id // Properties to hold the thresholds property int threshold1: _batterySettings.threshold1.rawValue - property int threshold2: _batterySettings.threshold2.rawValue + property int threshold2: _batterySettings.threshold2.rawValue + + function _recalcLowestBatteryIdFromVoltage() { + if (_activeVehicle) { + // If there is only one battery then it is the lowest + if (_activeVehicle.batteries.count === 1) { + _lowestBatteryId = _activeVehicle.batteries.get(0).id.rawValue + return + } + + // If we have valid voltage for all batteries we use that to determine lowest battery + let allHaveVoltage = true + for (var i = 0; i < _activeVehicle.batteries.count; i++) { + let battery = _activeVehicle.batteries.get(i) + if (isNaN(battery.voltage.rawValue)) { + allHaveVoltage = false + break + } + } + if (allHaveVoltage) { + let lowestBattery = _activeVehicle.batteries.get(0) + let lowestBatteryId = lowestBattery.id.rawValue + for (var i = 1; i < _activeVehicle.batteries.count; i++) { + let battery = _activeVehicle.batteries.get(i) + if (battery.voltage.rawValue < lowestBattery.voltage.rawValue) { + lowestBattery = battery + lowestBatteryId = battery.id.rawValue + } + } + _lowestBatteryId = lowestBatteryId + return + } + } + + // Couldn't determine lowest battery, show all + _lowestBatteryId = -1 + } + + function _recalcLowestBatteryIdFromPercentage() { + if (_activeVehicle) { + // If there is only one battery then it is the lowest + if (_activeVehicle.batteries.count === 1) { + _lowestBatteryId = _activeVehicle.batteries.get(0).id.rawValue + return + } + + // If we have valid percentage for all batteries we use that to determine lowest battery + let allHavePercentage = true + for (var i = 0; i < _activeVehicle.batteries.count; i++) { + let battery = _activeVehicle.batteries.get(i) + if (isNaN(battery.percentRemaining.rawValue)) { + allHavePercentage = false + break + } + } + if (allHavePercentage) { + let lowestBattery = _activeVehicle.batteries.get(0) + let lowestBatteryId = lowestBattery.id.rawValue + for (var i = 1; i < _activeVehicle.batteries.count; i++) { + let battery = _activeVehicle.batteries.get(i) + if (battery.percentRemaining.rawValue < lowestBattery.percentRemaining.rawValue) { + lowestBattery = battery + lowestBatteryId = battery.id.rawValue + } + } + _lowestBatteryId = lowestBatteryId + return + } + } + + // Couldn't determine lowest battery, show all + _lowestBatteryId = -1 + } + + function _recalcLowestBatteryIdFromChargeState() { + if (_activeVehicle) { + // If there is only one battery then it is the lowest + if (_activeVehicle.batteries.count === 1) { + _lowestBatteryId = _activeVehicle.batteries.get(0).id.rawValue + return + } + + // If we have valid chargeState for all batteries we use that to determine lowest battery + let allHaveChargeState = true + for (var i = 0; i < _activeVehicle.batteries.count; i++) { + let battery = _activeVehicle.batteries.get(i) + if (battery.chargeState.rawValue === MAVLink.MAV_BATTERY_CHARGE_STATE_UNDEFINED) { + allHaveChargeState = false + break + } + } + if (allHaveChargeState) { + let lowestBattery = _activeVehicle.batteries.get(0) + let lowestBatteryId = lowestBattery.id.rawValue + for (var i = 1; i < _activeVehicle.batteries.count; i++) { + let battery = _activeVehicle.batteries.get(i) + if (battery.chargeState.rawValue > lowestBattery.chargeState.rawValue) { + lowestBattery = battery + lowestBatteryId = battery.id.rawValue + } + } + _lowestBatteryId = lowestBatteryId + return + } + } + + // Couldn't determine lowest battery, show all + _lowestBatteryId = -1 + } + + function _recalcLowestBatteryId() { + if (!_activeVehicle || _activeVehicle.batteries.count === 0) { + _lowestBatteryId = -1 + return + } + if (_batterySettings.valueDisplay.rawValue === 0) { + // User wants percentage display so use that if available + _recalcLowestBatteryIdFromPercentage() + } else if (_batterySettings.valueDisplay.rawValue === 1) { + // User wants voltage display so use that if available + _recalcLowestBatteryIdFromVoltage() + } + // If we still dont have a lowest battery id then try charge state + if (_lowestBatteryId === -1) { + _recalcLowestBatteryIdFromChargeState() + } + } + + Component.onCompleted: _recalcLowestBatteryId() - Row { + Connections { + target: _activeVehicle ? _activeVehicle.batteries : null + function onCountChanged() {_recalcLowestBatteryId() } + } + + QGCPalette { id: qgcPal } + + RowLayout { id: batteryIndicatorRow anchors.top: parent.top anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth / 2 Repeater { model: _activeVehicle ? _activeVehicle.batteries : 0 Loader { - anchors.top: parent.top - anchors.bottom: parent.bottom + Layout.fillHeight: true sourceComponent: batteryVisual + visible: control._lowestBatteryId === -1 || object.id.rawValue === control._lowestBatteryId || !control._batterySettings.consolidateMultipleBatteries.rawValue property var battery: object } } } + MouseArea { anchors.fill: parent - onClicked: { - mainWindow.showIndicatorDrawer(batteryPopup, control) - } + onClicked: mainWindow.showIndicatorDrawer(batteryPopup, control) } Component { @@ -80,19 +212,19 @@ Item { id: batteryVisual Row { - anchors.top: parent.top - anchors.bottom: parent.bottom + Layout.fillHeight: true + spacing: ScreenTools.defaultFontPixelWidth / 4 function getBatteryColor() { switch (battery.chargeState.rawValue) { case MAVLink.MAV_BATTERY_CHARGE_STATE_OK: if (!isNaN(battery.percentRemaining.rawValue)) { if (battery.percentRemaining.rawValue > threshold1) { - return qgcPal.colorGreen + return qgcPal.colorGreen } else if (battery.percentRemaining.rawValue > threshold2) { - return qgcPal.colorYellowGreen + return qgcPal.colorYellowGreen } else { - return qgcPal.colorYellow + return qgcPal.colorYellow } } else { return qgcPal.text @@ -107,7 +239,7 @@ Item { default: return qgcPal.text } - } + } function getBatterySvgSource() { switch (battery.chargeState.rawValue) { @@ -118,8 +250,8 @@ Item { } else if (battery.percentRemaining.rawValue > threshold2) { return "/qmlimages/BatteryYellowGreen.svg" } else { - return "/qmlimages/BatteryYellow.svg" - } + return "/qmlimages/BatteryYellow.svg" + } } case MAVLink.MAV_BATTERY_CHARGE_STATE_LOW: return "/qmlimages/BatteryOrange.svg" // Low with orange svg @@ -158,6 +290,34 @@ Item { return qsTr("n/a") } + Timer { + id: debounceRecalcTimer + interval: 50 + running: false + repeat: false + onTriggered: { + control._recalcLowestBatteryId() + } + } + Connections { + target: battery.percentRemaining + function onRawValueChanged() { + debounceRecalcTimer.restart() + } + } + Connections { + target: battery.voltage + function onRawValueChanged() { + debounceRecalcTimer.restart() + } + } + Connections { + target: battery.chargeState + function onRawValueChanged() { + debounceRecalcTimer.restart() + } + } + QGCColoredImage { anchors.top: parent.top anchors.bottom: parent.bottom @@ -177,7 +337,7 @@ Item { QGCLabel { Layout.alignment: Qt.AlignHCenter verticalAlignment: Text.AlignVCenter - color: qgcPal.text + color: qgcPal.windowTransparentText text: getBatteryPercentageText() font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize visible: _showBoth || _showPercentage @@ -186,7 +346,7 @@ Item { QGCLabel { Layout.alignment: Qt.AlignHCenter font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize - color: qgcPal.text + color: qgcPal.windowTransparentText text: getBatteryVoltageText() visible: _showBoth || _showVoltage } @@ -283,34 +443,40 @@ Item { ColumnLayout { spacing: ScreenTools.defaultFontPixelHeight / 2 + property real batteryIconHeight: ScreenTools.defaultFontPixelWidth * 3 + FactPanelController { id: controller } SettingsGroupLayout { heading: qsTr("Battery Display") Layout.fillWidth: true - LabelledFactComboBox { - id: editModeCheckBox - label: qsTr("Value") - fact: _fact - visible: _fact,visible + FactCheckBoxSlider { + Layout.fillWidth: true + fact: _batterySettings.consolidateMultipleBatteries + text: qsTr("Only show battery with lowest charge") + visible: fact.visible + } - property Fact _fact: QGroundControl.settingsManager.batteryIndicatorSettings.valueDisplay + LabelledFactComboBox { + label: qsTr("Value") + fact: _batterySettings.valueDisplay + visible: fact.visible } ColumnLayout { QGCLabel { text: qsTr("Coloring") } RowLayout { - spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Reduced spacing between elements + spacing: ScreenTools.defaultFontPixelWidth // Battery 100% RowLayout { spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label QGCColoredImage { source: "/qmlimages/BatteryGreen.svg" - width: ScreenTools.defaultFontPixelWidth * 6 - height: width + width: height + height: batteryIconHeight fillMode: Image.PreserveAspectFit color: qgcPal.colorGreen } @@ -322,8 +488,8 @@ Item { spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field QGCColoredImage { source: "/qmlimages/BatteryYellowGreen.svg" - width: ScreenTools.defaultFontPixelWidth * 6 - height: width + width: height + height: batteryIconHeight fillMode: Image.PreserveAspectFit color: qgcPal.colorYellowGreen } @@ -345,8 +511,8 @@ Item { spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field QGCColoredImage { source: "/qmlimages/BatteryYellow.svg" - width: ScreenTools.defaultFontPixelWidth * 6 - height: width + width: height + height: batteryIconHeight fillMode: Image.PreserveAspectFit color: qgcPal.colorYellow } @@ -357,7 +523,7 @@ Item { enabled: fact.visible onEditingFinished: { // Validate and set the new threshold value - _batterySettings.setThreshold2(parseInt(text)); + _batterySettings.setThreshold2(parseInt(text)); } } } @@ -367,8 +533,8 @@ Item { spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label QGCColoredImage { source: "/qmlimages/BatteryOrange.svg" - width: ScreenTools.defaultFontPixelWidth * 6 - height: width + width: height + height: batteryIconHeight fillMode: Image.PreserveAspectFit color: qgcPal.colorOrange } @@ -380,8 +546,8 @@ Item { spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label QGCColoredImage { source: "/qmlimages/BatteryCritical.svg" - width: ScreenTools.defaultFontPixelWidth * 6 - height: width + width: height + height: batteryIconHeight fillMode: Image.PreserveAspectFit color: qgcPal.colorRed } @@ -392,8 +558,8 @@ Item { } Loader { - Layout.fillWidth: true - sourceComponent: expandedPageComponent + Layout.fillWidth: true + source: _activeVehicle.expandedToolbarIndicatorSource("Battery") } SettingsGroupLayout { @@ -408,7 +574,7 @@ Item { mainWindow.showKnownVehicleComponentConfigPage(AutoPilotPlugin.KnownPowerVehicleComponent) mainWindow.closeIndicatorDrawer() } - } + } } } } diff --git a/src/QmlControls/CMakeLists.txt b/src/QmlControls/CMakeLists.txt index 9d40c62ca8a7..c377e7bb5ae4 100644 --- a/src/QmlControls/CMakeLists.txt +++ b/src/QmlControls/CMakeLists.txt @@ -1,3 +1,8 @@ +# ============================================================================ +# QML Controls Module +# Reusable QML UI controls and controllers +# ============================================================================ + target_sources(${CMAKE_PROJECT_NAME} PRIVATE EditPositionDialogController.cc @@ -59,6 +64,9 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +# ---------------------------------------------------------------------------- +# QML Controls Module +# ---------------------------------------------------------------------------- qt_add_library(QGroundControlControlsModule STATIC) set_source_files_properties(ScreenTools.qml PROPERTIES @@ -73,7 +81,7 @@ qt_add_qml_module(QGroundControlControlsModule AltModeDialog.qml AppSettings.qml APMSubMotorDisplay.qml - AppMessages.qml + AppLogging.qml AutotuneUI.qml AxisMonitor.qml ClickableColor.qml @@ -84,7 +92,6 @@ qt_add_qml_module(QGroundControlControlsModule EditPositionDialog.qml ExclusiveGroupItem.qml FactSlider.qml - FactSliderPanel.qml FileButton.qml FlightModeMenu.qml HeightIndicator.qml @@ -121,6 +128,7 @@ qt_add_qml_module(QGroundControlControlsModule QGCColoredImage.qml QGCColumnButton.qml QGCComboBox.qml + QGCDelayButton.qml QGCDynamicObjectManager.qml QGCFileDialog.qml QGCFlickable.qml @@ -167,8 +175,9 @@ qt_add_qml_module(QGroundControlControlsModule VehicleRotationCal.qml VehicleSummaryRow.qml -# Due to the fact t hatset_source_files_properties with QT_RESOURCE_ALIAS doesn't work in cmombination with qt_qdd_add_qml_module on Windows. -# We had to move various qml files to this directory to get things to work. We will revisit this in the 5.1 release. +# NOTE: Due to Windows limitations with set_source_files_properties and qt_add_qml_module, +# various QML files from other modules are temporarily included here. +# This will be revisited in a future release. # src/AnalyzeView/ AnalyzePage.qml @@ -225,6 +234,7 @@ qt_add_qml_module(QGroundControlControlsModule GPSIndicatorPage.qml MainStatusIndicator.qml MainStatusIndicatorOfflinePage.qml + ParameterDownloadProgress.qml PlanViewToolBar.qml RemoteIDIndicatorPage.qml SignalStrength.qml @@ -237,7 +247,9 @@ qt_add_qml_module(QGroundControlControlsModule NO_PLUGIN ) -# Add JSON files +# ---------------------------------------------------------------------------- +# Control Configuration Resources +# ---------------------------------------------------------------------------- file(GLOB_RECURSE JSON_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.json) qt_add_resources(${CMAKE_PROJECT_NAME} json_controls PREFIX "/json" diff --git a/src/QmlControls/DropPanel.qml b/src/QmlControls/DropPanel.qml index bd439e31be64..4379a1d5b3e2 100644 --- a/src/QmlControls/DropPanel.qml +++ b/src/QmlControls/DropPanel.qml @@ -83,7 +83,7 @@ Popup { y: _arrowPointPositionY - _arrowPointWidth width: _arrowPointWidth height: _arrowPointWidth * 2 - + onPaint: { var context = getContext("2d") context.reset() diff --git a/src/QmlControls/EditPositionDialog.qml b/src/QmlControls/EditPositionDialog.qml index ef1fd57eba04..905d7bf2f0e1 100644 --- a/src/QmlControls/EditPositionDialog.qml +++ b/src/QmlControls/EditPositionDialog.qml @@ -47,7 +47,7 @@ QGCPopupDialog { id: coordinateSystemCombo Layout.fillWidth: true label: qsTr("Coordinate System") - model: showSetPositionFromVehicle && globals.activeVehicle ? + model: showSetPositionFromVehicle && globals.activeVehicle ? [ qsTr("Geographic"), qsTr("Universal Transverse Mercator"), qsTr("Military Grid Reference"), qsTr("Vehicle Position") ] : [ qsTr("Geographic"), qsTr("Universal Transverse Mercator"), qsTr("Military Grid Reference") ] } diff --git a/src/QmlControls/EditPositionDialogController.cc b/src/QmlControls/EditPositionDialogController.cc index 44d78de7e0b1..0729122f4c83 100644 --- a/src/QmlControls/EditPositionDialogController.cc +++ b/src/QmlControls/EditPositionDialogController.cc @@ -13,7 +13,7 @@ #include "Vehicle.h" #include "QGCLoggingCategory.h" -QGC_LOGGING_CATEGORY(EditPositionDialogControllerLog, "qgc.qmlcontrols.editpositiondialogcontroller") +QGC_LOGGING_CATEGORY(EditPositionDialogControllerLog, "QMLControls.EditPositionDialogController") QMap EditPositionDialogController::_metaDataMap; @@ -106,4 +106,3 @@ void EditPositionDialogController::setFromVehicle() { setCoordinate(MultiVehicleManager::instance()->activeVehicle()->coordinate()); } - diff --git a/src/QmlControls/FWLandingPatternMapVisual.qml b/src/QmlControls/FWLandingPatternMapVisual.qml index f1d3ff0bd601..a8ccc868f3fd 100644 --- a/src/QmlControls/FWLandingPatternMapVisual.qml +++ b/src/QmlControls/FWLandingPatternMapVisual.qml @@ -246,7 +246,7 @@ Item { itemCoordinate: _missionItem.landingCoordinate visible: _root.interactive - onItemCoordinateChanged: _missionItem.moveLandingPosition(itemCoordinate) + onItemCoordinateChanged: _missionItem.coordinate = itemCoordinate } } diff --git a/src/QmlControls/FactSlider.qml b/src/QmlControls/FactSlider.qml index ab5d24295ad5..7bbd563e0e10 100644 --- a/src/QmlControls/FactSlider.qml +++ b/src/QmlControls/FactSlider.qml @@ -32,7 +32,7 @@ ValueSlider { property Fact _fact: fact ? fact : _nullFact property bool _loadComplete: false - + Component.onCompleted: { _loadComplete = true if (fact && fact.minIsDefaultForType && fact.min == from) { diff --git a/src/QmlControls/FactSliderPanel.qml b/src/QmlControls/FactSliderPanel.qml deleted file mode 100644 index 98d4bf07ec25..000000000000 --- a/src/QmlControls/FactSliderPanel.qml +++ /dev/null @@ -1,168 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2020 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -import QGroundControl - -import QGroundControl.FactControls - -import QGroundControl.Controls - - -Column { - /// ListModel must contains elements which look like this: - /// ListElement { - /// title: "Roll sensitivity" - /// description: "Slide to the left to make roll control faster and more accurate. Slide to the right if roll oscillates or is too twitchy." - /// param: "MC_ROLL_TC" - /// min: 0 - /// max: 100 - /// step: 1 - /// } - property ListModel sliderModel - - property real _margins: ScreenTools.defaultFontPixelHeight - property bool _loadComplete: false - - Component.onCompleted: _loadComplete = true - - FactPanelController { - id: controller - } - - QGCPalette { id: qgcPal; colorGroupEnabled: enabled } - - Column { - id: sliderOuterColumn - anchors.left: parent.left - anchors.right: parent.right - spacing: _margins - - Repeater { - id: sliderRepeater - model: sliderModel - - Rectangle { - id: sliderRect - anchors.left: parent.left - anchors.right: parent.right - height: sliderColumn.y + sliderColumn.height + _margins - color: qgcPal.windowShade - - Column { - id: sliderColumn - anchors.margins: _margins - anchors.left: parent.left - anchors.right: parent.right - anchors.top: sliderRect.top - - QGCLabel { - text: title - font.bold: true - } - Item { - width: 1 - height: _margins - } - - Slider { - anchors.left: parent.left - anchors.right: parent.right - from: min - to: max - stepSize: step - //tickmarksEnabled: true // FIXME_QT6 - value: _fact.value - id: slider - property int handleWidth: 0 - - property Fact _fact: controller.getParameterFact(-1, param) - - onValueChanged: { - if (_loadComplete && enabled) { - _fact.value = value - } - } - - // FIXME-QT6 - Controls 2 doesn't do styling this way - /* - style: SliderStyle { - tickmarks: Repeater { - id: repeater - model: control.stepSize > 0 ? 1 + (control.to - control.from) / control.stepSize : 0 - property int unused: get() - function get() { - slider.handleWidth = styleData.handleWidth - return 0 - } - - Rectangle { - color: Qt.hsla(qgcPal.text.hslHue, qgcPal.text.hslSaturation, qgcPal.text.hslLightness, 0.5) - width: 2 - height: 4 - y: repeater.height - x: styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1)) - } - } - } - */ - } - - Item { // spacing - width: 1 - height: 4 - } - - RowLayout { - anchors.left: parent.left - anchors.right: parent.right - QGCLabel { - id: leftValueLabel - text: slider.from - horizontalAlignment: Text.AlignLeft - } - Item { - QGCLabel { - visible: slider.value != slider.from && slider.value != slider.to - text: Math.round(slider._fact.value*100000)/100000 - x: getX() - function getX() { - var span = slider.to - slider.from - var x = slider.handleWidth / 2 + (slider.value-slider.from)/span * (slider.width-slider.handleWidth) - width / 2 - // avoid overlapping text - var minX = leftValueLabel.x + leftValueLabel.width + _margins/2 - if (x < minX) x = minX - var maxX = rightValueLabel.x - _margins/2 - width - if (x > maxX) x = maxX - return x - } - } - } - QGCLabel { - id: rightValueLabel - text: slider.to - Layout.alignment: Qt.AlignRight - } - } - - QGCLabel { - text: description - anchors.left: parent.left - anchors.right: parent.right - wrapMode: Text.WordWrap - } - } - } - } - } -} diff --git a/src/QmlControls/FactValueGrid.cc b/src/QmlControls/FactValueGrid.cc index d1cbfba7ceed..4d98054ca597 100644 --- a/src/QmlControls/FactValueGrid.cc +++ b/src/QmlControls/FactValueGrid.cc @@ -94,12 +94,12 @@ void FactValueGrid::_activeVehicleChanged(Vehicle* activeVehicle) _initForNewVehicle(activeVehicle); } -FactValueGrid::~FactValueGrid() +FactValueGrid::~FactValueGrid() { _vehicleCardInstanceList.removeAll(this); } -QGCMAVLink::VehicleClass_t FactValueGrid::vehicleClass(void) const +QGCMAVLink::VehicleClass_t FactValueGrid::vehicleClass(void) const { return QGCMAVLink::vehicleClass(currentVehicle()->vehicleType()); } @@ -334,7 +334,7 @@ void FactValueGrid::_resetFromSettings(void) QGCCorePlugin::instance()->factValueGridCreateDefaultSettings(this); } _fontSize = settings.value(_fontSizeKey, DefaultFontSize).value(); - + // Initial setup of empty items int cRows = settings.value(_rowCountKey).toInt(); int cModelLists = settings.beginReadArray(_columnsKey); @@ -347,7 +347,7 @@ void FactValueGrid::_resetFromSettings(void) appendColumn(); } } - + // Fill in the items from settings for (int colIndex=0; colIndex